Compare commits

...

2 Commits

Author SHA1 Message Date
bdd140b94a Search & config file bugfixes. Doc updates. 2022-12-01 17:56:00 -05:00
7df22c80a3 Allow for multiple pages of panes. 2022-05-26 20:58:32 -04:00
10 changed files with 414 additions and 131 deletions

View File

@ -1,5 +1,5 @@
PREFIX = /usr/local PREFIX = /usr/local
CC = clang CC = g++
CCFLAGS = -Wall $(shell pkg-config --cflags sword) CCFLAGS = -Wall $(shell pkg-config --cflags sword)
TARGET = scriptura TARGET = scriptura
INCLUDES = -I/usr/include/sword -I/usr/include/ncursesw INCLUDES = -I/usr/include/sword -I/usr/include/ncursesw

View File

@ -5,8 +5,8 @@ This program was written to scratch a personal itch. I do a lot of daily comput
## Dependencies ## Dependencies
* clang * a C++ compiler and standard libraries installed - the Makefile is currently set for g++ but you could switch it to clang++ if preferred
* libsword and its development libraries (often available in most distributions' standard repos) * libsword and its development libraries (also available in most distributions' standard repos)
* ncurses and its development libraries (any modern Linux version should work) * ncurses and its development libraries (any modern Linux version should work)
* doxygen (optional, if you're a documentation nerd) * doxygen (optional, if you're a documentation nerd)
@ -21,7 +21,7 @@ To install scriptura (into /usr/local by default):
> >
> sudo make install > sudo make install
On first run, scriptura assumes that a King James module is intalled and available. Many modules from CrossWire are [available here](https://www.crosswire.org/ftpmirror/pub/sword/packages/rawzip/) and other frontends have their own repos. To install them from the command line: On first run, scriptura assumes that the King James with Apocrypha module is intalled and available. Many modules from CrossWire are [available here](https://www.crosswire.org/ftpmirror/pub/sword/packages/rawzip/) and other frontends have their own repos. To install them from the command line:
> mkdir -p $HOME/.sword/ > mkdir -p $HOME/.sword/
> >
> cd $HOME/.sword/ > cd $HOME/.sword/
@ -30,7 +30,7 @@ On first run, scriptura assumes that a King James module is intalled and availab
> >
> unzip KJVA.zip > unzip KJVA.zip
A configuration file will be created at $HOME/.config/scriptura.ini. You can see the sample config file in the codebase for available options. A configuration file will be created at $HOME/.config/scriptura.ini. You can see the sample config file in the codebase for available options. Just about everything you can modify from inside the program, with the sole exception of the 'panels' entry under [layout]. This determines if you want the two panes to stack horizontal ("h") or vertical ("v") and must be changed manually.
When the software is running, the '?' key will give you the list of commands available. Open any module you have installed and you can go to or search any text. When the software is running, the '?' key will give you the list of commands available. Open any module you have installed and you can go to or search any text.
@ -48,7 +48,6 @@ Other means of contact [can be found here](https://keyoxide.org/hkp/paladin1%40s
This project is basically a one-man operation. If you'd like to show your support financially, you can contribute via: This project is basically a one-man operation. If you'd like to show your support financially, you can contribute via:
- _Paypal - paladin1_ at _sdf.org_ - _Paypal - paladin1_ at _sdf.org_
- _BTC - bc1q9dfau346z38jth35gkaxacd3fljvfgw6cqcyyv_
## Disclaimer ## Disclaimer

View File

@ -35,7 +35,7 @@ char CLITEXT[] =
char HELPTEXT[] = char HELPTEXT[] =
"Scriptura\n\n" "Scriptura\n\n"
"Key bindings:\n" "Main key bindings:\n"
" ? this help message\n" " ? this help message\n"
" q quit\n\n" " q quit\n\n"
" ESC close floating window\n" " ESC close floating window\n"
@ -46,7 +46,11 @@ char HELPTEXT[] =
" arrow up/dn scroll up/down one line\n" " arrow up/dn scroll up/down one line\n"
" page up/dn scroll up/down one page\n" " page up/dn scroll up/down one page\n"
"\n" "\n"
"Formatting changes\n" "Pages:\n"
" 0-4 go to the numbered page\n"
" p create new page\n"
" d delete page\n"
"Formatting changes:\n"
" (takes effect at next search/go to, and depends on loaded module)\n" " (takes effect at next search/go to, and depends on loaded module)\n"
" f toggle footnotes\n" " f toggle footnotes\n"
" n toggle non-textual words\n" " n toggle non-textual words\n"
@ -56,6 +60,9 @@ char HELPTEXT[] =
"\n(Press any key to return to the main screen)\n" "\n(Press any key to return to the main screen)\n"
"\0"; "\0";
int DEFSEARCH = 1;
int DEFKEY = 0;
void wrapup(int exitval, const char* str) { void wrapup(int exitval, const char* str) {
endwin(); endwin();
if (str != NULL) perror(str); if (str != NULL) perror(str);
@ -97,10 +104,7 @@ int parseConf(sword::SWBuf buf) {
starray stappend(starray arr, const char* newstr) { starray stappend(starray arr, const char* newstr) {
arr.length++; arr.length++;
if (arr.length == 1) { if (arr.length > 1) {
free(arr.strings); // needed due to stinit()'s malloc(0)
arr.strings = (const char**) malloc(sizeof(char*));
} else {
arr.strings = (const char**) arr.strings = (const char**)
realloc(arr.strings, arr.length * sizeof(char*)); realloc(arr.strings, arr.length * sizeof(char*));
} }
@ -113,7 +117,7 @@ starray stappend(starray arr, const char* newstr) {
starray stinit(starray arr) { starray stinit(starray arr) {
arr.length = 0; arr.length = 0;
arr.strings = (const char**) malloc(0); arr.strings = (const char**) malloc(sizeof(char*));
return arr; return arr;
} }

15
free.h
View File

@ -23,6 +23,12 @@
#include <swconfig.h> #include <swconfig.h>
#include <swbuf.h> #include <swbuf.h>
// preprocessor directives - for some compiler-constant expressions
#define PAGELIMIT 5 // the maximum number of pages allowed
#define NUMPANES 2 // the max number of panes allowed -- this value
// as is in other places of the code
//! Structure to make managing arrays easier. Who doesn't like arrays? //! Structure to make managing arrays easier. Who doesn't like arrays?
typedef struct { typedef struct {
int length; //!< number of elements int length; //!< number of elements
@ -34,7 +40,8 @@ typedef struct {
* One is stored by each pane. */ * One is stored by each pane. */
typedef struct { typedef struct {
const char* modname; //!< name of module const char* modname; //!< name of module
const char* searchkey; //!< our search key for the module const char* searchkey; /*!< search key for module - if NULL or empty, the
* struct is considered empty */
int keytype; //!< 0 - versekey, 1 - treekey, 2 - direct int keytype; //!< 0 - versekey, 1 - treekey, 2 - direct
int searchtype; //!< type of search to be performed, if any int searchtype; //!< type of search to be performed, if any
const char* scope; //!< scope of search, if any const char* scope; //!< scope of search, if any
@ -46,6 +53,12 @@ extern char HELPTEXT[];
//! String giving the informative text on the command line, for '-h' //! String giving the informative text on the command line, for '-h'
extern char CLITEXT[]; extern char CLITEXT[];
//! Value of default searchtype for lookups keyed to verse
extern int DEFSEARCH;
//! Value of default keytype for lookups
extern int DEFKEY;
//! Global copy of the program's configuration file settings. //! Global copy of the program's configuration file settings.
extern sword::SWConfig config; extern sword::SWConfig config;

View File

@ -27,25 +27,23 @@
#include "pane.h" #include "pane.h"
// constructor // constructor
pane::pane(int starty, int startx, int lines, int cols, const char* title) { pane::pane(int starty, int startx, int lines, int cols, const char* title,
modkey defmod) {
buflocx = buflocy = 0; buflocx = buflocy = 0;
hasFocus = false; hasFocus = false;
rawtext = NULL; rawtext = NULL;
// set some default search parameters
mod.modname = config["defaults"]["module"];
mod.searchkey = config["defaults"]["searchkey"];
mod.keytype = 0;
mod.searchtype = 0;
mod.scope = config["defaults"]["scope"];
setTitle(title);
// set up the window -- resize will give us some initializing // set up the window -- resize will give us some initializing
setTitle(title);
pad = NULL; pad = NULL;
resize(starty, startx, lines, cols); resize(starty, startx, lines, cols);
redraw(true); redraw(true);
// forms/menus send in a null searchkey
if (defmod.searchkey != NULL) {
setModkey(defmod, title);
}
} }
// handle pane resizing // handle pane resizing
@ -113,7 +111,7 @@ void pane::renderText() {
* char so I think it has to be done this way */ * char so I think it has to be done this way */
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
int converted = mbstowcs(text, rawtext, 1); int converted = mbstowcs(text, rawtext, 1);
if (converted == -1) strncpy(&(rawtext[i-1]), "'", 1); if (converted == -1) strncpy(&(rawtext[i-1]), "'", 2);
} }
mbstowcs(text, rawtext, length - 1); mbstowcs(text, rawtext, length - 1);
@ -437,8 +435,8 @@ int pane::loadMenu(starray opts) {
int pane::printInstructions(int y, const char* inst) { int pane::printInstructions(int y, const char* inst) {
// NOTE -- starting indentation is hardcoded at two characters in // NOTE -- starting indentation is hardcoded at two characters in
int strctr = 0; long unsigned int strctr = 0;
int line = width - 3; long unsigned int line = width - 3;
while (strlen(&inst[strctr]) > line) { while (strlen(&inst[strctr]) > line) {
// set write length to be up to the last space, for word wrapping // set write length to be up to the last space, for word wrapping
while ((inst[line] != ' ') && (line > 0)) line--; while ((inst[line] != ' ') && (line > 0)) line--;
@ -584,7 +582,7 @@ void pane::setModule(const char* newtitle, const char* newmod,
void pane::setKey(const char* newkey) { void pane::setKey(const char* newkey) {
mod.searchkey = newkey; mod.searchkey = newkey;
setSearch(0, "Gen 1:1 - Rev 22:21"); setSearch(DEFSEARCH, "Gen 1:1 - Rev 22:21");
} }
void pane::setSearch(int type, const char* scope) { void pane::setSearch(int type, const char* scope) {
@ -592,6 +590,8 @@ void pane::setSearch(int type, const char* scope) {
mod.scope = scope; mod.scope = scope;
} }
modkey pane::getModkey() { void pane::setModkey(modkey newmod, const char* newtitle) {
return mod; setModule(newtitle, newmod.modname, newmod.keytype);
setKey(newmod.searchkey);
setSearch(newmod.searchtype, newmod.scope);
} }

11
pane.h
View File

@ -74,7 +74,8 @@ class pane {
int startx, //!< the pane's upper left Y coord int startx, //!< the pane's upper left Y coord
int lines, //!< the pane's number of lines int lines, //!< the pane's number of lines
int cols, //!< the pane's number of columns int cols, //!< the pane's number of columns
const char* title //!< starting titlebar text const char* title, //!< starting titlebar text
modkey defmod //!< starting modkey, or NULL if not applicable
); );
//! Resize the window & pad, in the case of terminal resizing. //! Resize the window & pad, in the case of terminal resizing.
@ -160,9 +161,11 @@ class pane {
const char* scope //!< scope to search within, in key format const char* scope //!< scope to search within, in key format
); );
/*! Gets the modkey associated with this pane. /*! Loads an entirely new modkey into the pane. Will call associated
* \returns modkey associated with this pane */ * redrawing functions. */
modkey getModkey(); void setModkey(modkey newmod, //!< new modkey to load in
const char* newtitle //!< new pane title to use
);
}; };

View File

@ -182,6 +182,11 @@ const char* scabbard::getModDescription(int modt, int mod) {
return (modules[modt].modlist)[mod]->getDescription(); return (modules[modt].modlist)[mod]->getDescription();
} }
const char* scabbard::getModDescription(const char* modname) {
sword::SWModule *text = swrd.getModule(modname);
return text->getDescription();
}
int scabbard::getKeyType(int modt) { int scabbard::getKeyType(int modt) {
return modules[modt].keytype; return modules[modt].keytype;
} }
@ -219,8 +224,8 @@ const char* scabbard::search(modkey mod) {
delete(key); delete(key);
delete(clone); delete(clone);
return retval; return retval;
}
} else return directSearch(text, mod); return directSearch(text, mod);
} }
/* really old code in case we need to pull output from diatheke /* really old code in case we need to pull output from diatheke

View File

@ -41,7 +41,8 @@
#include "free.h" #include "free.h"
//! Structure for associating a Sword module type to a list of modules. /*! Structure for associating a Sword module type (Bibles,
* commentaries, etc) to a list of modules. */
typedef struct { typedef struct {
char label[25]; //!< the module type char label[25]; //!< the module type
int keytype; //!< 0 - versekey, 1 - treekey, 2 - direct/alpha int keytype; //!< 0 - versekey, 1 - treekey, 2 - direct/alpha
@ -106,12 +107,17 @@ class scabbard {
int mod //!< from getModDescriptions() int mod //!< from getModDescriptions()
); );
/*! Get the full text name for a module. /*! Get the full text name for a module identified by menu indices.
* \returns the module description */ * \returns the module description */
const char* getModDescription(int modt, //!< from getModClassifications() const char* getModDescription(int modt, //!< from getModClassifications()
int mod //!< from getModDescriptions() int mod //!< from getModDescriptions()
); );
/*! Get the full text name for a module identified by key name.
* \returns the module description */
const char* getModDescription(const char* modname //!< module internal name
);
/*! Determine how a module is keyed/indexed. /*! Determine how a module is keyed/indexed.
* \returns the proper keytype; see the keytype field for modkey */ * \returns the proper keytype; see the keytype field for modkey */
int getKeyType(int modt //!< return value from getModClassifications() int getKeyType(int modt //!< return value from getModClassifications()

View File

@ -31,10 +31,12 @@
#include "pane.h" #include "pane.h"
#include "scabbard.h" #include "scabbard.h"
/* --[ ASSISTS / TOP NAMESPACE ]-- */
/* --[ ASSISTS / GLOBALS ]-- */ // quick array for page names
char* pages[PAGELIMIT][NUMPANES];
int NUMPANES; int currentpage, totalpages;
int halfcols, halflines; int halfcols, halflines;
int floatx, floaty, floatheight, floatwidth; int floatx, floaty, floatheight, floatwidth;
int startx1, starty1, startx2, starty2; int startx1, starty1, startx2, starty2;
@ -42,8 +44,30 @@ int panelength, panewidth;
sword::SWConfig config; 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. //! Refresh every portion of a list of panes we are given.
void wipeit(pane* panes) { void wipeit(pane* panes) {
clear();
foundation();
for (int i = 0; i < NUMPANES; i++) panes[i].redraw(true); for (int i = 0; i < NUMPANES; i++) panes[i].redraw(true);
} }
@ -51,28 +75,50 @@ void wipeit(pane* panes) {
starray showForm(const char* title, starray inputs, const char* secondinst, starray showForm(const char* title, starray inputs, const char* secondinst,
int floaty, int floatx, int floatheight, int floatwidth) { int floaty, int floatx, int floatheight, int floatwidth) {
// pull up the floating form // pull up the floating form
pane form = {floaty, floatx, floatheight, floatwidth, title}; modkey defmod;
defmod.searchkey = NULL;
pane form = {floaty, floatx, floatheight, floatwidth, title, defmod};
starray ret = form.loadForm(inputs, secondinst); starray ret = form.loadForm(inputs, secondinst);
// sanitize input? -- if (ret.length != 0)
return ret; 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. //! Save the config array -- we abstract this out in case this changes.
void saveit(char* configfile) { void saveit() {
/* This is clumsy but we have to use this particular constructor for config.save();
* config() so we can save to disk. The global config object uses a
* different constructor. */
sword::SWConfig newconfig(configfile);
newconfig.augment(config);
newconfig.save();
} }
//! Set various standard screen landmarks. //! Set various standard screen landmarks.
void landmarks() { void landmarks() {
// place background text foundation();
mvprintw(LINES - 1 , COLS - 20, "%s", "? - help q - quit");
refresh();
// split up the screen // split up the screen
halfcols = COLS / 2; halfcols = COLS / 2;
@ -108,7 +154,7 @@ void doresize(pane* p) {
// reset standard screen landmarks // reset standard screen landmarks
landmarks(); landmarks();
// reset and refresh main panes // reset and refresh main panes -- assumes NUMPANES
p[0].resize(starty1, startx1, panelength, panewidth); p[0].resize(starty1, startx1, panelength, panewidth);
p[1].resize(starty2, startx2, panelength, panewidth); p[1].resize(starty2, startx2, panelength, panewidth);
wipeit(p); wipeit(p);
@ -153,28 +199,33 @@ int main(int argc, char** argv) {
printf("Please ensure this directory exists and is writable.\n"); printf("Please ensure this directory exists and is writable.\n");
return -1; return -1;
} }
// set default module and other fields to avoid crashing
fprintf(cnf, "[defaults]\n");
fprintf(cnf, "scope=Gen 1:1 - Rev 22:21\n");
fprintf(cnf, "searchkey=2 Tim 1:7\n");
fprintf(cnf, "module=KJV\n");
fclose(cnf); fclose(cnf);
printf("done.\n"); printf("done.\n");
} }
} }
sword::SWConfig tempconfig(configfile); config.filename = configfile;
config.augment(tempconfig); config.load();
// ensure some needed config settings are in place // ensure some needed config settings are in place
if (! strcmp(config["layout"]["panels"], ""))
config["layout"]["panels"] = "v";
if (! strcmp(config["defaults"]["scope"], "")) if (! strcmp(config["defaults"]["scope"], ""))
config["defaults"]["scope"] = "Gen 1:1 - Rev 22:21"; config["defaults"]["scope"] = "Gen 1:1 - Rev 22:21";
if (! strcmp(config["defaults"]["searchkey"], "")) if (! strcmp(config["defaults"]["searchkey"], ""))
config["defaults"]["searchkey"] = "2 Tim 1:;7"; config["defaults"]["searchkey"] = "2 Tim 1:7";
if (! strcmp(config["defaults"]["module"], "")) if (! strcmp(config["defaults"]["module"], ""))
config["defaults"]["module"] = "KJV"; config["defaults"]["module"] = "KJV";
saveit(configfile); 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() // NOTE -- anything after this point must use our defined exit wrapup()
@ -203,33 +254,71 @@ int main(int argc, char** argv) {
init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(1, COLOR_RED, COLOR_BLACK);
} }
// two main panes // cycle through config file for any saved pages & sanitize
NUMPANES = 2; totalpages = 0;
landmarks(); int linking = 1;
pane* p = (pane*) malloc(sizeof(pane) * NUMPANES); for (int i = 0; i < PAGELIMIT; i++) {
const char* title = "Window"; for (int j = 0; j < NUMPANES; j++) {
if (! p) wrapup(1, "Error allocating memory for panes.\n"); /* sanitize input here --
p[0] = { starty1, startx1, panelength, panewidth, title }; * It's our own config but the user can edit it. If a searchkey doesn't
p[1] = { starty2, startx2, panelength, panewidth, title }; * 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);
pane* focustab; /* with no searchkey, if this is the first pane then stop linking here;
focustab = &(p[0]); * otherwise we can get by with setting the searchkey to the default */
focustab->toggleFocus(); 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"], "")))
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;
/* load up a scabbard & set blank starting data - scabbard args are /* load up a scabbard & set blank starting data - scabbard args are
* sanitized in the constructor */ * sanitized in the constructor */
scabbard scab = {}; scabbard scab = {};
char blank[] = "Open a text to use this window.\0";
// configure panes
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++) { for (int i = 0; i < NUMPANES; i++) {
if (config["defaults"]["module"] && config["defaults"]["searchkey"]) { modkey mod = getModkey(i);
// this isn't technically the title, but it's what we have now
p[i].setTitle(config["defaults"]["module"]); // NOTE -- assumes NUMPANES, for future work
p[i].loadText(scab.getSpan(p[i].getModkey())); int starty, startx;
} else { starty = (i == 0 ? starty1 : starty2);
p[i].loadText(blank); startx = (i == 0 ? startx1 : startx2);
p[i] = { starty, startx, panelength, panewidth,
scab.getModDescription(mod.modname), mod };
} }
}
wipeit(p); pane* focustab;
int infocus = 0;
focustab = &(p[infocus]);
focustab->toggleFocus();
loadPage(p, &scab);
// loop for input // loop for input
int ch; int ch;
@ -239,7 +328,8 @@ int main(int argc, char** argv) {
case '\t': { case '\t': {
// switch pane focus // switch pane focus
focustab->toggleFocus(); focustab->toggleFocus();
focustab = (focustab == &(p[0]) ? &(p[1]) : &(p[0])); infocus = (infocus + 1) % NUMPANES;
focustab = &(p[infocus]);
focustab->toggleFocus(); focustab->toggleFocus();
break; } break; }
@ -263,12 +353,64 @@ int main(int argc, char** argv) {
doresize(p); doresize(p);
break; 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': { case 'f': {
// toggle footnotes // toggle footnotes
int footnotes = ! parseConf(config["markup"]["footnotes"]); int footnotes = ! parseConf(config["markup"]["footnotes"]);
config["markup"]["footnotes"] = footnotes + '0'; config["markup"]["footnotes"] = footnotes + '0';
wipeit(p); saveit();
saveit(configfile);
break; } break; }
case 'g': { case 'g': {
@ -290,8 +432,10 @@ int main(int argc, char** argv) {
if ((ret.length != 0) && (strlen((ret.strings)[0]) > 0)) { if ((ret.length != 0) && (strlen((ret.strings)[0]) > 0)) {
// user entered something that's not a blank string // user entered something that's not a blank string
config[pages[currentpage][infocus]]["searchkey"] = (ret.strings)[0];
focustab->setKey((ret.strings)[0]); focustab->setKey((ret.strings)[0]);
focustab->loadText(scab.getSpan(focustab->getModkey())); focustab->loadText(scab.getSpan(getModkey(infocus)));
saveit();
} }
wipeit(p); wipeit(p);
break; } break; }
@ -300,15 +444,16 @@ int main(int argc, char** argv) {
// toggle non-textual words // toggle non-textual words
int textual = ! parseConf(config["markup"]["nontextual"]); int textual = ! parseConf(config["markup"]["nontextual"]);
config["markup"]["nontextual"] = textual + '0'; config["markup"]["nontextual"] = textual + '0';
wipeit(p); saveit();
saveit(configfile);
break; } break; }
case 'o': { case 'o': {
// load a module into the current pane // load a module into the current pane
const char* typetitle = "Choose a module type"; const char* typetitle = "Choose a module type";
modkey defmod;
defmod.searchkey = NULL;
pane menupane = {floaty, floatx, floatheight, floatwidth, pane menupane = {floaty, floatx, floatheight, floatwidth,
typetitle }; typetitle, defmod };
starray mc = scab.getModClassifications(); starray mc = scab.getModClassifications();
int modclass = menupane.loadMenu(mc); int modclass = menupane.loadMenu(mc);
if (modclass == -1) { if (modclass == -1) {
@ -320,7 +465,7 @@ int main(int argc, char** argv) {
} }
const char* modtitle = "Choose a module"; const char* modtitle = "Choose a module";
menupane = {floaty, floatx, floatheight, floatwidth, modtitle}; menupane = {floaty, floatx, floatheight, floatwidth, modtitle, defmod};
starray mds = scab.getModDescriptions(modclass); starray mds = scab.getModDescriptions(modclass);
int mod = menupane.loadMenu(mds); int mod = menupane.loadMenu(mds);
if (mod == -1) { if (mod == -1) {
@ -331,19 +476,50 @@ int main(int argc, char** argv) {
break; 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), focustab->setModule(scab.getModDescription(modclass, mod),
scab.getModName(modclass, mod), scab.getModName(modclass, mod),
scab.getKeyType(modclass)); scab.getKeyType(modclass));
focustab->loadText(scab.getSpan(focustab->getModkey())); focustab->loadText(scab.getSpan(getModkey(infocus)));
saveit();
wipeit(p); wipeit(p);
break; } 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': { case 'r': {
// toggle redletter // toggle redletter
int redletter = ! parseConf(config["markup"]["redletters"]); int redletter = ! parseConf(config["markup"]["redletters"]);
config["markup"]["redletters"] = redletter + '0'; config["markup"]["redletters"] = redletter + '0';
wipeit(p); saveit();
saveit(configfile);
break; } break; }
case 's': { case 's': {
@ -377,20 +553,36 @@ int main(int argc, char** argv) {
* the search scope (the last item), if any */ * the search scope (the last item), if any */
int i = 0; int i = 0;
for (i = 0; i < ret.length - 1; i++) { for (i = 0; i < ret.length - 1; i++) {
// user entered a scope but no search
//if (i == ret.length - 1) break;
if (strcmp((ret.strings)[i], "") != 0) { 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->setKey((ret.strings)[i]);
focustab->setSearch(i * -1, focustab->setSearch(i * -1, newscope);
(ret.strings)[ret.length-1]);
break; break;
} }
} }
if (i != ret.length - 1) if (i != ret.length - 1) {
focustab->loadText(scab.search(focustab->getModkey())); focustab->loadText(scab.search(getModkey(infocus)));
saveit();
}
} }
wipeit(p); wipeit(p);
break; } break; }
@ -399,22 +591,30 @@ int main(int argc, char** argv) {
// toggle Strong's numbers // toggle Strong's numbers
int strongs = ! parseConf(config["markup"]["strongs"]); int strongs = ! parseConf(config["markup"]["strongs"]);
config["markup"]["strongs"] = strongs + '0'; config["markup"]["strongs"] = strongs + '0';
wipeit(p); saveit();
saveit(configfile);
break; } break; }
case 'w': { case 'w': {
// toggle raw text // toggle raw text
int raw = ! parseConf(config["markup"]["rawtext"]); int raw = ! parseConf(config["markup"]["rawtext"]);
config["markup"]["rawtext"] = raw + '0'; config["markup"]["rawtext"] = raw + '0';
wipeit(p); saveit();
saveit(configfile);
break; } break; }
case '?': { case '?': {
// display help text // display help text
clear(); clear();
addstr(HELPTEXT); 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(); getch();
wipeit(p); wipeit(p);
break; } break; }

View File

@ -1,35 +1,88 @@
# Default configuration file
# Part of the Scriptura project
# default section must be present and is reverified on every run
[defaults] [defaults]
# which module to load by default - this should be the abbreviation used by the Sword module module=WEB
module=KJV
# When searching the Bible, what default portions are you searching - should be in standard Biblical notation format
scope=Gen 1:1 - Rev 22:21 scope=Gen 1:1 - Rev 22:21
# What verse are you loading on startup - also standard Biblical notation searchkey=2 Tim 1:7
searchkey=Mat 5:5 searchtype=1
# general ncurses layout
[layout] [layout]
# should panels be arranged vertically 'v' or horizontally 'h'
panels=v panels=v
# text markup options - most of these can be set during runtime, and are dependent on what data is present in the module
[markup] [markup]
# show footnotes (0 or 1) footnotes=0
footnotes=1
# if showing the words of Jesus in a different color, what color is used - this should be a standard ncurses color
lettercolor=cyan lettercolor=cyan
# if a translation has identified words as not part of the original text, do we italicize it (0 or 1)
nontextual=0 nontextual=0
# ignore all other markup options and give us the raw text stored in the module (0 or 1)
rawtext=0 rawtext=0
# do we show the words of Jesus in a separate color (0 or 1)
redletters=1 redletters=1
# if the module contains Strong's numbers, do we display them (0 or 1)
strongs=0 strongs=0
[page0-0]
keytype=0
module=WEB
scope=Gen 1:1 - Rev 22:21
searchkey=2 Tim 1:7
searchtype=1
[page0-1]
keytype=0
module=WEB
scope=Gen 1:1 - Rev 22:21
searchkey=2 Tim 1:7
searchtype=1
[page1-0]
keytype=0
module=WEB
scope=Gen 1:1 - Rev 22:21
searchkey=2 Tim 1:7
searchtype=1
[page1-1]
keytype=0
module=WEB
scope=Gen 1:1 - Rev 22:21
searchkey=2 Tim 1:7
searchtype=1
[page2-0]
keytype=0
module=WEB
scope=Gen 1:1 - Rev 22:21
searchkey=2 Tim 1:7
searchtype=1
[page2-1]
keytype=0
module=WEB
scope=Gen 1:1 - Rev 22:21
searchkey=2 Tim 1:7
searchtype=1
[page3-0]
keytype=
module=
scope=
searchkey=
searchtype=
[page3-1]
keytype=
module=
scope=
searchkey=
searchtype=
[page4-0]
keytype=
module=
scope=
searchkey=
searchtype=
[page4-1]
keytype=
module=
scope=
searchkey=
searchtype=