Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Latin Textbook and Look-up Table in C#

0.00/5 (No votes)
23 Apr 2010 1  
Wheelock's Latin textbook with Cassell's Latin Dictionary, written in C#.

Why Latin?

For years now I've been making an effort to learn a bit of latin. Though the learning has been considerably slow its given me the opportunity to write a Latin Project which has been helpful along the way. In a previous article Spell Weller, but Grammar's Up to You I make reference to this Latin Project without providing any code and had some haunting qualms about this remiss.  So I determined to dedicate an entire article exclusively to this project and reformat the data in order to make it possible for you to upload it onto this venue and make it publicly available.

The reason why I decided to try to learn latin in the first place may have been due to a small-c catholic upbringing which could do nothing with my innate atheistic ways, or it may stem from the night I cracked a rib watching Monty Python's Life of Brian.

romani_ite_domum.jpg

Whatever the case may be, you'll find that this application is better than any other Latin project you'll find anywhere. One program that compares is Whittaker's Words which does much of what this app's Look-up Table does in a DOS window interface. Not as user-friendly as it might otherwise be but given its download file size and ease to install it is really quite remarkable!

To start with I had to personally type the entire first-half (Latin to English from A-Z) of Cassell's Latin/English-English/Latin Dictionary (24000 word entries!) when I first began my Latin studies using Wheelock's Latin textbook about three years ago. Over a two year period I then wrote the first version of this program in VB in a piece-meal fashion by adding whatever new conjugations or declensions I learned from my hard-cover Latin textbook into the project so that my computer could ease the learning process. 

Then, about a year ago, I rewrote the whole thing in about one month using C#. This newer version is much much better. Since the original VB was so ill-planned (not planned at all!) this C# re-write is considerably faster and more efficient. Since then I've also typed in the Wheelock's Latin textbook and have made some modifications to the files which were originally written in a textfile format and are now in XML with formatting information taken from RTF files by way of code from the Writing Your Own RTF Converter Code-Project article which greatly facilitated the task.

The most difficult part in writing the original VB version was teaching the code to figure out what type of word each word entry in the Cassell's dictionary was.  Is it a noun?  an adjective?  which declension?  verb?  adverb, indeclinable, pronoun, and so on, because that dictionary did not have all the information that a latin scholar would know in simply looking at entry.  Definitions that started with the word 'to' like 'to run' or 'to write' were assumed to be verbs.  Most nouns have only two principal parts buts so do some adjectives.  In short the whole process of getting this working properly took the better part of a frustrating two years and I had no intention of going through all that again in the C# rewrite, so I used the VB code to copy every word entry from the first hand-typed version, figure out what each word is, then store this information in the word's definition and save it in a separate directory.  That done I was then prepared to take on the C# rewrite which you have here.

You have to understand that I publish this article, the software and the copyright protected Cassell's Dictionary & Wheelock's Latin textbook at the risk of receiving angry e-mails from this guy :

this_guy.jpg

who recorded the audio files which are incorporated into this program.

more on that later, but first, let's get this Latin Project up and running on your computer.

installation

The first thing you'll need to do is create a sub-directory 'Latin' on your C: hard-drive. I won't apologize for this bit of hard-coding but will only suggest that if you're an actual toga-wearing Latinist who already has, for some reason, a "c:\latin\" directory on your hard-drive and think that adding this project there will create a conflict just change the first line in the form

formLatinProject

which you can see here

namespace Latin_Project
{
    public partial class formLatinProject : Form
    {
        string strWorkingDirectory = "c:\\latin\\";

to whatever suits your fancy and then follow these instructions with that change taken into consideration.

The five compressed files listed at the top of this article need to be decompressed in the order they appear.

  1. decompress the first two zip files 0_Latin_AtoL.zip & 1_Latin_MtoZ_plus.zip into the c:\latin\ subdirectory and you'll have 26 sub-subdirectorys subDir_A to subDir_Z with all 24000 xml dictionary entries included.
  2. then decompress the next file, 2_Latin_Wheelock_V3.zip, in exactly the same way and you'll have a 27th sub-subdirectory called 'wheelock'.
  3. the 4th file, 3_Latin_Wheelock_Images.zip, needs to be decompressed into the c:\latin\wheelock\ sub-directory. And now you've got all the files you'll need to run the program
  4. for you non-coding newbies who stumbled in here without a clue what you were getting into, you'll have to down-load & install C# 2010 from Microsoft Express Download. this is where you might want to call the grand-kids for some help.
  5. assuming you now have all that on your computer, you can go ahead and download the source code compressed in the file latin_src_v8.zip, decompress it wherever you like, then load it inside your C# IDE(integrated-development environment) and press F5.
  6. Optional : you can download those audio files from Wheelock's website for free. decompress them directly into the c:\latin\ directory and the computer will detect them and figure out what to do.

    at this point your hard-drive should look something like this (minus those two mega files) :

    Directory_View.PNG

    Now you're all set!

    no, not quite. the program is good to go like that but you'll be missing out on the main feature the 'Look-Up Table' or LUT. I'll tell you more about that later, for now, when you run the program it'll tell you that it doesn't have the files for the LUT and you'll have to let it do some work on its own before you can have this very handy feature. The reason for this extra work is that the files which the program uses when its done are close to 1GB in size, and are therefore too big to include with this article. But no worries, when it prompts you and asks if you want to create the LUT files just say 'no' for now and it'll ask you again next time. I'll write more about the LUT down below.

    Latina est Gaudium

    Yes, Latin is a joy! but first you'll have to pick up on the language because this isn't pig-latin we're talkin' you know! The language is old and for all intents and purposes currently considered dead but that's no reason to kick it when its down. Let me tell you a bit about Latin and why this program is more than just an electronic text-book. Nouns all have a gender including a third gender called 'neuter', which means 'neither' in Latin(bet you didn't know that!). Then each noun and adjective has six cases & two numbers(singular & plural). What's more is adjectives have three different types of ways to be used positive, comparative & superlative, all adjectives have three genders, all have two numbers, and all have six cases :

    latin_101.PNG

    3 (genders) x 3 (types) x 2 (number) x 6 cases = 108 different ways to spell the same adjective.

    every adjective.

    and verbs?!? between passive & active, plural & singular, you have the persons 1st, 2nd & 3rd, present past & future, subjunctive? participles, infinitives... there's a lot! and they're all very similar but different one from the next. and that's if you figure out if the verb is 1st conjugation, 2nd or 3rd! or maybe its a third IO. then there's this scary thing called the 'passive periphrastic'.

    In short, what I'm saying is, if you're trying to learn latin with nothing but a textbook you're going to run into some trouble. and that's where this program comes in.

    Opifex : skilled worker

    when you first launch the program you'll be looking at the front cover to Wheelock's Latin. The gray underlined-type print beneath the image are all links to other parts of the book. The textbook part of the program works in a way similar to HTML and the internet, except that its a network of files that are all contained in a single directory on your hard-drive which you can navigate through. This is done using a class called xmlRecord in combination with classGraphics which I talked about in GCIDE : A Complete English Language Dictionary.

    The classXmlLatin is similar but does not have the links between files which join the network of files that make up the textbook. This is because the dictionary files which it handles do not require any network and are loaded exclusively using references from the Look-Up Table. Remember the LUT mentioned earlier? This is where the Latin actually happens.  The classLatin relies on classXmlLatin to retrieve the dictionary entries which it uses to create the look-up table.

    When you finally get around to letting the program build you your LUT you'll find that the program will be very MIPs needy while in the process of building this database. It will keep your computer particularly busy for nearly 24 hours! You can stop and restart it any time you like at no extra cost and you may want to just let it work overnight. That seems like a long time but if you're downloading this program because you need (or want) to learn latin then you won't want to go without it.

    classLatin uses an opifex, or skilled worker, to do all the work. The program only needs to instantiate one classLatin, this class then creates itself a resource handler as well as that famous opifex on which it relies. The resource handler manages the classWordInfo. These word-info objects hold all the information from the dictionary entries which the opifex requires to conjugate or decline, whatever the case may be. The classWordInfo also has two combo boxes which can be put on the screen if need be. Any number of these can be requested from the resource-manager and whenever the user makes a selection change on either combo boxes the classWordInfo will pass itself over to the opifex to deliver the Latin.

    The actual code for classOpifex is not altogether very complicated because all the work is divided into a multitude of functions that are named with their specific tasks in mind. For most conjugation/declension functions the opifex receives as parameter an instance of a classWordInfo. Since each function is written for one exclusive purpose that's all any of these functions need. In the original Visual Basic version of this program I made the mistake of trying to have one function do everything and discovered that that created new problems with every new chapter of the textbook. This C# version doesn't have any one function with an "I can do it all" attitude but rather relies on a small army of functions that each handle a different latin conjugation or declension.

    here's an example ....

    public classLatin_C.udtSolution conjugate_Present_Active_Indicative(classWordInfo word)
    {
        if (verbIsDeponent(word))
            return conjugate_Present_Active_Indicative_Deponent(word);
    
        classLatin_C.udtSolution udrRetVal = new classLatin_C.udtSolution();
        string[,] strSolution = new string[2, 3];
        udrRetVal.strSolution = strSolution;
        udrRetVal.typeSolution = getTypeSolution(classLatin_C.enuVerbTenses.Present_Active_Indicative );
        // 1st, 2nd, 3rd, 4th, 3rd-IO 
        string[, ,] strEndings = {{{"ō", "ās","at"}, {"āmus","ātis","ant"}},
                                   {{"eō", "ēs","et"}, {"ēmus","ētis","ent"}},
                                   {{"ō", "is","it"}, {"imus","itis","unt"}},
                                   {{"iō", "īs","it"}, {"īmus","ītis","iunt"}},
                                   {{"iō", "is","it"}, {"imus","itis","iunt"}}};
    
        string strBase = word.strWords[1];
        strBase = strBase.Substring(0, strBase.Length - "are".Length);
        classLatin_C.enuVerbConjugations verbConjugation = getVerbConjugationFromWordType(word.wordType);
    
        for (classLatin_C.enuNumber numberCounter = classLatin_C.enuNumber.Singular;
                                    numberCounter <= classLatin_C.enuNumber.Plural;
                                    numberCounter++)
            for (classLatin_C.enuPerson personCounter = classLatin_C.enuPerson.First;
                                        personCounter <= classLatin_C.enuPerson.Third;
                                        personCounter++)
                udrRetVal.strSolution[(int)numberCounter, (int)personCounter] = shortenLongVowels(strBase + strEndings[(int)verbConjugation, (int)numberCounter, (int)personCounter]);
        udrRetVal.strRowNames = getRowNamesOfConjugation();
        udrRetVal.strColumnNames = getColumnNamesOfDeclension();
    
        return udrRetVal;
    }
    

    In this example the opifex is being requested to conjugate the word it is provided in the Present Active Indicative. It will first ask "is this a deponent verb?" and if so branch off to the opifex function which will take care of it if it is. Otherwise it creates an instance of a structure called udtSolution, translate the verb tense into a typeSolution(two enumerated types). Then, because the latin rule for this case is to chop off the last letters of the verb's second participle (strWord[1] in the classWordInfo) before adding the personal endings, it generates a variable called strBase and proceeds to add the appropriate endings, fetches the row-names & column-names for this type of solution and wires it all back to the calling function.

    If you take a closer look at these functions you'll find that they all pretty much do the same thing with only minor variations for their specific tasks. Because there are so many ways to conjugate and decline nouns, adjectives and verbs in latin it was just easier to do things this way than to worry about saving code-space by consolidating them all into one, or even only just a few functions. This design makes debugging much easier. This way, if next year I find that I've been declining 3rd declension feminine superlative adjectives incorrectly, there's only one function to fix and it won't affect anything else when I debug it.

    bada-bing, bada-boom.

    in Combo two

    Whenever a dictionary entry appears on the screen you'll find it comes with a few extra features besides the definition. Looking at the two combo-boxes above the word's entry you'll see that the one on the left tells you what kind of word you're looking at, e.g. magnus, -a, -um : first declension adjective. Listed below this you would see the three genders for each of the three ways this adjective can be declined : positive, comparative & superlative. You can choose any one of these entries and get an output. Selecting any specific gender will give you the 2 numbers & 6 case spellings for this adjective for the gender/type you've chosen. Clicking on the Comparative heading, somewhere near the middle of the list, will produce another form with all 3 genders x 2 numbers x 6 cases(all the comparative spellings of magnus!) and if you're still not sure what you're looking for the top most heading with the word's type-info I mentioned earlier will spit out all 108 ways this particular adjective can be spelled onto the screen in one clean sweep.

    verbs & nouns are similar but with tenses, passive & active or in the case of nouns just the same as adjectives but only for the one gender. just play around with it you'll see what I mean.

    and in the right corner!

    the other combo-box is something I call Latin logic. This is loaded with links to the Wheelock's Latin textbook's pages that are specifically related for the particular word you're looking at. You might be curious to know what a 3rd Conjugation-IO verb is when you popup a dictionary form with the definition for the word capio, -ere and scanning over the list of entries there you'll find exactly what you're looking for. Then when you select an entry from the Latin Logic combobox a whole new instance of your Latin textbook pops up on exactly the right page, chapter 10, sub-chapter "IO verbs" for you to read at your delight.

    there's a LUT to do

    You really should have your program build you a Look-up table because without it you're missing out on a lut!(sic). Let me explain. if you haven't been able to actually see a dictionary entry and tell the opifex to get to work for you, that's because you haven't yet got your look-up table on-line. once you do, its a wiz. What the look-up table does is very similar to what the GCIDE article mentioned above does : it helps you find what you're looking for. It tells you what a word is, how many ways that particular spelling can be derived, and where to find the root of the word you're looking at. To do this the program goes over every entry in the Latin/English dictionary, it loads each word-entry one at a time, generates every possible spelling it knows how for all of these words then stores them into a database along with the means by which each spelling was obtained. Then when the user asks it what the word capiunt is, for example, it scans the database and tells you that capiunt is the word you would have found in your pocket Latin dictionary listed under capio, -ere, cepi, captum had you picked it up off the shelf, however, the LUT will also tell you that its conjugated in the Present Active Indicative, third person, plural, which you would otherwise have to figure out for yourself.

    LUT__hard__at_work.PNG

    The image above shows you the textbook page 'On a Temperamental Friend' at the top, clicking on the word difficilis which appears in the first latin sentence shows you the difficilis form which lists all the possible ways this particular spelling can be derived and selecting the first one pops up the next form in front of it(Dictionary entry with its two combo-boxes - F1) or the blue form in front of that one (positive masculine declension - F3). You'll notice that this word also shows the "audio" button which when pressed audibly tells you how the root of this word is pronounced (that is, if you've downloaded the audio files). Notice the pretty blue? feminine nouns & adjectives are displayed in pink and neuters in gray.

    Latin's particular because had you been looking for the word "ceperant" in your pocket dictionary you'd probably not think to look under "capio" and may or may not find it. But with this LUT all that is done for you! Unfortunately so is most of the thinking which makes it very easy to rely entirely on the program and not do any actual latin-learning at all, as I have so happily discovered.

    The LUT first gives you a list of ways the word can be derived. From there you can ask it to give you the dictionary form of the entry you've selected, or to just put the solution to the screen straightaway. You can do either of these by clicking F1 for the one or F3 for the other. Alternately you can click the left half of the form or the right and a pop-up helpful text tells you which you're about to select.

    The program's mode selection allows you to choose between workbook, textbook and vocabulary. The textbook is the default start-up value and this is where you click on words, links or images. Clicking on a latin word will cause the program to search the LUT for the word you clicked making reading latin just a little bit less daunting for any newbies. If you happen to click a word it doesn't find in the LUT the program won't scowl or argue but rather it'll kick back and wait for your next request.  This way you can click an english-word and need not worry about confusing it though this would be a good place to include the GCIDE project if you already have it up and running on your computer. The picBox's mouse-click event in the form classGraphicOutputPanel below shows you where :

    void pic_MouseClick(object sender, MouseEventArgs e)
    {   
        if (wordUnderMouse != null) 
        {
            switch (wordUnderMouse.eWordImageType)
            {
                case enuWordImageType.link:
                    loadFile(wordUnderMouse.strAddress);
                    cutNavigation();
                    addNavigationPage();
                    break;
    
                case enuWordImageType.solution:
                    frmPopUp.Owner = mainForm;
                    frmPopUp.showText(wordUnderMouse.strAddress);
                    break;
    
                case enuWordImageType.image:
                    Form frmPic = new Form();
                    PictureBox pic = new PictureBox();
                    frmPic.Controls.Add(pic);
                    pic.SizeMode = PictureBoxSizeMode.AutoSize;
                    pic.Image = wordUnderMouse.bmp;
                    frmPic.Size = new Size(pic.Width + 15, pic.Height + 35);
                    frmPic.Text = wordUnderMouse.strText;
                    pic.Location = new Point(0, 0);
                    frmPic.KeyDown += new KeyEventHandler(cLibLatin.frm_KeyDown);
                    frmPic.Show();
                    break;
    
                case enuWordImageType.plain:
                    cLibLatin.searchLUT(cLib.removeNonAlpha(wordUnderMouse.strText));
                    // add GCIDE project's searchLUT here
                    break;
            }
        }
    }
    

    The textbook is nothing more than a textbox with one extra feature, you guessed it, it is a Latin-LUT friendly zone. Just place the cursor on the word you've typed and want to know more about and then press F3.

Presenting the Lovely Look-Up Table Panel

The left of the main form includes a hidden panel for your LUT controls.

    LUT_panel_v2.PNG

    aside from giving credit to this program's source material it offers you the option of using the Latin-LUT in any other program by enabling a timer which tests the contents of Microsoft's clipboard for text. Whenever it finds text there that it hasn't seen before it'll clip off the first word and plug it into the Look-up table. This is convenient when you're reading screen-plays of Supernatural or researching the Vatican's Documenta Lingua Latina for a good exorcism or if you're just curious to know if that familiar brand-name you've trusted all these years is actually a Latin epithet in disguise. To use it, when you've checked the checkbox on the LUT-panel, you need only select the text you want to run through the LUT, i.e. on your web-browser, and copy it into the operating-system's clipboard by highlighting it and then pressing the key-combination Ctrl-C.

    Search Panel

    search_pnl_v2.PNG

    Latin-wise, sometimes you need to search your textbook for something you may have missed. And that's what this does. Selecting the heading checkbox searches every Chapter title & sub-chapter title from your Wheelock's textbook for the words you're looking for. glossary searches only the glossary words and their english definitions. This is convenient when you're trying to translate from english to latin. If you want to know the words for "girl" in latin you can search the glossary for it and the results will give you the glossary word puella, -ae which has the word girl in its definition, from there you can click on the word puella and have the look-up table spill out the 12-part declension in pretty pink!  Not checking either of these check-boxes will search everything in the textbook apart from the glossary entries, from every smallest footnote to the longest dreariest chapter, ahem, I mean "to the most erudite chapter".

    Code-wise, you may find the panel's buttons interesting. The background image is stored in the program's resources and then copied onto the panel at run-time. Then each button/checkbox is placed onto the panel, and for each one, a bitmap is created from the same source image and placed strategically onto that object so as to complete the picture. This makes for a nice graphic effect.

    As for the actual code which does the searching its got some similarities to the LUT in that it relies on binary-stream files with records of identical size which can be retrieved & saved in random access fashion. These records each contain a word that can be searched, along with the file in which it can be found and three integer variables used to create a binary-tree/linked list that make up the database. Whereas the LUT uses two separate files, one for the binary tree and the other to hold all the linked list entries which each leaf in the binary tree point to, the search engine has all the information on a single file. or three, rather, since there are three different types of searches : normal, glossary & heading, which each "require" their own file. I say "require" in quotations but if the three different trees had their root nodes at records 1, 2 & 3 respectively then all three search engines could be stored in the same file. You would only need to know how many trees you want in the file and reserve those three entries for the root-nodes of each tree. Putting that idea aside, this program uses three different files for the three different search modes. There's only one actual search-engine and since the getRecord() and putRecord() functions get a mode parameter they can open and close the appropriate file at every execution. The mode is an enumerated type and the three file names are stored in an array with the enumerated type's integer values in mind so that getting the right file is as easy as indexing it with the mode type.

    classSearchRec_FileTreeElement getRecord(long index, enuTypesSearches Mode)
    {            
        FileStream fs;
        if (index < 0)
            return null;
        if (System.IO.File.Exists(strSearchBinFilenameAndDirectory[(int)Mode]))
        {
            fs = new FileStream(strSearchBinFilenameAndDirectory[(int)Mode], FileMode.Open);
            fs.Position = getBinPosition(index);
    
            BinaryFormatter formatter = new BinaryFormatter();
            string strFilename = (string)formatter.Deserialize(fs);
            string strWord = (string)formatter.Deserialize(fs);
    
            classSearchRec_FileTreeElement cRetVal = new classSearchRec_FileTreeElement(strWord, strFilename);
    
            cRetVal.lngLeft = (long)formatter.Deserialize(fs);
            cRetVal.lngRight = (long)formatter.Deserialize(fs);
            cRetVal.lngNext = (long)formatter.Deserialize(fs);
    
            cRetVal.intSizeLinkedList = (int)formatter.Deserialize(fs);
            fs.Close();
            return cRetVal;
        }
        return null;
    }
    
    

    You can see in the second if statement above where the IO system tests the existence of the search-engine's file which is stored in strSearchBinFilenameAndDirectory[] array indexed by the integer value of the input parameter Mode.

    public class classSearchRec_FileTreeElement
        {
            public long lngLeft;
            public long lngRight;
            public long lngNext;
            public string strWord;
            public string strFilename;
            public int intSizeLinkedList;
    
            public classSearchRec_FileTreeElement(string Word, string filename)
            {
                lngLeft = -1;
                lngRight = -1;
                lngNext = -1;
                intSizeLinkedList = 1;
                strWord = Word;
                strFilename = filename;
            }
        }
    
    

    A more interesting part of the search engine may be the way it does the boolean searches. Whenever the user types more than one word into the search engine the program needs to find the results and put them to the screen. To understand how this works you first have to know that when the search-engine's database was created every file was scanned, every word in every file was entered into the appropriate databases and NO DOUBLES of any one word from any one file made their way into the database. So even if the word "the" appears twelve times in any one particular file it is entered into the database as having been found in that file "at least once" and no more. Also, whenever multiple files have the same word the binary tree has to branch out at that particular leaf into a linked list (all in the same file using the same record type which has all three integer indices for binary tree links intLeft & intRight as well as the linked-list intNext). Whenever inserting new finds into the binary-tree leaf's linked-list the leaf of the tree (as opposed to a node in the linked list it points to) keeps track of the number of entries in the linked list, this amounts to keeping track of how many files have this particular word in them without having to count the number of entries in the linked list.  Since the search engine need only be built once and is then only referenced and is never edited, storing this value in the first element of the list makes it readily available.

    And so, when the search engine finds itself having to cross reference two or more lists of filenames to see if any of them contain both of the words you're comparing it does this by first generating a binary tree in RAM memory of the shortest list then runs every entry from the next shortest list into this binary tree. Filenames contained in the second list which are also found in the first list's bin-tree of filenames then go into another bin-tree which is subsequently used for the third word's file list. The code keeps an array of two pointers to the roots of these RAM trees and alternates between the two scanning bigger and bigger lists through smaller and smaller trees until it eventually scans an entire list without finding a single filename in the previous tree, at which point it quits, or it has scanned all the lists and now holds the names of all the files which contain every search word the user is looking for in the last created RAM bin-tree.

    public class classSearchRec_RAMTReeElement
        {
            public classSearchRec_RAMTReeElement Left;
            public classSearchRec_RAMTReeElement Right;
            public classSearchRec_RAMTReeElement Next;
            public string strWord;
            public string strFilename;
            public int intSizeLinkedList;
    
            public classSearchRec_RAMTReeElement(string Word, string filename)
            {
                Left = null ;
                Right = null ;
                Next = null;
                intSizeLinkedList = 1;
                strWord = Word;
                strFilename = filename;
            }
        }
    

    all this is done in the classSearchEngine's

    public string[] searchWord(string strSearchWords, enuTypesSearches eMode)
    

    pop-up Form

    The button seen in the image below at the top right of the form with the ^ on it produces a new pop-up form loaded with the current page and has all the features of the app's main form. It's "home" navigation button will return you to the file it was loaded with and so if you get lost stomping the grounds you can always find your way.

    Panels & Modes

    side_panels.PNG

    In the image above you can see the three different side-panels which appear on the screen. I call them side-panels because the tab that sticks out along the left edge of the form can be clicked on to bring them to the front, and that's what they look like when all three of have been brought forward in that way, when you click on them again all you see are the words on the left edge sticking out the side, thus the name. I've already talked about the search panel & the LUT vanity-panel but you haven't heard about the other one labeled mode. No mystery here and it may not need be said but this is where you select what mode you want the app to be in.

    Vocabulary mode

    From the mode-panel you can select either of the three modes : textbook, workbook & Vocabulary mode. Clicking on the Vocabulary radio-button brings you here :

    vocabulary_mode.PNG

    This is the vocabulary mode. You can review your latin-vocabulary using flash-cards on your computer. The classVocabulary is the groupbox you see in the above image with the word Vocabulary written on it. The pic grows & shrinks to keep up appearances but more importantly the combo-box on the right has a list of all of Wheelock's Vocabulary Chapter files (forty chapters) and you can select or unselect from which chapters you want your flashcards to be chosen at random the next time you review your latin vocabulary. Whenever you select a chapter all the words contained in that chapter are added to a running list of words to review. Then when you click the button Review Words beneath the combo-box a number of new words are randomly picked out of your selected review chapters and added to your current list of vocabulary words before the flash card appears with the first word for you to study.

    flash-card_front.PNG

    The image above shows you the front of the flash card. The word appears in latin, in this case the Latin verb cogito with its four principal parts. Moving the mouse around you'll find that the three words near the top "Delete", "Next" & "Quit" are click-activated and work like buttons. But first you'll want to flip the card over. To do this you grab the image anywhere that's not a button and move the mouse vertically while holding down the mouse-button. This will animate a flipping motion which allows you to display either side of the flash-card depending on the position of the mouse.

    The flipping of the card illusion is a simple trick. There are two picture boxes in stretch-size mode fill-docked to the form, one on top of the other. The program keeps track of which side you're looking at with one variable and remembers which side you grabbed with another so that when the mouse is moved the image is contracted & expanded depending on where it was grabbed, above or below the middle of the screen, and where the mouse cursor is at the moment, also relative to the middle of the screen, then decides which image belongs in front of the other.  This is all done in MouseMove event of classFlashCard (in the classVocabulary.cs file).

    Notice the boolean global variable bolIgnoreMouseMove. Because the action all happens inside the mouse-move event handler which will move the form, affect the image beneath the mouse, and cause more events to handle, those events must be ignored until its done doing what its gotta do or they will cascade into disaster.  This boolean is tested before it is set at the start of the event-handler and then it is reset before exiting, allowing the handler to cause more events in the process of updating the form certain that those events will be ignored until its done.

    void pic_MouseMove(object sender, MouseEventArgs e)
    {
        if (bolIgnoreMouseMove)
            return;
        bolIgnoreMouseMove = true;
    
        if (bolGrab)
        {
            Point ptMousePosition = new Point(MousePosition.X, MousePosition.Y);
            if (distanceBetweenTwoPoints(ptMousePosition, ptOldMousePosition) > 5)
            {
                ptOldMousePosition = ptMousePosition;
    
                int intNewHeight = Latin_Project.Properties.Resources.flashcard_source_back.Height;
                int intNewTop = intMidScreen;
                int intGrabDist_Abs = Math.Abs(intGrabDistFromCenter);
                int intMidHeight = Height / 2;
                int intDistanceFromCenter = Math.Abs(e.Y - intMidHeight);
    
                bool bolSetToNewValues = false;
    
                if (intGrabDistFromCenter < 0)
                { // grabbed it above mid-point
                    if (e.Y > intMidHeight - intGrabDist_Abs)
                    { // in the process of flipping or completed action
                        if (e.Y > intMidHeight)
                        { // seeing the side not-grabbed
                            eVisibleSide = otherSide(eGrabSide);
    
                            if (e.Y < intMidHeight + intGrabDist_Abs)
                            { // fraction of size (still flipping)
                                intNewHeight = (int)(((double)intDistanceFromCenter / (double)intGrabDist_Abs) * Latin_Project.Properties.Resources.flashcard_source_back.Height);
                            }
                            bolSetToNewValues = true;
                        }
                        else
                        { // still seeing the grabbed-side
                            eVisibleSide = eGrabSide;
    
                            if (e.Y > intMidHeight - intGrabDist_Abs)
                            { // fraction of size (still flipping)                                    
                                intNewHeight = (int)(((double)intDistanceFromCenter / (double)intGrabDist_Abs) * Latin_Project.Properties.Resources.flashcard_source_back.Height);
                            }
                            bolSetToNewValues = true;
                        }
                    }
                }
                else
                { // grabbed below mid-point -> (intGrabDistFromCenter > 0)
                    if (e.Y < intMidHeight)
                    { // seeing the side not-grabbed
                        eVisibleSide = otherSide(eGrabSide);
    
                        if (e.Y > intMidHeight - intGrabDist_Abs)
                        { // fraction of size (still flipping)
                            intNewHeight = (int)(((double)intDistanceFromCenter / (double)intGrabDist_Abs) * Latin_Project.Properties.Resources.flashcard_source_back.Height);
                        }
                        bolSetToNewValues = true;
                    }
                    else
                    { // still seeing the grabbed-side
                        eVisibleSide = eGrabSide;
    
                        if (e.Y < intMidHeight + intGrabDist_Abs)
                        { // fraction of size (still flipping)
                            intNewHeight = (int)(((double)intDistanceFromCenter / (double)intGrabDist_Abs) * Latin_Project.Properties.Resources.flashcard_source_back.Height);
                        }
                        bolSetToNewValues = true;
                    }
                }
                if (bolSetToNewValues)
                {
                    Height = intNewHeight;
                    Top = intMidScreen - intNewHeight / 2;
                }
    
                pic[(int)eVisibleSide].BringToFront();
            }
        }
        else
        {
            cWordUnderMouse = mainForm.cLibGrText.getWordUnderMouse(new Point(e.X, e.Y), uWords[(int)eVisibleSide].cWord);
    
            if (cWordUnderMouse != null && cWordUnderMouse.eWordImageType == enuWordImageType.link)
                Cursor = Cursors.Hand;
            else
                Cursor = curNormal;
        }
    
        bolIgnoreMouseMove = false;
    }
    

    flash-card_back.PNG

    cogito ergo sum...

    The numbers on the top left tell you how many flash cards are still left in this review session and the number of flashcards still left for the next. Clicking on the "Delete" option discards the one you're looking at so that it won't automatically be included in the bunch the next time you review your latin vocabulary. Alternately, clicking the "Next" option replaces the card you're holding back in the review pile and another one is randomly picked from the lot.

    Salve!

    Latin may not be the oldest language around but it has spawned all the Romance languages spoken today. It can be found throughout history from "Iacta alea est" to "Sic semper Tyrannis" as well as literature's great heroes like "Captain Nemo". Latin is everywhere and if you don't want to actually learn the language, or you don't have the time, energy or will, you can still use this program to make simple translations.

    and who knows when next you'll need to make a quick demonic exorcism!

    updates

    April 16, 2010 : updated source code, fixed a few minor bugs, moved the side panels to the bottom & added a new 'popup' form button.

    April 20, 2010 : moved the side panels, added 'Vocabulary words' & flash-cards

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here