Logo Search packages:      
Sourcecode: xabacus version File versions  Download package

xabacus.c

/*
 * @(#)xabacus.c
 *
 * Copyright 1993 - 2009  David A. Bagley, bagleyd@tux.org
 *
 * Abacus demo and neat pointers from
 * Copyright 1991 - 1998  Luis Fernandes, elf@ee.ryerson.ca
 *
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of the author not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * This program is distributed in the hope that it will be "useful",
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

/* Driver file for Abacus */

#ifndef WINVER
static const char aboutHelp[] = {
"Abacus Version 7.5.5\n"
"Send bugs (reports or fixes) to the author: "
"David Bagley <bagleyd@tux.org>\n"
"The latest version is at: "
"http://www.tux.org/~bagleyd/abacus.html\n"
"Some coding was also done by Luis Fernandes <elf@ee.ryerson.ca>\n"
"and Sarat Chandran <saratcmahadevan@yahoo.com>"
};

static const char optionsHelp[] = {
"[-geometry [{width}][x{height}] [{+-}{xoff}[{+-}{yoff}]]]\n"
"[-display [{host}]:[{vs}]] [-[no]mono] [-[no]{reverse|rv}]\n"
"[-{foreground|fg} {color}] [-{background|bg} {color}]\n"
"[-{border|bd} {color}] [-frame {color}]\n"
"[-primaryBeadColor {color}] [-leftAuxBeadColor {color}]\n"
"[-rightAuxBeadColor {color}] [-secondaryBeadColor {color}]\n"
"[-highlightBeadColor {color}] [-primaryRailColor {color}]\n"
"[-secondaryRailColor {color}] [-highlightRailColor {color}]\n"
"[-bumpSound {filename}] [-moveSound {filename}]\n"
"[-[no]sound] [-delay msecs] [-[no]script] [-[no]demo]\n"
"[-demopath {path}] [-{demofont|demofn} {fontname}]\n"
"[-{demoforeground|demofg} {color}]\n"
"[-[no]teach] [-[no]rightToLeftAdd] [-[no]rightToLeftMult]\n"
"[-[no]lee] [-rails {int}] [-leftAuxRails {int}]\n"
"[-rightAuxRails {int}] [-[no]vertical]\n"
"[-colorScheme {int}] [-[no]slot] [-[no]diamond]\n"
"[-railIndex {int}] [-[no]topOrient] [-[no]bottomOrient]\n"
"[-topNumber {int}] [-bottomNumber {int}] [-topFactor {int}]\n"
"[-bottomFactor {int}] [-topSpaces {int}] [-bottomSpaces {int}]\n"
"[-topPiece {int}] [-bottomPiece {int}] [-topPiecePercent {int}]\n"
"[-bottomPiecePercent {int}] [-shiftPercent {int}]\n"
"[-subdeck {int}] [-subbead {int}] [-[no]sign]\n"
"[-decimalPosition {int}] [-[no]group] [-groupSize {int}]\n"
"[-[no]decimalComma] [-base {int}] [-[no]eighth]\n"
"[-anomaly {int}] [-shiftAnomaly {int}] [-anomalySq {int}]\n"
"[-shiftAnomalySq {int}] [-displayBase {int}]\n"
"[-[no]pressOffset] [-[no]romanNumerals]\n"
"[-[no]latin] [-[no]ancientRoman] [-[no]modernRoman]\n"
"[-{chinese|japanese|korean|roman|russian|danish|generic}]\n"
"[-{it|uk|fr}] [-version]"
};
#endif

#ifdef HAVE_MOTIF
static const char options1Help[] = {
"-geometry {+|-}X{+|-}Y    "
"This option sets the initial position of the abacus "
"window (resource name \"geometry\").\n"
"-display host:dpy    "
"This option specifies the X server to contact.\n"
"-[no]mono       "
"This option allows you to display the abacus window "
"on a color screen as if it were monochrome\n"
"       (resource name \"mono\").\n"
"-[no]{reverse|rv}    "
"This option allows you to see the abacus window in "
"reverse video (resource name \"reverseVideo\").\n"
"-{foreground|fg} color    "
"This option specifies the foreground of  the  abacus "
"window (resource name \"foreground\").\n"
"-{background|bg} color    "
"This option specifies the background of  the  abacus "
"window (resource name \"background\").\n"
"-{border|bd} color   "
"This option specifies the foreground of the border "
"of the beads (resource name \"borderColor\").\n"
"-frame color    "
"This option specifies the foreground  of  the  frame "
"(resource name \"frameColor\").\n"
"-primaryBeadColor color   "
"This option specifies the foreground  of  the  beads "
"(resource name \"primaryBeadColor\").\n"
"-leftAuxBeadColor color   "
"This option specifies the foreground  of  the  beads "
"for the left auxiliary abacus in\n"
"       Lee's Abacus (resource name \"leftAuxBeadColor\").\n"
"-rightAuxBeadColor color  "
"This option specifies the foreground  of  the  beads "
"for the right auxiliary abacus in\n"
"       Lee's Abacus (resource name \"rightAuxBeadColor\").\n"
"-secondaryBeadColor color "
"This option specifies the secondary color of the beads "
"(resource name \"secondaryBeadColor\").\n"
"-highlightBeadColor color "
"This option specifies the highlight color of the beads "
"(resource name \"highlightBeadColor\").\n"
"-primaryRailColor color   "
"This option specifies the foreground  of  the  rails "
"(resource name \"primaryRailColor\").\n"
"-secondaryRailColor color "
"This option specifies the secondary color of the rails "
"(resource name \"secondaryRailColor\").\n"
"-highlightRailColor color "
"This option specifies the highlight color of the rails "
"(resource name \"highlightRailColor\").\n"
"-bumpSound filename  "
"This option specifies the file for the bump sound "
"for the movement of the beads.\n"
"-moveSound filename  "
"This option specifies the file for the move sound "
"for the sliding of the decimal point marker.\n"
"-[no]sound      "
"This option specifies if a sliding bead should "
"make a sound or not (resource name \"sound\").\n"
"-delay msecs    "
"This option specifies the number of milliseconds it "
"takes to move a bead or a group of beads\n"
"       one space (resource name \"delay\").\n"
"-[no]script     "
"This option specifies to log application to  stdout, "
"every  time  the  user  clicks  to  move  the\n"
"       beads (resource name \"script\"). The output  is  a  set  "
"of deck,  rail,  beads  added  or  subtracted,  and\n"
"       the number of text lines (4).  This can be edited to add "
"text to the lesson and used as a new demo keeping\n"
"       the generated numbers and the number of  lines constant."
"  It writes to Abacus.les by default.\n"
"-[no]demo       "
"This option specifies to run in demo mode.  In  this "
"mode, the abacus is controlled by the current\n"
"       lesson (resource name \"demo\").  When started with "
"the demo option, a window contains descriptive text,\n"
"       and user prompts are displayed in this window.  Pressing "
"'q' during  the  demo  will quit it.  Clicking the\n"
"       left mouse-button with the pointer  in  the  window "
"will restart the demo (beginning of current lesson).\n"
"       The demo uses Abacus1_1.les for the first Chinese Abacus "
"lesson, Abacusjp1_1.les for the first Japanese (and\n"
"       Roman) Abacus lesson, Abacusko1_1.les for the Korean "
"Abacus, and Abacusru1_1.les for the Russian Abacus.\n"
"-demopath path  "
"This option specifies the path for the demo,  possibly"
"  /usr/local/share/games/xabacus  (resource\n"
"       name \"demoPath\").  It initially looks for Abacus1_1.les,"
" first chapter, lesson 1.  If it finds that, it\n"
"       will later look for Abacus1_2.les, etc.\n"
"-{demofont|demofn} fontstring    "
"This option specifies the font for the "
"explanatory text that appears in the\n"
"       secondary window, during the demo.  The default font is "
"18 point Times-Roman (-*-times-*-r-*-*-*-180-*).\n"
"       The alternate font is 8x13 (resource name \"demoFont\").\n"
"-{demoforeground|demofg} color   "
"This option specifies the foreground of  the  abacus "
"demo window (resource name\n"
"       \"demoForeground\").\n"
"-{demobackground|demobg} color   "
"This option specifies the background of  the  abacus "
"demo window (resource name\n"
"       \"demoBackground\").\n"
"-[no]teach "
"This option specifies to run in teach mode.  In "
"this mode, the abacus is controlled\n"
"       by the the addition, subtraction, multiplication, "
"division (resource name \"teach\").\n"
"-[no]rightToLeftAdd "
"This option specifies the order for teach starting "
"side for addition and subtraction.  The\n"
"       default is the traditional left to right.  "
"Right to left seems easier though (resource name \"rightToLeftAdd\").\n"
"-[no]rightToLeftMult  "
"This option specifies the order for teach starting "
"side for multiplication.  The default is\n"
"       the traditional left to right.  Right to left "
"seems more straight forward (resource name \"rightToLeftMult\")."
};
static const char options2Help[] = {
"-[no]lee   "
"This option allows you to turn on and off the two extra "
"auxiliary abaci (resource name \"lee\").\n"
"-rails int      "
"This option specifies the number of rails (resource "
"name \"rails\").\n"
"-leftAuxRails int "
"This option allows you to set the number of the "
"rails for the left auxiliary abacus in Lee's Abacus\n"
"       (resource name \"leftAuxRails\").\n"
"-rightAuxRails int  "
"This option allows you to set the number of the "
"rails for the right auxiliary abacus in Lee's Abacus\n"
"        (resource name \"rightAuxRails\").\n"
"-[no]vertical "
"This option allows you to have vertical rails "
"(resource name \"vertical\").\n"
"-colorScheme int    "
"This option specifies the color scheme for the "
"abacus (resource name \"colorScheme\") where\n"
"       0-> none, 1-> color middle (2 beads beads but if odd "
"color 1 bead), 2-> color first of group, 3-> both 1 and\n"
"       2, 4-> color first half (but if odd color middle "
"bead).\n"
"-[no]slot       "
"This option allows you to have either slots or rails "
"(resource name \"slot\").\n"
"-[no]diamond    "
"This option allows you to have diamond or round beads "
"(resource name \"diamond\").\n"
"-railIndex int  "
"This option specifies the index of color for the "
"rails of the abacus (resource name \"railIndex\")\n"
"       where a value is 0 or 1.\n"
"-[no]topOrient    "
"This option specifies the orientation of the beads "
"on top (resource name \"topOrient\").\n"
"-[no]bottomOrient    "
"This option specifies the orientation of the beads "
"on bottom (resource name \"bottomOrient\").\n"
"-topNumber int    "
"This option specifies the number  of  beads on top "
"(resource name \"topNumber\").\n"
"-bottomNumber int    "
"This option specifies the number of beads on  bottom "
"(resource name \"bottomNumber\").\n"
"-topFactor int    "
"This option specifies the multiply  factor  for  the "
"beads on top (resource name \"topFactor\").\n"
"-bottomFactor int    "
"This option specifies the multiply  factor  for  the "
"beads on bottom (resource name \"bottomFactor\").\n"
"-topSpaces int    "
"This option specifies the number of spaces on top "
"(resource name \"topSpaces\").\n"
"-bottomSpaces int    "
"This option specifies the number of spaces on bottom "
"(resource name \"bottomSpaces\").\n"
"-topPiece int     "
"This option specifies the number of pieces on top "
"(resource name \"topPiece\").\n"
"-bottomPiece int     "
"This option specifies the number of pieces on bottom "
"(resource name \"bottomPiece\").\n"
"-topPiecePercent int   "
"This option specifies the number of piece percents on top "
"(resource name \"topPiecePercent\").\n"
"-bottomPiecePercent int   "
"This option specifies the number of piece percents on bottom "
"(resource name \"bottomPiecePercent\").\n"
"-shiftPercent int    "
"This option specifies the shift of rails for piece percents "
"and also may influence the\n"
"       precision of the calculation (resource name "
"\"shiftPercent\").\n"
"-subdeck int    "
"This option specifies the special subdecks column "
"(resource name " "\"subdeck\").\n"
"-subbead int    "
"This option specifies the special subbeads column "
"(resource name " "\"subbead\").\n"
"-[no]sign       "
"This option allows you to set the abacus to allow "
"negatives (resource name \"sign\").\n"
"-decimalPosition int "
"This option specifies the number of rails to the "
"right of the decimal point\n"
"       (normally 2) (resource name \"decimalPosition\").\n"
"-[no]group      "
"This option allows you to group the displayed "
"digits for readability (resource name \"group\").\n"
"-groupSize int       "
"This option specifies the group size to the left of the "
"decimal point (normally 3) (resource\n"
"       name \"groupSize\").\n"
"-[no]decimalComma    "
"This option allows you to swap \".\" for \",\" "
"to allow for different display format (resource\n"
"       name \"decimalComma\").\n"
"-base int       "
"This option specifies the base used (default is base "
"10) (resource name \"base\").  By default,\n"
"       one has to set the format mode to not be "
"\"Generic\" for this to work (unless you know what you are doing).\n"
"-[no]eighth    "
"This option specifies the base for the Roman subdeck, "
"(if set, the resource is set to 8, else\n"
"       it is set to 12) (resource "
"name \"subbase\")."
};
static const char options3Help[] = {
"-anomaly int    "
"This option specifies the offset from base for a "
"multiplicative factor of the rail with the\n"
"       anomaly (if none, this is set to 0) "
"(resource name \"anomalySq\").\n"
"-shiftAnomaly int    "
"This option specifies the offset from decimal point "
"for the anomaly (usually 2) (resource name\n"
"       \"shiftAnomaly\").\n"
"-anomalySq int       "
"This option specifies the offset from base for the "
"second anomaly (if none, this is set to 0)\n"
"       (resource name \"anomalySq\").\n"
"-shiftAnomalySq int  "
"This option specifies the offset in rails from the "
"first anomaly (usually 2) (resource\n"
"       name \"shiftAnomalySq\").\n"
"-displayBase int     "
"This option specifies the base displayed (default is "
"base 10) (resource name \"displayBase\").\n"
"       If this is different then \"base\" then it is "
"implemented using \"long long\" and the calculation is\n"
"       limited by its bounds.  Also the fractional "
"part does not scale with the \"displayBase\" so if the\n"
"       \"displayBase\" is greater than the \"base\" it "
"looses some precision.  Also no rounding is done.\n"
"-[no]pressOffset     "
"This option allows you to put a pixel space between all the "
"beads so there is room for the bead\n"
"       to move when pressed (resource name \"pressOffset\").\n"
"-[no]romanNumerals   "
"This option allows you to set the abacus to "
"allow Roman Numerals (resource name\n"
"       \"romanNumerals\").  Roman Numerals above 3999 are "
"normally represented with bars on top, due to ASCII\n"
"       constraints this is represented instead in lower case "
"(historically case was ignored).   Roman Numerals above\n"
"       3,999,999 were not represented historically.  "
"Roman numerals change with displayBase in an\n"
"       \"experimental\" way.  When used with twelfths and "
"subdecks, named fraction symbols are used.  Due to\n"
"       ASCII constraints the sigma is represented as E, the "
"backwards C is represented as a Q, the mu as a u, and\n"
"       the Z with a - through the center as a z.  If "
"available, decimal input is ignored.\n"
"-[no]latin  "
"This option allows you to set the abacus to "
"allow latin fractions instead of a symbolic notation in the\n"
"       Roman numeral output (resource name "
"\"latin\").\n"
"-[no]ancientRoman    "
"This option allows you to set the abacus to "
"allow ancient Roman numerals instead of the modern in\n"
"       the Roman numeral output (resource name "
"\"ancientRoman\").\n"
"-[no]modernRoman     "
"This option allows you to set the abacus to "
"allow modern Roman numerals instead of the ancient on\n"
"       the Roman Hand abacus (resource name "
"\"modernRoman\").\n"
"-chinese    "
"This option specifies the format of the abacus "
"(resource name \"format\") to \"Chinese\" for the Chinese\n"
"       Saun-pan.\n"
"-japanese   "
"This option specifies the format of the abacus "
"(resource name \"format\") to \"Japanese\" for the Japanese\n"
"       post-WWII Soroban.  "
"This is also similar to the Roman Hand Abacus.\n"
"-korean     "
"This option specifies the format of the abacus "
"(resource name \"format\") to \"Korean\" for the Korean\n"
"       Supan or Japanese pre-WWII Soroban.\n"
"-roman      "
"This option specifies the format of the abacus "
"(resource name \"format\") to \"Roman\" for the Roman\n"
"       Hand Abacus, note beads will move in slots.  "
"To complete, specify \"romanNumerals\".\n"
"-russian    "
"This option specifies the format of the abacus "
"(resource name \"format\") to \"Russian\" for the Russian\n"
"       Schoty.  To complete, specify \"piece\" resource to be 4.\n"
"-danish     "
"This option specifies the format of the abacus "
"(resource name \"format\") to \"Danish\" for the Danish\n"
"       Elementary School Abacus teaching aid.\n"
"-generic    "
"This option specifies the format of the abacus "
"(resource name \"format\") to \"Generic\".  This option specifies\n"
"       a format that is more configurable by using "
"resources, since there are few rules to govern its behavior.\n"
"-it         "
"This option specifies the country code of the museum of the "
"abacus in Museum of the Thermae, Rome.\n"
"-uk         "
"This option specifies the country code of the museum of the "
"abacus in British Museum in London.\n"
"-fr         "
"This option specifies the country code of the museum of the "
"abacus in Cabinet de medailles, Bibliotheque\n"
"       nationale, Paris.\n"
"-version    "
"This option tells you what version of xabacus you have."
};
#endif

#if defined(HAVE_MOTIF) || defined(WINVER)
static const char descriptionHelp[] = {
"This is an implementation of the classic Chinese abacus "
"(Saun-pan) which has its origins in the 12th century.\n"
"\n"
"The device  has two decks.  Each deck, separated by a  partition,"
" normally  has  13  rails  on which are  mounted  beads.\n"
"Each rail on the top deck contains 1 or 2  beads,  and  each "
"rod  on the bottom deck contains 4 or 5 beads.  Each bead on\n"
"the upper deck has a value of five, while each bead  on  the "
"lower  deck has value of one.  Beads are considered counted,\n"
"when moved towards the partition separating the decks, i.e. "
"to add a value of one, a bead in the bottom deck is moved up,\n"
"and to add a value of 5, a bead in the top deck is moved "
"down.\n"
"\n"
"The basic operations of the abacus are addition and subtraction. "
"  Multiplication can be done by mentally multiplying the\n"
"digits  and adding up the intermediate results  on the abacus. "
" Division would be similar  where the intermediate results\n"
"are subtracted.  There are techniques like using your thumb "
"with forefinger  which does not apply with mouse entry.  Also\n"
"with multiplication,  one can  carry out calculations  on "
"different parts  of the abacus, here it is nice to  have a long\n"
"abacus.\n"
"\n"
"The pre-WWII Japanese abacus (Soroban)  (or Korean Supan) is "
"similar to the Chinese abacus but has only one bead per rail\n"
"on the top deck.  The later Japanese abacus was further "
"simplified to have only 4 beads per rail on the bottom deck.\n"
"\n"
"The Roman hand-abacus predates the  Chinese  abacus  and  is "
"very similar to the later Japanese abacus, but seems to have\n"
"fallen out of use with the Fall  of  the  Roman  Empire  (at "
"least 3 are in existence).  The Roman abaci are brass plates\n"
"where the beads move in slots.  In addition to the normal  7 "
"columns  of  beads, they generally have 2 special columns on\n"
"the right side.  In two examples:  the first  special column "
"was for 12ths  (12 uncia (ounces) = 1 as) and had one  extra\n"
"bead in the bottom deck.  Also the last column was a combination "
"of halves, quarters, and twelfths of an ounce and had no\n"
"beads in the top deck and 4 at the bottom (beads did not have "
"to come to the top  to be  counted  but at one of 3  marked\n"
"points, where the top bead was for halves, the next bead for "
"quarters,  and the last two beads for twelfths).  In another\n"
"surviving example: the 2 special columns were switched and the "
"combination column was broken into 3 separate slots.\n"
"\n"
"The Russian abacus was invented in the 17th  century,  here "
"the beads are moved from right to left.  It has colored beads\n"
"in the middle for ease of use.  Quarters represent  1/4  Rubles "
"and are only  present  historically on the Russian abacus\n"
"(Schoty).  Some of the older  Schoty  have a extra place for "
"the  1/4  Kopek  (quarter percent)  as well as the 1/4 Ruble\n"
"(quarter).\n"
"\n"
"The Danish abacus was used in the early 20th century in "
"elementary schools as a teaching aid.\n"
"\n"
"The Mesoamerican Nepohualtzintzin is a Japanese abacus base 20. "
"  The Mesoamericans had base 20 with the exception of the\n"
"3rd decimal  place where instead of 20 * 20 = 400 the third place "
"marked 360  and the 4th place was 20 * 360, etc..  They\n"
"independently  created their own zero  (only Babylon (base 60) "
"and  India (base 10) have done this)  but the anomaly took\n"
"away its true power.\n"
"\n"
"An easy way  of figuring out time  in seconds given hours, "
"minutes, and seconds,  can be done on the abacus  with special\n"
"anomaly \"watch\" settings.\n"
"\n"
"The Chinese Solid-and-Broken-Bar System is a base 12 numbering "
"system and not really an abacus.  When the abacus is setup\n"
"in this way though (topFactor 3, topNumber 3, bottomNumber 2, "
"base 12, displayBase 12), it is easy to relate the two.\n"
"\n"
"The signed bead is an invention of the  author  and  is  not "
"present  on  any historical abacus (to his knowledge) and is\n"
"used to represent negatives.  \"New & Improved\" abacus "
" models have two auxiliary  decks stacked above the principal decks\n"
"that enable multiplication, division, square-root, and "
"cube-root computations to be performed with equal ease as addition\n"
"and subtraction."
};

static const char featuresHelp[] = {
"Click \"mouse-left\" button on a bead  you want to move.  The "
"beads will shift  themselves to vacate the area of the column\n"
"that was clicked.\n"
"\n"
"Click \"mouse-right\" button, or press \"C\" or \"c\" keys, to "
"clear the abacus.\n"
"\n"
"Press \"O\" or \"o\" keys to toggle the demo mode.\n"
"\n"
"Press \"$\" key to toggle the teach mode.\n"
"\n"
"In teach mode, \"+\" key toggles starting side to sum,  "
"\"*\" key toggles for starting side for multiplicand.\n"
"\n"
"Press \"~\" or \"`\" keys to complement the beads on the rails.\n"
"\n"
"Press \"I\" or \"i\" keys to increment the number of rails.  "
"Press \"D\" or \"d\" keys to decrement the number of rails.\n"
"\n"
"Press \"F\" or \"f\" keys to switch between Chinese, Japanese, "
"Korean, Roman, Russian, and Danish formats.  There is an\n"
"extra \"Generic\" format, this allows one to break some rules "
"binding the other formats (for example, if one wanted more\n"
"beads on top deck than on bottom deck you would use this, in "
"addition to resource option changes).\n"
"\n"
"Press \"E\" or \"e\" keys to switch between it, uk, and fr "
"museum formats.\n"
"\n"
"Press \"M\" or \"m\" keys to toggle the availability of Roman "
"Numerals.\n"
"\n"
"Press \"S\" or \"s\" keys to toggle the availability of sign "
"bead.\n"
"\n"
"Press \"U\" or \"u\" keys to toggle the availability of quarter "
"beads.  (Mutually exclusive to twelfth beads).   Intended for\n"
"the Russian Abacus.\n"
"\n"
"Press \"P\" or \"p\" keys to  toggle the availability  of quarter  "
"percent beads.   (Dependent on  quarter beads  (or twelfth\n"
"beads)).  Intended for the  older Russian Abacus.\n"
"\n"
"Press \"T\" or \"t\" keys to  toggle the availability of twelfth "
"beads.  (Mutually exclusive to quarter beads).  Intended for\n"
"the Roman Abacus.\n"
"\n"
"Press \"B\" or \"b\" keys to  toggle the availability  of subdecks."
"  (Dependent on twelfth beads (or quarter beads) and Roman\n"
"format).  Intended for the Roman Abacus, the lowest value of the "
"rightmost beads are a twelfth of the column, second from\n"
"right.\n"
"\n"
"Press \"H\" or \"h\" keys to  toggle the availability  of subdecks."
"  (Dependent on twelfth beads (or quarter beads) and Roman\n"
"format).  Intended for the Roman Abacus, the lowest value of the "
"rightmost beads are an eighth of the column, second from\n"
"right.\n"
"\n"
"Press \"L\" or \"l\" keys to  toggle the availability of anomaly "
"bars.  Intended to used with Japanese Abacus and base 20 for\n"
"the Mesoamerican Abacus. (Mutually exclusive to watch bars).\n"
"\n"
"Press \"W\" or \"w\" keys to  toggle the availability  of watch "
"bars.  Intended to represent seconds  where hours and minutes\n"
"can be set.  (Mutually exclusive to anomaly bars).\n"
"\n"
"Press \">\" or \".\" keys to speed up the movement of beads.  "
"Press \"<\" or \",\" keys to slow down the movement of beads.\n"
"\n"
"Press \"@\" key to toggle the sound.\n"
"\n"
"Press \"Esc\" key to hide program.\n"
"\n"
"Press \"Q\", \"q\", or \"CTRL-C\" keys to kill program.\n"
"\n"
"The abacus may be resized.  Beads will reshape depending  on "
"the room they have.  Demo Mode:  In this mode, the abacus is\n"
"controlled by the  program.   When  started  with  the  demo "
"option,  a  second window is presented that should be placed\n"
"directly below the abacus-window. Descriptive text, and user "
"prompts  are  displayed in this window.  Pressing 'q' during\n"
"the demo will quit it.  Clicking the left mouse-button  with "
"the  pointer  in the window will restart the demo (beginning\n"
"of current lesson)."
};

static const char referencesHelp[] = {
"Luis Fernandes  http://www.ee.ryerson.ca/~elf/abacus/\n"
"Lee Kai-chen, How to Learn Lee's Abacus, 1958, 58 pages.\n"
"Abacus Guide Book, 57 pages.\n"
"Georges Ifrah, The Universal history of Numbers, Wiley Press "
"2000, pp 209-211, 288-294.\n"
"Review of above: http://www.ams.org/notices/200201/rev-dauben.pdf\n"
"David Eugene Smith, History of Mathematics Volume II, "
"Dover Publications, Inc 1958, pp 156-195."
};
#endif

static const char *formatString[] =
{
      "Chinese Saun-pan",
      "Japanese Soroban",
      "Korean Supan",
      "Roman Hand-abacus",
      "Russian Schoty",
      "Danish Abacus",
      "Generic Abacus"
};

#ifdef WINVER
#include "AbacusP.h"
#define TITLE "wabacus"

static AbacusRec widget;

char calc[120] = "";

#define MAX_DIGITS 256        /* This limits the number of rails */

static void
forceNormalParams(AbacusWidget w)
{
      int base = DEFAULT_BASE;
      int displayBase = DEFAULT_BASE;

      w->abacus.base = base;
      w->abacus.displayBase = displayBase;
}

static void
forceDemoParams(AbacusWidget w)
{
      int min, mode;
      int bottomPiece, bottomPiecePercent;
      Boolean demo, sign;

      forceNormalParams(w);
      mode = w->abacus.mode;
      sign = w->abacus.sign;
      bottomPiece = w->abacus.decks[BOTTOM].piece;
      bottomPiecePercent = w->abacus.decks[BOTTOM].piecePercent;
      demo = w->abacus.demo;
      if (mode == GENERIC) {
            changeFormatAbacus(w);
      }
      min = ((sign) ? 1: 0) + ((bottomPiece) ? 1 : 0) +
            ((bottomPiecePercent) ? 1 + w->abacus.shiftPercent : 0) +
            ((demo) ? MIN_DEMO_RAILS : MIN_RAILS);
      while (w->abacus.rails < min) {
            incrementAbacus(w);
      }
}

static void
forceTeachParams(AbacusWidget w)
{
      w->abacus.displayBase = w->abacus.base;
      w->abacus.sign = False;
      w->abacus.anomaly = 0;
      w->abacus.anomalySq = 0;
      if (w->abacus.mode == GENERIC) {
            changeFormatAbacus(w);
      }     
      (void) InvalidateRect(w->core.hWnd, NULL, TRUE);
}

void
setAbacus(AbacusWidget w, int reason)
{
      switch (reason) {
      case ACTION_SCRIPT:
#if 0
            {
                  int deck, rail, number;

                  deck = w->abacus.deck;
                  rail = w->abacus.rail;
                  number = w->abacus.number;
                  (void) printf("%d %d %d %d\n", PRIMARY,
                        deck, rail, number);
            }
#endif
            break;
      case ACTION_BASE_DEFAULT:
            forceNormalParams(w);
            break;
      case ACTION_DEMO_DEFAULT:
            clearAbacus(w);
            forceDemoParams(w);
            break;
      case ACTION_CLEAR:
            clearAbacus(w);
            if (w->abacus.demo) {
                  clearAbacusDemo(w);
            }
            break;
      case ACTION_COMPLEMENT:
            complementAbacus(w);
            break;
      case ACTION_CLEAR_NODEMO:
            clearAbacus(w);
            break;
      case ACTION_HIDE:
            ShowWindow(w->core.hWnd, SW_SHOWMINIMIZED);
            break;
      case ACTION_CLEAR_QUERY:
            break;
      case ACTION_DEMO:
            toggleDemoAbacusDemo(w);
            break;
      case ACTION_NEXT:
            showNextAbacusDemo(w);
            break;
      case ACTION_REPEAT:
            showRepeatAbacusDemo(w);
            break;
      case ACTION_JUMP:
            showJumpAbacusDemo(w);
            break;
      case ACTION_MORE:
            showMoreAbacusDemo(w);
            break;
      case ACTION_INCREMENT:
      case ACTION_DECREMENT:
      case ACTION_FORMAT:
      case ACTION_ROMAN_NUMERAL:
      case ACTION_SIGN:
      case ACTION_QUARTER:
      case ACTION_QUARTER_PERCENT:
      case ACTION_TWELFTH:
      case ACTION_SUBDECK:
      case ACTION_ANOMALY:
      case ACTION_WATCH:
            break;
      }
}

/* input dialog box handle */
HWND hCalcDlg = NULL;
HWND hDemoDlg = NULL;
HWND hTeachDlg = NULL;

void
setAbacusString(AbacusWidget w, int reason, char *string)
{
      (void) strncpy(calc, string, 120);
      if (reason == ACTION_SCRIPT || reason == ACTION_IGNORE) {
            char szBuf[MAX_DIGITS + 67];
            int mode = w->abacus.mode;

            (void) sprintf(szBuf, "%s  %s", string,
                  (mode < 0 || mode >= MAX_FORMATS) ? "Abacus" :
                  formatString[mode]);
            SetWindowText(w->core.hWnd, (LPSTR) szBuf);
      }
      SetDlgItemText(hCalcDlg, ACTION_CALC, calc);
}

static LRESULT CALLBACK
about(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
      if (message == WM_COMMAND &&
                  (LOWORD(wParam) == IDOK ||
                  LOWORD(wParam) == IDCANCEL)) {
            (void) EndDialog(hDlg, TRUE);
            return TRUE;
      }
      return FALSE;
}

static LRESULT CALLBACK
teach(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
      if (message == WM_COMMAND) {
            if (LOWORD(wParam) == IDOK) {
                  char str[120];
                  GetDlgItemText(hDlg, ACTION_TEACH, str, 120);
                  widget.core.hDC = GetDC(widget.core.hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  widget.abacus.teach = True;
                  (void) teachStep(&widget, str, 0);
                  if (strcmp(str, "q") == 0) {
                        destroyAbacus(&widget, NULL);
                  }
                  /*setAbacusString(&widget, ACTION_IGNORE, str);*/
                  (void) ReleaseDC(widget.core.hWnd,
                        widget.core.hDC);
                  /*SetDlgItemText(hDlg, ACTION_TEACH, calc);*/
                  return TRUE;
            } else if (LOWORD(wParam) == IDCANCEL) {
                  widget.abacus.teach = False;
                  (void) EndDialog(hDlg, TRUE);
                  hTeachDlg = NULL;
                  return TRUE;
            }
      }
      return FALSE;
}

static LRESULT CALLBACK
calculation(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
      if (message == WM_COMMAND) {
            if (LOWORD(wParam) == IDOK) {
                  char str[120];
                  GetDlgItemText(hDlg, ACTION_CALC, str, 120);
                  widget.core.hWnd = GetParent(hDlg);
                  widget.core.hDC = GetDC(widget.core.hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  calculate(&widget, str, 0);
                  (void) ReleaseDC(widget.core.hWnd,
                        widget.core.hDC);
                  SetDlgItemText(hDlg, ACTION_CALC, calc);
                  return TRUE;
            } else if (LOWORD(wParam) == IDCANCEL) {
                  (void) DestroyWindow(hDlg);
                  hCalcDlg = NULL;
                  return TRUE;
            }
      }
      return FALSE;
}

static LRESULT CALLBACK
demo(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
      if (message == WM_COMMAND) {
            if (LOWORD(wParam) == IDOK) {
                  char str[120];
                  GetDlgItemText(hDlg, ACTION_DEMO1, str, 120);
                  widget.core.hWnd = GetParent(hDlg);
                  widget.core.hDC = GetDC(widget.core.hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  //demo(&widget, str, 0);
                  (void) ReleaseDC(widget.core.hWnd,
                        widget.core.hDC);
                  SetDlgItemText(hDlg, ACTION_DEMO4, "fix me");
                  return TRUE;
            } else if (LOWORD(wParam) == IDCANCEL) {
                  widget.abacus.demo = False;
                  (void) EndDialog(hDlg, TRUE);
                  hDemoDlg = NULL;
                  return TRUE;
            }
      }
      return FALSE;
}

static LRESULT CALLBACK
WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
      HBRUSH brush = (HBRUSH) NULL;
      PAINTSTRUCT paint;

      widget.core.hWnd = hWnd;
      if (GetFocus()) {
            if (!widget.abacus.focus) {
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_BRUSH));
                  enterAbacus(&widget);
                  (void) EndPaint(hWnd, &paint);
            }
      } else {
            if (widget.abacus.focus) {
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_BRUSH));
                  leaveAbacus(&widget);
                  (void) EndPaint(hWnd, &paint);
            }
      }
      switch (message) {
      case WM_CREATE:
            initializeAbacus(&widget, brush);
            initializeAbacusDemo(&widget);
            break;
      case WM_DESTROY:
            destroyAbacus(&widget, brush);
#if 0
            if (widget.abacus.demo)
                  destroyAbacusDemo();
#endif
            break;
      case WM_SIZE:
            resizeAbacus(&widget);
            (void) InvalidateRect(hWnd, NULL, TRUE);
            break;
      case WM_PAINT:
            widget.core.hDC = BeginPaint(hWnd, &paint);
            (void) SelectObject(widget.core.hDC,
                  GetStockObject(NULL_PEN));
            exposeAbacus(&widget);
            if (widget.abacus.demo) {
                  exposeAbacusDemo(&widget);
            }
            (void) EndPaint(hWnd, &paint);
            break;
      case WM_LBUTTONDOWN:
            widget.core.hDC = GetDC(hWnd);
            (void) SelectObject(widget.core.hDC,
                  GetStockObject(NULL_PEN));
#if 0
            if (widget.abacus.demo) {
                  clearAbacus(&widget);
                  clearAbacusDemo(&widget);
            } else
#endif
            {
                  selectAbacus(&widget, LOWORD(lParam),
                        HIWORD(lParam));
            }
            (void) ReleaseDC(hWnd, widget.core.hDC);
            break;
      case WM_LBUTTONUP:
      case WM_NCMOUSEMOVE:
            widget.core.hDC = GetDC(hWnd);
            (void) SelectObject(widget.core.hDC,
                  GetStockObject(NULL_PEN));
            releaseAbacus(&widget);
            (void) ReleaseDC(hWnd, widget.core.hDC);
            break;
      case WM_COMMAND:
            switch (LOWORD(wParam)) {
            case ACTION_EXIT:
                  if (hDemoDlg != NULL) {
                        (void) EndDialog(hDemoDlg, TRUE);
                        hDemoDlg = NULL;
                  }
                  if (widget.abacus.demo)
                        toggleDemoAbacusDemo(&widget);
                  else
                        destroyAbacus(&widget, brush);
                  break;
            case ACTION_HIDE:
                  hideAbacus(&widget);
                  break;
            case ACTION_CLEAR:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  clearAbacus(&widget);
                  if (widget.abacus.demo) {
                        clearAbacusDemo(&widget);
                  }
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_COMPLEMENT:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  complementAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_INCREMENT:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  incrementAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_DECREMENT:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  decrementAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_FORMAT:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  changeFormatAbacus(&widget);
                  if (widget.abacus.demo) {
                        clearAbacusDemo(&widget);
                        clearAbacus(&widget);
                  }
                  break;
            case ACTION_MUSEUM:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  changeMuseumAbacus(&widget);
                  break;
            case ACTION_ROMAN_NUMERAL:
                  toggleRomanNumeralsAbacus(&widget);
                  break;
            case ACTION_GROUP:
                  toggleGroupingAbacus(&widget);
                  break;
            case ACTION_SIGN:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  toggleNegativeSignAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_QUARTER:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  toggleQuartersAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_QUARTER_PERCENT:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  toggleQuarterPercentsAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_TWELFTH:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  toggleTwelfthsAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_SUBDECK:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  toggleSubdecksAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_EIGHTH:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  toggleEighthsAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_ANOMALY:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  toggleAnomalyAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_WATCH:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  toggleWatchAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
#if 0
            case ACTION_VERTICAL:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  VerticalAbacus(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
#endif
            case ACTION_DEMO:
#define DEMO_DIALOG
#ifdef DEMO_DIALOG
                  if (hDemoDlg != NULL) {
                        (void) DestroyWindow(hDemoDlg);
                        hDemoDlg = NULL;
                        widget.abacus.demo = False;
                  }
                  hDemoDlg = CreateDialog(widget.core.hInstance,
                        "Demo", hWnd,
                        (DLGPROC) demo);
#endif
                  if (!widget.abacus.demo)
                        toggleDemoAbacusDemo(&widget);
                  resizeAbacus(&widget);
                  (void) InvalidateRect(hWnd, NULL,
                        TRUE);
                  break;
            case ACTION_NEXT:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  /* showNextAbacus(&widget); */
                  if (widget.abacus.demo)
                        showNextAbacusDemo(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_REPEAT:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  /* showRepeatAbacus(&widget); */
                  if (widget.abacus.demo)
                        showRepeatAbacusDemo(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_JUMP:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  /* showJumpAbacus(&widget); */
                  if (widget.abacus.demo)
                        showJumpAbacusDemo(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_CHAPTER1:
            case ACTION_CHAPTER2:
            case ACTION_CHAPTER3:
            case ACTION_CHAPTER4:
            case ACTION_CHAPTER5:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  /* showChapterAbacus(&widget); */
                  if (widget.abacus.demo)
                        showChapterAbacusDemo(&widget,
                              LOWORD(wParam) - ACTION_CHAPTER1);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_MORE:
                  widget.core.hDC = GetDC(hWnd);
                  (void) SelectObject(widget.core.hDC,
                        GetStockObject(NULL_PEN));
                  /* showMoreAbacus(&widget); */
                  if (widget.abacus.demo)
                        showMoreAbacusDemo(&widget);
                  (void) ReleaseDC(hWnd,
                        widget.core.hDC);
                  break;
            case ACTION_SPEED:
                  speedUpAbacus(&widget);
                  break;
            case ACTION_SLOW:
                  slowDownAbacus(&widget);
                  break;
            case ACTION_SOUND:
                  toggleSoundAbacus(&widget);
                  break;
            case ACTION_CALC:
                  if (hCalcDlg != NULL) {
                        (void) DestroyWindow(hCalcDlg);
                        hCalcDlg = NULL;
                  }
                  hCalcDlg = CreateDialog(widget.core.hInstance,
                        "Calculate", hWnd,
                        (DLGPROC) calculation);
                  break;
            case ACTION_TEACH:
                  /*(void) DialogBox(widget.core.hInstance,
                        "Teach", hWnd,
                        (DLGPROC) teach);*/
                  forceTeachParams(&widget);
                  if (hTeachDlg != NULL) {
                        (void) DestroyWindow(hTeachDlg);
                        hTeachDlg = NULL;
                        widget.abacus.teach = False;
                  }
                  hTeachDlg = CreateDialog(widget.core.hInstance,
                        "Teach", hWnd,
                        (DLGPROC) teach);
                  break;
            case ACTION_RIGHT_TO_LEFT_ADD:
                  toggleRightToLeftAddAbacus(&widget);
                  break;
            case ACTION_RIGHT_TO_LEFT_MULT:
                  toggleRightToLeftMultAbacus(&widget);
                  break;
            case ACTION_DESCRIPTION:
                  (void) MessageBox(hWnd,
                        descriptionHelp,
                        "Description", MB_OK);
                  break;
            case ACTION_FEATURES:
                  (void) MessageBox(hWnd, featuresHelp,
                        "Features", MB_OK);
                  break;
            case ACTION_REFERENCES:
                  (void) MessageBox(hWnd, referencesHelp,
                        "References", MB_OK);
                  break;
            case ACTION_ABOUT:
                  (void) DialogBox(widget.core.hInstance,
                        "About", hWnd,
                        (DLGPROC) about);
                  break;
            }
            break;
      default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
      }
      return FALSE;
}

void drawDemoText(const char* line, int i)
{
      if (hDemoDlg == NULL || line == NULL)
            return;
      SetDlgItemText(hDemoDlg, ACTION_DEMO1 + i, line);
}

void drawTeachText(const char* line, int i)
{
      if (hTeachDlg == NULL || line == NULL)
            return;
      SetDlgItemText(hTeachDlg, ACTION_TEACH1 + i, line);
}

int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
            int numCmdShow)
{
      HWND hWnd;
      MSG msg;
      WNDCLASS wc;
      HACCEL hAccel;

      if (!hPrevInstance) {
            wc.style = CS_HREDRAW | CS_VREDRAW;
            wc.lpfnWndProc = WindowProc;
            wc.cbClsExtra = 0;
            wc.cbWndExtra = 0;
            wc.hInstance = hInstance;
            wc.hIcon = LoadIcon(hInstance, TITLE);
            wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
            wc.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH);
            wc.lpszMenuName = TITLE;
            wc.lpszClassName = TITLE;
            if (!RegisterClass(&wc))
                  return FALSE;
      }
      widget.core.hInstance = hInstance;
      hWnd = CreateWindow(TITLE,
            TITLE,
            WS_OVERLAPPEDWINDOW,
            (signed) CW_USEDEFAULT,
            (signed) CW_USEDEFAULT,
            (signed) CW_USEDEFAULT,
            (signed) CW_USEDEFAULT,
            HWND_DESKTOP,
            (HMENU) NULL,
            hInstance,
            (void *) NULL);
      if (!hWnd)
            return FALSE;
      hAccel = (HACCEL) LoadAccelerators(hInstance, TITLE);
      (void) ShowWindow(hWnd, numCmdShow);
      (void) UpdateWindow(hWnd);
      while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
            if (!IsDialogMessage(hCalcDlg, &msg) &&
                        !IsDialogMessage(hTeachDlg, &msg)) {
                  if (!TranslateAccelerator(hWnd, hAccel, &msg)) {
                        (void) TranslateMessage(&msg);
                        (void) DispatchMessage(&msg);
                  }
            }
      }
      return (int) msg.wParam;
}

#else
#include "xwin.h"
#include <X11/Shell.h>
#include <X11/cursorfont.h>
#ifdef HAVE_MOTIF
#include <Xm/PanedW.h>
#include <Xm/RowColumn.h>
#include <Xm/Text.h>
#include <Xm/LabelG.h>
#include <Xm/MessageB.h>
#include <Xm/PushBG.h>
#include <Xm/CascadeB.h>
#include <Xm/Scale.h>
#include <Xm/ToggleB.h>
#include <Xm/ToggleBG.h>
#include <Xm/Form.h>
#ifdef MOUSEBITMAPS
#include "pixmaps/mouse-l.xbm"
#include "pixmaps/mouse-r.xbm"
#endif
#endif
#include "Abacus.h"
#ifdef HAVE_XPM
#include <X11/xpm.h>
#ifdef CONSTPIXMAPS
#include "abacus.t.xpm"
#include "abacus.p.xpm"
#include "abacus.s.xpm"
#include "abacus.m.xpm"
#include "abacus.l.xpm"
#include "abacus.xpm"
#else
#include "pixmaps/abacus.t.xpm"
#include "pixmaps/abacus.p.xpm"
#include "pixmaps/abacus.s.xpm"
#include "pixmaps/abacus.m.xpm"
#include "pixmaps/abacus.l.xpm"
#include "pixmaps/abacus.xpm"
#endif
#define RESIZE_XPM(s) ((char **) (((s)<=32)?\
(((s)<=22)?(((s)<=16)?abacus_t_xpm:abacus_p_xpm):\
(((s)<=24)?abacus_s_xpm:abacus_m_xpm)):\
(((s)<=48)?abacus_l_xpm:abacus_xpm)))
#endif
#include "pixmaps/abacus.xbm"
#define DEFINE_XBM (char *) abacus_bits, abacus_width, abacus_height

#define FILE_NAME_LENGTH 1024

#ifdef HAVE_MOTIF
#define MAX_RAILS 24          /* Totally arbitrary */
#define DEMO 0
#define TEACH 1
#define NORMAL 2
#define BASE 3
static const char *choiceString[] =
{
      "Demo", "Teach", "Normal", "Base"
};
#define CHOICES sizeof choiceString / sizeof choiceString[0]
#else
/* Needs Motif */
#ifdef LEE_ABACUS
#undef LEE_ABACUS
#endif
#define MAX_DIGITS 256        /* This limits the number of rails */
#define TITLE_LENGTH (MAX_DIGITS+FILE_NAME_LENGTH+3)
#endif

#ifdef HAVE_MOTIF
static Widget mainPanel, baseForm, teachRowCol;
static Widget sizeSlider, abacusBaseSlider, displayBaseSlider;
static Widget choiceMenu, choiceSubMenu;
static Widget formatMenu, formatSubMenu;
static Widget choiceOptions[CHOICES], formatOptions[MAX_MODES];
#ifdef LEE_ABACUS
static Widget leftAuxAbacus = NULL, rightAuxAbacus = NULL;
static Widget leftAuxTracker = NULL, rightAuxTracker = NULL;
#endif
#ifdef TOGGLES
/* No longer support this, as its hard to keep the TOGGLES correct, since
   many events can be triggered to bypass them. */
static Widget romanNumeralsSwitch, groupSwitch, signSwitch, pieceSwitch;
static Widget piecePercentSwitch, twelfthSwitch, anomalySwitch, watchSwitch;
static Widget subdeckSwitch, eighthSwitch;
static Widget rightToLeftAddSwitch, rightToLeftMultSwitch;
#endif
static Widget tracker, teachTracker = NULL, teachLine[3];
static Widget descriptionDialog, featuresDialog;
static Widget optionsDialog, options1Dialog, options2Dialog, options3Dialog;
static Widget referencesDialog, aboutDialog;
static Widget clearDialog;
static char *mathBuffer = NULL;
static Boolean baseSet = False;
static Arg arg[4];
#else
static Widget shell = NULL;
static char titleDsp[TITLE_LENGTH];
#endif
static Pixmap pixmap = None;
static char titleDspDemo[FILE_NAME_LENGTH + 6];
static Widget topLevel, abacus, abacusDemo = NULL;
static char *progDsp;

static void
usage(char *programName)
{
      unsigned int i;

      (void) fprintf(stderr, "usage: %s\n", programName);
      for (i = 0; i < strlen(optionsHelp); i++) {
            if (i == 0 || optionsHelp[i - 1] == '\n')
                  (void) fprintf(stderr, "\t");
            (void) fprintf(stderr, "%c", (optionsHelp[i]));
      }
      (void) fprintf(stderr, "\n");
      exit(1);
}

static XrmOptionDescRec options[] =
{
      {(char *) "-mono", (char *) "*abacus.mono", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nomono", (char *) "*abacus.mono", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-rv", (char *) "*abacus.reverseVideo", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-reverse", (char *) "*abacus.reverseVideo", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-norv", (char *) "*abacus.reverseVideo", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-noreverse", (char *) "*abacus.reverseVideo", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-foreground", (char *) "*abacus.Foreground", XrmoptionSepArg, NULL},
      {(char *) "-fg", (char *) "*abacus.Foreground", XrmoptionSepArg, NULL},
      {(char *) "-background", (char *) "*Background", XrmoptionSepArg, NULL},
      {(char *) "-bg", (char *) "*Background", XrmoptionSepArg, NULL},
      {(char *) "-border", (char *) "*abacus.borderColor", XrmoptionSepArg, NULL},
      {(char *) "-bd", (char *) "*abacus.borderColor", XrmoptionSepArg, NULL},
      {(char *) "-frame", (char *) "*abacus.frameColor", XrmoptionSepArg, NULL},
      {(char *) "-primaryBeadColor", (char *) "*abacus.primaryBeadColor", XrmoptionSepArg, NULL},
      {(char *) "-leftAuxBeadColor", (char *) "*abacus.leftAuxBeadColor", XrmoptionSepArg, NULL},
      {(char *) "-rightAuxBeadColor", (char *) "*abacus.rightAuxBeadColor", XrmoptionSepArg, NULL},
      {(char *) "-secondaryBeadColor", (char *) "*abacus.secondaryBeadColor", XrmoptionSepArg, NULL},
      {(char *) "-highlightBeadColor", (char *) "*abacus.highlightBeadColor", XrmoptionSepArg, NULL},
      {(char *) "-primaryRailColor", (char *) "*abacus.primaryRailColor", XrmoptionSepArg, NULL},
      {(char *) "-secondaryRailColor", (char *) "*abacus.secondaryRailColor", XrmoptionSepArg, NULL},
      {(char *) "-highlightRailColor", (char *) "*abacus.highlightRailColor", XrmoptionSepArg, NULL},
      {(char *) "-bumpSound", (char *) "*abacus.bumpSound", XrmoptionSepArg, NULL},
      {(char *) "-moveSound", (char *) "*abacus.moveSound", XrmoptionSepArg, NULL},
      {(char *) "-sound", (char *) "*abacus.sound", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nosound", (char *) "*abacus.sound", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-delay", (char *) "*abacus.delay", XrmoptionSepArg, NULL},
      {(char *) "-demo", (char *) "*abacus.demo", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nodemo", (char *) "*abacus.demo", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-script", (char *) "*abacus.script", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-noscript", (char *) "*abacus.script", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-demopath", (char *) "*abacus.demoPath", XrmoptionSepArg, NULL},
      {(char *) "-demofont", (char *) "*abacus.demoFont", XrmoptionSepArg, NULL},
      {(char *) "-demofn", (char *) "*abacus.demoFont", XrmoptionSepArg, NULL},
      {(char *) "-demoforeground", (char *) "*abacus.demoForeground", XrmoptionSepArg, NULL},
      {(char *) "-demofg", (char *) "*abacus.demoForeground", XrmoptionSepArg, NULL},
      {(char *) "-demobackground", (char *) "*abacus.demoBackground", XrmoptionSepArg, NULL},
      {(char *) "-demobg", (char *) "*abacus.demoBackground", XrmoptionSepArg, NULL},
      {(char *) "-teach", (char *) "*abacus.teach", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-noteach", (char *) "*abacus.teach", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-rightToLeftAdd", (char *) "*abacus.rightToLeftAdd", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-norightToLeftAdd", (char *) "*abacus.rightToLeftAdd", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-rightToLeftMult", (char *) "*abacus.rightToLeftMult", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-norightToLeftMult", (char *) "*abacus.rightToLeftMult", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-lee", (char *) "*abacus.lee", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nolee", (char *) "*abacus.lee", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-rails", (char *) "*abacus.rails", XrmoptionSepArg, NULL},
      {(char *) "-rightAuxRails", (char *) "*abacus.rightAuxRails", XrmoptionSepArg, NULL},
      {(char *) "-leftAuxRails", (char *) "*abacus.leftAuxRails", XrmoptionSepArg, NULL},
      {(char *) "-vertical", (char *) "*abacus.vertical", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-novertical", (char *) "*abacus.vertical", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-colorScheme", (char *) "*abacus.colorScheme", XrmoptionSepArg, NULL},
      {(char *) "-slot", (char *) "*abacus.slot", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-noslot", (char *) "*abacus.slot", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-diamond", (char *) "*abacus.diamond", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nodiamond", (char *) "*abacus.diamond", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-railIndex", (char *) "*abacus.railIndex", XrmoptionSepArg, NULL},
      {(char *) "-topOrient", (char *) "*abacus.topOrient", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-notopOrient", (char *) "*abacus.topOrient", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-bottomOrient", (char *) "*abacus.bottomOrient", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nobottomOrient", (char *) "*abacus.bottomOrient", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-topNumber", (char *) "*abacus.topNumber", XrmoptionSepArg, NULL},
      {(char *) "-bottomNumber", (char *) "*abacus.bottomNumber", XrmoptionSepArg, NULL},
      {(char *) "-topFactor", (char *) "*abacus.topFactor", XrmoptionSepArg, NULL},
      {(char *) "-bottomFactor", (char *) "*abacus.bottomFactor", XrmoptionSepArg, NULL},
      {(char *) "-topSpaces", (char *) "*abacus.topSpaces", XrmoptionSepArg, NULL},
      {(char *) "-bottomSpaces", (char *) "*abacus.bottomSpaces", XrmoptionSepArg, NULL},
      {(char *) "-topPiece", (char *) "*abacus.topPiece", XrmoptionSepArg, NULL},
      {(char *) "-bottomPiece", (char *) "*abacus.bottomPiece", XrmoptionSepArg, NULL},
      {(char *) "-topPiecePercent", (char *) "*abacus.topPiecePercent", XrmoptionSepArg, NULL},
      {(char *) "-bottomPiecePercent", (char *) "*abacus.bottomPiecePercent", XrmoptionSepArg, NULL},
      {(char *) "-shiftPercent", (char *) "*abacus.shiftPercent", XrmoptionSepArg, NULL},
      {(char *) "-subdeck", (char *) "*abacus.subdeck", XrmoptionSepArg, NULL},
      {(char *) "-subbead", (char *) "*abacus.subbead", XrmoptionSepArg, NULL},
      {(char *) "-sign", (char *) "*abacus.sign", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nosign", (char *) "*abacus.sign", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-decimalPosition", (char *) "*abacus.decimalPosition", XrmoptionSepArg, NULL},
      {(char *) "-group", (char *) "*abacus.group", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nogroup", (char *) "*abacus.group", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-groupSize", (char *) "*abacus.groupSize", XrmoptionSepArg, NULL},
      {(char *) "-decimalComma", (char *) "*abacus.decimalComma", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nodecimalComma", (char *) "*abacus.decimalComma", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-base", (char *) "*abacus.base", XrmoptionSepArg, NULL},
      {(char *) "-eighth", (char *) "*abacus.subbase", XrmoptionNoArg, (char *) "8"},
      {(char *) "-noeighth", (char *) "*abacus.subbase", XrmoptionNoArg, (char *) "12"},
      {(char *) "-anomaly", (char *) "*abacus.anomaly", XrmoptionSepArg, NULL},
      {(char *) "-shiftAnomaly", (char *) "*abacus.shiftAnomaly", XrmoptionSepArg, NULL},
      {(char *) "-anomalySq", (char *) "*abacus.anomalySq", XrmoptionSepArg, NULL},
      {(char *) "-shiftAnomalySq", (char *) "*abacus.shiftAnomalySq", XrmoptionSepArg, NULL},
      {(char *) "-displayBase", (char *) "*abacus.displayBase", XrmoptionSepArg, NULL},
      {(char *) "-pressOffset", (char *) "*abacus.pressOffset", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nopressOffset", (char *) "*abacus.pressOffset", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-romanNumerals", (char *) "*abacus.romanNumerals", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-noromanNumerals", (char *) "*abacus.romanNumerals", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-latin", (char *) "*abacus.latin", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nolatin", (char *) "*abacus.latin", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-ancientRoman", (char *) "*abacus.ancientRoman", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-noancientRoman", (char *) "*abacus.ancientRoman", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-modernRoman", (char *) "*abacus.modernRoman", XrmoptionNoArg, (char *) "TRUE"},
      {(char *) "-nomodernRoman", (char *) "*abacus.modernRoman", XrmoptionNoArg, (char *) "FALSE"},
      {(char *) "-chinese", (char *) "*abacus.format", XrmoptionNoArg, (char *) "chinese"},
      {(char *) "-japanese", (char *) "*abacus.format", XrmoptionNoArg, (char *) "japanese"},
      {(char *) "-korean", (char *) "*abacus.format", XrmoptionNoArg, (char *) "korean"},
      {(char *) "-roman", (char *) "*abacus.format", XrmoptionNoArg, (char *) "roman"},
      {(char *) "-russian", (char *) "*abacus.format", XrmoptionNoArg, (char *) "russian"},
      {(char *) "-danish", (char *) "*abacus.format", XrmoptionNoArg, (char *) "danish"},
      {(char *) "-generic", (char *) "*abacus.format", XrmoptionNoArg, (char *) "generic"},
      {(char *) "-it", (char *) "*abacus.museum", XrmoptionNoArg, (char *) "it"},
      {(char *) "-uk", (char *) "*abacus.museum", XrmoptionNoArg, (char *) "uk"},
      {(char *) "-fr", (char *) "*abacus.museum", XrmoptionNoArg, (char *) "fr"},
      {(char *) "-version", (char *) "*abacus.versionOnly", XrmoptionNoArg, (char *) "TRUE"}
};

static void
printState(Widget w, char *msg
#ifndef HAVE_MOTIF
, int mode
#endif
)
{
#ifdef HAVE_MOTIF
      XtVaSetValues(w, XmNvalue, msg, NULL);
#else
      (void) sprintf(titleDsp, "%s  %s",
            msg,
            (mode < 0 || mode >= MAX_FORMATS) ? "Abacus" : formatString[mode]);
      XtVaSetValues(w, XtNtitle, titleDsp, NULL);
#endif
}

#ifdef LEE_ABACUS
#define AUXWIN(n) (n == LEFT_AUX) ? ((leftAuxAbacus) ? leftAuxAbacus : abacus) : ((n == RIGHT_AUX ) ? ((rightAuxAbacus) ? rightAuxAbacus : abacus) : abacus)
#else
#define AUXWIN(n) abacus
#endif

/* There's probably a better way to assure that they are the same but I do
 * not know it off hand. */
static void
initializeDemo(void)
{
      int mode;
      Boolean mono, reverse;
      Pixel demoForeground, demoBackground;
      String demoPath, demoFont;

      XtVaGetValues(abacus,
            XtNmono, &mono,
            XtNreverseVideo, &reverse,
            XtNmode, &mode,
            XtNdemoForeground, &demoForeground,
            XtNdemoBackground, &demoBackground,
            XtNdemoPath, &demoPath,
            XtNdemoFont, &demoFont, NULL);
      XtVaSetValues(abacusDemo,
            XtNmono, mono,
            XtNreverseVideo, reverse,
            XtNmode, mode,
            XtNdemoForeground, demoForeground,
            XtNdemoBackground, demoBackground,
            XtNdemoPath, demoPath,
            XtNdemoFont, demoFont,
            XtNframed, True, NULL);
}

#ifdef LEE_ABACUS
static void
initializeAux(void)
{
      Boolean mono, reverse, script;
      Pixel frameColor;

      XtVaGetValues(abacus,
            XtNmono, &mono,
            XtNreverseVideo, &reverse,
            XtNscript, &script,
            XtNframeColor, &frameColor, NULL);
      if (leftAuxAbacus) {
            XtVaSetValues(leftAuxAbacus,
                  XtNmono, mono,
                  XtNreverseVideo, reverse,
                  XtNscript, script,
                  XtNframeColor, frameColor, NULL);
            XtVaSetValues(abacus,
                  XtNleftAuxAbacus, leftAuxAbacus, NULL);
      }
      if (rightAuxAbacus) {
            XtVaSetValues(rightAuxAbacus,
                  XtNmono, mono,
                  XtNreverseVideo, reverse,
                  XtNscript, script,
                  XtNframeColor, frameColor, NULL);
            XtVaSetValues(abacus,
                  XtNrightAuxAbacus, rightAuxAbacus, NULL);
      }
}
#endif

static void
callbackAbacusDemo(Widget w, caddr_t clientData, abacusCallbackStruct *callData)
{
#ifndef HAVE_MOTIF
      int mode;

      XtVaGetValues(w,
            XtNmode, &mode, NULL);
#endif
      switch (callData->reason) {
      case ACTION_BASE_DEFAULT:
#ifdef HAVE_MOTIF
            XmScaleSetValue(abacusBaseSlider, DEFAULT_BASE);
#endif
            break;
      case ACTION_HIDE:
            (void) XIconifyWindow(XtDisplay(topLevel),
                  XtWindow(topLevel),
                  XScreenNumberOfScreen(XtScreen(topLevel)));
#ifndef HAVE_MOTIF
            (void) XIconifyWindow(XtDisplay(shell),
                  XtWindow(shell),
                  XScreenNumberOfScreen(XtScreen(shell)));
#endif
            break;
      case ACTION_MOVE:
#ifndef LEE_ABACUS
            if (callData->aux == PRIMARY)
#endif
            {
                  XtVaSetValues(AUXWIN(callData->aux),
                        XtNdeck, callData->deck,
                        XtNrail, callData->rail,
                        XtNnumber, callData->number, NULL);
            }
            break;
      case ACTION_CLEAR_NODEMO:
      case ACTION_CLEAR:
            XtVaSetValues(abacus, XtNdeck, CLEAR_DECK, NULL);
#ifdef LEE_ABACUS
            /* Abacus demo clear is more complete */
            if (leftAuxAbacus)
                  XtVaSetValues(leftAuxAbacus,
                        XtNdeck, CLEAR_DECK, NULL);
            if (rightAuxAbacus)
                  XtVaSetValues(rightAuxAbacus,
                        XtNdeck, CLEAR_DECK, NULL);
#endif
            break;
      case ACTION_DEMO:
            XtDestroyWidget(abacusDemo);
#ifdef HAVE_MOTIF
            XtVaSetValues(choiceMenu,
                  XmNmenuHistory, choiceOptions[NORMAL], NULL);
#else
            XtDestroyWidget(shell);
#endif
            XtVaSetValues(abacus,
                  XtNdemo, False, NULL);
#ifdef LEE_ABACUS
            if (leftAuxAbacus)
                  XtVaSetValues(leftAuxAbacus,
                        XtNdemo, False, NULL);
            if (rightAuxAbacus)
                  XtVaSetValues(rightAuxAbacus,
                        XtNdemo, False, NULL);
#endif
            break;
      }
      if (callData->reason == ACTION_SCRIPT || callData->reason == ACTION_IGNORE) {
#ifdef HAVE_MOTIF
#ifdef LEE_ABACUS
            if (leftAuxTracker && leftAuxAbacus && w == leftAuxAbacus)
                  printState(leftAuxTracker, callData->buffer);
            else if (rightAuxTracker && rightAuxAbacus && w == rightAuxAbacus)
                  printState(rightAuxTracker, callData->buffer);
            else
#endif
                  printState(tracker, callData->buffer);
#else
            printState(XtParent(w), callData->buffer, mode);
#endif
      }
}

static void
forceNormalParams(Widget w)
{
      int base = DEFAULT_BASE;
      int displayBase = DEFAULT_BASE;

#ifdef HAVE_MOTIF
      if (baseSet) {
            XmScaleSetValue(abacusBaseSlider, base);
            XmScaleSetValue(displayBaseSlider, displayBase);
      }
#endif
      XtVaSetValues(abacus,
            XtNbase, base,
            XtNdisplayBase, displayBase, NULL);
#ifdef LEE_ABACUS
      if (leftAuxAbacus)
            XtVaSetValues(leftAuxAbacus,
                  XtNbase, base,
                  XtNdisplayBase, displayBase, NULL);
      if (rightAuxAbacus)
            XtVaSetValues(rightAuxAbacus,
                  XtNbase, base,
                  XtNdisplayBase, displayBase, NULL);
#endif
}

static void
forceDemoParams(Widget w)
{
      int rails, min, mode;
      int bottomPiece, bottomPiecePercent, shiftPercent;
      Boolean demo, teach, sign;

      forceNormalParams(w);
      XtVaGetValues(abacus,
            XtNmode, &mode,
            XtNrails, &rails,
            XtNsign, &sign,
            XtNbottomPiece, &bottomPiece,
            XtNbottomPiecePercent, &bottomPiecePercent,
            XtNshiftPercent, &shiftPercent,
            XtNdemo, &demo,
            XtNteach, &teach, NULL);
      if (mode == GENERIC) {
            mode = CHINESE;
            XtVaSetValues(abacus,
                  XtNmode, mode, NULL);
#ifdef HAVE_MOTIF
            XtVaSetValues(formatMenu,
                  XmNmenuHistory, formatOptions[mode], NULL);
#endif
      }
      min = ((sign) ? 1: 0) + ((bottomPiece != 0) ? 1 : 0) +
            ((bottomPiecePercent != 0) ? 1 + shiftPercent : 0) +
            ((demo || teach) ? MIN_DEMO_RAILS : MIN_RAILS);
      if (rails < min) {
#ifdef HAVE_MOTIF
            XmScaleSetValue(sizeSlider, min);
#endif
            XtVaSetValues(abacus, XtNrails, min, NULL);
      }
}

#ifdef HAVE_MOTIF
static void
forceTeachAbacus(Widget abacus, int base)
{
      int anomaly, anomalySq, displayBase, mode;
      Boolean sign;

      XtVaGetValues(abacus,
            XtNsign, &sign,
            XtNanomaly, &anomaly,
            XtNanomalySq, &anomalySq,
            XtNmode, &mode,
            XtNdisplayBase, &displayBase, NULL);
      if (sign)
            XtVaSetValues(abacus,
                  XtNsign, False, NULL);
      if (anomaly != 0)
            XtVaSetValues(abacus,
                  XtNanomaly, 0, NULL);
      if (anomalySq != 0)
            XtVaSetValues(abacus,
                  XtNanomalySq, 0, NULL);
      if (displayBase != base)
            XtVaSetValues(abacus,
                  XtNdisplayBase, base, NULL);
      if (mode == GENERIC) {
            mode = CHINESE;
            XtVaSetValues(abacus,
                  XtNmode, mode, NULL);
            XtVaSetValues(formatMenu,
                  XmNmenuHistory, formatOptions[mode], NULL);
      }
}

static void
forceTeachParams(Widget w)
{
      int base;

      XtVaGetValues(abacus,
            XtNbase, &base, NULL);
      forceTeachAbacus(abacus, base);
      if (baseSet) {
            XmScaleSetValue(displayBaseSlider, base);
      }
#ifdef LEE_ABACUS
      if (leftAuxAbacus)
            forceTeachAbacus(leftAuxAbacus, base);
      if (rightAuxAbacus)
            forceTeachAbacus(rightAuxAbacus, base);
#endif      
}
#endif

static void
initialize(Widget w)
{
      Boolean versionOnly;

      XtVaGetValues(w, XtNversionOnly, &versionOnly, NULL);
      if (versionOnly) {
            (void) printf("%s\n", aboutHelp);
            exit(0);
      }
#ifdef LEE_ABACUS
      initializeAux();
#endif
}

static void
createDemo(void)
{
      (void) sprintf(titleDspDemo, "%s-demo", progDsp);
#ifdef HAVE_MOTIF
      abacusDemo = XtCreateManagedWidget(titleDspDemo,
            abacusDemoWidgetClass, mainPanel, NULL, 0);
      XtAddCallback(abacusDemo, XtNselectCallback,
            (XtCallbackProc) callbackAbacusDemo, (XtPointer) NULL);
#else
      shell = XtCreateApplicationShell(titleDsp,
            topLevelShellWidgetClass, NULL, 0);
      XtVaSetValues(shell,
            XtNiconPixmap, pixmap,
            XtNinput, True,
            XtNtitle, titleDspDemo, NULL);
      abacusDemo = XtCreateManagedWidget("abacus",
            abacusDemoWidgetClass, shell, NULL, 0);
      XtAddCallback(abacusDemo, XtNselectCallback,
            (XtCallbackProc) callbackAbacusDemo,
            (XtPointer) NULL);
#endif
      initializeDemo();
#ifndef HAVE_MOTIF
      XtRealizeWidget(shell);
#endif
      VOID XGrabButton(XtDisplay(abacusDemo), (unsigned int) AnyButton,
            AnyModifier, XtWindow(abacusDemo), TRUE,
            (unsigned int) (ButtonPressMask |
            ButtonMotionMask | ButtonReleaseMask),
            GrabModeAsync, GrabModeAsync, XtWindow(abacusDemo),
            XCreateFontCursor(XtDisplay(abacusDemo), XC_hand2));
}

#ifdef HAVE_MOTIF
static void
abacusTeachListener(Widget w, caddr_t clientData, abacusCallbackStruct *callData)
{
      Boolean demo;
      XmString string = (XmString) XmTextGetString(w);

      XtVaGetValues(abacus,
            XtNdemo, &demo, NULL);
      if (demo)
            return;
      if (string != NULL && strlen((char *) string) > 0)
            XtVaSetValues(abacus,
                  XtNdeck, TEACH_DECK,
                  XtNteachBuffer, string, NULL);
}

static void
createTeach(void)
{
      teachRowCol = XtVaCreateManagedWidget("teachRowCol",
            xmRowColumnWidgetClass, mainPanel,
            XtNheight, 120, NULL);
      teachTracker = XtCreateManagedWidget("0",
            xmTextWidgetClass, teachRowCol, NULL, 0);
      teachLine[0] = XtVaCreateManagedWidget("teachText1",
            xmLabelGadgetClass, teachRowCol,
            XtVaTypedArg, XmNlabelString,
            XmRString, "Enter calculation X+Y, X-Y, X*Y, or X/Y where X positive and result positive.", 120, NULL);
      teachLine[1] = XtVaCreateManagedWidget("teachText2",
            xmLabelGadgetClass, teachRowCol,
            XtVaTypedArg, XmNlabelString,
            XmRString, "Press enter to go through calculation steps.", 120, NULL);
      teachLine[2] = XtVaCreateManagedWidget("teachText3",
            xmLabelGadgetClass, teachRowCol,
            XtVaTypedArg, XmNlabelString,
            XmRString, "", 120, NULL);
      XtAddCallback(teachTracker, XmNactivateCallback,
            (XtCallbackProc) abacusTeachListener, (XtPointer) NULL);
}

static void
destroyTeach(void)
{
      XtDestroyWidget(teachTracker);
      XtDestroyWidget(teachLine[0]);
      XtDestroyWidget(teachLine[1]);
      XtDestroyWidget(teachLine[2]);
      XtDestroyWidget(teachRowCol);
      
      teachTracker = NULL;
}
#endif

static void
callbackAbacus(Widget w, caddr_t clientData, abacusCallbackStruct *callData)
{
      int rails, mode;
      Boolean romanNumerals, group, sign, demo, teach;
      int topPiece, bottomPiece, topPiecePercent, bottomPiecePercent;
      int subdeck, anomaly, anomalySq;

      XtVaGetValues(w,
            XtNdemo, &demo,
            XtNteach, &teach,
            XtNmode, &mode, NULL);
      switch (callData->reason) {
      case ACTION_HIDE:
            (void) XIconifyWindow(XtDisplay(topLevel),
                  XtWindow(topLevel),
                  XScreenNumberOfScreen(XtScreen(topLevel)));
#ifndef HAVE_MOTIF
            if (demo)
                  (void) XIconifyWindow(XtDisplay(shell),
                        XtWindow(shell),
                        XScreenNumberOfScreen(XtScreen(shell)));
#endif
            break;
      case ACTION_CLEAR_QUERY:
#ifdef HAVE_MOTIF
            XtManageChild(clearDialog);
#else
            XtVaSetValues(w, XtNmenu, ACTION_CLEAR, NULL);
#endif
            break;
      case ACTION_SCRIPT:
            (void) printf("%d %d %d %d 4\n",
                  callData->aux, callData->deck,
                  callData->rail, callData->number);
            (void) printf(
                  "Lesson\n\n\nPress Space-bar to Continue\n");
            break;
      case ACTION_MOVE:
#ifndef LEE_ABACUS
            if (callData->aux == PRIMARY)
#endif
            {
                  XtVaSetValues(AUXWIN(callData->aux),
                        XtNdeck, callData->deck,
                        XtNrail, callData->rail,
                        XtNnumber, callData->number, NULL);
            }
            break;
      case ACTION_CLEAR:
            if (callData->aux == PRIMARY)
                  XtVaSetValues(abacusDemo, XtNdeck, CLEAR_DECK, NULL);
            else
                  XtVaSetValues(AUXWIN(callData->aux),
                        XtNdeck, CLEAR_DECK, NULL);
            break;
      case ACTION_DEMO:
            demo = !demo;
            if (demo) {
                  forceDemoParams(abacus);
                  createDemo();
            } else {
                  XtDestroyWidget(abacusDemo);
#ifndef HAVE_MOTIF
                  XtDestroyWidget(shell);
#endif
            }
            XtVaSetValues(abacus, XtNdemo, demo, NULL);
#ifdef LEE_ABACUS
            if (leftAuxAbacus)
                  XtVaSetValues(leftAuxAbacus,
                        XtNdemo, demo, NULL);
            if (rightAuxAbacus)
                  XtVaSetValues(rightAuxAbacus,
                        XtNdemo, demo, NULL);
#endif
            break;
#ifdef HAVE_MOTIF
      case ACTION_TEACH:
            if (teachTracker == NULL) {
                  forceTeachParams(abacus);
                  createTeach();
            } else {
                  destroyTeach();
            }
            XtVaSetValues(abacus, XtNteach,
                  (teachTracker == NULL) ? True : False, NULL);
            break;
      case ACTION_TEACH_LINE:
            if (callData->line >= 0 && callData->line <= 2) {
                  static XmStringCharSet charSet =
                        (XmStringCharSet) XmSTRING_DEFAULT_CHARSET;

                  if (teachTracker == NULL)
                        createTeach();
                  XtVaSetValues(teachLine[callData->line],
                        XmNlabelString,
                        XmStringCreateLtoR(callData->teachBuffer, charSet),
                        NULL);
            }
            break;
#endif
      case ACTION_NEXT:
            XtVaSetValues(abacusDemo, XtNdeck, NEXT_DECK, NULL);
            break;
      case ACTION_REPEAT:
            XtVaSetValues(abacusDemo, XtNdeck, REPEAT_DECK, NULL);
            break;
      case ACTION_JUMP:
            XtVaSetValues(abacusDemo, XtNdeck, JUMP_DECK, NULL);
            break;
      case ACTION_MORE:
            XtVaSetValues(abacusDemo, XtNdeck, MORE_DECK, NULL);
            break;
      case ACTION_INCREMENT:
            if (w == abacus) {
                  XtVaGetValues(w,
                        XtNrails, &rails, NULL);
#ifdef HAVE_MOTIF
                  if (rails > MAX_RAILS)
                        XtVaSetValues(sizeSlider,
                              XmNmaximum, rails, NULL);
                  XmScaleSetValue(sizeSlider, rails);
#endif
            }
            break;
      case ACTION_DECREMENT:
            if (w == abacus) {
                  XtVaGetValues(w,
                        XtNrails, &rails, NULL);
#ifdef HAVE_MOTIF
                  XmScaleSetValue(sizeSlider, rails);
                  if (rails >= MAX_RAILS)
                        XtVaSetValues(sizeSlider,
                              XmNmaximum, rails, NULL);
#endif
            }
            break;
      case ACTION_FORMAT:
            if (w == abacus) {
#ifdef HAVE_MOTIF
                  XtVaSetValues(formatMenu,
                        XmNmenuHistory, formatOptions[mode],
                        NULL);
#endif
                  if (demo) {
                        XtVaSetValues(abacusDemo,
                              XtNdeck, CLEAR_DECK,
                              XtNmode, mode, NULL);
                  }
            }
            break;
      case ACTION_ROMAN_NUMERAL:
            XtVaGetValues(w,
                  XtNromanNumerals, &romanNumerals,
                  NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            XmToggleButtonSetState(romanNumeralsSwitch,
                  romanNumerals, False);
#endif
#endif
            break;
      case ACTION_GROUP:
            XtVaGetValues(w,
                  XtNgroup, &group,
                  NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            XmToggleButtonSetState(groupSwitch,
                  group, False);
#endif
#endif
            break;
      case ACTION_SIGN:
            XtVaGetValues(w,
                  XtNsign, &sign, NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            if (w == abacus) {
                  XmToggleButtonSetState(signSwitch, sign, False);
            }
#endif
#endif
            break;
      case ACTION_QUARTER:
            XtVaGetValues(w,
                  XtNbottomPiece, &bottomPiece,
                  XtNtopPiece, &topPiece, NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            if (w == abacus) {
                  XmToggleButtonSetState(pieceSwitch,
                        (bottomPiece == QUARTERS), False);
            }
#endif
#endif
            break;
      case ACTION_QUARTER_PERCENT:
            XtVaGetValues(w,
                  XtNbottomPiecePercent, &bottomPiecePercent,
                  XtNtopPiecePercent, &topPiecePercent, NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            if (w == abacus) {
                  XmToggleButtonSetState(piecePercentSwitch,
                        (bottomPiecePercent == QUARTER_PERCENTS), False);
            }
#endif
#endif
            break;
      case ACTION_TWELFTH:
            XtVaGetValues(w,
                  XtNtopPiece, &topPiece,
                  XtNbottomPiece, &bottomPiece, NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            if (w == abacus) {
                  XmToggleButtonSetState(twelfthSwitch,
                        (topPiece * bottomPiece == TWELFTHS), False);
            }
#endif
#endif
            break;
      case ACTION_SUBDECK:
            XtVaGetValues(w,
                  XtNsubdeck, &subdeck, NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            if (w == abacus) {
                  XmToggleButtonSetState(subdeckSwitch,
                        (subdeck == 3), False);
            }
#endif
#endif
            break;
      case ACTION_ANOMALY:
            XtVaGetValues(w,
                  XtNanomaly, &anomaly,
                  XtNanomalySq, &anomalySq, NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            if (w == abacus) {
                  XmToggleButtonSetState(anomalySwitch,
                        (anomaly == 2 && anomalySq == 0), False);
            }
#endif
#endif
            break;
      case ACTION_WATCH:
            XtVaGetValues(w,
                  XtNanomaly, &anomaly,
                  XtNanomalySq, &anomalySq, NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            if (w == abacus) {
                  XmToggleButtonSetState(anomalySwitch,
                        (anomaly == 4 && anomalySq == 4), False);
            }
#endif
#endif
            break;
#if 0
      case ACTION_VERTICAL:
            XtVaGetValues(w,
                  XtNvertical, &vertical, NULL);
#ifdef HAVE_MOTIF
#ifdef TOGGLES
            if (w == abacus) {
                  XmToggleButtonSetState(verticalSwitch,
                        vertical, False);
            }
#endif
#endif
            break;
#endif
      }
      if (/*callData->reason == ACTION_SCRIPT || */callData->reason == ACTION_IGNORE) {
#ifdef HAVE_MOTIF
#ifdef LEE_ABACUS
            if (leftAuxTracker && leftAuxAbacus && w == leftAuxAbacus)
                  printState(leftAuxTracker, callData->buffer);
            else if (rightAuxTracker && rightAuxAbacus && w == rightAuxAbacus)
                  printState(rightAuxTracker, callData->buffer);
            else
#endif
                  printState(tracker, callData->buffer);
#else
            printState(XtParent(w), callData->buffer, mode);
#endif
      }
}

#ifdef HAVE_MOTIF
static void
abacusMathListener(Widget w, caddr_t clientData, abacusCallbackStruct *callData)
{
      Boolean demo, teach;
      unsigned int i, j = 0;
      int aux;

      XtVaGetValues(abacus,
            XtNdemo, &demo,
            XtNteach, &teach, NULL);
      if (demo || teach)
            return;
      if (leftAuxTracker && leftAuxAbacus && w == leftAuxTracker) {
            aux = LEFT_AUX;
      } else if (rightAuxTracker && rightAuxAbacus &&
                  w == rightAuxTracker) {
            aux = RIGHT_AUX;
      } else {
            aux = PRIMARY;
      }
      if (mathBuffer)
            XtFree(mathBuffer);
      mathBuffer = XmTextGetString(w);
      /* strip out blanks */
      for (i = 0; i < strlen(mathBuffer); i++) {
            if (mathBuffer[i] == '[' || mathBuffer[i] == ']') {
                  mathBuffer[j] = '\0';
                  break;
            } else if (mathBuffer[i] != ' ' &&
                        mathBuffer[i] != '\t') {
                  mathBuffer[j] = mathBuffer[i];
                  j++;
            }
      }
      /* check for math ops */
      XtVaSetValues(abacus,
            XtNaux, aux,
            XtNdeck, CALC_DECK,
            XtNmathBuffer, mathBuffer, NULL);
}

static void
abacusClearListener(Widget w, XtPointer clientData, XmAnyCallbackStruct *cbs)
{
      if (cbs->reason == XmCR_OK) {
            XtVaSetValues(abacus, XtNmenu, ACTION_CLEAR, NULL);
      }
}

static void
railChangeListener(Widget w, XtPointer clientData, XmScaleCallbackStruct *cbs)
{
      int rails = cbs->value, old, min;
      int bottomPiece, bottomPiecePercent, shiftPercent;
      Boolean sign, demo;

      XtVaGetValues(abacus,
            XtNrails, &old,
            XtNsign, &sign,
            XtNbottomPiece, &bottomPiece,
            XtNbottomPiecePercent, &bottomPiecePercent,
            XtNshiftPercent, &shiftPercent,
            XtNdemo, &demo, NULL);
      min = ((sign) ? 1 : 0) + ((bottomPiece == 0) ? 0 : 1) +
            ((bottomPiecePercent == 0) ? 0 : 1 + shiftPercent) +
            ((demo) ? MIN_DEMO_RAILS : MIN_RAILS);
      if (rails < min) {
            XmScaleSetValue(sizeSlider, old);
      } else if (old != rails)
            XtVaSetValues(abacus,
                  XtNrails, rails, NULL);
}

static void
abacusBaseChangeListener(Widget w, XtPointer clientData, XmScaleCallbackStruct *cbs)
{
      int base = cbs->value, old, bottomSpaces, mode;
      int bottomPiece, bottomPiecePercent;
      Boolean demo, teach;

      XtVaGetValues(abacus,
            XtNbase, &old,
            XtNbottomPiece, &bottomPiece,
            XtNbottomPiecePercent, &bottomPiecePercent,
            XtNbottomSpaces, &bottomSpaces,
            XtNmode, &mode,
            XtNdemo, &demo,
            XtNteach, &teach, NULL);
      if (demo) {
            base = DEFAULT_BASE;
            XmScaleSetValue(abacusBaseSlider, base);
      /* Odd bases produce round-off errors but OK */
      /* When base divisible by 4, kind of silly but OK */
      /* Well some of these have enough room in Russian but not others */
      } else if ((base == 2 || base == 4) && bottomSpaces < 3) {
            XtVaSetValues(abacus,
                  XtNbottomSpaces, 3,
                  XtNbase, base, NULL);
      } else if ((base == 3 || base == 6 || base == 9) && bottomSpaces < 2) {
            XtVaSetValues(abacus,
                  XtNbottomSpaces, 2,
                  XtNbase, base, NULL);
      } else {
            XtVaSetValues(abacus,
                  XtNbase, base, NULL);
      }
      if (demo || teach) {
            XmScaleSetValue(displayBaseSlider, base);
            XtVaSetValues(abacus,
                  XtNdisplayBase, base, NULL);
#ifdef LEE_ABACUS
            if (leftAuxAbacus)
                  XtVaSetValues(leftAuxAbacus,
                        XtNbase, base,
                        XtNdisplayBase, base, NULL);
            if (rightAuxAbacus)
                  XtVaSetValues(rightAuxAbacus,
                        XtNbase, base,
                        XtNdisplayBase, base, NULL);
#endif      
#ifdef LEE_ABACUS
      } else {
            if (leftAuxAbacus)
                  XtVaSetValues(leftAuxAbacus,
                        XtNbase, base, NULL);
            if (rightAuxAbacus)
                  XtVaSetValues(rightAuxAbacus,
                        XtNbase, base, NULL);
#endif      
      }
}

static void
displayBaseChangeListener(Widget w, XtPointer clientData, XmScaleCallbackStruct *cbs)
{
      int displayBase = cbs->value, old, base;
      Boolean demo, teach;

      XtVaGetValues(abacus,
            XtNbase, &base,
            XtNdisplayBase, &old,
            XtNdemo, &demo,
            XtNteach, &teach, NULL);
      if (demo)
            base = DEFAULT_BASE;
      if (demo || teach) {
            XmScaleSetValue(displayBaseSlider, base);
#ifdef LEE_ABACUS
            if (leftAuxAbacus)
                  XtVaSetValues(leftAuxAbacus,
                        XtNbase, base,
                        XtNdisplayBase, displayBase, NULL);
            if (rightAuxAbacus)
                  XtVaSetValues(rightAuxAbacus,
                        XtNbase, base,
                        XtNdisplayBase, displayBase, NULL);
#endif      
      } else {
            XtVaSetValues(abacus,
                  XtNdisplayBase, displayBase, NULL);
#ifdef LEE_ABACUS
            if (leftAuxAbacus)
                  XtVaSetValues(leftAuxAbacus,
                        XtNdisplayBase, displayBase, NULL);
            if (rightAuxAbacus)
                  XtVaSetValues(rightAuxAbacus,
                        XtNdisplayBase, displayBase, NULL);
#endif
      }
}

#ifdef TOGGLES
#if 0
static void
verticalToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean vertical = cbs->set;

      XtVaSetValues(abacus,
            XtNvertical, vertical, NULL);
}
#endif

static void
romanNumeralToggle(Widget w, XtPointer clientData,
            XmToggleButtonCallbackStruct *cbs)
{
      Boolean romanNumerals = cbs->set;

      XtVaSetValues(abacus,
            XtNromanNumerals, romanNumerals, NULL);
}

static void
groupToggle(Widget w, XtPointer clientData,
            XmToggleButtonCallbackStruct *cbs)
{
      Boolean group = cbs->set;

      XtVaSetValues(abacus,
            XtNgroup, group, NULL);
}

static void
signToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean sign = cbs->set;

      XtVaSetValues(abacus,
            XtNsign, sign, NULL);
}

static void
quarterToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean quarter = cbs->set;

      XtVaSetValues(abacus,
            XtNtopPiece, 0,
            XtNbottomPiece, (quarter) ? QUARTERS : 0, NULL);
}

static void
quarterPercentToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean quarterPercent = cbs->set;

      XtVaSetValues(abacus,
            XtNtopPiecePercent, 0,
            XtNbottomPiecePercent, (quarterPercent) ? QUARTER_PERCENTS : 0, NULL);
}

static void
twelfthToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean twelfth = cbs->set;

      XtVaSetValues(abacus,
            XtNtopPiece, 2,
            XtNbottomPiece, (twelfth) ? TWELFTHS / 2 : 0, NULL);
}

static void
subdeckToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean subdeck = cbs->set;

      XtVaSetValues(abacus,
            XtNsubbase, TWELFTHS,
            XtNsubdeck, (subdeck) ? DEFAULT_SUBDECKS : 0,
            XtNsubbead, DEFAULT_SUBBEADS, NULL);
}

static void
eighthToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean subdeck = cbs->set;

      XtVaSetValues(abacus,
            XtNsubbase, EIGHTHS,
            XtNsubdeck, (subdeck) ? DEFAULT_SUBDECKS : 0,
            XtNsubbead, DEFAULT_SUBBEADS, NULL);
}

static void
anomalyToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean anomaly = cbs->set;

      XtVaSetValues(abacus,
            XtNanomaly, (anomaly) ? 2 : 0,
            XtNanomalySq, 0, NULL);
}

static void
watchToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean watch = cbs->set;

      XtVaSetValues(abacus,
            XtNanomaly, (watch) ? 4 : 0,
            XtNanomalySq, (watch) ? 4 : 0, NULL);
}

static void
rightToLeftAddToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean rightToLeftAdd = cbs->set;

      XtVaSetValues(abacus,
            XtNrightToLeftAdd, rightToLeftAdd, NULL);
}

static void
rightToLeftMultToggle(Widget w, XtPointer clientData, XmToggleButtonCallbackStruct *cbs)
{
      Boolean rightToLeftMult = cbs->set;

      XtVaSetValues(abacus,
            XtNrightToLeftMult, rightToLeftMult, NULL);
}

#endif

static void
createBase(int base, int displayBase)
{
      baseForm = XtVaCreateManagedWidget("baseForm",
            xmFormWidgetClass, mainPanel,
            XmNheight, 55,
            XmNfractionBase, 2,
            NULL);
      abacusBaseSlider = XtVaCreateManagedWidget("abacusBase",
            xmScaleWidgetClass, baseForm,
            XmNtopAttachment, XmATTACH_FORM,
            XmNleftAttachment, XmATTACH_FORM,
            XmNbottomAttachment, XmATTACH_FORM,
            XmNrightAttachment, XmATTACH_POSITION,
            XmNrightPosition, 1,
            XtVaTypedArg, XmNtitleString, XmRString, "      Abacus Base", 18,
            XmNminimum, MIN_BASE,
            XmNmaximum, MAX_BASE,
            XmNscaleWidth, (MAX_BASE - MIN_BASE + 1) * 4,
            XmNvalue, base,
            XmNshowValue, True,
            XmNorientation, XmHORIZONTAL, NULL);
      XtAddCallback(abacusBaseSlider, XmNvalueChangedCallback,
            (XtCallbackProc) abacusBaseChangeListener, (XtPointer) NULL);
      displayBaseSlider = XtVaCreateManagedWidget("displayBase",
            xmScaleWidgetClass, baseForm,
            XmNtopAttachment, XmATTACH_FORM,
            XmNrightAttachment, XmATTACH_FORM,
            XmNbottomAttachment, XmATTACH_FORM,
            XmNleftAttachment, XmATTACH_WIDGET,
            XmNleftWidget, abacusBaseSlider,
            XtVaTypedArg, XmNtitleString, XmRString, "     Display Base", 18,
            XmNminimum, MIN_BASE,
            XmNmaximum, MAX_BASE,
            XmNscaleWidth, (MAX_BASE - MIN_BASE + 1) * 4,
            XmNvalue, displayBase,
            XmNshowValue, True,
            XmNorientation, XmHORIZONTAL, NULL);
      XtAddCallback(displayBaseSlider, XmNvalueChangedCallback,
            (XtCallbackProc) displayBaseChangeListener, (XtPointer) NULL);
}

static void
destroyBase(void)
{
      XtDestroyWidget(displayBaseSlider);
      XtDestroyWidget(abacusBaseSlider);
      XtDestroyWidget(baseForm);
}

static void
choiceListener(Widget w, void *value, void *clientData)
{
      Boolean wasDemo, wasTeach;
      int val = (int) value;

      XtVaGetValues(abacus,
            XtNdemo, &wasDemo, NULL);
      XtVaGetValues(abacus,
            XtNteach, &wasTeach, NULL);
      if (wasDemo && val == DEMO) {
            return;
      }
      if (wasTeach && val == TEACH) {
            return;
      }
      if (wasDemo) {
            XtVaSetValues(abacus, XtNdemo, !wasDemo, NULL);
            XtDestroyWidget(abacusDemo);
      }
      if (val == DEMO) {
            if (wasTeach) {
                  XtVaSetValues(abacus, XtNteach, !wasTeach, NULL);
                  destroyTeach();
            }
            if (baseSet) {
                  forceNormalParams(abacus);
                  baseSet = False;
                  destroyBase();
            }
            forceDemoParams(abacus);
            XtVaSetValues(abacus, XtNdemo, !wasDemo, NULL);
            createDemo();
      } else if (val == TEACH) {
            forceTeachParams(abacus);
            XtVaSetValues(abacus, XtNteach, !wasTeach, NULL);
            createTeach();
      } else if (val == BASE) {
            if (baseSet)
                  return;
            baseSet = True;
            createBase(DEFAULT_BASE, DEFAULT_BASE);
      } else {
            if (wasTeach) {
                  XtVaSetValues(abacus, XtNteach, !wasTeach, NULL);
                  destroyTeach();
            }
            if (baseSet) {
                  forceNormalParams(abacus);
                  baseSet = False;
                  destroyBase();
            }
      }
}

static void
formatListener(Widget w, void *value, void *clientData)
{
      Boolean demo;
      int mode = (int) value, oldMode;

      XtVaGetValues(abacus,
            XtNmode, &oldMode,
            XtNdemo, &demo, NULL);
      if (mode < 0 || mode > MAX_FORMATS)
            mode = GENERIC;
      if (demo && mode == GENERIC) {
            XtVaSetValues(formatMenu,
                  XmNmenuHistory, formatOptions[oldMode], NULL);
            return;
      }
      XtVaSetValues(abacus, XtNmode, mode, NULL);
      if (demo) {
            XtVaSetValues(abacusDemo,
                  XtNdeck, CLEAR_DECK,
                  XtNmode, mode, NULL);
      }
}

static void
fileMenuListener(Widget w, void *value, void *clientData)
{
      int val = (int) value;

      if (val == 0)
            exit(0);
}

static void
controlsMenuListener(Widget w, void *value, void *clientData)
{
      int val = (int) value;

      XtVaSetValues(abacus, XtNmenu, val + ACTION_CLEAR, NULL);
}

static Widget
createQuery(Widget w, char *text, char *title, XtCallbackProc callback)
{
      Widget button, messageBox;
      char titleDsp[FILE_NAME_LENGTH + 8];
      XmString titleString = NULL, messageString = NULL;
      static XmStringCharSet charSet =
            (XmStringCharSet) XmSTRING_DEFAULT_CHARSET;

      messageString = XmStringCreateLtoR(text, charSet);
      (void) sprintf(titleDsp, "%s: %s\n", progDsp, title);
      titleString = XmStringCreateSimple((char *) titleDsp);
      XtSetArg(arg[0], XmNdialogTitle, titleString);
      XtSetArg(arg[1], XmNmessageString, messageString);
      messageBox = XmCreateWarningDialog(w, (char *) "queryBox",
            arg, 2);
      button = XmMessageBoxGetChild(messageBox, XmDIALOG_HELP_BUTTON);
      XtUnmanageChild(button);
      XmStringFree(titleString);
      XmStringFree(messageString);
      XtAddCallback(messageBox, XmNokCallback,
            (XtCallbackProc) abacusClearListener, (XtPointer) NULL);
      XtAddCallback(messageBox, XmNcancelCallback,
            (XtCallbackProc) abacusClearListener, (XtPointer) NULL);
      return messageBox;
}

static Widget
createHelp(Widget w, char *text, char *title)
{
      Widget button, messageBox;
      char titleDsp[FILE_NAME_LENGTH + 8];
      XmString titleString = NULL, messageString = NULL, buttonString = NULL;
      static XmStringCharSet charSet =
            (XmStringCharSet) XmSTRING_DEFAULT_CHARSET;

      messageString = XmStringCreateLtoR(text, charSet);
      (void) sprintf(titleDsp, "%s: %s\n", progDsp, title);
      titleString = XmStringCreateSimple((char *) titleDsp);
      buttonString = XmStringCreateSimple((char *) "OK");
      XtSetArg(arg[0], XmNdialogTitle, titleString);
      XtSetArg(arg[1], XmNokLabelString, buttonString);
      XtSetArg(arg[2], XmNmessageString, messageString);
      messageBox = XmCreateInformationDialog(w, (char *) "helpBox",
            arg, 3);
      button = XmMessageBoxGetChild(messageBox, XmDIALOG_CANCEL_BUTTON);
      XtUnmanageChild(button);
      button = XmMessageBoxGetChild(messageBox, XmDIALOG_HELP_BUTTON);
      XtUnmanageChild(button);
      XmStringFree(titleString);
      XmStringFree(buttonString);
      XmStringFree(messageString);
      return messageBox;
}

static void
helpMenuListener(Widget w, XtPointer value, XtPointer clientData)
{
      int val = (int) value;

      switch (val) {
      case 0:
            XtManageChild(descriptionDialog);
            break;
      case 1:
            XtManageChild(featuresDialog);
            break;
      case 2:
            XtManageChild(optionsDialog);
            break;
      case 3:
            XtManageChild(options1Dialog);
            break;
      case 4:
            XtManageChild(options2Dialog);
            break;
      case 5:
            XtManageChild(options3Dialog);
            break;
      case 6:
            XtManageChild(referencesDialog);
            break;
      case 7:
            XtManageChild(aboutDialog);
            break;
      default:
            {
                  char *buf;

                  intCat(&buf, "helpMenuListener: %d", val);
                  XtWarning(buf);
                  free(buf);
            }
      }
}

#endif

int
main(int argc, char **argv)
{
      Boolean demo;
      int pixmapSize;
#ifdef HAVE_MOTIF
      Boolean teach;
      int base = DEFAULT_BASE, displayBase = DEFAULT_BASE;
      Widget menuBar, pullDownMenu, widget;
      Widget menuBarPanel, controlPanel;
      Widget controlRowCol, trackerRowCol;
      Widget auxForm;
#ifdef LEE_ABACUS
      Widget auxTrackerForm;
      Boolean lee;
      Pixel leftAuxColor, rightAuxColor;
      int leftAuxRails, rightAuxRails;
#endif
#ifdef TOGGLES
      Widget toggleRowCol;
#endif
      XmString fileString, controlsString;
      XmString quitString, clearString, complementString;
      XmString incrementString, decrementString;
      XmString romanNumeralsString, groupString, signString;
      XmString quarterString, quarterPercentString;
      XmString twelfthString, subdeckString, eighthString;
      XmString anomalyString, watchString;
      XmString rightToLeftAddString, rightToLeftMultString;
      XmString speedString, slowString, soundString;
      XmString choice, choiceStrings[CHOICES];
      XmString format, formatStrings[MAX_MODES], museum;
      int rails, mode;
      int bottomPiece, bottomPiecePercent, topPiece;
      int anomaly, subdeck, subbase;
      Boolean romanNumerals, group, sign;
      unsigned int i;
#endif

      progDsp = argv[0];
      topLevel = XtInitialize(argv[0], "Abacus",
            options, XtNumber(options), &argc, argv);
      if (argc != 1)
            usage(argv[0]);

#ifdef HAVE_MOTIF
      menuBarPanel = XtVaCreateManagedWidget("menuBarPanel",
            xmPanedWindowWidgetClass, topLevel,
            XmNseparatorOn, False,
            XmNsashWidth, 1,
            XmNsashHeight, 1, NULL);
      fileString = XmStringCreateSimple((char *) "File");
      controlsString = XmStringCreateSimple((char *) "Controls");
      menuBar = XmVaCreateSimpleMenuBar(menuBarPanel, (char *) "menuBar",
            XmVaCASCADEBUTTON, fileString, 'F',
            XmVaCASCADEBUTTON, controlsString, 'C',
            NULL);
      XmStringFree(fileString);
      XmStringFree(controlsString);
      quitString = XmStringCreateSimple((char *) "Exit");
      (void) XmVaCreateSimplePulldownMenu(menuBar, (char *) "fileMenu",
            0, fileMenuListener,
            XmVaSEPARATOR,
            XmVaPUSHBUTTON, quitString, 'x', NULL, NULL,
            NULL);
      XmStringFree(quitString);
      clearString = XmStringCreateSimple((char *) "Clear");
      complementString = XmStringCreateSimple((char *) "Complement ~");
      incrementString = XmStringCreateSimple((char *) "Increment");
      decrementString = XmStringCreateSimple((char *) "Decrement");
      format = XmStringCreateSimple((char *) "Format");
      museum = XmStringCreateSimple((char *) "Museum");
      romanNumeralsString = XmStringCreateSimple((char *) "Roman Numerals");
      groupString = XmStringCreateSimple((char *) "Group");
      signString = XmStringCreateSimple((char *) "Sign");
      quarterString = XmStringCreateSimple((char *) "Quarter");
      quarterPercentString = XmStringCreateSimple((char *) "Quarter Percent");
      twelfthString = XmStringCreateSimple((char *) "Twelfth");
      subdeckString = XmStringCreateSimple((char *) "Subdeck");
      eighthString = XmStringCreateSimple((char *) "Eighth");
      anomalyString = XmStringCreateSimple((char *) "Anomaly");
      watchString = XmStringCreateSimple((char *) "Watch");
      rightToLeftAddString = XmStringCreateSimple((char *) "Right To Left Add +");
      rightToLeftMultString = XmStringCreateSimple((char *) "Right To Left Multiplicand *");
      speedString = XmStringCreateSimple((char *) "Speed >");
      slowString = XmStringCreateSimple((char *) "Slow <");
      soundString = XmStringCreateSimple((char *) "Sound @");
      (void) XmVaCreateSimplePulldownMenu(menuBar, (char *) "controlsMenu",
            1, controlsMenuListener,
            XmVaPUSHBUTTON, clearString, 'C', NULL, NULL,
            XmVaPUSHBUTTON, complementString, '~', NULL, NULL,
            XmVaPUSHBUTTON, incrementString, 'I', NULL, NULL,
            XmVaPUSHBUTTON, decrementString, 'D', NULL, NULL,
            XmVaPUSHBUTTON, format, 'F', NULL, NULL,
            XmVaPUSHBUTTON, museum, 'e', NULL, NULL,
            XmVaPUSHBUTTON, romanNumeralsString, 'm', NULL, NULL,
            XmVaPUSHBUTTON, groupString, 'G', NULL, NULL,
            XmVaPUSHBUTTON, signString, 'S', NULL, NULL,
            XmVaPUSHBUTTON, quarterString, 'u', NULL, NULL,
            XmVaPUSHBUTTON, quarterPercentString, 'P', NULL, NULL,
            XmVaPUSHBUTTON, twelfthString, 'T', NULL, NULL,
            XmVaPUSHBUTTON, subdeckString, 'b', NULL, NULL,
            XmVaPUSHBUTTON, eighthString, 'h', NULL, NULL,
            XmVaPUSHBUTTON, anomalyString, 'l', NULL, NULL,
            XmVaPUSHBUTTON, watchString, 'W', NULL, NULL,
            XmVaPUSHBUTTON, rightToLeftAddString, '+', NULL, NULL,
            XmVaPUSHBUTTON, rightToLeftMultString, '*', NULL, NULL,
            XmVaPUSHBUTTON, speedString, '>', NULL, NULL,
            XmVaPUSHBUTTON, slowString, '<', NULL, NULL,
            XmVaPUSHBUTTON, soundString, '@', NULL, NULL,
            NULL);
      XmStringFree(clearString);
      XmStringFree(complementString);
      XmStringFree(incrementString);
      XmStringFree(decrementString);
      XmStringFree(format);
      XmStringFree(museum);
      XmStringFree(romanNumeralsString);
      XmStringFree(groupString);
      XmStringFree(signString);
      XmStringFree(quarterString);
      XmStringFree(quarterPercentString);
      XmStringFree(twelfthString);
      XmStringFree(subdeckString);
      XmStringFree(eighthString);
      XmStringFree(anomalyString);
      XmStringFree(watchString);
      XmStringFree(rightToLeftAddString);
      XmStringFree(rightToLeftMultString);
      XmStringFree(speedString);
      XmStringFree(slowString);
      XmStringFree(soundString);
      pullDownMenu = XmCreatePulldownMenu(menuBar,
            (char *) "helpPullDown", NULL, 0);
      widget = XtVaCreateManagedWidget("Help",
            xmCascadeButtonWidgetClass, menuBar,
            XmNsubMenuId, pullDownMenu,
            XmNmnemonic, 'H', NULL);
      XtVaSetValues(menuBar, XmNmenuHelpWidget, widget, NULL);
      widget = XtVaCreateManagedWidget("Description",
            xmPushButtonGadgetClass, pullDownMenu,
            XmNmnemonic, 'D', NULL);
      XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 0);
      widget = XtVaCreateManagedWidget("Features",
            xmPushButtonGadgetClass, pullDownMenu,
            XmNmnemonic, 'F', NULL);
      XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 1);
      widget = XtVaCreateManagedWidget("Options",
            xmPushButtonGadgetClass, pullDownMenu,
            XmNmnemonic, 'O', NULL);
      XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 2);
      widget = XtVaCreateManagedWidget("Options1",
            xmPushButtonGadgetClass, pullDownMenu,
            XmNmnemonic, '1', NULL);
      XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 3);
      widget = XtVaCreateManagedWidget("Options2",
            xmPushButtonGadgetClass, pullDownMenu,
            XmNmnemonic, '2', NULL);
      XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 4);
      widget = XtVaCreateManagedWidget("Options3",
            xmPushButtonGadgetClass, pullDownMenu,
            XmNmnemonic, '3', NULL);
      XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 5);
      widget = XtVaCreateManagedWidget("References",
            xmPushButtonGadgetClass, pullDownMenu,
            XmNmnemonic, 'R', NULL);
      XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 6);
      widget = XtVaCreateManagedWidget("About",
            xmPushButtonGadgetClass, pullDownMenu,
            XmNmnemonic, 'A', NULL);
      XtAddCallback(widget, XmNactivateCallback, helpMenuListener, (char *) 7);
      XtManageChild(menuBar);
      descriptionDialog = createHelp(menuBar, (char *) descriptionHelp,
            (char *) "Description");
      featuresDialog = createHelp(menuBar, (char *) featuresHelp,
            (char *) "Features");
      optionsDialog = createHelp(menuBar, (char *) optionsHelp,
            (char *) "Options");
      options1Dialog = createHelp(menuBar, (char *) options1Help,
            (char *) "Options1");
      options2Dialog = createHelp(menuBar, (char *) options2Help,
            (char *) "Options2");
      options3Dialog = createHelp(menuBar, (char *) options3Help,
            (char *) "Options3");
      referencesDialog = createHelp(menuBar, (char *) referencesHelp,
            (char *) "References");
      aboutDialog = createHelp(menuBar, (char *) aboutHelp,
            (char *) "About");
      clearDialog = createQuery(topLevel,
            (char *) "Are you sure you want to destroy the calculation?",
            (char *) "Clear Query",
            (XtCallbackProc) abacusClearListener);
      mainPanel = XtCreateManagedWidget("mainPanel",
            xmPanedWindowWidgetClass, menuBarPanel,
            NULL, 0);
      controlPanel = XtVaCreateManagedWidget("controlPanel",
            xmPanedWindowWidgetClass, mainPanel,
            XmNseparatorOn, False,
            XmNsashWidth, 1,
            XmNsashHeight, 1,
            NULL);
#ifdef MOUSEBITMAPS
      {
            /* Takes up valuable real estate. */
            Widget bitmapRowCol;
            Pixmap mouseLeftCursor, mouseRightCursor;
            Pixel fg, bg;

            bitmapRowCol = XtVaCreateManagedWidget("bitmapRowCol",
                  xmRowColumnWidgetClass, controlPanel,
                  XmNnumColumns, 4,
                  XmNpacking, XmPACK_COLUMN, NULL);
            (void) XtVaGetValues(bitmapRowCol,
                  XmNforeground, &fg,
                  XmNbackground, &bg, NULL);
            mouseLeftCursor = XCreatePixmapFromBitmapData(
                  XtDisplay(bitmapRowCol),
                  RootWindowOfScreen(XtScreen(bitmapRowCol)),
                  (char *) mouse_left_bits,
                  mouse_left_width, mouse_left_height, fg, bg,
                  DefaultDepthOfScreen(XtScreen(bitmapRowCol)));
            mouseRightCursor = XCreatePixmapFromBitmapData(
                  XtDisplay(bitmapRowCol),
                  RootWindowOfScreen(XtScreen(bitmapRowCol)),
                  (char *) mouse_right_bits,
                  mouse_right_width, mouse_right_height, fg, bg,
                  DefaultDepthOfScreen(XtScreen(bitmapRowCol)));
            (void) XtVaCreateManagedWidget("mouseLeftText",
                  xmLabelGadgetClass, bitmapRowCol,
                  XtVaTypedArg, XmNlabelString,
                  XmRString, "Move bead", 10, NULL);
            (void) XtVaCreateManagedWidget("mouseLeft",
                  xmLabelGadgetClass, bitmapRowCol,
                  XmNlabelType, XmPIXMAP,
                  XmNlabelPixmap, mouseLeftCursor, NULL);
            (void) XtVaCreateManagedWidget("mouseRightText",
                  xmLabelGadgetClass, bitmapRowCol,
                  XtVaTypedArg, XmNlabelString,
                  XmRString, "    Clear", 10, NULL);
            (void) XtVaCreateManagedWidget("mouseRight",
                  xmLabelGadgetClass, bitmapRowCol,
                  XmNlabelType, XmPIXMAP,
                  XmNlabelPixmap, mouseRightCursor, NULL);
      }
#endif
      controlRowCol = XtVaCreateManagedWidget("controlRowCol",
            xmRowColumnWidgetClass, controlPanel,
            XmNnumColumns, 1,
            XmNorientation, XmHORIZONTAL,
            XmNpacking, XmPACK_COLUMN, NULL);
#ifdef TOGGLES
      toggleRowCol = XtVaCreateManagedWidget("toggleRowCol",
            xmRowColumnWidgetClass, controlPanel,
            XmNorientation, XmHORIZONTAL,
            XmNpacking, XmPACK_COLUMN, NULL);
#endif
      auxForm = XtVaCreateManagedWidget("auxForm",
            xmFormWidgetClass, mainPanel,
            NULL);
      abacus = XtVaCreateManagedWidget("abacus",
#ifdef LEE_KO
            XtNformat, "Korean",
#endif
            abacusWidgetClass, auxForm,
            XmNbottomAttachment, XmATTACH_FORM,
            XmNleftAttachment, XmATTACH_FORM,
            XmNrightAttachment, XmATTACH_FORM,
            XmNtopAttachment, XmATTACH_POSITION,
            NULL);
      trackerRowCol = XtVaCreateManagedWidget("trackerRowCol",
            xmRowColumnWidgetClass, controlPanel, NULL);
#ifdef LEE_ABACUS
      XtVaGetValues(abacus,
            XtNlee, &lee,
            XtNleftAuxBeadColor, &leftAuxColor,
            XtNrightAuxBeadColor, &rightAuxColor,
            XtNleftAuxRails, &leftAuxRails,
            XtNrightAuxRails, &rightAuxRails,
            XtNbase, &base,
            XtNdisplayBase, &displayBase, NULL);
      if (lee) {
            XtVaSetValues(auxForm,
                  XmNfractionBase, leftAuxRails + rightAuxRails,
                  NULL);
            auxTrackerForm = XtVaCreateManagedWidget("auxTrackerRowCol",
                  xmFormWidgetClass, trackerRowCol,
                  XmNfractionBase, leftAuxRails + rightAuxRails,
                  NULL);
            leftAuxTracker = XtVaCreateManagedWidget("0",
                  xmTextWidgetClass, auxTrackerForm,
                  XmNtopAttachment, XmATTACH_FORM,
                  XmNleftAttachment, XmATTACH_FORM,
                  XmNrightAttachment, XmATTACH_POSITION,
                  XmNbottomAttachment, XmATTACH_FORM,
                  XmNrightPosition, leftAuxRails,
                  NULL);
            XtAddCallback(leftAuxTracker, XmNactivateCallback,
                  (XtCallbackProc) abacusMathListener, (XtPointer) NULL);
            rightAuxTracker = XtVaCreateManagedWidget("0",
                  xmTextWidgetClass, auxTrackerForm,
                  XmNtopAttachment, XmATTACH_FORM,
                  XmNrightAttachment, XmATTACH_FORM,
                  XmNleftAttachment, XmATTACH_WIDGET,
                  XmNbottomAttachment, XmATTACH_FORM,
                  XmNleftWidget, leftAuxTracker,
                  NULL);
            XtAddCallback(rightAuxTracker, XmNactivateCallback,
                  (XtCallbackProc) abacusMathListener, (XtPointer) NULL);
      }
#endif
      tracker = XtCreateManagedWidget("0",
            xmTextWidgetClass, trackerRowCol, NULL, 0);
      XtAddCallback(tracker, XmNactivateCallback,
            (XtCallbackProc) abacusMathListener, (XtPointer) NULL);
#ifdef LEE_ABACUS
      if (lee) {
            XtVaSetValues(abacus,
                  XmNtopPosition, 5,
                  NULL);
      }
      if (lee) {
            leftAuxAbacus = XtVaCreateManagedWidget("leftAuxAbacus",
                  abacusWidgetClass, auxForm,
                  XmNtopAttachment, XmATTACH_FORM,
                  XmNleftAttachment, XmATTACH_FORM,
                  XmNbottomAttachment, XmATTACH_WIDGET,
                  XmNrightAttachment, XmATTACH_POSITION,
                  XmNbottomWidget, abacus,
                  XtNformat, "Japanese",
                  XtNaux, LEFT_AUX,
                  XtNprimaryBeadColor, leftAuxColor,
                  XmNrightPosition, leftAuxRails,
                  XtNrails, leftAuxRails,
                  XtNbase, base,
                  XtNdisplayBase, displayBase,
                  NULL);
            XtAddCallback(leftAuxAbacus, XtNselectCallback,
                  (XtCallbackProc) callbackAbacus, (XtPointer) NULL);
            rightAuxAbacus = XtVaCreateManagedWidget("rightAuxAbacus",
                  abacusWidgetClass, auxForm,
                  XmNtopAttachment, XmATTACH_FORM,
                  XmNrightAttachment, XmATTACH_FORM,
                  XmNbottomAttachment, XmATTACH_WIDGET,
                  XmNbottomWidget, abacus,
                  XmNleftAttachment, XmATTACH_WIDGET,
                  XmNleftWidget, leftAuxAbacus,
                  XtNformat, "Japanese",
                  XtNaux, RIGHT_AUX,
                  XtNprimaryBeadColor, rightAuxColor,
                  XtNrails, rightAuxRails,
                  XtNbase, base,
                  XtNdisplayBase, displayBase,
                  NULL);
            XtAddCallback(rightAuxAbacus, XtNselectCallback,
                  (XtCallbackProc) callbackAbacus, (XtPointer) NULL);
      }
#endif
#else
      abacus = XtCreateManagedWidget("abacus",
            abacusWidgetClass, topLevel, NULL, 0);
#endif
      XtVaGetValues(abacus,
#ifdef HAVE_MOTIF
            XtNrails, &rails,
            XtNromanNumerals, &romanNumerals,
            XtNgroup, &group,
            XtNsign, &sign,
            XtNbottomPiece, &bottomPiece,
            XtNbottomPiecePercent, &bottomPiecePercent,
            XtNtopPiece, &topPiece,
            XtNanomaly, &anomaly,
            XtNsubdeck, &subdeck,
            XtNsubbase, &subbase,
            XtNbase, &base,
            XtNdisplayBase, &displayBase,
            XtNmode, &mode,
            XtNteach, &teach,
#endif
            XtNdemo, &demo,
            XtNpixmapSize, &pixmapSize, NULL);
#ifdef HAVE_XPM
      {
            XpmAttributes xpmAttributes;
            XpmColorSymbol transparentColor[1] = {{NULL,
                  (char *) "none", 0 }};
            Pixel bg;

            xpmAttributes.valuemask = XpmColorSymbols | XpmCloseness;
            xpmAttributes.colorsymbols = transparentColor;
            xpmAttributes.numsymbols = 1;
            xpmAttributes.closeness = 40000;
            XtVaGetValues(topLevel, XtNbackground, &bg, NULL);
            transparentColor[0].pixel = bg;
            (void) XpmCreatePixmapFromData(XtDisplay(topLevel),
                  RootWindowOfScreen(XtScreen(topLevel)),
                  RESIZE_XPM(pixmapSize), &pixmap, NULL,
                  &xpmAttributes);
      }
      if (pixmap == (Pixmap) NULL)
#endif
            pixmap = XCreateBitmapFromData(XtDisplay(topLevel),
                  RootWindowOfScreen(XtScreen(topLevel)),
                  (char *) abacus_bits,
                  abacus_width, abacus_height);
      XtVaSetValues(topLevel,
#ifdef HAVE_MOTIF
            XmNkeyboardFocusPolicy, XmPOINTER, /* not XmEXPLICIT */
#else
            XtNinput, True,
#endif
            XtNiconPixmap, pixmap, NULL);
      XtAddCallback(abacus, XtNselectCallback,
            (XtCallbackProc) callbackAbacus, (XtPointer) NULL);

#ifdef HAVE_MOTIF
#ifdef TOGGLES
      {
      romanNumeralsSwitch = XtVaCreateManagedWidget("Roman",
            xmToggleButtonGadgetClass, toggleRowCol, NULL);
      XtAddCallback(romanNumeralsSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) romanNumeralToggle, (XtPointer) NULL);
      XmToggleButtonSetState(romanNumeralsSwitch, romanNumerals, False);
      groupSwitch = XtVaCreateManagedWidget("Group",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(groupSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) groupToggle, (XtPointer) NULL);
      XmToggleButtonSetState(groupSwitch, group, False);
      signSwitch = XtVaCreateManagedWidget("Signed",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(signSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) signToggle, (XtPointer) NULL);
      XmToggleButtonSetState(signSwitch, sign, False);
      pieceSwitch = XtVaCreateManagedWidget("Piece",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(pieceSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) quarterToggle, (XtPointer) NULL);
      XmToggleButtonSetState(pieceSwitch, (bottomPiece == QUARTERS), False);
      piecePercentSwitch = XtVaCreateManagedWidget("PiecePercent",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(piecePercentSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) quarterPercentToggle, (XtPointer) NULL);
      XmToggleButtonSetState(piecePercentSwitch, (bottomPiecePercent != 0),
            False);
      twelfthSwitch = XtVaCreateManagedWidget("Twelfth",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(twelfthSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) twelfthToggle, (XtPointer) NULL);
      XmToggleButtonSetState(twelfthSwitch,
            (topPiece * bottomPiece == TWELFTHS), False);
      anomalySwitch = XtVaCreateManagedWidget("Anomaly",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(anomalySwitch, XmNvalueChangedCallback,
            (XtCallbackProc) anomalyToggle, (XtPointer) NULL);
      XmToggleButtonSetState(anomalySwitch, (anomaly == 2), False);
      watchSwitch = XtVaCreateManagedWidget("Watch",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(anomalySwitch, XmNvalueChangedCallback,
            (XtCallbackProc) watchToggle, (XtPointer) NULL);
      XmToggleButtonSetState(anomalySwitch, (anomaly == 4), False);
      subdeckSwitch = XtVaCreateManagedWidget("Subdeck",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(subdeckSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) subdeckToggle, (XtPointer) NULL);
      XmToggleButtonSetState(subdeckSwitch, (subdeck == 3 && subbase == 12),
            False);
      eighthSwitch = XtVaCreateManagedWidget("Eighth",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(eighthSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) eighthToggle, (XtPointer) NULL);
      XmToggleButtonSetState(eighthSwitch, (subdeck == 3 && subbase == 8),
            False);
      rightToLeftAddSwitch = XtVaCreateManagedWidget("RightToLeftAdd",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(rightToLeftAddSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) rightToLeftAddToggle, (XtPointer) NULL);
      XmToggleButtonSetState(rightToLeftAddSwitch, rightToLeftAdd,
            False);
      rightToLeftMultSwitch = XtVaCreateManagedWidget("RightToLeftMult",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(rightToLeftMultSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) rightToLeftMultToggle, (XtPointer) NULL);
      XmToggleButtonSetState(rightToLeftMultSwitch, rightToLeftMult,
            False);
#if 0
      verticalSwitch= XtVaCreateManagedWidget("Vertical",
            xmToggleButtonGadgetClass, toggleRowCol,
            XmNset, FALSE, NULL);
      XtAddCallback(verticalSwitch, XmNvalueChangedCallback,
            (XtCallbackProc) verticalToggle, (XtPointer) NULL);
#endif
      }
#endif
      sizeSlider = XtVaCreateManagedWidget("rails",
            xmScaleWidgetClass, controlRowCol,
            XtVaTypedArg, XmNtitleString, XmRString, "      Abacus Size", 18,
            XmNminimum, MIN_RAILS,
            XmNmaximum, MAX_RAILS,
            XmNscaleWidth, (MAX_RAILS - MIN_RAILS + 1) * 6,
            XmNvalue, rails,
            XmNshowValue, True,
            XmNorientation, XmHORIZONTAL, NULL);
      XtAddCallback(sizeSlider, XmNvalueChangedCallback,
            (XtCallbackProc) railChangeListener, (XtPointer) NULL);
      baseSet = ((base != DEFAULT_BASE || displayBase != DEFAULT_BASE) &&
            !demo);
      if (mode < 0 || mode > MAX_FORMATS)
            mode = GENERIC;
      if (demo && mode == GENERIC)
            mode = CHINESE;
      choice = XmStringCreateSimple((char *) "Choice:");
      choiceSubMenu = XmCreatePulldownMenu(controlRowCol,
            (char *) "choiceSubMenu", NULL, 0);
      for (i = 0; i < CHOICES; i++) {
            choiceStrings[i] =
                  XmStringCreateSimple((char *) choiceString[i]);
            XtSetArg(arg[0], XmNlabelString, choiceStrings[i]);
            XtSetArg(arg[1], XmNmnemonic, choiceString[i][0]);
            choiceOptions[i] =
                  XmCreatePushButtonGadget(choiceSubMenu,
                  (char *) choiceString[i], arg, 2);
            XtAddCallback(choiceOptions[i],
                  XmNactivateCallback, choiceListener, (char *) i);
      }
      XtManageChildren(choiceOptions, CHOICES);
      XtSetArg(arg[0], XmNlabelString, choice);
      XtSetArg(arg[1], XmNmnemonic, 'C');
      XtSetArg(arg[2], XmNsubMenuId, choiceSubMenu);
      XtSetArg(arg[3], XmNmenuHistory,
                  choiceOptions[((demo) ? DEMO : ((teach) ? TEACH : ((baseSet) ? BASE : NORMAL)))]);
      choiceMenu = XmCreateOptionMenu(controlRowCol,
            (char *) "ChoiceMenu", arg, 4);
      for (i = 0; i < CHOICES; i++)
            XmStringFree(choiceStrings[i]);
      XmStringFree(choice);
      XtManageChild(choiceMenu);
      format = XmStringCreateSimple((char *) "Format:");
      formatSubMenu = XmCreatePulldownMenu(controlRowCol,
            (char *) "formatSubMenu", NULL, 0);
      for (i = 0; i < MAX_MODES; i++) {
            formatStrings[i] =
                  XmStringCreateSimple((char *) formatString[i]);
            XtSetArg(arg[0], XmNlabelString, formatStrings[i]);
            XtSetArg(arg[1], XmNmnemonic, formatString[i][0]);
            formatOptions[i] = XmCreatePushButtonGadget(formatSubMenu,
                  (char *) formatString[i], arg, 2);
            XtAddCallback(formatOptions[i],
                  XmNactivateCallback, formatListener, (char *) i);
      }
      XtManageChildren(formatOptions, MAX_MODES);
      XtSetArg(arg[0], XmNlabelString, format);
      XtSetArg(arg[1], XmNmnemonic, 'F');
      XtSetArg(arg[2], XmNsubMenuId, formatSubMenu);
      XtSetArg(arg[3], XmNmenuHistory, formatOptions[mode]);
      formatMenu = XmCreateOptionMenu(controlRowCol, (char *) "FormatMenu",
            arg, 4);
      for (i = 0; i < MAX_MODES; i++)
            XmStringFree(formatStrings[i]);
      XmStringFree(format);
      XtManageChild(formatMenu);
      if (baseSet) {
            createBase(base, displayBase);
      } else {
            forceNormalParams(abacus);
      }
#endif
      initialize(abacus);
      if (demo)
            forceDemoParams(abacus);
#ifdef HAVE_MOTIF
      else if (teach)
            forceTeachParams(abacus);
#endif
      XtRealizeWidget(topLevel);
      VOID XGrabButton(XtDisplay(abacus), (unsigned int) AnyButton,
            AnyModifier, XtWindow(abacus), TRUE,
            (unsigned int) (ButtonPressMask |
            ButtonMotionMask | ButtonReleaseMask),
            GrabModeAsync, GrabModeAsync, XtWindow(abacus),
            XCreateFontCursor(XtDisplay(abacus), XC_hand2));
      if (demo) {
            createDemo();
#ifdef HAVE_MOTIF
      } else if (teach) {
            createTeach();
#endif
      }
      XtMainLoop();

#ifdef VMS
      return 1;
#else
      return 0;
#endif
}
#endif

Generated by  Doxygen 1.6.0   Back to index