Click here to Skip to main content
15,120,567 members
Articles / Programming Languages / C#
Article
Posted 24 Sep 2019

Tagged as

Stats

34.2K views
1.6K downloads
45 bookmarked

Creative Writer's Word-Processor - C#2022

Rate me:
Please Sign up or sign in to vote.
4.96/5 (33 votes)
14 Aug 2021CPOL33 min read
In this article, you will learn about a word-processor that makes use of multiple dictionaries, pop-up definitions and an interactive multi-button picturebox expedited with a swift Sweep And Prune algorithm.
An easy to use tool dedicated to creative writing and composition.

Part I: How I Scraped Merriam-Webster's Dictionary

Part II: Merriam-Webster's Dictionary: Converting HTML to RTF

Zipped Dictionary Files 

Update : 2021/11/19 14h00

Image 1

Introduction

I'm a writer. I write. Most of the time, I write software but every so often, I need to change speed and write a story, or even a novel. Anyone can use commercially available word-processors and do very well. I'm willing to guess that not many authors, aside from the independently-destitute like myself, build & test their own word-processors. Few spend weeks and months typing dictionaries into databases to incorporate them into their writing tools and none post articles about how they scraped Merriam-Webster's website to complement their hacking skills with a bit of prose.

What you're reading here today is the result of all that.

Background

Back when WiFi was not freely available in soup kitchens, internet cafés charged good coin to let you see the marvels of the information super-highway and librarians shushed you off after only one hour of working at a dusty old CRT screen chugging along their slow and overcrowded pipe, so scraping non-existing online dictionaries was a thing of fantasy.  Word processors could spell check but getting a definition required leafing through a heavy book at the frightening peril of getting a paper-cut.  So, if you wanted a word-processor to include a dictionary, you either packed your CD-ROM with what you could afford and wobbled from one app to the other while trying not to crash your 30MHz 386, or you just looked it up in grandpa's big-word book.  Unless you were hardcore enough to borrow a dictionary from the library and renewed it every three weeks while you typed each word entry into your PC, then saved them onto your crowded harddrive. Yes, yes, I think the word hardcore is appropriate, otherwise I'd have to resort to self-abusive language unsuitable for the non-TOR tip of the internet.

So, if you've read my previous article, Spell Weller but Grammar's Up to You, then you're probably aware of my affinity for spending 100s of hours, typing dictionary after dictionary, day-in & day-out, into my computer(this particular neurosis of mine is currently in remission and hasn't affected my day-to-day in over 14 years, thanks to my new 'scraping-hobby' and a semblance of a social life). It took me only six weeks to type the mediocre volume: Webster's New Explorer Dictionary and six months to type the Larousse Dictionnaire du Français d'aujourd'hui French Dictionary (for which I made an app that conjugates, "bien sur" ... an app which I have since lost and will have to eventually write again).  A dozen dictionaries and hundreds of typos later, I give them all to you, dear reader, for free. Just go to the web-link above to access them off my GoogleDrive


Since this app relies heavily on the files I scraped from Merriam Webster's website, please feel free to make a donation to them in thanks for their great work.


Installation

The app auto-detects the dictionary files and directories but you have to put them where they are expected to be found.  If you want to use the dictionaries (main reason why you'd want this app in the first place) you must create a directory on your hard-drive

C:\Words_Dictionaries\

Then download the dictionaries you want from my Google Drive and decompress them in the above directory.

When you're done, it should look something like this:

Image 2

Note to the Newbie

For those of you who wandered in here without a clue as to how to run a C# application, don't worry. It's a simple matter.

  1. Download the source code and extract it from the .zip file somewhere you'll know where to find it.
  2. Download Visual Studio 2019 from Microsoft and follow the instructions to install that essential Software Development Kit (SDK).
  3. Double click the fancy new icon that appears on your desktop.
  4. Select the 'open solution' button when prompted - or use the file-menu to open a 'solution'.
  5. Find the source-code you downloaded.
  6. Select the words.sln file and press ok.
  7. When it's loaded, you can
    • run the app in debugger-mode (slightly slower than the executable) by pressing F5
    • or, compile the application by pressing the Ctrl-Shift-B key-combination and then find the executable in the source-code's subdirectory c:/WhereverYouPutTheSourceCode/Bin/Debug/words.exe

The source code on offer here today for the low-low price of zip-nothing is the word-processor I wrote ten years ago that I used to write my fourth novel Cleats of the Counter-Revolution. I've worked on this app considerably since then and it's looking pretty good.

Using the App

This word processor is a little different from most.  It's designed to keep your notes, chapters and stories organized.  Essentially, you create a 'project' and then add files that you write to your project.  All the files in your project are listed in the ListBox at the top left of your work area and you can scroll through these, view and edit them in the text-editor located below the list.  When you select a note from the Project's listbox in this way, the content of that file will appear in the Note-Editor below the list of note headings.  Whatever changes you make to your notes will be saved to its RichTextFile whenever you log off or select a different note.

Image 3

The square button at the bottom left of the Notes-Editor (circled in green in the image above) lets you move the Notes Editor over top of the Main Editor to get a better view of it and its contents.  Clicking this button will move the Notes-Editor away from its normal location and will place it in front of (and hiding) the Main-Editor.

You can use the context-menu (right click your mouse) to add Headings (call them 'notes' or 'files').  When you do, a blue boxed area with a textbox will appear waiting for you to give this new 'note' a name or 'heading'.  This will create a new RichTextFile that will be included in your project.  Its filename will be a combination of both your Project's name AND the 'note' heading, saved in the same directory as your Project.  If you change this file's name using your Windows Explorer, the app will not find it when you select it in the list of notes.  If this should happen, the Notes Editor's work area will remain as it was before you made your last selection.  If you want to delete or rename an existing 'Note', since its best not to tinker with the linker file thing and change the file's name without telling the app where to find it when it loads your project, you can use the same ListBox ContextMenu to create a New heading, or Rename/Delete any of your project's existing Headings.

N.B.  because the Notes-Editor will save changes to your text automatically and then load the appropriate file when you select a different note, the undo/redo sequence of edits you would normally have in the Main Editor will be reset every time you make a different Note selection.  This means you won't be able to undo (Ctrl-Z) any changes you made before the last reset.

The Notes-Editor is a fully-functioning Word-Processor in itself with all the tools and functions you may be used to (mostly) in other commercial word-processors and the same Windows fast-key Control-combinations (e.g. Ctrl-X = cut, Ctrl-V = paste) are valid here as well.

Accents

Both the Notes-Editor and the Main-Editor allow the user to type accents into their text.  As of this writing the accents that are included are the main three french language accents

accent-grave           ì            (little hat to the left)

accent-aïgue           í            (little hat to the right)

accent-circonflex    î            (little hat proper)

To type in an accented vowel, the user need only precede the intended vowel with the character '\' (which is sacrificed by the app for this single purpose) and then follow this character with either the intended vowel or the QWERTY keyboard key located above or below that same vowel.  Once the app recognizes the two-character (e.g. "\e") combination these two characters are removed from the text and replaced by the appropriate accented vowel.  The key above results in Accent-grave (little hat to the left) and the key below produces the accent-aïgue(little hat to the right) and the vowel itself results in the accent-circonflex (little hat proper).

These codes are currently hard-coded into the software and can easily be changed in the classKeyboard 's instantiation method.

Main Editor

Since changing the selection in the listbox above the Notes-Editor will change the file you see in that work space, you won't be able to view other notes while you work there.

This is where the Main Editor comes in.  You can edit any file you're working on in the Main Editor by selecting the Note you want in the list of Headings and then, using the context menu in the ListBox where you make that selection, click on 'Edit Selection'.  Your story, note or novel chapter will then appear in the Main Editor and will free your Notes-Editor for you to reference your character-notes, history-notes, other chapters or those inspirational love-letters that you diligently typed all weepy-eyed with joy.  Whichever note you're editing in the Main Editor cannot be edited simultaneously in the alternate Notes-Editor, you can still view/edit other notes there but you can only view the one which is being edited in the Main  Editor.  

Most of the buttons in the control bar above your Main Editor's work area are self-evident with the exception of these two :

  1. Image 4 is Toggle ToolTip - ToolTip is the descriptive text that appears above a button or object to help identify it.  When you toggle this off you won't get the messages that tell you what things are.  So, if you're not seeing those helpful messages and you want to know what things are called, just clicked on it once and the ToolTip will be enabled.
  2. Image 5 this is the Highlighter feature.  When you press this icon (or the Ctrl-Shift-Z key combination while working in either Text-Editors) you can highlight the selected text.  Alternately, if no text is selected, you can insert highlighted open and close braces Image 6 in which you can then add a quick highlighted note where the cursor is.  You can access this feature in the editor's context menu where you will also find a means to set which color you want your highlighter to be.
  3. Image 7 is your CopyCutter icon - toggles on/off a data-gathering tool convenient when doing on-line research CopyCutter automatically inserts text 'copied' to Windows ClipBoard.  When toggled on this icon turns red Image 8 and can be set to automatically shut off after a period of time when left unused.  see below
  4. Image 9is your word underline icon.   toggles on/off.  When toggled on, all words that begin with the same spelling as the word currently beneath the mouse cursor will be underlined.  Helps discover how many times you repeat yourself.

Highlighter

To highlight some text, you need only press either of these key-combinations 

  • Ctrl-Shift-Alt-Z  - scroll down
  • Ctrl-Shift-Alt-A  - scroll up
  • Ctrl-Shift-Alt-Q - insert highlighted square braces (similar to the toolbar ! button mentioned previously)

If no text is selected, the app will assume you intend to highlight the sentence around the cursor's current location.  It will then detect which Highlighter-Color is selected and set the text's color to the next available option in your sequence of colors.  The color of your text and the name of the Highlighter sequence currently selected will appear in the Highlighter label at the top right of your text-editor.  You can use your mouse-wheel over-top of the colored Highlighter label to cycle through your choices.

You can define the colors you want to use by right-clicking your mouse in the editing area and select the Context Menu's Highlighter->SetBackgroundColor.   

The following groupbox will appear

Image 10

You can see in the image above that there are 9 color combinations with their Titles listed in the Highlighter Color Sequence Editor.  When you scroll through the colors, the editor will select the next valid entry in your sequence along the scrolling direction and alter the color of your selected text.  You can toggle off/on any of these selections by clicking the check-box to the left of each color.  To change the colors, simply use the context menu over the textbox of the color your want to change.

 

Image 11

The Clause Delimiter String is a collection of characters that are used to determine the start and end of the area you want to highlight in a text.  When you scroll through the HighLighter Color Sequence and no text is selected, the app will search for characters found in the Clause Delimiter String before & after the cursor's current location and assume you intended to alter that text's Highlight color.  You can change the characters in the Clause Delimiter String by pressing the small '*' button at the bottom left of the Highlighter Color Sequence Editor (previous image) and the Clause Delimiters groupbox (shown above) will appear. 

If you wanted to only alter sub-clauses in your text (and not complete sentences) you could add the comma to the textbox above and the app will detect commas in your text and halt its search for the start/end of your 'clauses' accordingly.  This would have been of no use to Jack Kerouac but then he wrote novels on rolls of toilet paper and didn't believe in punctuation anyway ... woe that we could all be so free

The Reset button will re-initialize the Clause Delimiter String to the 'factory default' of ".?!:;".

Not shown here (not seen is the 'new line' character which cannot be removed from the Clause Delimiter String and will always be assumed to terminate a sentence or clause).

 

Word Shift

You can slide the word currently under the cursor either forward/backward in the text with the Ctrl-Shift-Alt Right/Left arrow key combinations. 

 

Copy Cutter

Copy Cutter is a great on-line research tool.  Either RichTextBox will allow you to toggle the CopyCutter feature independently by either clicking the CopyCutter the ToolBar icon Image 12 or through the context menu.  When toggled on, Copy Cutter's ToolBar icon will turn red Image 13 and check the content of MicroSoft's ClipBoard once every second.  When it does, it will paste any new text or image it finds there into the RichTextBox as if you had Pasted it there yourself.  This way, you can select text on a web-site and copy it into the MS-ClipBoard (either by Ctrl-C, Ctrl-X key combinations or with the context menu's 'Copy' menu item) without ever returning to the Word app itself.  Just read, highlight and copy.   Entire Wiki page, Project Gutenberg eBooks or news articles can be quickly summarized into your project's research notes using this feature.  Every item found in the ClipBoard is inserted on its own line with a leading hyphen.    Highlight or indent, at your leisure.

Copy_Cutter-Auto-Shut-Off feature is found in the Main Editor's context menu Options described below.  A delay period can be programmed for the Copy-Cutter feature to automatically shut itself off if left unused for too long.  

While CopyCutter feature is enabled, it will disable the selected UseClipBoard dictionary.

 

Reader

The Reader option can be toggled on/off from the RichTextBox's context menu.  When toggled on, user-input will be limited to the arrows, Home and End keys and will not allow any editing to be made to the text.  Wherever you move the the cursor, the app will highlight the entire line of text which should help you deliver public readings without skipping a line or repeating yourself.  When your thumb isn't there to help you direct your eyes to the next line this highlight-the-text-as-you-reading feature can be handy.

 

Dictionary Word-List

The Dictionary Word-List allows you to scroll through all the word entries in a selected dictionary.  This feature is located at the bottom right of the Main Editor's work area.  You'll notice in the context menu there is a Word-List option available.  When you click on the context-menu's Word-List you'll toggle its visible property and either Hide the one you see, or Show it where it ain't.   If you use the context-menu here you'll see a list of the Dictionaries you have installed on your computer.  Choose a selection and that Dictionary's entire list of Headings will appear for you to scroll using the MouseWheel.  When you type in the Main-Editor the Word-List will automatically cycle to the word nearest(in spelling) to the word under the cursor when you pause in your typing.  This may help you find words you're not sure how to spell, like 'hemorrhage', which is a word I still can't spell right, even after typing the entire Medical Dictionary!

While typing in the Main-Editor you can use three fast-keys to control the Headings-List

  • Ctrl-Alt- A  scroll-up
  • Ctrl-Alt- Z  scroll-down
  • Ctrl-Alt- Q  insert selected heading from heading list into your text

The Word-List files can be downloaded from the the link at the top or from here.  Each one of the files in this compressed download contains one dictionary's Word-List.  And each Dictionary expects to find its own Word-List file in its own directory.  However, if you decompress the lot into the C:\Words_Dictionaries\ directory then the app will find them there and copy them to where it needs them to be.  If you download the Source-Code without adding these files to the C:\Words_Dictionaries\ directory then the app will generate a new copy for you whenever you select a dictionary Word-List which hasn't been used before ... but these can take some time to build.  The smaller dictionaries only take a minute or less, but some of the bigger ones can take up to an hour.  You can watch it work while you have your coffee or you can just download them... either way works.  

If you let the app build its own Word-List, its best to let it finish once it has started because if it initializes the final output file without completing the task, the next time you launch the app, it will detect the presence of that file, assume its been completed and work with what it has without telling you that there is a problem(the data is incomplete).  Should this happen, exit the program and go to that Dictionary's main sub-directory, e.g. in the case of MerriamWebster's English Dictionary go to 

C:\Words_Dictionaries\MWENDict\

using your Windows Explorer and delete the half-completed file

C:\Words_Dictionaries\MWENDict\HeadingList.fil

then launch the app again.

Image 14

Dictionary Searches

You can program the app to search any combination of dictionaries you want by going to the main menu bar near the top and select Dictionary Selection.  When you do, the Dictionary Selection form will appear as shown below.

Image 15

As you can see from the image above, all the dictionaries are listed on individual panels.  Each of these panels can be scrolled left/right with the scroll bar at the bottom.  For each panel, you have the option to toggle on any, or all, of the available dictionaries.  Each dictionary can be either set to search only the Heading of a word entry in the dictionary, the content of its definition, both the Heading and the Content or not at all.   You'll notice that the Rhymer.com dictionary is missing two buttons, this is due to the fact that its a really awesome dictionary and does not require a content-search since the heading list is exhaustive.  The Key-combination you want to assign for each of these panels is printed at the top and, although the Ctl-Shift keys are common to all of these panels you can program which letter-or-number you want to combine with these to summon that particular search.  In the image above you can see in the first panel on the left that the Merriam-Webster's English Dictionary matches its Heading search with the Ctrl-Shift-W key combination.  So, whatever word is beneath your cursor in either of the editors, you can search for it's definition in the Merriam-Webster's dictionary by making this key-combination.  You can create new panels using the Add button near the top of this form or delete any panel with thae Delete button located at the bottom of the panel you wish to delete.

There are two other buttons at the bottom of each panel besides the Delete button.  Both of these buttons are toggles.

  1. Use Clip Board - when this is toggled-on, any other panel with this same button toggled-on will have its button toggled-off, and the most recent selection will be used for the 'Use Clip Board' feature.  This feature allows you to highlight text from a web-browser, other word-processor or any Windows environment when you Copy selected text(either with the mouse or the Ctrl-C or Ctrl-X key combinations) into Microsoft's 'clipboard' memory space.  The app will test the content of this memory once every second, and if it finds new text there it will then run that text through the selected panel's search and pop-up the results as if the Ctrl-Shift-Key combination for that panel had been pressed over that word in the editor's work area.  Both the Use-Clip-Board and the CopyCutter options use the same MS-ClipBoard test and will not search the selected dictionary if the CopyCutter feature is enabled.
  2. Pop-Up Reference - the selection of the Pop-Up-Reference toggle button is similar to the Use Clip Board.  The Pop-Up Reference feature, however, applies to the Dictionary output form, which I will talk about further below.  When this feature is enabled, letting the mouse hover over a word in the Dictionary Definition Output textbox tells the selected dictionary to search the word resting beneath the mouse and provide its definition in a pop-up window.

You have the means of setting a delay time for the pop-up definition to appear when the mouse hovers in the Dictionary Results text box. In the Dictionary Selection form shown below, there is a 'PopUpDelay Time' button near the top.

Image 16

When you move the mouse cursor over it, a listbox appears providing you with option settings.

Image 17

You can set the delay time required for the mouse to hover motionless over a word (in the Dictionary search result textbox before that word's definition Pops-Up. Clicking on a word in the Dictionary Result Text Box will still launch a search for that word and the results will appear as a regular word-search but, alternately, if you selected the last option 'Right-Mouse Click', that word's definition will appear in a 'Pop-Up' window(as if it were a hover search) leaving your original search results intact rather than generate an entirely different search and search results.

This can be handy when you're searching through lists of rhyming words from the Rhyming dictionary or lists of synonyms pulled out of a thesaurus. The word you're looking at in the definition may look and sound good for where you are in your creative writing but you may not be certain of its definition, having this pop-up means of quickly getting that word's definition can be a real time saver. Might spare you the embarrassment of shamelessly abusing your readers by rhyming euphoniously but not getting your meaning across, if you know what I mean.

Dictionary Results Form

In the image below you can see the results of a search through the headings of the English thesaurus for the word 'boot'.  At the top of the Dictionary Results Form you'll find a list of results.  You can move the mouse over this list to get a quick pop-up view of the entry's definition.  Alternately, you can click on an entry and its definition will appear in a new Definition Navigator that will appear immediately beneath the list, as shown below.  In the Definition Navigator below you can see that the search result for the word 'saunter' was searched previously and is still visible beneath the current result's definition of the word 'boot'.   Each word has its own Definition Navigator which allows you to load the definitions of any of the words in their display area by clicking on the word you wish to load in a way that is similar to clicking on links in your web-browser.  Alternately, the Pop-Up Reference you've selected (mentioned above) will be searched when you click on a word with a right-mouse-click and its definition will pop-up beneath your mouse cursor.   The number of Definition Navigators allowed on the screen is limited by the Option described below.  You can delete any one you want any time by clicking the 'X' button in its top-right corner or clear them all at once by pressing the 'Clear' button near the bottom of the Search-Results list above.  Having the ability to keep entries on the screen near each other like this lets you mix and match words to help you construct a better sentence.

Quick Insert Word List - you can store Definition Navigator words into a list of words which can be quickly added to you text with a single click.  Any word that is currently under the mouse cursor will be replaced by the quick-insert word when you left click the selected word from the list.  To add a word to this list, you can left-click the right-most button on the Definition Navigator's list of buttons.  To remove a word from this list, you can either send it back to the Definition Navigator panel with a right-mouse click on the right half of the button or delete the word and NOT send it back to the Definition Navigator panel with a right-mouse click on the left half of the button.

Image 18

While in either editor's work area, you can use any of these key-combinations to manipulate the Dictionary Results Form.

  • Ctrl-Shift-Z scroll down the list of results
  • Ctrl-Shift-A scroll up the list of results
  • Ctrl-Shift-X deletes all copies and removes them from the screen
  • Ctrl-Shift-S delete most recent search result

You can left-click on a word in the panelDefinitionNavigator and summon that word's definition in the same dictionary where the current definition was loaded.  Three navigation buttons

  1. ¤ home
  2. < back
  3. > forward 
  4. X delete this Definition Navigator
  5.     sends this word to the Quick-Word-insert list

can be used to cycle through the definitions that particular panelDefinitionNavigator has provided.

 

N.B. the Dictionary Search Result fast-keys to scroll up/down are identical to the Heading-List scroll up/down insert text commands and the Highlighter scroll up/down.  They all Scroll-Up on key-press 'A' and Scroll-Down on key-press 'Z' where 

  1. Search             Ctrl-Shift          + A/Z
  2. Heading List   Ctrl-Alt             + A/Z
  3. Highlighter     Ctrl-Shift-Alt    + A/Z

 

Options 

Image 19

You can access the options panel using the main editor's context menu and click on Options menu item.

The interface is user-friendly and intuitive although the options themselves may not be self-evident.  Here are some quick notes to help you figure them out.

  • Dictionary Form Mouse Pop Up Definition & Word List Form Mouse Pop Up Definition are toggle options that when set to true tell the app to display the definition of the word beneath the mouse in their respective lists.  The definition will appear in a pop-up form near your mouse cursor.
  • Notes List Reorder Lock is a toggle option which, when set to true, denies the user the means to change the order of the notes as they appear in the list.  This is to prevent any shuffling of chapters in a novel.  When toggled off (set to false) you can click on the Notes listbox and move the heading up/down using the arrow keys.
  • AutoSave - is a toggle option which, when set to true, your main working area will be automatically saved every five minutes and overwrite the file it was loaded from.  Very convenient, if that's what you want.
  • Numeral Insert - is a toggle option which, when toggled on (set to true), lets you keep a numerical order in your text by typing the pound key '#'.  This character will be replaced by a numeral that will be insert/added to existing numerals in your text and update any numerals that follow similar to a Numbered-List you might find in other Word-Processors but without the formatting.
  • Two Word Swap - when set true, you can swap two words in your text(one for the other and the other for the first) by leaving the cursor on one of the two words and then pressing the Ctrl key and Left-Mouse clicking the second word to be swapped
  • Definition Navigators List - is a numeric value that limits the number of Definition Navigators that will appear when you make a new search preventing your computer from slowing down when it runs out of memory if you neglect to delete existing Definition Navigators in the list.  You can change this value by using the Mouse-Wheel over the option.
  • Copy Cutter Auto Shut Off Delay - Copy Cutter can be programmed to automatically shut off when it is unused for a given period of time.  The settings available are 1, 5, 15, 30 or 60 minutes before Copy Cutter automatically disables itself.  You can disable the Auto-Shut Off feature by setting this option to Never

 

Using the Code(stuff for coders and other dorks of the computer variety)

MultiButtonPictureBox

The classMultiButtonPic is essentially a picture box with rectangles mapped out onto it that your user can interact with using the mouse cursor.

Image 20

Everything beneath the 'add' & 'hide' buttons (except the textbox with the 'W') are buttons drawn on a common picture box. The Ctrl 'button' is the only one that doesn't interact. The rows of four '¤' set the selected search style for each dictionary. There are also three buttons at the bottom 'Use Clip Board', 'Pop Up Reference' & 'Delete'. The response time is great. Each button can be set to toggle independently from the others and the widget's internal mouse events can be overwritten in order to use these buttons like they were radio-buttons on a common panel.

Here's the Mouse-click event inside the multi-button:

C#
//

MouseEventHandler _eventHandler_MouseClick;
public MouseEventHandler eventHandler_MouseClick
{
  get { return _eventHandler_MouseClick; }
  set
  {
    if (_eventHandler_MouseClick != value)
    {
      if (_eventHandler_MouseClick != null)
        MouseClick -= _eventHandler_MouseClick;
      MouseClick -= Event_MouseClick;

      _eventHandler_MouseClick = value;
            
      if (_eventHandler_MouseClick != null)
        MouseClick += _eventHandler_MouseClick;
      else
        MouseClick += Event_MouseClick;
    }
  }
}

virtual public void Event_MouseClick(object sender, MouseEventArgs e)
{
  if (cButtonUnderMouse != null)
  {
    if (cButtonUnderMouse.CanBeToggled)
      cButtonUnderMouse.Toggle();
  }
}
//

by setting the MBP (instance of a classMultiButtonPic) _eventHandler_MouseClick to an event in your app, that event will override the MBP's mouse click event. Here's how it happens in this app:

The event is set in the panelSelectDictionary class:

C#
//
cMBP.eventHandler_MouseClick = cMBP_HTMLTagList_Click;
//

Then the event itself takes over the job of toggling what needs to be toggled in order for groups of buttons to be mutually exclusively toggled (like radio-buttons on a common panel) in the case of the search-type rows of buttons next to the names of the dictionaries.

C#
//

private void cMBP_HTMLTagList_Click(object sender, EventArgs e)
{
classCK_Objects.classMultiButtonPic cMBPSender = (classCK_Objects.classMultiButtonPic)sender;
classCK_Objects.classMultiButtonPic.classButton cBtn = 
   (classCK_Objects.classMultiButtonPic.classButton)cMBPSender.cButtonUnderMouse;
if (cBtn != null)
{
  if (cBtn.Tag != null)
  {
    classButtonArray cBtnArray = (classButtonArray)cBtn.Tag;
    classBinTrees.classDictionary cDictionary = cBtnArray.cDictionary;
    int intIndex = cBtnArray.lstButtons.IndexOf(cBtn);
    if (intIndex < (int)enuSearchType._num)
    {
      cBtnArray.eSearchType = (intIndex >= 0 && intIndex < (int)enuSearchType._num)
                        ? (enuSearchType)intIndex
                        : enuSearchType._num;
      for (int intBtnCounter = 0; 
                intBtnCounter < (int)enuSearchType._num; intBtnCounter++)
      {
        cBtn = cBtnArray.lstButtons[intBtnCounter];
        cBtn.Toggled
            = cBtn.Highlight
            = intBtnCounter == intIndex;
      }
      cMBPSender.Refresh();
      if (cBtnPopupReference.Toggled)
        formDictionarySelection.instance.pnlPopUpReference = this;
    }
  }
  else
  {
    panelSelector.enuTypeButton eButtonType = (panelSelector.enuTypeButton)cBtn.obj;
    switch (eButtonType)
    {
      case panelSelector.enuTypeButton.UseClipBoard:
        {
          cBtn.Toggle();
          if (cBtn.Toggled)
          {
            for (int intPnlCounter = 0; intPnlCounter < 
                       formDictionarySelection.instance.lstPnlSelector.Count; intPnlCounter++)
            {
              panelSelector pnlSel = 
                           formDictionarySelection.instance.lstPnlSelector[intPnlCounter];
              if (pnlSel != this && pnlSel.cBtnUseClipBoard.Toggled)
              {
                pnlSel.cBtnUseClipBoard.Toggled = false;
                pnlSel.cBtnUseClipBoard.Highlight = false;
                pnlSel.cMBP.Refresh();
              }
            }
            formDictionarySelection.instance.pnlUseClipBoard = this;
          }
          else
          {
            formDictionarySelection.instance.pnlUseClipBoard = null;
          }
          formDictionarySelection.instance.TmrClipboard_Set();
        }
        break;

// some cases deleted for brevity

      default:
        {
        }
        break;
    }
  }
}

//

In the case of the UseClipBoard button, it needs to look at other panels to make certain that no two panels list dictionaries to be searched whenever your user copies text onto the MS clipboard (which is convenient for use outside this word-processor, e.g., while in your web-browser).

panelSP - a panel with scroll bars

The Dictionary Selection form and the Dictionary Output form's list of 'copied' results both use the panelSP class that includes a pair of scroll bars on a regular Windows panel. It is a convenient way to display objects because you can add as many panels onto it (presumably with other objects on each panel) in as large an internal area as you like.  The way it works is by adding panels to it the panelSP will provide you with a SweepAndPruneElement (I'll call these spEle's for this explanation). You can then set the location of each of these spEle's anywhere you want and the panelSP will make room for them in its internal imaginary space called recArea.  The panelSP has another rectangular property call recVisible. This recVisible is the same size as the panel on your screen but its location will vary with the values of the scroll bars. By sweeping the scroll bars the user is telling the panelSP which region of its recArea is to be displayed on the screen and then panelSP figures out which of your panels (spEle's) should be visible and then it will position those MS Panel objects where they are supposed to be on the screen given the scroll bar values. The panelSP allows you to group many objects (as long as they're contained in an MS panel) onto it and will provide the user with scroll-bars to see all those objects. You, as a programmer, never set the panel's location (resize it all you like) but rather position it in the panelSP's imaginary recArea and that data will inform the panelSP as to what needs to appear on the screen given the user's scroll-bar values. 

Sweep And Prune

The Sweep and Prune algorithm is implemented as its own class and can be used with any kind of objects from characters in a string (just set the Y value to a constant and use the character index as X) to a map of the cities in Britney Spears's World Tour. You create elements, define their regions and add them to the map. Then when you ask it 'what is under Point(x, y)?' it spits-up whatever you've got hiding under there. Here's a good article to help you understand the algorithm.  Both the MultiButtonPic class and the panelSP class, as well as the SPObjects class all use similar Sweep and Prune algorithms to keep track of the objects they are handling.

 

Definition Navigator - RTX twins

Many of the dictionaries are not stored in RichTextFiles(.rtf) but some are plain old TextFiles(.txt) and have no formatting with regards to Fonts or color.  This is because I, believing that having these resources would help me in my writing (though I learned nothing about medicine while typing the Medical Dictionary having these tools at my disposal while writing has greatly improved my talents), hand-typed, page-by-page (with strategically inserted typos for your reading entertainment) a dozen dictionaries before going mad... stabbing my neighbours and spending years in jail proudly boasting having nothing but violence on my criminal record.  By the time I got out of prison there was this thing called the 'Internet' and scraping 100s of thousands of files off commercial websites like Merriam-Webster was way cooler than spending endless hours typing word-definitions into my computer everyday and gathering Postal-Steam in the process.  This experience, along with having read Amy Vanderbilt : Complete Book of Etiquette while eating in homeless kitchens surrounded by emotionally unstable alcoholics and homeless drug addicts has, oddly enough, made me a better writer.

Let me make a list of all the dictionaries and where they came from ... just for the record

Hand Typed Dictionaries 

  1. Dictionary : Crime
  2. Dictionary : English
  3. Dictionary : Euphemisms
  4. Dictionary : Italian
  5. Dictionary : Latin(revised)
  6. Dictionary : Medical
  7. Dictionary : Nineteenth Century Slang
  8. Dictionary : Prison Slang
  9. Dictionary : Proper Names
  10. Dictionary : Southern Expressions
  11. Dictionary : Spanish
  12. Dictionnaire : Francais
  13. Thesaurus : English

Parsed from a downloaded PDF file

  1. Dictionary : Nautical 

Scraped from the Internet

  1. Dictionary : English MW
  2. Dictionary : Irish-English
  3. Thesaurus : English MW

That means that all the dictionaries listed above, with the exception of the last three are TextFiles.  In order to embellish the output of these files, this app loads the TextFiles, finds the Heading (the first line of text) and prints the heading in a different Font/Color than the rest of that word entry's definition.  In order to do this it has to add the Heading, set the font & color then add the definition.  Doing this the was causing the RichTextBox to print things sequentially.  This resulted in a mess performance for the user to watch as the definition was being written in blue then re-fonted, then set to black ... its a mess.  Weeks could be spent letting my laptop re-build each TextFile into an appropriate RichTextFile but that will wait until I have another box on the side to perform these chores.  Perhaps there's another way to load the RichTextBox that is not so jumpy but... I haven't put the effort in to figure that out.

Since my resolved means of performing this operation is so unsightly, and may cause epileptic seizures to the unwary, I implemented something I resorted to in my previous project Dlús : Irish Language Word Processor where I was asked to translate some English text into Irish in the scraped Irish dictionary's definition, and make this an option for the user.  This required editing the RichTextFile while loading it into the RichTextBox you see on the screen and resulted in the same poor quality that editing a TextFile to display into a RichTextBox required.  The solution I opted for was to use two RichTextBoxes drawn in front of each.  Do the work on the one that's not visible(behind the first) then bring it to the front when its done.  Kind of like drawing graphics for a video-game : use two screen images, flash the next one on the screen when you're done drawing it.

Here's some of the code.  The array of two RichTextBoxes are initialized in 

public panelDefinitionNavigator(ref classDictionary cDictionary)
{
    // code excised for brevity

    rtx_Array[0] = new RichTextBox();
    rtx_Array[1] = new RichTextBox();

    for (int intRTXCounter = 0; intRTXCounter < rtx_Array.Length; intRTXCounter++)
    {
        Controls.Add(rtx);
        rtx.SizeChanged += rtx_SizeChanged;
        rtx.Tag = (object)this;
        rtx.BorderStyle = BorderStyle.None;
        rtx.KeyDown += formDictionaryOutput.RtxOutput_KeyDown;
        rtx.KeyUp += formDictionaryOutput.RtxOutput_KeyUp;
        rtx.KeyPress += formDictionaryOutput.RtxOutput_KeyPress;
        rtx.MouseEnter += formDictionaryOutput.RtxOutput_MouseEnter;
        rtx.MouseEnter += _MouseEnter;
        rtx.MouseLeave += formDictionaryOutput.RtxOutput_MouseLeave;
        rtx.MouseDoubleClick += formDictionaryOutput.RtxOutput_MouseDoubleClick;
        rtx.MouseMove += formDictionaryOutput.RtxOutput_MouseMove;
        rtx.MouseDown += formDictionaryOutput.RtxOutput_MouseDown;
        rtx.MouseWheel += formDictionaryOutput.RtxOutput_MouseWheel;
        rtx.VScroll += formDictionaryOutput.RtxOutput_VScroll;
        RTX_Swap();
    }

    // code excised for brevity
}

Since I had the code working with a single RichTextBox called rtx, creating an array with a different name and adding a property that used the same name as the original RichTextBox incorporated the changes with the rest of the code much easier.  There is an integer keeping track of which of the two RichTextBoxes in the array is referred to as rtx by the rest of the project and the work of loading/editing the next definition is done in the alternate RichTextBox which is hidden behind the current.  When that work is done, a call to the RTX_Swap() method swaps the current/alternate RichTextBoxes and pulls the current one to the front of the other.  And we're done.

public RichTextBox[] rtx_Array = new RichTextBox[2];

int intRTXIndex_Current = 0;
void RTX_Swap()
{
    intRTXIndex_Current = (intRTXIndex_Current + 1) % 2;
    rtx.BringToFront();
}

public  RichTextBox rtx      { get { return rtx_Array[intRTXIndex_Current]; }}
private RichTextBox rtx_Next { get { return rtx_Array[(intRTXIndex_Current + 1) % 2]; }}

in the code below you can see the rtx_Next being written to and then pulled to the front with a call to RTX_Swap()

case enuFileExtensions.txt:
    {
        classFileContent cFileContent = new classFileContent(cDictionary.strSourceDirectory, 
                                                             strFilename.Substring(strFilename.Length - 12, 8));
        if (cFileContent.Heading != null && cFileContent.Definition != null)
        {
            RichTextBox rtxRef = rtx_Next;
            rtxRef.Clear();
            classStringLibrary.RTX_AppendText(ref rtxRef, 
                                              cFileContent.Heading.Trim(), 
                                              formDictionaryOutput.fntWordHeading, 
                                              Color.Blue, 
                                              0);
            classStringLibrary.RTX_AppendNL(ref rtxRef);
            if (cFileContent.alt_Heading != null)
            {
                classStringLibrary.RTX_AppendText(ref rtxRef, 
                                                  cFileContent.alt_Heading.Trim(), 
                                                  formDictionaryOutput.fntWordHeading, 
                                                  Color.LightBlue, 
                                                  0);
                classStringLibrary.RTX_AppendNL(ref rtxRef);
            }

            classStringLibrary.RTX_AppendText(ref rtxRef, 
                                              cFileContent.Definition, 
                                              formDictionaryOutput.fntWordDefinition, 
                                              Color.Black, 
                                              0);
            rtxRef.Select(0, 0);
            rtxRef.ScrollToCaret();
            RTX_Swap();
        }
    }
    break;

Definition Navigator Click or DoubleClick

I was having trouble getting the MouseDoubleClick event (insert word into text) NOT trigger the MouseClick event(display selected word's definition beneath the moust) until I used a Timer to delay and execute the MouseClick only if the MouseDoubleClick doesn't happen.  When the user only wants to explore definitions of words by single-clicking on them in the Navigator, the Navigator loads a new definition and puts it on the screen.  A mouse double-click tells the Navigator to insert the word being double-clicked into the text the user is working on.  But... the double-click event cannot happen without the first click causing the regular single-mouse-click event from occurring.  This resulted in the user clicking once(the first of the double clicks) loading a new definition and then double-clicking the word in the new definition, thereby inserting the wrong word into the user's text. 

To solve this, I added a timer.  The timer has a 500ms delay and is enabled at the MouseDown event.

public static void RtxOutput_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Right)
    {
        WordUnderMouse_PopUpDefinition(sender);
    }
    else
    {
        rtxOutput_MouseClick_Sender = sender;
        rtxOutput_MouseClick_MouseEventArgs = e;
        tmrClick_WaitForDoubleClick.Interval = 500;
        tmrClick_WaitForDoubleClick.Enabled = !tmrClick_WaitForDoubleClick.Enabled;
    }
}

The MouseDown event's parameters are recorded and the timer is enabled.  Since the MouseDoubleClick disables the timer, the MouseClick event is aborted when the user double-clicks.

public static void RtxOutput_MouseDoubleClick(object sender, MouseEventArgs e)
{
    tmrClick_WaitForDoubleClick.Enabled = false;
    WordUnderMouse_Insert(sender, new Point(e.X, e.Y));
}

But if the user takes too long (500ms) or actually only intends a single-click, then the timer's event is triggered and the work to be done for a single-click is executed.

 
private void tmrClick_WaitForDoubleClick_Tick(object sender, EventArgs e)
{
    tmrClick_WaitForDoubleClick.Enabled = false;
    _RtxOutput_MouseClick(rtxOutput_MouseClick_Sender, rtxOutput_MouseClick_MouseEventArgs);
}



public static void _RtxOutput_MouseClick(object sender, MouseEventArgs e)
{
    if (bolIgnoreMouseUp)
    {
        bolIgnoreMouseUp = false;
        return;
    }
    rtxCalling = (RichTextBox)sender;
    switch (e.Button)
    {
        case MouseButtons.Left:
            {
                WordUnderMouse_Search(sender, new Point(e.X, e.Y));
            }
            break;

        case MouseButtons.Right:
            {
                WordUnderMouse_PopUpDefinition(sender);
            }
            break;

        case MouseButtons.Middle:
            break;
    }
}

 

Updates

Download Code Updates report

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Christ Kennedy
CEO unemployable
Canada Canada
Christ Kennedy grew up in the suburbs of Montreal and is a bilingual Quebecois with a bachelor’s degree in computer engineering from McGill University. He is unemployable and currently living in Moncton, N.B. writing his next novel.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA18-Aug-21 18:26
professionalȘtefan-Mihai MOGA18-Aug-21 18:26 
GeneralRe: My vote of 5 Pin
Christ Kennedy19-Aug-21 2:07
mvaChrist Kennedy19-Aug-21 2:07 
QuestionA novelist-programmer, eh? Me too. Pin
DaveCline12-Jul-21 11:13
MemberDaveCline12-Jul-21 11:13 
AnswerRe: A novelist-programmer, eh? Me too. Pin
Christ Kennedy13-Jul-21 7:48
mvaChrist Kennedy13-Jul-21 7:48 
GeneralRe: A novelist-programmer, eh? Me too. Pin
Jacques Abada16-Aug-21 3:13
MemberJacques Abada16-Aug-21 3:13 
GeneralRe: A novelist-programmer, eh? Me too. Pin
Christ Kennedy16-Aug-21 8:40
mvaChrist Kennedy16-Aug-21 8:40 
GeneralMy vote of 5 Pin
Vincent Radio12-Jul-21 8:05
professionalVincent Radio12-Jul-21 8:05 
GeneralRe: My vote of 5 Pin
Christ Kennedy13-Jul-21 7:38
mvaChrist Kennedy13-Jul-21 7:38 
QuestionSpell checker Pin
Franc Morales10-Jul-21 22:34
MemberFranc Morales10-Jul-21 22:34 
AnswerRe: Spell checker Pin
Christ Kennedy11-Jul-21 1:18
mvaChrist Kennedy11-Jul-21 1:18 
GeneralMy vote of 3 Pin
Vincent Radio25-May-21 4:37
professionalVincent Radio25-May-21 4:37 
GeneralRe: My vote of 3 Pin
Christ Kennedy25-May-21 12:31
mvaChrist Kennedy25-May-21 12:31 
3?
thanks?
my code is perfect until i don't find a bug...

PraiseMessage Closed Pin
22-May-21 8:56
MemberMember 1521159322-May-21 8:56 
Questionsubject Pin
Mohit Singh 202122-May-21 0:05
professionalMohit Singh 202122-May-21 0:05 
AnswerRe: subject Pin
Christ Kennedy22-May-21 2:44
mvaChrist Kennedy22-May-21 2:44 
QuestionObject reference not set to an instance of an object Pin
dawiemos10-Jan-20 23:30
Memberdawiemos10-Jan-20 23:30 
AnswerRe: Object reference not set to an instance of an object Pin
Christ Kennedy23-Jan-20 5:09
mvaChrist Kennedy23-Jan-20 5:09 
GeneralRe: Object reference not set to an instance of an object Pin
dawiemos29-Mar-20 21:26
Memberdawiemos29-Mar-20 21:26 
GeneralRe: Object reference not set to an instance of an object Pin
Christ Kennedy30-Jan-21 6:06
mvaChrist Kennedy30-Jan-21 6:06 
QuestionCode Critique Pin
Marc Clifton10-Oct-19 4:03
mvaMarc Clifton10-Oct-19 4:03 
AnswerRe: Code Critique Pin
Christ Kennedy11-Oct-19 5:23
mvaChrist Kennedy11-Oct-19 5:23 
Questionmail.com is blocked Pin
dawiemos26-Sep-19 10:59
Memberdawiemos26-Sep-19 10:59 
AnswerRe: mail.com is blocked Pin
Christ Kennedy26-Sep-19 23:52
mvaChrist Kennedy26-Sep-19 23:52 
QuestionMissing RHYMDICT on your server Pin
monglung25-Sep-19 11:27
professionalmonglung25-Sep-19 11:27 
AnswerRe: Missing RHYMDICT on your server Pin
Christ Kennedy25-Sep-19 16:07
mvaChrist Kennedy25-Sep-19 16:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.