Compare commits
2 Commits
d57854add5
...
bdd140b94a
Author | SHA1 | Date |
---|---|---|
Paul Mosier | bdd140b94a | |
Paul Mosier | 7df22c80a3 |
2
Makefile
2
Makefile
|
@ -1,5 +1,5 @@
|
|||
PREFIX = /usr/local
|
||||
CC = clang
|
||||
CC = g++
|
||||
CCFLAGS = -Wall $(shell pkg-config --cflags sword)
|
||||
TARGET = scriptura
|
||||
INCLUDES = -I/usr/include/sword -I/usr/include/ncursesw
|
||||
|
|
|
@ -5,8 +5,8 @@ This program was written to scratch a personal itch. I do a lot of daily comput
|
|||
|
||||
|
||||
## Dependencies
|
||||
* clang
|
||||
* libsword and its development libraries (often available in most distributions' standard repos)
|
||||
* 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 (also available in most distributions' standard repos)
|
||||
* ncurses and its development libraries (any modern Linux version should work)
|
||||
* doxygen (optional, if you're a documentation nerd)
|
||||
|
||||
|
@ -21,7 +21,7 @@ To install scriptura (into /usr/local by default):
|
|||
>
|
||||
> 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/
|
||||
>
|
||||
> cd $HOME/.sword/
|
||||
|
@ -30,7 +30,7 @@ On first run, scriptura assumes that a King James module is intalled and availab
|
|||
>
|
||||
> 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.
|
||||
|
||||
|
@ -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:
|
||||
- _Paypal - paladin1_ at _sdf.org_
|
||||
- _BTC - bc1q9dfau346z38jth35gkaxacd3fljvfgw6cqcyyv_
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
|
18
free.cpp
18
free.cpp
|
@ -35,7 +35,7 @@ char CLITEXT[] =
|
|||
|
||||
char HELPTEXT[] =
|
||||
"Scriptura\n\n"
|
||||
"Key bindings:\n"
|
||||
"Main key bindings:\n"
|
||||
" ? this help message\n"
|
||||
" q quit\n\n"
|
||||
" ESC close floating window\n"
|
||||
|
@ -46,7 +46,11 @@ char HELPTEXT[] =
|
|||
" arrow up/dn scroll up/down one line\n"
|
||||
" page up/dn scroll up/down one page\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"
|
||||
" f toggle footnotes\n"
|
||||
" n toggle non-textual words\n"
|
||||
|
@ -56,6 +60,9 @@ char HELPTEXT[] =
|
|||
"\n(Press any key to return to the main screen)\n"
|
||||
"\0";
|
||||
|
||||
int DEFSEARCH = 1;
|
||||
int DEFKEY = 0;
|
||||
|
||||
void wrapup(int exitval, const char* str) {
|
||||
endwin();
|
||||
if (str != NULL) perror(str);
|
||||
|
@ -97,10 +104,7 @@ int parseConf(sword::SWBuf buf) {
|
|||
|
||||
starray stappend(starray arr, const char* newstr) {
|
||||
arr.length++;
|
||||
if (arr.length == 1) {
|
||||
free(arr.strings); // needed due to stinit()'s malloc(0)
|
||||
arr.strings = (const char**) malloc(sizeof(char*));
|
||||
} else {
|
||||
if (arr.length > 1) {
|
||||
arr.strings = (const char**)
|
||||
realloc(arr.strings, arr.length * sizeof(char*));
|
||||
}
|
||||
|
@ -113,7 +117,7 @@ starray stappend(starray arr, const char* newstr) {
|
|||
|
||||
starray stinit(starray arr) {
|
||||
arr.length = 0;
|
||||
arr.strings = (const char**) malloc(0);
|
||||
arr.strings = (const char**) malloc(sizeof(char*));
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
|
15
free.h
15
free.h
|
@ -23,6 +23,12 @@
|
|||
#include <swconfig.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?
|
||||
typedef struct {
|
||||
int length; //!< number of elements
|
||||
|
@ -34,7 +40,8 @@ typedef struct {
|
|||
* One is stored by each pane. */
|
||||
typedef struct {
|
||||
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 searchtype; //!< type of search to be performed, 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'
|
||||
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.
|
||||
extern sword::SWConfig config;
|
||||
|
||||
|
|
34
pane.cpp
34
pane.cpp
|
@ -27,25 +27,23 @@
|
|||
#include "pane.h"
|
||||
|
||||
// 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;
|
||||
hasFocus = false;
|
||||
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
|
||||
setTitle(title);
|
||||
pad = NULL;
|
||||
resize(starty, startx, lines, cols);
|
||||
redraw(true);
|
||||
|
||||
// forms/menus send in a null searchkey
|
||||
if (defmod.searchkey != NULL) {
|
||||
setModkey(defmod, title);
|
||||
}
|
||||
}
|
||||
|
||||
// handle pane resizing
|
||||
|
@ -113,7 +111,7 @@ void pane::renderText() {
|
|||
* char so I think it has to be done this way */
|
||||
for (int i = 0; i < length; i++) {
|
||||
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);
|
||||
|
||||
|
@ -437,8 +435,8 @@ int pane::loadMenu(starray opts) {
|
|||
|
||||
int pane::printInstructions(int y, const char* inst) {
|
||||
// NOTE -- starting indentation is hardcoded at two characters in
|
||||
int strctr = 0;
|
||||
int line = width - 3;
|
||||
long unsigned int strctr = 0;
|
||||
long unsigned int line = width - 3;
|
||||
while (strlen(&inst[strctr]) > line) {
|
||||
// set write length to be up to the last space, for word wrapping
|
||||
while ((inst[line] != ' ') && (line > 0)) line--;
|
||||
|
@ -574,7 +572,7 @@ void pane::formClean(FORM* form, FIELD** fields, int numfields) {
|
|||
}
|
||||
|
||||
void pane::setModule(const char* newtitle, const char* newmod,
|
||||
int keytype) {
|
||||
int keytype) {
|
||||
mod.modname = newmod;
|
||||
mod.keytype = keytype;
|
||||
setTitle(newtitle);
|
||||
|
@ -584,7 +582,7 @@ void pane::setModule(const char* newtitle, const char* newmod,
|
|||
|
||||
void pane::setKey(const char* 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) {
|
||||
|
@ -592,6 +590,8 @@ void pane::setSearch(int type, const char* scope) {
|
|||
mod.scope = scope;
|
||||
}
|
||||
|
||||
modkey pane::getModkey() {
|
||||
return mod;
|
||||
void pane::setModkey(modkey newmod, const char* newtitle) {
|
||||
setModule(newtitle, newmod.modname, newmod.keytype);
|
||||
setKey(newmod.searchkey);
|
||||
setSearch(newmod.searchtype, newmod.scope);
|
||||
}
|
||||
|
|
11
pane.h
11
pane.h
|
@ -74,7 +74,8 @@ class pane {
|
|||
int startx, //!< the pane's upper left Y coord
|
||||
int lines, //!< the pane's number of lines
|
||||
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.
|
||||
|
@ -160,9 +161,11 @@ class pane {
|
|||
const char* scope //!< scope to search within, in key format
|
||||
);
|
||||
|
||||
/*! Gets the modkey associated with this pane.
|
||||
* \returns modkey associated with this pane */
|
||||
modkey getModkey();
|
||||
/*! Loads an entirely new modkey into the pane. Will call associated
|
||||
* redrawing functions. */
|
||||
void setModkey(modkey newmod, //!< new modkey to load in
|
||||
const char* newtitle //!< new pane title to use
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
|
11
scabbard.cpp
11
scabbard.cpp
|
@ -182,6 +182,11 @@ const char* scabbard::getModDescription(int modt, int mod) {
|
|||
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) {
|
||||
return modules[modt].keytype;
|
||||
}
|
||||
|
@ -214,13 +219,13 @@ const char* scabbard::search(modkey mod) {
|
|||
sword::SWKey *clone =
|
||||
(key->parseVerseList(mod.scope, mod.scope, true)).clone();
|
||||
sword::ListKey lk =
|
||||
text->search(mod.searchkey, mod.searchtype, 0, clone);
|
||||
text->search(mod.searchkey, mod.searchtype, 0, clone);
|
||||
const char* retval = parseVerses(text, lk);
|
||||
delete(key);
|
||||
delete(clone);
|
||||
return retval;
|
||||
|
||||
} else return directSearch(text, mod);
|
||||
}
|
||||
return directSearch(text, mod);
|
||||
}
|
||||
|
||||
/* really old code in case we need to pull output from diatheke
|
||||
|
|
10
scabbard.h
10
scabbard.h
|
@ -41,7 +41,8 @@
|
|||
#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 {
|
||||
char label[25]; //!< the module type
|
||||
int keytype; //!< 0 - versekey, 1 - treekey, 2 - direct/alpha
|
||||
|
@ -106,12 +107,17 @@ class scabbard {
|
|||
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 */
|
||||
const char* getModDescription(int modt, //!< from getModClassifications()
|
||||
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.
|
||||
* \returns the proper keytype; see the keytype field for modkey */
|
||||
int getKeyType(int modt //!< return value from getModClassifications()
|
||||
|
|
340
scriptura.cpp
340
scriptura.cpp
|
@ -31,10 +31,12 @@
|
|||
#include "pane.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 floatx, floaty, floatheight, floatwidth;
|
||||
int startx1, starty1, startx2, starty2;
|
||||
|
@ -42,8 +44,30 @@ 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);
|
||||
}
|
||||
|
||||
|
@ -51,28 +75,50 @@ void wipeit(pane* panes) {
|
|||
starray showForm(const char* title, starray inputs, const char* secondinst,
|
||||
int floaty, int floatx, int floatheight, int floatwidth) {
|
||||
// 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);
|
||||
// sanitize input? -- if (ret.length != 0)
|
||||
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(char* configfile) {
|
||||
/* This is clumsy but we have to use this particular constructor for
|
||||
* config() so we can save to disk. The global config object uses a
|
||||
* different constructor. */
|
||||
sword::SWConfig newconfig(configfile);
|
||||
newconfig.augment(config);
|
||||
newconfig.save();
|
||||
void saveit() {
|
||||
config.save();
|
||||
}
|
||||
|
||||
//! Set various standard screen landmarks.
|
||||
void landmarks() {
|
||||
// place background text
|
||||
mvprintw(LINES - 1 , COLS - 20, "%s", "? - help q - quit");
|
||||
refresh();
|
||||
foundation();
|
||||
|
||||
// split up the screen
|
||||
halfcols = COLS / 2;
|
||||
|
@ -108,7 +154,7 @@ void doresize(pane* p) {
|
|||
// reset standard screen landmarks
|
||||
landmarks();
|
||||
|
||||
// reset and refresh main panes
|
||||
// reset and refresh main panes -- assumes NUMPANES
|
||||
p[0].resize(starty1, startx1, panelength, panewidth);
|
||||
p[1].resize(starty2, startx2, panelength, panewidth);
|
||||
wipeit(p);
|
||||
|
@ -153,28 +199,33 @@ int main(int argc, char** argv) {
|
|||
printf("Please ensure this directory exists and is writable.\n");
|
||||
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);
|
||||
printf("done.\n");
|
||||
}
|
||||
}
|
||||
sword::SWConfig tempconfig(configfile);
|
||||
config.augment(tempconfig);
|
||||
config.filename = configfile;
|
||||
config.load();
|
||||
|
||||
// ensure some needed config settings are in place
|
||||
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";
|
||||
config["defaults"]["searchkey"] = "2 Tim 1:7";
|
||||
if (! strcmp(config["defaults"]["module"], ""))
|
||||
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()
|
||||
|
||||
|
@ -203,33 +254,71 @@ int main(int argc, char** argv) {
|
|||
init_pair(1, COLOR_RED, COLOR_BLACK);
|
||||
}
|
||||
|
||||
// two main panes
|
||||
NUMPANES = 2;
|
||||
landmarks();
|
||||
pane* p = (pane*) malloc(sizeof(pane) * NUMPANES);
|
||||
const char* title = "Window";
|
||||
if (! p) wrapup(1, "Error allocating memory for panes.\n");
|
||||
p[0] = { starty1, startx1, panelength, panewidth, title };
|
||||
p[1] = { starty2, startx2, panelength, panewidth, title };
|
||||
// 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);
|
||||
|
||||
pane* focustab;
|
||||
focustab = &(p[0]);
|
||||
focustab->toggleFocus();
|
||||
/* 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"], "")))
|
||||
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
|
||||
* sanitized in the constructor */
|
||||
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++) {
|
||||
if (config["defaults"]["module"] && config["defaults"]["searchkey"]) {
|
||||
// this isn't technically the title, but it's what we have now
|
||||
p[i].setTitle(config["defaults"]["module"]);
|
||||
p[i].loadText(scab.getSpan(p[i].getModkey()));
|
||||
} else {
|
||||
p[i].loadText(blank);
|
||||
}
|
||||
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 };
|
||||
}
|
||||
wipeit(p);
|
||||
|
||||
pane* focustab;
|
||||
int infocus = 0;
|
||||
focustab = &(p[infocus]);
|
||||
focustab->toggleFocus();
|
||||
loadPage(p, &scab);
|
||||
|
||||
// loop for input
|
||||
int ch;
|
||||
|
@ -239,7 +328,8 @@ int main(int argc, char** argv) {
|
|||
case '\t': {
|
||||
// switch pane focus
|
||||
focustab->toggleFocus();
|
||||
focustab = (focustab == &(p[0]) ? &(p[1]) : &(p[0]));
|
||||
infocus = (infocus + 1) % NUMPANES;
|
||||
focustab = &(p[infocus]);
|
||||
focustab->toggleFocus();
|
||||
break; }
|
||||
|
||||
|
@ -263,12 +353,64 @@ int main(int argc, char** argv) {
|
|||
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';
|
||||
wipeit(p);
|
||||
saveit(configfile);
|
||||
saveit();
|
||||
break; }
|
||||
|
||||
case 'g': {
|
||||
|
@ -290,8 +432,10 @@ int main(int argc, char** argv) {
|
|||
|
||||
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(focustab->getModkey()));
|
||||
focustab->loadText(scab.getSpan(getModkey(infocus)));
|
||||
saveit();
|
||||
}
|
||||
wipeit(p);
|
||||
break; }
|
||||
|
@ -300,15 +444,16 @@ int main(int argc, char** argv) {
|
|||
// toggle non-textual words
|
||||
int textual = ! parseConf(config["markup"]["nontextual"]);
|
||||
config["markup"]["nontextual"] = textual + '0';
|
||||
wipeit(p);
|
||||
saveit(configfile);
|
||||
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 };
|
||||
typetitle, defmod };
|
||||
starray mc = scab.getModClassifications();
|
||||
int modclass = menupane.loadMenu(mc);
|
||||
if (modclass == -1) {
|
||||
|
@ -320,7 +465,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
const char* modtitle = "Choose a module";
|
||||
menupane = {floaty, floatx, floatheight, floatwidth, modtitle};
|
||||
menupane = {floaty, floatx, floatheight, floatwidth, modtitle, defmod};
|
||||
starray mds = scab.getModDescriptions(modclass);
|
||||
int mod = menupane.loadMenu(mds);
|
||||
if (mod == -1) {
|
||||
|
@ -331,19 +476,50 @@ int main(int argc, char** argv) {
|
|||
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(focustab->getModkey()));
|
||||
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';
|
||||
wipeit(p);
|
||||
saveit(configfile);
|
||||
saveit();
|
||||
break; }
|
||||
|
||||
case 's': {
|
||||
|
@ -376,21 +552,37 @@ int main(int argc, char** argv) {
|
|||
/* 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++) {
|
||||
// user entered a scope but no search
|
||||
//if (i == ret.length - 1) break;
|
||||
|
||||
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,
|
||||
(ret.strings)[ret.length-1]);
|
||||
focustab->setSearch(i * -1, newscope);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (i != ret.length - 1)
|
||||
focustab->loadText(scab.search(focustab->getModkey()));
|
||||
if (i != ret.length - 1) {
|
||||
focustab->loadText(scab.search(getModkey(infocus)));
|
||||
saveit();
|
||||
}
|
||||
}
|
||||
wipeit(p);
|
||||
break; }
|
||||
|
@ -399,22 +591,30 @@ int main(int argc, char** argv) {
|
|||
// toggle Strong's numbers
|
||||
int strongs = ! parseConf(config["markup"]["strongs"]);
|
||||
config["markup"]["strongs"] = strongs + '0';
|
||||
wipeit(p);
|
||||
saveit(configfile);
|
||||
saveit();
|
||||
break; }
|
||||
|
||||
case 'w': {
|
||||
// toggle raw text
|
||||
int raw = ! parseConf(config["markup"]["rawtext"]);
|
||||
config["markup"]["rawtext"] = raw + '0';
|
||||
wipeit(p);
|
||||
saveit(configfile);
|
||||
saveit();
|
||||
break; }
|
||||
|
||||
case '?': {
|
||||
// display help text
|
||||
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();
|
||||
wipeit(p);
|
||||
break; }
|
||||
|
|
|
@ -1,35 +1,88 @@
|
|||
# Default configuration file
|
||||
# Part of the Scriptura project
|
||||
|
||||
|
||||
# default section must be present and is reverified on every run
|
||||
[defaults]
|
||||
# which module to load by default - this should be the abbreviation used by the Sword module
|
||||
module=KJV
|
||||
# When searching the Bible, what default portions are you searching - should be in standard Biblical notation format
|
||||
module=WEB
|
||||
scope=Gen 1:1 - Rev 22:21
|
||||
# What verse are you loading on startup - also standard Biblical notation
|
||||
searchkey=Mat 5:5
|
||||
searchkey=2 Tim 1:7
|
||||
searchtype=1
|
||||
|
||||
|
||||
# general ncurses layout
|
||||
[layout]
|
||||
# should panels be arranged vertically 'v' or horizontally 'h'
|
||||
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]
|
||||
# show footnotes (0 or 1)
|
||||
footnotes=1
|
||||
# if showing the words of Jesus in a different color, what color is used - this should be a standard ncurses color
|
||||
footnotes=0
|
||||
lettercolor=cyan
|
||||
# if a translation has identified words as not part of the original text, do we italicize it (0 or 1)
|
||||
nontextual=0
|
||||
# ignore all other markup options and give us the raw text stored in the module (0 or 1)
|
||||
rawtext=0
|
||||
# do we show the words of Jesus in a separate color (0 or 1)
|
||||
redletters=1
|
||||
# if the module contains Strong's numbers, do we display them (0 or 1)
|
||||
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=
|
||||
|
||||
|
|
Loading…
Reference in New Issue