home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / i / iv26_w_3.zip / EXAMPLES / IDRAW / PDMENU.C < prev    next >
C/C++ Source or Header  |  1991-12-30  |  12KB  |  438 lines

  1. /*
  2.  * Copyright (c) 1987, 1988, 1989 Stanford University
  3.  *
  4.  * Permission to use, copy, modify, distribute, and sell this software and its
  5.  * documentation for any purpose is hereby granted without fee, provided
  6.  * that the above copyright notice appear in all copies and that both that
  7.  * copyright notice and this permission notice appear in supporting
  8.  * documentation, and that the name of Stanford not be used in advertising or
  9.  * publicity pertaining to distribution of the software without specific,
  10.  * written prior permission.  Stanford makes no representations about
  11.  * the suitability of this software for any purpose.  It is provided "as is"
  12.  * without express or implied warranty.
  13.  *
  14.  * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  15.  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
  16.  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  17.  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  18.  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  19.  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  20.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  21.  */
  22.  
  23. /*
  24.  * Implements pulldown menu classes.
  25.  */
  26.  
  27. #include "istring.h"
  28. #include "pdmenu.h"
  29. #include <InterViews/event.h>
  30. #include <InterViews/font.h>
  31. #include <InterViews/frame.h>
  32. #include <InterViews/painter.h>
  33. #include <InterViews/sensor.h>
  34. #include <InterViews/shape.h>
  35. #include <InterViews/world.h>
  36.  
  37. /* Define the initial number of elements to allocate space for. */
  38.  
  39. static const int INITIALSIZE = 15;
  40.  
  41. /*
  42.  * PullDownMenuBar starts with no currently active or stored
  43.  * activators although it allocates some initial space to store them.
  44.  */
  45.  
  46. PullDownMenuBar::PullDownMenuBar () {
  47.     cur = nil;
  48.     sizeactivators = INITIALSIZE;
  49.     numactivators = 0;
  50.     activators = new PullDownMenuActivator*[sizeactivators];
  51. }
  52.  
  53. /*
  54.  * Free storage allocated for the dynamic array.
  55.  */
  56.  
  57. PullDownMenuBar::~PullDownMenuBar () {
  58.     delete activators;
  59. }
  60.  
  61. /*
  62.  * Enter stores an interior activator and tells it it can get its
  63.  * highlight painter from us.
  64.  */
  65.  
  66. void PullDownMenuBar::Enter (PullDownMenuActivator* act) {
  67.     if (numactivators == sizeactivators) {
  68.     GrowActivators();
  69.     }
  70.     activators[numactivators++] = act;
  71.     act->SetHighlighterParent(this);
  72. }
  73.  
  74. /*
  75.  * Contains returns true if the child is one of the bar's activators
  76.  * or one of the activators' commands.
  77.  */
  78.  
  79. boolean PullDownMenuBar::Contains (Interactor* child) {
  80.     for (int i = 0; i < numactivators; i++) {
  81.     if (activators[i] == child) {
  82.         return true;
  83.     }
  84.     }
  85.  
  86.     for (i = 0; i < numactivators; i++) {
  87.     if (activators[i]->Contains(child)) {
  88.         return true;
  89.     }
  90.     }
  91.  
  92.     return false;
  93. }
  94.  
  95. /*
  96.  * MenuActive returns true if any of the bar's activators have opened
  97.  * a menu.
  98.  */
  99.  
  100. boolean PullDownMenuBar::MenuActive () {
  101.     return cur != nil;
  102. }
  103.  
  104. /*
  105.  * MenuShouldActivate returns true if any of the bar's activators
  106.  * EXCEPT the given one have opened a menu.
  107.  */
  108.  
  109. boolean PullDownMenuBar::MenuShouldActivate (PullDownMenuActivator* act) {
  110.     return cur != nil && cur != act;
  111. }
  112.  
  113. /*
  114.  * MenuActivate highlights the given activator, opens the activator's
  115.  * menu, and stores it as the currently active activator.
  116.  */
  117.  
  118. void PullDownMenuBar::MenuActivate (PullDownMenuActivator* act) {
  119.     act->Highlight(true);
  120.     act->Open();
  121.     cur = act;
  122. }
  123.  
  124. /*
  125.  * MenuDeactivate closes the currently active activator's menu,
  126.  * unhighlights the activator, and stores no more active activators.
  127.  */
  128.  
  129. void PullDownMenuBar::MenuDeactivate () {
  130.     cur->Close();
  131.     cur->Highlight(false);
  132.     cur = nil;
  133. }
  134.  
  135. /*
  136.  * GrowActivators increases the dynamic array's size to make room for
  137.  * more activators to be stored.
  138.  */
  139.  
  140. void PullDownMenuBar::GrowActivators () {
  141.     PullDownMenuActivator** oldacts = activators;
  142.     sizeactivators += INITIALSIZE/2;
  143.     activators = new PullDownMenuActivator*[sizeactivators];
  144.     memmove(activators, oldacts, numactivators * sizeof(PullDownMenuActivator*));
  145.     delete oldacts;
  146. }
  147.  
  148. /*
  149.  * PullDownMenuActivator stores the bar it belongs to and its text
  150.  * label.  It starts off with an empty menu and no stored commands
  151.  * although it allocates some initial space to store the commands.  It
  152.  * must catch the same mouse button PullDownMenuCommand catches.
  153.  */
  154.  
  155. PullDownMenuActivator::PullDownMenuActivator (PullDownMenuBar* b,
  156. const char* n) {
  157.     b->Enter(this);
  158.     bar = b;
  159.     name = strdup(n ? n : "");
  160.     menu = new ShadowFrame(nil,5,5);
  161.     menu->SetCanvasType(CanvasSaveUnder);
  162.     sizecommands = INITIALSIZE;
  163.     numcommands = 0;
  164.     commands = new PullDownMenuCommand*[sizecommands];
  165.     input = new Sensor(onoffEvents);
  166.     input->CatchButton(DownEvent, LEFTMOUSE);
  167.     input->CatchButton(UpEvent, LEFTMOUSE);
  168. }
  169.  
  170. /*
  171.  * Free storage allocated for members.
  172.  */
  173.  
  174. PullDownMenuActivator::~PullDownMenuActivator () {
  175.     delete name;
  176.     delete menu;
  177.     delete commands;
  178. }
  179.  
  180. /*
  181.  * SetMenu sets the menu's actual contents.
  182.  */
  183.  
  184. void PullDownMenuActivator::SetMenu (Scene* box) {
  185.     menu->Insert(box);
  186. }
  187.  
  188. /*
  189.  * Handle works together with the bar to determine whether any
  190.  * activator has opened or should open a menu and accordingly tells
  191.  * the bar to deactivate or activate an activator.
  192.  */
  193.  
  194. void PullDownMenuActivator::Handle (Event& e) {
  195.     switch (e.eventType) {
  196.     case DownEvent:
  197.     if (!bar->MenuActive()) {
  198.         bar->MenuActivate(this);
  199.         while (e.eventType != UpEvent) {
  200.         Read(e);
  201.         if (e.eventType == UpEvent) {
  202.             bar->MenuDeactivate();
  203.         }
  204.         if (bar->Contains(e.target)) {
  205.             e.target->Handle(e);
  206.         }
  207.         }
  208.     }
  209.     break;
  210.     case UpEvent:
  211.     break;
  212.     case OnEvent:
  213.     if (bar->MenuShouldActivate(this)) {
  214.         bar->MenuDeactivate();
  215.         bar->MenuActivate(this);
  216.     }
  217.     break;
  218.     case OffEvent:
  219.     break;
  220.     }
  221. }
  222.  
  223. /*
  224.  * Enter stores an interior command and tells it it can get its
  225.  * highlight painter from our bar like us too.
  226.  */
  227.  
  228. void PullDownMenuActivator::Enter (PullDownMenuCommand* cmd) {
  229.     if (numcommands == sizecommands) {
  230.     GrowCommands();
  231.     }
  232.     commands[numcommands++] = cmd;
  233.     cmd->SetHighlighterParent(bar);
  234. }
  235.  
  236. /*
  237.  * Contains returns true if the command is one of the activator's
  238.  * commands.
  239.  */
  240.  
  241. boolean PullDownMenuActivator::Contains (Interactor* cmd) {
  242.     for (int i = 0; i < numcommands; i++) {
  243.     if (commands[i] == cmd) {
  244.         return true;
  245.     }
  246.     }
  247.     return false;
  248. }
  249.  
  250. /*
  251.  * Open inserts the menu into the scene with the menu's top left
  252.  * corner aligned with the activator's bottom left corner.
  253.  */
  254.  
  255. void PullDownMenuActivator::Open () {
  256.     World* world = GetWorld();
  257.     Coord l = 0;
  258.     Coord b = 0;
  259.     GetRelative(l, b, world);
  260.     world->InsertPopup(menu, l, b, TopLeft);
  261. }
  262.  
  263. /*
  264.  * Close makes the activator's menu disappear.
  265.  */
  266.  
  267. void PullDownMenuActivator::Close () {
  268.     menu->Parent()->Remove(menu);
  269. }
  270.  
  271. /*
  272.  * Reconfig pads the activator's shape to accomodate its text label.
  273.  * Basing padding on the font in use ensures the padding will change
  274.  * proportionally with changes in the font's size.
  275.  */
  276.  
  277. static const float WIDTHPAD = 1.0;/* fraction of font->Width(EM) */
  278. static const float CMDHTPAD = 0.1;/* fraction of font->Height() */
  279. static const float ACTHTPAD = 0.2;/* fraction of font->Height() */
  280. static const char* EM = "m";      /* widest alphabetic character in any font */
  281.  
  282. void PullDownMenuActivator::Reconfig () {
  283.     Highlighter::Reconfig();
  284.     Font* font = output->GetFont();
  285.     int xpad = round(WIDTHPAD * font->Width(EM));
  286.     int ypad = round(ACTHTPAD * font->Height());
  287.     shape->width = font->Width(name) + (2 * xpad);
  288.     shape->height = font->Height() + (2 * ypad);
  289.     shape->Rigid(shape->width - xpad, 0, 2 * ypad, 0);
  290. }
  291.  
  292. /*
  293.  * Redraw displays the text label.
  294.  */
  295.  
  296. void PullDownMenuActivator::Redraw (Coord l, Coord b, Coord r, Coord t) {
  297.     output->ClearRect(canvas, l, b, r, t);
  298.     output->Text(canvas, name, name_x, name_y);
  299. }
  300.  
  301. /*
  302.  * Resize calculates the text label's position.
  303.  */
  304.  
  305. void PullDownMenuActivator::Resize () {
  306.     Font* font = output->GetFont();
  307.     name_x = max(0, (xmax - font->Width(name) + 1) / 2);
  308.     name_y = (ymax - font->Height() + 1) / 2;
  309. }
  310.  
  311. /*
  312.  * GrowCommands increases the dynamic array's size to make room for
  313.  * more commands to be stored.
  314.  */
  315.  
  316. void PullDownMenuActivator::GrowCommands () {
  317.     PullDownMenuCommand** oldcmds = commands;
  318.     sizecommands += INITIALSIZE/2;
  319.     commands = new PullDownMenuCommand*[sizecommands];
  320.     memmove(commands, oldcmds, numcommands * sizeof(PullDownMenuCommand*));
  321.     delete oldcmds;
  322. }
  323.  
  324. /*
  325.  * PullDownMenuCommand stores the activator it belongs to and its text
  326.  * labels.  It catches only one mouse button to prevent the user from
  327.  * accidentally executing a command upon another button's release.
  328.  */
  329.  
  330. PullDownMenuCommand::PullDownMenuCommand (PullDownMenuActivator* a,
  331. const char* n, const char* k) {
  332.     if (a != nil) {
  333.     a->Enter(this);
  334.     }
  335.     activator = a;
  336.     name = strdup(n ? n : "");
  337.     key = strdup(k ? k : "");
  338.     input = new Sensor(onoffEvents);
  339.     input->CatchButton(UpEvent, LEFTMOUSE);
  340. }
  341.  
  342. PullDownMenuCommand::~PullDownMenuCommand () {
  343.     delete name;
  344.     delete key;
  345. }
  346.  
  347. /*
  348.  * Highlight or unhighlight the command when the mouse passes
  349.  * over and execute the command when the user releases the button
  350.  * types its associated character (mapped by the program).
  351.  */
  352.  
  353. void PullDownMenuCommand::Handle (Event& e) {
  354.     switch (e.eventType) {
  355.     case OnEvent:
  356.     Highlight(true);
  357.     break;
  358.     case OffEvent:
  359.     Highlight(false);
  360.     break;
  361.     case UpEvent:
  362.     Execute(e);
  363.     Highlight(false);
  364.     break;
  365.     case KeyEvent:
  366.     activator->Highlight(true);
  367.     activator->Flush();
  368.     Execute(e);
  369.     activator->Highlight(false);
  370.     break;
  371.     }
  372. }
  373.  
  374. /*
  375.  * Execute carries out the command's purpose.
  376.  */
  377.  
  378. void PullDownMenuCommand::Execute (Event&) {
  379.     /* define it in your subclass */
  380. }
  381.  
  382. /*
  383.  * Reconfig pads the command's shape to accomodate its text labels.
  384.  */
  385.  
  386. void PullDownMenuCommand::Reconfig () {
  387.     Highlighter::Reconfig();
  388.     Font* font = output->GetFont();
  389.     int xpad = round(WIDTHPAD * font->Width(EM));
  390.     int ypad = round(CMDHTPAD * font->Height());
  391.     shape->width = font->Width(name) + (2 * xpad);
  392.     shape->width += font->Width(key) + (2 * xpad);
  393.     shape->height = font->Height() + (2 * ypad);
  394.     shape->Rigid(0, hfil, 0, 0);
  395. }
  396.  
  397. /*
  398.  * Redraw displays the text labels.
  399.  */
  400.  
  401. void PullDownMenuCommand::Redraw (Coord l, Coord b, Coord r, Coord t) {
  402.     output->ClearRect(canvas, l, b, r, t);
  403.     output->Text(canvas, name, name_x, name_y);
  404.     output->Text(canvas, key, key_x, key_y);
  405. }
  406.  
  407. /*
  408.  * Resize calculates the text labels' positions.
  409.  */
  410.  
  411. void PullDownMenuCommand::Resize () {
  412.     Font* font = output->GetFont();
  413.     int xpad = round(WIDTHPAD * font->Width(EM));
  414.     name_x = xpad;
  415.     name_y = (ymax - font->Height() + 1) / 2;
  416.     key_x = xmax - font->Width(key) - xpad;
  417.     key_y = name_y;
  418. }
  419.  
  420. /*
  421.  * PullDownMenuDivider listens to no events so it will neither
  422.  * highlight itself nor execute any command.
  423.  */
  424.  
  425. PullDownMenuDivider::PullDownMenuDivider () : (nil, nil, nil) {
  426.     Listen(noEvents);
  427. }
  428.  
  429. /*
  430.  * Redraw displays a horizontal line spanning the canvas.
  431.  */
  432.  
  433. void PullDownMenuDivider::Redraw (Coord l, Coord b, Coord r, Coord t) {
  434.     output->ClearRect(canvas, l, b, r, t);
  435.     Coord hy = ymax / 2;
  436.     output->Line(canvas, l, hy, r, hy);
  437. }
  438.