Bible eBook Prototype More details at: http://mbed.org/users/davervw/notebook/ebible-abstract/

Dependencies:   SDHCFileSystem TextLCD mbed BibleIO

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BibleUI.cpp Source File

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 }