Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
Paul Mosier | 0a8acd40cc | |
Paul Mosier | 8959f4ef97 | |
Paul Mosier | 8bf642a8a9 | |
Paul Mosier | 90bb1d499c | |
Paul Mosier | 0e430dcd9e | |
Paul Mosier | bdd140b94a | |
Paul Mosier | 7df22c80a3 | |
Paul Mosier | d57854add5 | |
Paul Mosier | 874a084cda | |
Paul Mosier | 1d51da03bb |
|
@ -32,3 +32,5 @@
|
|||
*.out
|
||||
*.app
|
||||
|
||||
push-checklist.org
|
||||
scriptura
|
||||
|
|
12
Makefile
12
Makefile
|
@ -1,9 +1,9 @@
|
|||
PREFIX = /usr/local
|
||||
CC = clang
|
||||
CCFLAGS = -Wall
|
||||
PREFIX ?= /usr/local
|
||||
CC = g++
|
||||
CCFLAGS ?= -Wall $(shell pkg-config --cflags sword)
|
||||
TARGET = scriptura
|
||||
INCLUDES = -I/usr/include/sword -I/usr/include/ncursesw
|
||||
LDFLAGS = -lmenuw -lformw -lncursesw -lsword -lstdc++
|
||||
LDFLAGS = -lmenuw -lformw -lncursesw -lstdc++ $(shell pkg-config --libs sword)
|
||||
SOURCE = free.cpp scabbard.cpp pane.cpp scriptura.cpp
|
||||
|
||||
|
||||
|
@ -27,11 +27,11 @@ doc:
|
|||
|
||||
|
||||
install:
|
||||
cp -v scriptura $PREFIX/bin
|
||||
install -m 0755 -D scriptura $(DESTDIR)$(PREFIX)/bin/scriptura
|
||||
|
||||
|
||||
uninstall:
|
||||
rm -v $PREFIX/bin
|
||||
rm -v $(DESTDIR)$(PREFIX)/bin
|
||||
|
||||
|
||||
clean:
|
||||
|
|
12
README.md
12
README.md
|
@ -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,13 +30,14 @@ 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.
|
||||
|
||||
|
||||
## Known issues
|
||||
Menus and forms will not render correctly if your terminal window is too narrow. Resize the terminal and this should work.
|
||||
* Menus and forms will not render correctly if your terminal window is too narrow. Resize the terminal and this should work.
|
||||
* Extended ASCII characters, typically in the Latin-1 set, will not render due to a quirk of how ncurses handles UTF-8.
|
||||
|
||||
|
||||
## Feedback / Support / Gratuity
|
||||
|
@ -48,7 +49,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
|
||||
|
|
39
free.cpp
39
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);
|
||||
|
@ -68,18 +75,25 @@ void trim(char* str) {
|
|||
while (len-- && isspace(str[len])) str[len] = 0;
|
||||
}
|
||||
|
||||
int strmatch(wchar_t text[], const wchar_t* key, int matchnow) {
|
||||
//int strmatch(wchar_t text[], const wchar_t* key, int matchnow) {
|
||||
int strmatch(char text[], const char* key, int matchnow) {
|
||||
int retval = -1;
|
||||
int length = wcslen(key);
|
||||
wchar_t* substr = (wchar_t*) malloc((length + 1) * sizeof(wchar_t));
|
||||
wmemset(substr, L'\0', length + 1);
|
||||
//int length = wcslen(key);
|
||||
int length = strlen(key);
|
||||
//wchar_t* substr = (wchar_t*) malloc((length + 1) * sizeof(wchar_t));
|
||||
char* substr = (char*) malloc((length + 1) * sizeof(char*));
|
||||
//wmemset(substr, L'\0', length + 1);
|
||||
memset(substr, '\0', length + 1);
|
||||
if (! substr) wrapup(1, "Error allocating memory in strmatch.\n");
|
||||
|
||||
int i = 0;
|
||||
int textlen = wcslen(text);
|
||||
//int textlen = wcslen(text);
|
||||
int textlen = strlen(text);
|
||||
while ((text[i] != '\0') && (i < (textlen - length))) {
|
||||
wmemcpy(substr, &text[i], length);
|
||||
if (! wcscmp(substr, key)) {
|
||||
//wmemcpy(substr, &text[i], length);
|
||||
memcpy(substr, &text[i], length);
|
||||
//if (! wcscmp(substr, key)) {
|
||||
if (! strcmp(substr, key)) {
|
||||
retval = i;
|
||||
break;
|
||||
}
|
||||
|
@ -97,10 +111,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 +124,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;
|
||||
}
|
||||
|
||||
|
|
18
free.h
18
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;
|
||||
|
||||
|
@ -62,7 +75,8 @@ int parseConf(sword::SWBuf buf);
|
|||
/*! Determine if a substring is present at either the beginning of a string or
|
||||
* at any point within it, depending on a toggle for a flag -- return integer
|
||||
* of matching index. */
|
||||
int strmatch(wchar_t text[], const wchar_t* key, int matchnow);
|
||||
//int strmatch(wchar_t text[], const wchar_t* key, int matchnow);
|
||||
int strmatch(char text[], const char* key, int matchnow);
|
||||
|
||||
//! Append a new string to a starray.
|
||||
starray stappend(starray arr, const char* newstr);
|
||||
|
|
134
pane.cpp
134
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
|
||||
|
@ -103,19 +101,11 @@ void pane::renderText() {
|
|||
int nontextual = parseConf(config["markup"]["nontextual"]);
|
||||
// int footnotes = 0; not implemented yet
|
||||
|
||||
// convert input to wide characters for proper rendering
|
||||
wchar_t* text = (wchar_t*) malloc(sizeof(wchar_t) * (length + 1));
|
||||
// copy our unformatted text to local scope for window formatting
|
||||
char* text = (char*) malloc(sizeof(char*) * length + 1);
|
||||
if (! text) wrapup(1, "Error allocating memory in renderText.\n");
|
||||
wmemset(text, L'\0', length + 1);
|
||||
|
||||
/* some typographical quotes won't go through mbstowcs, so alter those; this
|
||||
* seems inefficient, but we don't know how many bytes are in each multibyte
|
||||
* 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);
|
||||
}
|
||||
mbstowcs(text, rawtext, length - 1);
|
||||
memset(text, '\0', strlen(rawtext) + 1);
|
||||
strncpy(text, rawtext, length);
|
||||
|
||||
// kept for debugging
|
||||
//fwprintf(stderr, L"(O): %ls\n\n", text);
|
||||
|
@ -127,12 +117,14 @@ void pane::renderText() {
|
|||
for (int p = 0; p < 2; p++) {
|
||||
|
||||
// loop through the text
|
||||
for (int i = 0; i < length; i++) {
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
|
||||
/* check if we're in markup - it's not printed and it doesn't
|
||||
* affect our line lengths, so spin through it */
|
||||
if (inmarkup) {
|
||||
if (text[i] == '>') inmarkup = 0;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -140,22 +132,22 @@ void pane::renderText() {
|
|||
if ((text[i] == '<') && (! rawonly)) {
|
||||
inmarkup = 1;
|
||||
|
||||
if ((! strmatch(&text[i], L"</q>", 1)) && (p == 1)) {
|
||||
if ((! strmatch(&text[i], "</q>", 1)) && (p == 1)) {
|
||||
// end of redletter bracket
|
||||
makered = 0;
|
||||
|
||||
} else if ((! strmatch(&text[i], L"</transChange>", 1))
|
||||
} else if ((! strmatch(&text[i], "</transChange>", 1))
|
||||
&& (p == 1)) {
|
||||
// end of interpretive text bracket
|
||||
makeital = 0;
|
||||
|
||||
} else if ((! strmatch(&text[i], L"<p>", 1)) && (p == 0)) {
|
||||
} else if ((! strmatch(&text[i], "<p>", 1)) && (p == 0)) {
|
||||
// paragraph break - replace </p> with </>'\n'
|
||||
int endindex = strmatch(&text[i], L"</p>", 0);
|
||||
int endindex = strmatch(&text[i], "</p>", 0);
|
||||
if (endindex != -1)
|
||||
wmemcpy(&text[i + endindex + 2], L">\n", 2);
|
||||
memcpy(&text[i + endindex + 2], ">\n", 2);
|
||||
|
||||
} else if ((! strmatch(&text[i], L"<w savlm=", 1))
|
||||
} else if ((! strmatch(&text[i], "<w savlm=", 1))
|
||||
&& (p == 0) && (strongs == 1)) {
|
||||
/* Strong's number - the format is below, but note
|
||||
* there may be 1+ strong:[G|H]NNNN(N)'s to find,
|
||||
|
@ -164,29 +156,28 @@ void pane::renderText() {
|
|||
* <w savlm="strong:[G|H]NNNN(N)">word</w> */
|
||||
|
||||
// 1. get boundary of open tag
|
||||
int endbracket = strmatch(&text[i], L">", 0);
|
||||
int endbracket = strmatch(&text[i], ">", 0);
|
||||
|
||||
// 2. get Strong's parameters
|
||||
wchar_t* num = (wchar_t*) malloc(sizeof(wchar_t*)
|
||||
* 100);
|
||||
char* num = (char*) malloc(sizeof(char*) * 100);
|
||||
if (! num) wrapup(1,
|
||||
"Error declaring memory in renderText.\n");
|
||||
int numidx = 0;
|
||||
|
||||
int strnum = strmatch(&text[i], L"strong:", 0) + 7;
|
||||
int strnum = strmatch(&text[i], "strong:", 0) + 7;
|
||||
while ((strnum < endbracket) && (strnum != -1)) {
|
||||
int nextspace = strmatch(&text[i+strnum], L" ", 0);
|
||||
int space1 = strmatch(&text[i+strnum], L"\"", 0);
|
||||
int nextspace = strmatch(&text[i+strnum], " ", 0);
|
||||
int space1 = strmatch(&text[i+strnum], "\"", 0);
|
||||
int len = ((nextspace == -1) || (space1 < nextspace)
|
||||
? space1
|
||||
: nextspace);
|
||||
|
||||
if (numidx != 0) wmemcpy(&num[numidx++], L" ", 1);
|
||||
wmemcpy(&num[numidx], &text[i+strnum], len);
|
||||
if (numidx != 0) memcpy(&num[numidx++], " ", 1);
|
||||
memcpy(&num[numidx], &text[i+strnum], len);
|
||||
numidx += len;
|
||||
|
||||
int nextnum =
|
||||
strmatch(&text[i+strnum], L"strong:", 0);
|
||||
strmatch(&text[i+strnum], "strong:", 0);
|
||||
strnum = (nextnum != -1
|
||||
? strnum + nextnum + 7
|
||||
: -1);
|
||||
|
@ -197,27 +188,27 @@ void pane::renderText() {
|
|||
* below zero, otherwise get word boundaries */
|
||||
int wordstart = endbracket + 1;
|
||||
int endtag =
|
||||
(endbracket > strmatch(&text[i], L"/", 0)
|
||||
(endbracket > strmatch(&text[i], "/", 0)
|
||||
? endbracket + 1
|
||||
: strmatch(&text[i], L"</w>", 0));
|
||||
: strmatch(&text[i], "</w>", 0));
|
||||
|
||||
// 4. determine word boundaries & rewrite
|
||||
int wordlen = endtag - wordstart;
|
||||
wchar_t* word = (wchar_t*) malloc(sizeof(wchar_t*)
|
||||
char* word = (char*) malloc(sizeof(char*)
|
||||
* (wordlen == 0 ? 1 : wordlen));
|
||||
|
||||
if (! word) wrapup(1,
|
||||
"Error rewriting markup in renderText.\n");
|
||||
wmemcpy(word, &text[i+wordstart], wordlen);
|
||||
memcpy(word, &text[i+wordstart], wordlen);
|
||||
|
||||
// rewrite
|
||||
int start = i + endtag - wordlen - numidx - 4;
|
||||
wmemcpy(&text[start], L"\"", 1);
|
||||
wmemcpy(&text[start + 1], L">", 1);
|
||||
wmemcpy(&text[start + 2], word, wordlen);
|
||||
wmemcpy(&text[start + 2 + wordlen], L"[", 1);
|
||||
wmemcpy(&text[start + 3 + wordlen], num, numidx);
|
||||
wmemcpy(&text[start + 3 + wordlen + numidx], L"]", 1);
|
||||
memcpy(&text[start], "\"", 1);
|
||||
memcpy(&text[start + 1], ">", 1);
|
||||
memcpy(&text[start + 2], word, wordlen);
|
||||
memcpy(&text[start + 2 + wordlen], "[", 1);
|
||||
memcpy(&text[start + 3 + wordlen], num, numidx);
|
||||
memcpy(&text[start + 3 + wordlen + numidx], "]", 1);
|
||||
|
||||
// clean up so we can do this again
|
||||
free(num);
|
||||
|
@ -226,12 +217,12 @@ void pane::renderText() {
|
|||
// kept for debugging
|
||||
//fwprintf(stderr, L"(I): %ls\n", text);
|
||||
|
||||
} else if ((! strmatch(&text[i], L"<q marker", 1))
|
||||
} else if ((! strmatch(&text[i], "<q marker", 1))
|
||||
&& (p == 1) && (redletter == 1)) {
|
||||
// start of redletter bracket
|
||||
makered = 1;
|
||||
|
||||
} else if ((! strmatch(&text[i], L"<transChange type=\"added\"", 1))
|
||||
} else if ((! strmatch(&text[i], "<transChange type=\"added\"", 1))
|
||||
&& (p == 1) && (nontextual == 1)) {
|
||||
// start of interpretive text bracket
|
||||
makeital = 1;
|
||||
|
@ -241,6 +232,17 @@ void pane::renderText() {
|
|||
continue;
|
||||
} // markup check
|
||||
|
||||
/* determine how large this multibyte character is -- we will need it to
|
||||
* determine our advance amount in the loop */
|
||||
int offset = 0;
|
||||
size_t charlen = mbrlen(&text[i], 5, NULL);
|
||||
while (((int) mbrlen(&text[i+offset], 5, NULL) == -1) &&
|
||||
(i + offset < length)) {
|
||||
/* mbrlen() says the next char is not a valid multibyte char, so find
|
||||
* the length by figuring out where the succeeding character is */
|
||||
offset++;
|
||||
}
|
||||
|
||||
if (p == 0) {
|
||||
// handle word wrapping
|
||||
if (text[i] == ' ') {
|
||||
|
@ -280,15 +282,21 @@ void pane::renderText() {
|
|||
|
||||
} else {
|
||||
// printing -- pull out the single character we care about
|
||||
wchar_t single[] = L"\0\0";
|
||||
wcsncpy(&single[0], &text[i], 1);
|
||||
|
||||
wattrset(pad, COLOR_PAIR(makered)
|
||||
| (makeital ? A_ITALIC : 0));
|
||||
waddwstr(pad, single);
|
||||
|
||||
if (((int) charlen == -1) && (offset == 1)) {
|
||||
/* This is an extended ascii character (probably Latin-1) and not
|
||||
* UTF-8. In setting up ncurses for UTF-8 we set a locale and seem to
|
||||
* make these characters unprintable. To avoid massive rendering
|
||||
* errors we have to substitute them with something else. */
|
||||
waddstr(pad, "?");
|
||||
|
||||
} else waddnstr(pad, &text[i], charlen);
|
||||
}
|
||||
|
||||
printable++;
|
||||
i += ((int) charlen == -1 ? offset : (int) charlen);
|
||||
} // text loop
|
||||
|
||||
// text rewriting debugging
|
||||
|
@ -320,9 +328,7 @@ void pane::setTitle(const char* newtitle) {
|
|||
|
||||
void pane::retitle() {
|
||||
(hasFocus ? wattrset(win, A_STANDOUT) : standend());
|
||||
mvwprintw(win, 0, 1, "%s", " ");
|
||||
mvwprintw(win, 0, 3, "%s", titlebar);
|
||||
mvwprintw(win, 0, 3+strlen(titlebar), "%s", " ");
|
||||
mvwprintw(win, 0, 1, "%s%s%s", " ", titlebar, " ");
|
||||
wstandend(win);
|
||||
}
|
||||
|
||||
|
@ -437,8 +443,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 +580,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 +590,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 +598,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
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
|
133
scabbard.cpp
133
scabbard.cpp
|
@ -20,10 +20,15 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "scabbard.h"
|
||||
|
||||
scabbard::scabbard() {
|
||||
// initialize the thing!
|
||||
swrd = *(new sword::SWMgr());
|
||||
|
||||
/* store what markup information the user has requested be shown
|
||||
* SWBuf.c_str() returns the ascii code 48 for '0', so we have to do some
|
||||
* math to get the proper boolean setting */
|
||||
|
@ -31,18 +36,22 @@ scabbard::scabbard() {
|
|||
markstrongs = parseConf(config["markup"]["strongs"]);
|
||||
markfoot = parseConf(config["markup"]["footnotes"]);
|
||||
|
||||
/* everything following is based on constructing the menu used to display
|
||||
* to the reader about what modules the system has - start with a
|
||||
* temporary array of module types */
|
||||
int tmpnum = 4;
|
||||
modtype tmpmods[4];
|
||||
strcpy(tmpmods[0].label, "Generic Books");
|
||||
// construct the menu list of modules
|
||||
constructModlist();
|
||||
}
|
||||
|
||||
void scabbard::constructModlist() {
|
||||
int tmpnum = 5;
|
||||
modtype tmpmods[5];
|
||||
strcpy(tmpmods[0].label, sword::SWMgr::MODTYPE_GENBOOKS);
|
||||
tmpmods[0].keytype = 1;
|
||||
strcpy(tmpmods[1].label, "Biblical Texts");
|
||||
strcpy(tmpmods[1].label, sword::SWMgr::MODTYPE_BIBLES);
|
||||
tmpmods[1].keytype = 0;
|
||||
strcpy(tmpmods[2].label, "Lexicons / Dictionaries");
|
||||
strcpy(tmpmods[2].label, sword::SWMgr::MODTYPE_LEXDICTS);
|
||||
tmpmods[2].keytype = 2;
|
||||
strcpy(tmpmods[3].label, "Commentaries");
|
||||
strcpy(tmpmods[3].label, sword::SWMgr::MODTYPE_COMMENTARIES);
|
||||
tmpmods[3].keytype = 0;
|
||||
strcpy(tmpmods[4].label, sword::SWMgr::MODTYPE_DAILYDEVOS);
|
||||
tmpmods[3].keytype = 0;
|
||||
|
||||
// need some throwaway ints
|
||||
|
@ -182,6 +191,26 @@ 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::modExists(const char* modname) {
|
||||
sword::SWModule *text = swrd.getModule(modname);
|
||||
return (text == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
starray scabbard::getModules() {
|
||||
starray retval;
|
||||
retval = stinit(retval);
|
||||
for (iter = swrd.Modules.begin(); iter != swrd.Modules.end(); iter++) {
|
||||
sword::SWModule *mod = iter->second;
|
||||
retval = stappend(retval, mod->getName());
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int scabbard::getKeyType(int modt) {
|
||||
return modules[modt].keytype;
|
||||
}
|
||||
|
@ -214,14 +243,94 @@ 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(text);
|
||||
delete(key);
|
||||
delete(clone);
|
||||
return retval;
|
||||
}
|
||||
return directSearch(text, mod);
|
||||
}
|
||||
|
||||
} else return directSearch(text, mod);
|
||||
void scabbard::installKJVA() {
|
||||
sword::SWBuf swordhome = getenv("HOME");
|
||||
swordhome += "/.sword";
|
||||
|
||||
// ensure some directories exist -- apparently installmgr doesn't create them
|
||||
if ((mkdir(swordhome + "/mods.d", 00755) != 0) && (errno != EEXIST)) {
|
||||
printf("Error creating directory to perform the install:\n");
|
||||
printf("~/.sword/mods.d\n");
|
||||
printf("Please create manually or check directory permissions.\n");
|
||||
exit(0);
|
||||
}
|
||||
sword::SWBuf finaldir = swordhome.c_str();
|
||||
finaldir += "/modules";
|
||||
mkdir(finaldir.c_str(), 00755);
|
||||
finaldir += "/texts";
|
||||
mkdir(finaldir.c_str(), 00755);
|
||||
finaldir += "/ztext";
|
||||
mkdir(finaldir.c_str(), 00755);
|
||||
finaldir += "/kjva";
|
||||
if ((mkdir(finaldir.c_str(), 00755) != 0) && (errno != EEXIST)) {
|
||||
printf("Error creating directory to perform the install:\n");
|
||||
printf("~/.sword/modules/texts/ztext/kjva\n");
|
||||
printf("Please create manually or check directory permissions.\n");
|
||||
exit(0);
|
||||
}
|
||||
printf("Directories created for new module.\n");
|
||||
|
||||
// installmgr -init
|
||||
sword::InstallMgr* im = new sword::InstallMgr(swordhome + "/InstallMgr");
|
||||
im->setUserDisclaimerConfirmed(true);
|
||||
|
||||
// sync config (installmgr -sc)
|
||||
sword::InstallSource is("FTP");
|
||||
is.caption = "CrossWire";
|
||||
is.source = "ftp.crosswire.org";
|
||||
is.directory = "/pub/sword/raw";
|
||||
|
||||
sword::SWConfig installconf(swordhome + "/InstallMgr/InstallMgr.conf");
|
||||
if (! installconf["General"]["PassiveFTP"])
|
||||
installconf["General"]["PassiveFTP"] = "true";
|
||||
if (! installconf["General"]["TimeoutMillis"])
|
||||
installconf["General"]["TimeoutMillis"] = "10000";
|
||||
if (! installconf["General"]["UnverifiedPeerAllowed"])
|
||||
installconf["General"]["UnverifiedPeerAllowed"] = false;
|
||||
if (! installconf["General"]["FTPSource"])
|
||||
installconf["Sources"]["FTPSource"] = is.getConfEnt();
|
||||
installconf.save();
|
||||
im->refreshRemoteSourceConfiguration();
|
||||
|
||||
/* refresh remote source (installmgr -r)
|
||||
* apparently we can't reuse our prior declared InstallSource here */
|
||||
sword::InstallSourceMap::iterator source = im->sources.find("CrossWire");
|
||||
sword::InstallSource *is2 = source->second;
|
||||
if (source == im->sources.end()) {
|
||||
fprintf(stderr, "Couldn't find remote source [%s]\n", "CrossWire");
|
||||
}
|
||||
|
||||
if (int refresh = im->refreshRemoteSource(is2)) {
|
||||
printf("Refresh call failed, error: %d\n", refresh);
|
||||
printf("Most likely this is an InstalMgr configuration issue.\n");
|
||||
printf("Please ensure your SWORD environment is properly configured,\n");
|
||||
printf(" or download & install the module manually before trying again.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// install module (installmgr -ri CrossWire KJVA)
|
||||
sword::SWMgr* rmgr = is2->getMgr();
|
||||
sword::SWModule* modname = rmgr->getModule("KJVA");
|
||||
if (im->installModule(new sword::SWMgr(), 0, modname->getName(), is2)) {
|
||||
printf("Error installing module.\n");
|
||||
printf("Most likely this is an environment configuration issue.\n");
|
||||
printf("Please ensure your SWORD environment is properly configured,\n");
|
||||
printf(" or download & install the module manually before trying again.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// update internal module list & refresh SWMgr w/installed module info
|
||||
swrd = *(new sword::SWMgr());
|
||||
constructModlist();
|
||||
}
|
||||
|
||||
/* really old code in case we need to pull output from diatheke
|
||||
|
|
32
scabbard.h
32
scabbard.h
|
@ -27,6 +27,7 @@
|
|||
#include <swbuf.h>
|
||||
#include <versekey.h>
|
||||
#include <treekey.h>
|
||||
#include <installmgr.h>
|
||||
|
||||
#include <osisredletterwords.h>
|
||||
#include <osisstrongs.h>
|
||||
|
@ -41,7 +42,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
|
||||
|
@ -82,13 +84,16 @@ class scabbard {
|
|||
//!< modkey containing the search term
|
||||
);
|
||||
|
||||
//! Construct the internal list of modules used in the selection menu.
|
||||
void constructModlist();
|
||||
|
||||
public:
|
||||
//! Takes as input various preferences on text markup from our config file
|
||||
scabbard();
|
||||
|
||||
/*! Struct to hold listing of different modules - each modtype corresponds
|
||||
* to a different type of module - see comments for modtype */
|
||||
modtype modules[4];
|
||||
modtype modules[5];
|
||||
|
||||
/*! Get all module types loaded in and accessible by Sword.
|
||||
* \returns the module types loaded into an array */
|
||||
|
@ -106,12 +111,26 @@ 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. Vulnerable
|
||||
* to malicious input.
|
||||
* \returns the module description */
|
||||
const char* getModDescription(const char* modname //!< module internal name
|
||||
);
|
||||
|
||||
//! Return an array of all module names.
|
||||
starray getModules();
|
||||
|
||||
/*! Given a text name for a module, determine if it exists.
|
||||
*/
|
||||
int modExists(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()
|
||||
|
@ -123,11 +142,14 @@ class scabbard {
|
|||
);
|
||||
|
||||
/*! Search for a term or passage.
|
||||
* \returns the text requested, or in the case of a non-versekeyed text,
|
||||
* the nearest alphabetical entry to the search term supplied */
|
||||
* \returns the text requested, or in the case of a non-versekeyed text,
|
||||
* the nearest alphabetical entry to the search term supplied */
|
||||
const char* search(modkey mod //!< the modkey supplied by the pane
|
||||
);
|
||||
|
||||
//! Install KJVA from ftp.crosswire.org.
|
||||
void installKJVA();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
387
scriptura.cpp
387
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,40 +199,80 @@ 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 = *(new sword::SWConfig(configfile));
|
||||
|
||||
// ensure some needed config settings are in place
|
||||
/* 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"], ""))
|
||||
config["defaults"]["module"] = "KJV";
|
||||
saveit(configfile);
|
||||
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);
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
// get color settings
|
||||
if (! strcmp(config["markup"]["lettercolor"], "green")) {
|
||||
|
@ -203,33 +289,69 @@ 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"]);
|
||||
}
|
||||
|
||||
/* 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";
|
||||
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);
|
||||
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++;
|
||||
}
|
||||
}
|
||||
wipeit(p);
|
||||
|
||||
// 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;
|
||||
|
@ -239,7 +361,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 +386,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 +465,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 +477,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 +498,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 +509,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 +585,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 +624,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