Bible eBook Prototype More details at: http://mbed.org/users/davervw/notebook/ebible-abstract/
Dependencies: SDHCFileSystem TextLCD mbed BibleIO
BibleUI.cpp
00001 /* Bible UI Class Implementation - KJV Bible eBook Browser 00002 * 00003 * Copyright (c) 2011 David R. Van Wagner davervw@yahoo.com 00004 * 00005 * Permission is hereby granted, free of charge, to any person obtaining a copy 00006 * of this software and associated documentation files (the "Software"), to deal 00007 * in the Software without restriction, including without limitation the rights 00008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00009 * copies of the Software, and to permit persons to whom the Software is 00010 * furnished to do so, subject to the following conditions: 00011 * 00012 * The above copyright notice and this permission notice shall be included in 00013 * all copies or substantial portions of the Software. 00014 * 00015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 00021 * THE SOFTWARE. 00022 */ 00023 00024 #include "BibleUI.h" 00025 00026 //extern Serial console; 00027 00028 BibleUI::BibleUI(BibleIO &bible, TextLCD &textlcd, DigitalIn& left, DigitalIn& right) 00029 :HolyBible(bible), lcd(textlcd), lb(left), rb(right), led1(LED1), led2(LED2), led3(LED3), led4(LED4) 00030 { 00031 //console.printf("\n\n" "Bible LCD\n\n"); 00032 lcd.cls(); 00033 lcd.printf("BibleLCD"); 00034 00035 wait(0.5); 00036 00037 show_title(); 00038 00039 book = 0; 00040 chapter = 0; 00041 verse = 0; 00042 offset = 0; 00043 disp_len = 0; 00044 } 00045 00046 void BibleUI::start() 00047 { 00048 main(); 00049 } 00050 00051 void BibleUI::display_nav() 00052 { 00053 lcd.cls(); 00054 char* book_name = HolyBible.title_book(book); 00055 if (strlen(book_name) >= lcd.columns()) 00056 book_name[lcd.columns()] = 0; 00057 lcd.printf("%s%s%d:%d", book_name, strlen(book_name)==lcd.columns()?"":"\n", chapter+1, verse+1); 00058 //console.printf("%s %d:%d", abbrev, chapter+1, verse+1); 00059 delete [] book_name; 00060 } 00061 00062 bool BibleUI::append_next_verse(char*& text, short len, short book, short chapter, short verse) 00063 { 00064 int size = 0; 00065 do 00066 { 00067 // get next verse 00068 char* next_verse = HolyBible.text_verse(book, chapter, ++verse); 00069 if (next_verse == 0) 00070 return size>0; // stop at end of chapter, return true if text has lengthened at all 00071 size += strlen(next_verse); 00072 00073 // combine 00074 char* new_verse = new char[strlen(text)+strlen(next_verse)+2]; // add room for space delimiter and nul 00075 strcpy(new_verse, text); 00076 strcat(new_verse, " "); 00077 strcat(new_verse, next_verse); 00078 delete [] text; 00079 delete [] next_verse; 00080 text = new_verse; 00081 } while (size < len); // repeat until enough characters added 00082 00083 return size>0; 00084 } 00085 00086 void BibleUI::display_verse() 00087 { 00088 // fix positioning, check ranges 00089 char* text = 0; 00090 if (offset < 0) 00091 { 00092 if (offset > -lcd.rows()*lcd.columns()) 00093 offset = 0; // be sure to show beginning of verse 00094 else 00095 { 00096 if (verse > 0) // previous verse 00097 --verse; 00098 else 00099 { 00100 if (chapter > 0) // last verse in previous chapter 00101 { 00102 --chapter; 00103 verse = HolyBible.get_num_verses(book, chapter)-1; 00104 } 00105 else if (book > 0) // last verse in previous book 00106 { 00107 --book; 00108 chapter = HolyBible.get_num_chapters(book)-1; 00109 verse = HolyBible.get_num_verses(book, chapter)-1; 00110 } 00111 else //end of bible 00112 { 00113 book = HolyBible.get_num_books() - 1; 00114 chapter = HolyBible.get_num_chapters(book)-1; 00115 verse = HolyBible.get_num_verses(book, chapter)-1; 00116 } 00117 display_nav(); 00118 wait(0.5); 00119 } 00120 } 00121 text = HolyBible.text_verse(book, chapter, verse); 00122 if (offset < 0) 00123 { 00124 int len = strlen(text); 00125 if (len > lcd.rows()*lcd.columns()) 00126 offset = len-lcd.rows()*lcd.columns(); 00127 else 00128 offset = 0; 00129 } 00130 } 00131 else 00132 text = HolyBible.text_verse(book, chapter, verse); 00133 if (offset == 0) 00134 { 00135 char* text_start = strchr(text, ' '); 00136 //console.printf("%s\n", text_start); 00137 } 00138 if (offset >= strlen(text)) 00139 { 00140 offset = 0; 00141 delete [] text; 00142 text = HolyBible.text_verse(book, chapter, ++verse); // next verse 00143 if (text == 0) 00144 { 00145 text = HolyBible.text_verse(book, ++chapter, verse=0); // next chapter 00146 if (text == 0) 00147 text = HolyBible.text_verse(++book, chapter=0, verse=0); // next book 00148 if (text == 0) 00149 text = HolyBible.text_verse(book=0, chapter=0, verse=0); // end 00150 display_nav(); 00151 wait(0.5); 00152 } 00153 } 00154 00155 // append enough text to fill screen and then some 00156 append_next_verse(text, lcd.rows()*(lcd.columns()+1), book, chapter, verse); 00157 00158 // find current position within verse(s) 00159 char* verse_text = text + offset; 00160 00161 // make a copy starting at offset so it can be modified 00162 char* copy = new char[strlen(verse_text)+1]; 00163 strcpy(copy, verse_text); 00164 delete [] text; // note: text was allocated, verse_text was just pointed into it 00165 text = 0; 00166 verse_text = 0; 00167 00168 // word wrap 00169 disp_len = word_wrap(copy); 00170 /* 00171 if (strlen(copy) > lcd.rows()*lcd.columns()) 00172 copy[lcd.rows()*lcd.columns()] = 0; 00173 disp_len = strlen(copy); 00174 */ 00175 00176 // display 00177 lcd.locate(0, 0); 00178 lcd.printf("%s", copy); 00179 00180 // pad with spaces (avoids cls which can cause some flashing) 00181 int max_len = lcd.rows()*lcd.columns(); 00182 for (int i=strlen(copy); i<max_len; ++i) 00183 lcd.printf(" "); 00184 00185 // free up storage 00186 delete [] copy; 00187 copy = 0; 00188 } 00189 00190 static bool alphanumeric(char c) 00191 { 00192 return c >= '0' && c <= '9' 00193 || c >= 'A' && c <= 'Z' 00194 || c >= 'a' && c <= 'z'; 00195 } 00196 00197 static bool punc_or_space(char c) 00198 { 00199 return !alphanumeric(c); 00200 } 00201 00202 int BibleUI::word_wrap(char* &text) 00203 { 00204 // make copy of original text for later comparison 00205 char* orig = new char[strlen(text)+1]; 00206 strcpy(orig, text); 00207 00208 // remove unwanted strings 00209 remove_string(text, "# "); 00210 remove_string(text, "["); 00211 remove_string(text, "]"); 00212 00213 // scan each line 00214 for (int row=0; row<lcd.rows(); ++row) 00215 { 00216 int start = row * lcd.columns(); 00217 int end = start + lcd.columns(); 00218 00219 // remove spaces at start of line 00220 while (start < strlen(text) && text[start]==' ') 00221 { 00222 // delete one character 00223 int left = start; 00224 int right = strlen(text+left+1); 00225 char* new_text = new char[left+right+1]; 00226 memcpy(new_text, text, left); 00227 new_text[left] = 0; 00228 strcat(new_text, text+left+1); 00229 delete [] text; 00230 text = new_text; 00231 } 00232 00233 // insert spaces to wrap words 00234 if (strlen(text) > end && !punc_or_space(text[end])) 00235 { 00236 int left; 00237 for (left=end-1; left>=start && !punc_or_space(text[left]); --left) 00238 ; 00239 if (left > start && left < end) 00240 { 00241 ++left; 00242 int right=strlen(text+left); 00243 int spaces=end-left; 00244 //printf("row=%d total=%d left=%d spaces=%d right=%d\n", row, strlen(text), left, spaces, right); 00245 char* new_text = new char[left+spaces+right+1]; 00246 memcpy(new_text, text, left); 00247 new_text[left] = 0; 00248 for (int i=0; i<spaces; ++i) 00249 strcat(new_text, " "); 00250 strcat(new_text, text+left); 00251 delete [] text; 00252 text = new_text; 00253 } 00254 } 00255 } 00256 00257 // truncate to screen size 00258 int max_len = lcd.rows()*lcd.columns(); 00259 if (strlen(text)>=max_len) 00260 text[max_len] = 0; 00261 00262 // make copy with non-spaces 00263 char* nospaces = new char[strlen(text)+1]; 00264 int len = 0; 00265 for (int i=0; i<strlen(text); ++i) 00266 if (text[i] != ' ') 00267 nospaces[len++] = text[i]; 00268 nospaces[len] = 0; 00269 00270 // calculate the number of characters displayed 00271 int src=0; 00272 for (int dest=0; dest<len; ++dest) 00273 { 00274 // non-space character must be there, advance in original until find it 00275 while (orig[src++] != nospaces[dest]) 00276 ; 00277 } 00278 00279 // free up storage 00280 delete [] nospaces; 00281 nospaces = 0; 00282 delete [] orig; 00283 orig = 0; 00284 00285 // return length of source string that matches wrapped text, so know how to advance to next portion of verse 00286 return src; 00287 } 00288 00289 void BibleUI::remove_string(char* &text, char* find) 00290 { 00291 while (true) 00292 { 00293 char* p = strstr(text, find); 00294 if (p == 0) 00295 return; 00296 int left = p-text; 00297 int right = strlen(p+strlen(find)); 00298 char* new_text = new char[left+right+1]; 00299 memcpy(new_text, text, left); 00300 new_text[left] = 0; 00301 strcat(new_text, p+strlen(find)); 00302 delete [] text; 00303 text = new_text; 00304 } 00305 } 00306 00307 void BibleUI::show_title() 00308 { 00309 lcd.cls(); 00310 char* title = HolyBible.title(); 00311 lcd.printf("%s", title); 00312 //console.printf("%s\n", title); 00313 delete [] title; 00314 title = 0; 00315 } 00316 00317 void BibleUI::main() 00318 { 00319 enum emode { MODE_OFFSET=0, MODE_VERSE=1, MODE_CHAPTER=2, MODE_BOOK=3, MODE_4xBOOK=4, MODE_BOOKMARK=5 }; 00320 00321 emode mode = MODE_OFFSET; 00322 00323 display_nav(); 00324 wait(0.5); 00325 display_verse(); 00326 00327 // debug: display all text 00328 /* 00329 int last_book = book; 00330 int pages = -1; 00331 while (book >= last_book) 00332 { 00333 ++pages; 00334 last_book = book; 00335 00336 display_verse(); 00337 00338 char* book_name = HolyBible.title_book(book); 00339 console.printf("%s %hd:%hd \r", book_name, chapter+1, verse+1); 00340 delete [] book_name; 00341 00342 //wait(0.025); 00343 offset += disp_len; 00344 } 00345 lcd.cls(); 00346 lcd.printf("%d", pages); 00347 return; 00348 */ 00349 00350 Timer timer; 00351 int old_buttons = 0; // start assuming nothing pressed 00352 while (true) 00353 { 00354 // wait for something to happen 00355 int new_buttons = rb + 2*lb; 00356 if (new_buttons != old_buttons) 00357 { 00358 // simple debounce logic, wait for 25ms of steady state else start over 00359 bool debounce = false; 00360 int buttons = new_buttons; 00361 timer.reset(); 00362 timer.start(); 00363 00364 led1 = lb; 00365 led2 = rb; 00366 00367 while (timer.read_ms() < 25) 00368 { 00369 int new_buttons = rb + 2*lb; 00370 if (new_buttons != buttons) 00371 { 00372 debounce = false; 00373 break; 00374 } 00375 else 00376 debounce = true; 00377 } 00378 //console.printf("debounce: %d\n", new_buttons); 00379 00380 if (debounce && new_buttons == 0) 00381 { 00382 while (true) 00383 { 00384 int new_buttons = rb + 2*lb; 00385 led1 = lb; 00386 led2 = rb; 00387 00388 if (new_buttons != 0) 00389 break; 00390 if (timer.read_ms() > 2000) 00391 { 00392 mode = MODE_OFFSET; 00393 display_verse(); 00394 break; 00395 } 00396 } 00397 buttons = new_buttons = 0; // pretend we didn't see a change yet 00398 } 00399 else if (debounce && new_buttons != 0) 00400 { 00401 timer.reset(); 00402 timer.start(); 00403 00404 emode next_mode = mode; 00405 00406 int start_ms = 0; 00407 while(true) 00408 { 00409 int new_buttons = rb + 2*lb; 00410 led1 = lb; 00411 led2 = rb; 00412 00413 if (buttons != new_buttons) 00414 { 00415 mode = next_mode; 00416 if (mode == MODE_OFFSET || mode == MODE_VERSE) 00417 display_verse(); 00418 break; 00419 } 00420 00421 { 00422 int elapsed = timer.read_ms(); 00423 int inc_elapsed = elapsed - start_ms; 00424 if (elapsed >= 4000 && inc_elapsed >= 250 || mode == MODE_4xBOOK && inc_elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500) ) 00425 { 00426 //console.printf("4xBOOK elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); 00427 if (buttons == 3) 00428 { 00429 } 00430 if (buttons == 2) 00431 { 00432 book-=4; 00433 if (book < 0) 00434 book = 0; 00435 } 00436 if (buttons == 1) 00437 { 00438 book+=4; 00439 if (book >= HolyBible.get_num_books()) 00440 book = HolyBible.get_num_books()-1; 00441 } 00442 if (buttons != 3) 00443 { 00444 chapter = 0; 00445 verse = 0; 00446 offset = 0; 00447 display_nav(); 00448 next_mode = MODE_4xBOOK; 00449 } 00450 start_ms += inc_elapsed; 00451 } 00452 else if (elapsed >= 3000 && inc_elapsed >= 250 || mode == MODE_BOOK && inc_elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500) ) 00453 { 00454 //console.printf("BOOK elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); 00455 if (buttons == 3) 00456 { 00457 } 00458 if (buttons == 2) 00459 { 00460 if (chapter == 0 && --book < 0) 00461 book = 0; 00462 chapter = 0; 00463 verse = 0; 00464 offset = 0; 00465 } 00466 if (buttons == 1) 00467 { 00468 if (++book >= HolyBible.get_num_books()) 00469 { 00470 book = HolyBible.get_num_books()-1; 00471 chapter = HolyBible.get_num_chapters(book)-1; 00472 verse = HolyBible.get_num_verses(book, chapter)-1; 00473 offset = 0; 00474 } 00475 else 00476 { 00477 chapter = 0; 00478 verse = 0; 00479 offset = 0; 00480 } 00481 } 00482 if (buttons != 3) 00483 { 00484 display_nav(); 00485 next_mode = MODE_BOOK; 00486 } 00487 start_ms += inc_elapsed; 00488 } 00489 else if (elapsed >= 2000 && inc_elapsed >= 250 || mode == MODE_CHAPTER && inc_elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500) ) 00490 { 00491 //console.printf("CHAPTER elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); 00492 if (buttons == 3) 00493 { 00494 } 00495 if (buttons == 2) 00496 { 00497 if (verse == 0 && --chapter < 0) 00498 { 00499 if (--book < 0) 00500 { 00501 book = 0; 00502 chapter = 0; 00503 } 00504 else 00505 chapter = HolyBible.get_num_chapters(book)-1; 00506 } 00507 } 00508 if (buttons == 1) 00509 { 00510 if (++chapter >= HolyBible.get_num_chapters(book)) 00511 { 00512 if (++book >= HolyBible.get_num_books()) 00513 { 00514 book = HolyBible.get_num_books()-1; 00515 chapter = HolyBible.get_num_chapters(book)-1; 00516 } 00517 else 00518 chapter = 0; 00519 } 00520 } 00521 if (buttons != 3) 00522 { 00523 verse = 0; 00524 offset = 0; 00525 display_nav(); 00526 next_mode = MODE_CHAPTER; 00527 } 00528 start_ms += inc_elapsed; 00529 } 00530 else if (elapsed >= 1000 && inc_elapsed >= 250 || mode == MODE_VERSE && inc_elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500) ) 00531 { 00532 //console.printf("VERSE elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); 00533 if (buttons == 3 && next_mode == MODE_BOOKMARK) 00534 { 00535 if (!HolyBible.bookmark_add(book, chapter, verse)) 00536 HolyBible.bookmark_del(book, chapter, verse); 00537 for (int i=0; i<3; ++i) 00538 { 00539 led4 = 1; 00540 wait(0.2); 00541 led4 = 0; 00542 wait(0.2); 00543 } 00544 00545 next_mode = MODE_OFFSET; 00546 } 00547 if (buttons == 2) 00548 { 00549 if (offset > 0) 00550 offset = 0; 00551 else if (--verse < 0) 00552 { 00553 verse = 0; 00554 offset = -lcd.rows()*lcd.columns(); 00555 } 00556 else 00557 offset = 0; 00558 } 00559 if (buttons == 1) 00560 { 00561 char* text = HolyBible.text_verse(book, chapter, verse); 00562 offset = strlen(text); 00563 delete [] text; 00564 } 00565 display_verse(); 00566 next_mode = MODE_VERSE; 00567 start_ms += inc_elapsed; 00568 } 00569 else if (mode == MODE_OFFSET && elapsed >= 25 && elapsed < 1000 && (start_ms == 0 || inc_elapsed >= 500)) 00570 { 00571 //console.printf("OFFSET elapsed=%d inc_elapsed=%d\n", elapsed, inc_elapsed); 00572 if (buttons == 3) 00573 next_mode = MODE_BOOKMARK; 00574 if (buttons == 2) 00575 offset -= lcd.rows()*lcd.columns(); 00576 if (buttons == 1) 00577 offset += disp_len; 00578 if (buttons != 3) 00579 { 00580 display_verse(); 00581 next_mode = MODE_OFFSET; 00582 } 00583 start_ms += inc_elapsed; 00584 } 00585 else if (mode == MODE_BOOKMARK && elapsed >= 25 && (start_ms == 0 || inc_elapsed >= 500)) 00586 { 00587 if (buttons == 2) 00588 { 00589 if (HolyBible.bookmark_prev(book, chapter, verse)) 00590 { 00591 offset = 0; 00592 display_nav(); 00593 } 00594 } 00595 if (buttons == 1) 00596 { 00597 if (HolyBible.bookmark_next(book, chapter, verse)) 00598 { 00599 offset = 0; 00600 display_nav(); 00601 } 00602 } 00603 start_ms += inc_elapsed; 00604 } 00605 } 00606 } 00607 } 00608 old_buttons = new_buttons; 00609 } 00610 } 00611 } 00612 00613 void BibleUI::indexing(int progress, void* context) 00614 { 00615 //console.printf("Indexing %d%%\n", progress); 00616 TextLCD& lcd = *(TextLCD*)context; 00617 lcd.cls(); 00618 lcd.printf("Indexing"); 00619 lcd.locate(0,1); 00620 lcd.printf("%d%%", progress); 00621 }
Generated on Sat Jul 16 2022 05:22:41 by 1.7.2