Bible eBook Prototype More details at: http://mbed.org/users/davervw/notebook/ebible-abstract/
Dependencies: SDHCFileSystem TextLCD mbed BibleIO
BibleUI.cpp
- Committer:
- davervw
- Date:
- 2011-02-27
- Revision:
- 0:b9d9145827e2
File content as of revision 0:b9d9145827e2:
/* Bible UI Class Implementation - KJV Bible eBook Browser * * Copyright (c) 2011 David R. Van Wagner davervw@yahoo.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "BibleUI.h" //extern Serial console; BibleUI::BibleUI(BibleIO &bible, TextLCD &textlcd, DigitalIn& left, DigitalIn& right) :HolyBible(bible), lcd(textlcd), lb(left), rb(right), led1(LED1), led2(LED2), led3(LED3), led4(LED4) { //console.printf("\n\n" "Bible LCD\n\n"); lcd.cls(); lcd.printf("BibleLCD"); wait(0.5); show_title(); book = 0; chapter = 0; verse = 0; offset = 0; disp_len = 0; } void BibleUI::start() { main(); } void BibleUI::display_nav() { lcd.cls(); char* book_name = HolyBible.title_book(book); if (strlen(book_name) >= lcd.columns()) book_name[lcd.columns()] = 0; lcd.printf("%s%s%d:%d", book_name, strlen(book_name)==lcd.columns()?"":"\n", chapter+1, verse+1); //console.printf("%s %d:%d", abbrev, chapter+1, verse+1); delete [] book_name; } bool BibleUI::append_next_verse(char*& text, short len, short book, short chapter, short verse) { int size = 0; do { // get next verse char* next_verse = HolyBible.text_verse(book, chapter, ++verse); if (next_verse == 0) return size>0; // stop at end of chapter, return true if text has lengthened at all size += strlen(next_verse); // combine char* new_verse = new char[strlen(text)+strlen(next_verse)+2]; // add room for space delimiter and nul strcpy(new_verse, text); strcat(new_verse, " "); strcat(new_verse, next_verse); delete [] text; delete [] next_verse; text = new_verse; } while (size < len); // repeat until enough characters added return size>0; } void BibleUI::display_verse() { // fix positioning, check ranges char* text = 0; if (offset < 0) { if (offset > -lcd.rows()*lcd.columns()) offset = 0; // be sure to show beginning of verse else { if (verse > 0) // previous verse --verse; else { if (chapter > 0) // last verse in previous chapter { --chapter; verse = HolyBible.get_num_verses(book, chapter)-1; } else if (book > 0) // last verse in previous book { --book; chapter = HolyBible.get_num_chapters(book)-1; verse = HolyBible.get_num_verses(book, chapter)-1; } else //end of bible { book = HolyBible.get_num_books() - 1; chapter = HolyBible.get_num_chapters(book)-1; verse = HolyBible.get_num_verses(book, chapter)-1; } display_nav(); wait(0.5); } } text = HolyBible.text_verse(book, chapter, verse); if (offset < 0) { int len = strlen(text); if (len > lcd.rows()*lcd.columns()) offset = len-lcd.rows()*lcd.columns(); else offset = 0; } } else text = HolyBible.text_verse(book, chapter, verse); if (offset == 0) { char* text_start = strchr(text, ' '); //console.printf("%s\n", text_start); } if (offset >= strlen(text)) { offset = 0; delete [] text; text = HolyBible.text_verse(book, chapter, ++verse); // next verse if (text == 0) { text = HolyBible.text_verse(book, ++chapter, verse=0); // next chapter if (text == 0) text = HolyBible.text_verse(++book, chapter=0, verse=0); // next book if (text == 0) text = HolyBible.text_verse(book=0, chapter=0, verse=0); // end display_nav(); wait(0.5); } } // append enough text to fill screen and then some append_next_verse(text, lcd.rows()*(lcd.columns()+1), book, chapter, verse); // find current position within verse(s) char* verse_text = text + offset; // make a copy starting at offset so it can be modified char* copy = new char[strlen(verse_text)+1]; strcpy(copy, verse_text); delete [] text; // note: text was allocated, verse_text was just pointed into it text = 0; verse_text = 0; // word wrap disp_len = word_wrap(copy); /* if (strlen(copy) > lcd.rows()*lcd.columns()) copy[lcd.rows()*lcd.columns()] = 0; disp_len = strlen(copy); */ // display lcd.locate(0, 0); lcd.printf("%s", copy); // pad with spaces (avoids cls which can cause some flashing) int max_len = lcd.rows()*lcd.columns(); for (int i=strlen(copy); i<max_len; ++i) lcd.printf(" "); // free up storage delete [] copy; copy = 0; } static bool alphanumeric(char c) { return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'; } static bool punc_or_space(char c) { return !alphanumeric(c); } int BibleUI::word_wrap(char* &text) { // make copy of original text for later comparison char* orig = new char[strlen(text)+1]; strcpy(orig, text); // remove unwanted strings remove_string(text, "# "); remove_string(text, "["); remove_string(text, "]"); // scan each line for (int row=0; row<lcd.rows(); ++row) { int start = row * lcd.columns(); int end = start + lcd.columns(); // remove spaces at start of line while (start < strlen(text) && text[start]==' ') { // delete one character int left = start; int right = strlen(text+left+1); char* new_text = new char[left+right+1]; memcpy(new_text, text, left); new_text[left] = 0; strcat(new_text, text+left+1); delete [] text; text = new_text; } // insert spaces to wrap words if (strlen(text) > end && !punc_or_space(text[end])) { int left; for (left=end-1; left>=start && !punc_or_space(text[left]); --left) ; if (left > start && left < end) { ++left; int right=strlen(text+left); int spaces=end-left; //printf("row=%d total=%d left=%d spaces=%d right=%d\n", row, strlen(text), left, spaces, right); char* new_text = new char[left+spaces+right+1]; memcpy(new_text, text, left); new_text[left] = 0; for (int i=0; i<spaces; ++i) strcat(new_text, " "); strcat(new_text, text+left); delete [] text; text = new_text; } } } // truncate to screen size int max_len = lcd.rows()*lcd.columns(); if (strlen(text)>=max_len) text[max_len] = 0; // make copy with non-spaces char* nospaces = new char[strlen(text)+1]; int len = 0; for (int i=0; i<strlen(text); ++i) if (text[i] != ' ') nospaces[len++] = text[i]; nospaces[len] = 0; // calculate the number of characters displayed int src=0; for (int dest=0; dest<len; ++dest) { // non-space character must be there, advance in original until find it while (orig[src++] != nospaces[dest]) ; } // free up storage delete [] nospaces; nospaces = 0; delete [] orig; orig = 0; // return length of source string that matches wrapped text, so know how to advance to next portion of verse return src; } void BibleUI::remove_string(char* &text, char* find) { while (true) { char* p = strstr(text, find); if (p == 0) return; int left = p-text; int right = strlen(p+strlen(find)); char* new_text = new char[left+right+1]; memcpy(new_text, text, left); new_text[left] = 0; strcat(new_text, p+strlen(find)); delete [] text; text = new_text; } } void BibleUI::show_title() { lcd.cls(); char* title = HolyBible.title(); lcd.printf("%s", title); //console.printf("%s\n", title); delete [] title; title = 0; } void BibleUI::main() { enum emode { MODE_OFFSET=0, MODE_VERSE=1, MODE_CHAPTER=2, MODE_BOOK=3, MODE_4xBOOK=4, MODE_BOOKMARK=5 }; emode mode = MODE_OFFSET; display_nav(); wait(0.5); display_verse(); // debug: display all text /* int last_book = book; int pages = -1; while (book >= last_book) { ++pages; last_book = book; display_verse(); char* book_name = HolyBible.title_book(book); console.printf("%s %hd:%hd \r", book_name, chapter+1, verse+1); delete [] book_name; //wait(0.025); offset += disp_len; } lcd.cls(); lcd.printf("%d", pages); return; */ Timer timer; int old_buttons = 0; // start assuming nothing pressed while (true) { // wait for something to happen int new_buttons = rb + 2*lb; if (new_buttons != old_buttons) { // simple debounce logic, wait for 25ms of steady state else start over bool debounce = false; int buttons = new_buttons; timer.reset(); timer.start(); led1 = lb; led2 = rb; while (timer.read_ms() < 25) { int new_buttons = rb + 2*lb; if (new_buttons != buttons) { debounce = false; break; } else debounce = true; } //console.printf("debounce: %d\n", new_buttons); if (debounce && new_buttons == 0) { while (true) { int new_buttons = rb + 2*lb; led1 = lb; led2 = rb; if (new_buttons != 0) break; if (timer.read_ms() > 2000) { mode = MODE_OFFSET; display_verse(); break; } } buttons = new_buttons = 0; // pretend we didn't see a change yet } else if (debounce && new_buttons != 0) { timer.reset(); timer.start(); emode next_mode = mode; int start_ms = 0; while(true) { int new_buttons = rb + 2*lb; led1 = lb; led2 = rb; if (buttons != new_buttons) { mode = next_mode; if (mode == MODE_OFFSET || mode == MODE_VERSE) display_verse(); break; } { int elapsed = timer.read_ms(); int inc_elapsed = elapsed - start_ms; if (elapsed >= 4000 && inc_elapsed >= 250 || mode == MODE_4xBOOK && inc_elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500) ) { //console.printf("4xBOOK elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); if (buttons == 3) { } if (buttons == 2) { book-=4; if (book < 0) book = 0; } if (buttons == 1) { book+=4; if (book >= HolyBible.get_num_books()) book = HolyBible.get_num_books()-1; } if (buttons != 3) { chapter = 0; verse = 0; offset = 0; display_nav(); next_mode = MODE_4xBOOK; } start_ms += inc_elapsed; } else if (elapsed >= 3000 && inc_elapsed >= 250 || mode == MODE_BOOK && inc_elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500) ) { //console.printf("BOOK elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); if (buttons == 3) { } if (buttons == 2) { if (chapter == 0 && --book < 0) book = 0; chapter = 0; verse = 0; offset = 0; } if (buttons == 1) { if (++book >= HolyBible.get_num_books()) { book = HolyBible.get_num_books()-1; chapter = HolyBible.get_num_chapters(book)-1; verse = HolyBible.get_num_verses(book, chapter)-1; offset = 0; } else { chapter = 0; verse = 0; offset = 0; } } if (buttons != 3) { display_nav(); next_mode = MODE_BOOK; } start_ms += inc_elapsed; } else if (elapsed >= 2000 && inc_elapsed >= 250 || mode == MODE_CHAPTER && inc_elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500) ) { //console.printf("CHAPTER elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); if (buttons == 3) { } if (buttons == 2) { if (verse == 0 && --chapter < 0) { if (--book < 0) { book = 0; chapter = 0; } else chapter = HolyBible.get_num_chapters(book)-1; } } if (buttons == 1) { if (++chapter >= HolyBible.get_num_chapters(book)) { if (++book >= HolyBible.get_num_books()) { book = HolyBible.get_num_books()-1; chapter = HolyBible.get_num_chapters(book)-1; } else chapter = 0; } } if (buttons != 3) { verse = 0; offset = 0; display_nav(); next_mode = MODE_CHAPTER; } start_ms += inc_elapsed; } else if (elapsed >= 1000 && inc_elapsed >= 250 || mode == MODE_VERSE && inc_elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500) ) { //console.printf("VERSE elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); if (buttons == 3 && next_mode == MODE_BOOKMARK) { if (!HolyBible.bookmark_add(book, chapter, verse)) HolyBible.bookmark_del(book, chapter, verse); for (int i=0; i<3; ++i) { led4 = 1; wait(0.2); led4 = 0; wait(0.2); } next_mode = MODE_OFFSET; } if (buttons == 2) { if (offset > 0) offset = 0; else if (--verse < 0) { verse = 0; offset = -lcd.rows()*lcd.columns(); } else offset = 0; } if (buttons == 1) { char* text = HolyBible.text_verse(book, chapter, verse); offset = strlen(text); delete [] text; } display_verse(); next_mode = MODE_VERSE; start_ms += inc_elapsed; } else if (mode == MODE_OFFSET && elapsed >= 25 && elapsed < 1000 && (start_ms == 0 || inc_elapsed >= 500)) { //console.printf("OFFSET elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); if (buttons == 3) next_mode = MODE_BOOKMARK; if (buttons == 2) offset -= lcd.rows()*lcd.columns(); if (buttons == 1) offset += disp_len; if (buttons != 3) { display_verse(); next_mode = MODE_OFFSET; } start_ms += inc_elapsed; } else if (mode == MODE_BOOKMARK && elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500)) { if (buttons == 2) { if (HolyBible.bookmark_prev(book, chapter, verse)) { offset = 0; display_nav(); } } if (buttons == 1) { if (HolyBible.bookmark_next(book, chapter, verse)) { offset = 0; display_nav(); } } start_ms += inc_elapsed; } } } } old_buttons = new_buttons; } } } void BibleUI::indexing(int progress, void* context) { //console.printf("Indexing %d%%\n", progress); TextLCD& lcd = *(TextLCD*)context; lcd.cls(); lcd.printf("Indexing"); lcd.locate(0,1); lcd.printf("%d%%", progress); }