home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / df3os2.zip / POPDOWN.CPP < prev    next >
C/C++ Source or Header  |  1993-11-13  |  12KB  |  424 lines

  1. // ------------- popdown.cpp
  2.  
  3. #include <ctype.h>
  4. #include "dflatpp.h"
  5.  
  6. static Color col = {
  7.     BLACK,            // fg
  8.     CYAN,            // bg
  9.     BLACK,            // selected fg
  10.     LIGHTGRAY,        // selected bg
  11.     BLACK,            // frame fg
  12.     CYAN,            // frame bg
  13.     DARKGRAY,        // inactive fg
  14.     CYAN            // inactive bg
  15. };
  16.  
  17. PopDown::PopDown(DFWindow *par, MenuSelection **Selections)
  18.                         : ListBox(5, 5, par)
  19. {
  20.     windowtype = PopdownWindow;
  21.     selections = Selections;
  22.     SetAttribute(BORDER | SHADOW | SAVESELF);
  23.     selection = 0;
  24.     DblBorder = False;
  25.     isopen = False;
  26.     iscascaded = False;
  27.     if (selections != 0)    {
  28.         MenuDimensions();
  29.         SetTextLength(menuwidth * menuheight);
  30.         for (int i = 0; i < menuheight; i++)    {
  31.             MenuSelection &ms = **(selections+i);
  32.             BuildMenuLine(i);
  33.             if (ms.type == CASCADER)    {
  34.                 ms.cascade = new PopDown(this, ms.cascaders);
  35.                 ms.cascade->isCascaded() = True;
  36.             }
  37.         }
  38.         rect.Right() = rect.Left() + menuwidth;
  39.         rect.Bottom() = rect.Top() + menuheight + 1;
  40.     }
  41.     colors = col;
  42.     shortcutfg = RED;
  43. }
  44.  
  45. // ---- shut down a popdown menu
  46. void PopDown::CloseWindow()
  47. {
  48.     if (selections != 0)    {
  49.         // --- delete all cascader popdowns
  50.         for (int i = 0; selections[i]; i++)    {
  51.             MenuSelection &ms = *selections[i];
  52.             if (ms.type == CASCADER && ms.cascade != 0)
  53.                 delete ms.cascade;
  54.         }
  55.     }
  56.     ListBox::CloseWindow();
  57. }
  58.  
  59. // ------- pop down the menu
  60. void PopDown::OpenMenu(int left, int top)
  61. {
  62.     Rect rc(0, 0, desktop.screen().Width()-1,
  63.         desktop.screen().Height()-1);
  64.     DFWindow *Wnd = Parent();
  65.     while (Wnd != 0 && Wnd->WindowType() == PopdownWindow)
  66.         Wnd = Wnd->Parent();
  67.     if (Wnd != 0 && (Wnd = Wnd->Parent()) != 0)    {
  68.         Rect rc = Wnd->ClientRect();
  69.         if (Parent()->WindowType() == MenubarWindow)
  70.             if (Wnd->WindowType() == ApplicationWindow)
  71.                 if (Wnd->Attribute() & TOOLBAR)
  72.                     rc = Rect(rc.Left(), rc.Top()-3, rc.Right(), rc.Bottom());
  73.         left = min(max(left, rc.Left()),
  74.                     rc.Right() - ClientWidth());
  75.         top = min(max(top, rc.Top()),
  76.                     rc.Bottom() - ClientHeight());
  77.     }
  78.     left = min(max(left, rc.Left()),
  79.                     rc.Right()-ClientWidth()-1);
  80.     top = min(max(top, rc.Top()),
  81.                     rc.Bottom()-ClientHeight()-1);
  82.     isopen = True;
  83.     Move(left, top);
  84.     CaptureFocus();
  85.     Paint();        // in case a command attribute changed
  86. }
  87.  
  88. // ---------- deactivate the popdown menu
  89. void PopDown::CloseMenu(Bool SendESC)
  90. {
  91.     if (isopen)    {
  92.         // ------- close any open cascaded menus
  93.         PopDown *Wnd = (PopDown *)First();
  94.         while (Wnd != 0)    {
  95.             Wnd->CloseMenu();
  96.             Wnd = (PopDown *) (Wnd->Next());
  97.         }
  98.         Hide();
  99.         isopen = False;
  100.         ReleaseFocus();
  101.         if (Parent() && !iscascaded && SendESC)
  102.             Parent()->Keyboard(ESC);
  103.     }
  104. }
  105.  
  106. void PopDown::Show()
  107. {
  108.     if (isopen)
  109.         ListBox::Show();
  110. }
  111.  
  112. // -------- build a menu line
  113. void PopDown::BuildMenuLine(int sel)
  114. {
  115.     int wd = menuwidth;
  116.     String ln;
  117.     if (selections[sel]->type == SEPARATOR)
  118.         ln = String(--wd, LINE);
  119.     else    {
  120.         ln = String(" ");
  121.         ln += *(selections[sel]->label);
  122.         int r = wd-ln.Strlen();
  123.         ln += String(r, ' ');
  124.         if (selections[sel]->type == CASCADER)
  125.             ln[wd-1] = CASCADEPOINTER;
  126.     }
  127.     AddText(ln);
  128. }
  129.  
  130. // -------- compute menu width
  131. void PopDown::MenuDimensions()
  132. {
  133.     int txlen = 0;
  134.     for (int i = 0; selections[i] != 0; i++)  {
  135.         if (selections[i]->type != SEPARATOR)    {
  136.             int lblen = (selections[i]->label)->Strlen()-1;
  137.             txlen = max(txlen, lblen);
  138.         }
  139.     }
  140.     menuwidth = txlen+4;
  141.     menuheight = i;
  142. }
  143.  
  144. // ------ display a menu line
  145. void PopDown::DisplayMenuLine(int lno)
  146. {
  147.     if (isopen)    {
  148.         int fg, bg;
  149.         int isActive = selections[lno]->isEnabled();
  150.         int sfg = shortcutfg;
  151.         if (lno == selection)    {
  152.             fg = colors.sfg;
  153.             bg = colors.sbg;
  154.         }
  155.         else if (isActive)    {
  156.             fg = colors.fg;
  157.             bg = colors.bg;
  158.         }
  159.         else     {
  160.             fg = colors.hfg;
  161.             bg = colors.hbg;
  162.         }
  163.         if (!isActive)
  164.             shortcutfg = fg;
  165.         WriteShortcutLine(lno, fg, bg);
  166.         shortcutfg = sfg;
  167.     }
  168. }
  169.  
  170. // ------ set no selection current
  171. void PopDown::ClearSelection()
  172. {
  173.     if (selection != -1)    {
  174.         int sel = selection;
  175.         selection = -1;
  176.         DisplayMenuLine(sel);
  177.     }
  178. }
  179.  
  180. // ------ set a current menu selection
  181. void PopDown::SetSelection(int sel)
  182. {
  183.     ClearSelection();
  184.     if (sel >= 0 && sel < wlines)    {
  185.         selection = sel;
  186.         DisplayMenuLine(sel);
  187.     }
  188. }
  189.  
  190. // ---------- paint the menu
  191. void PopDown::Paint()
  192. {
  193.     if (text == 0)
  194.         ListBox::Paint();
  195.     else    {
  196.         for (int i = 0; i < wlines; i++)    {
  197.             if (selections[i]->type == TOGGLE)    {
  198.                 int off = TextLine(i) - (const char *)*text;
  199.                 if (selections[i]->toggle == On)
  200.                     (*text)[off] = CheckMark();
  201.                 else
  202.                     (*text)[off] = ' ';
  203.             }
  204.             DisplayMenuLine(i);
  205.         }
  206.     }
  207. }
  208.  
  209. // --------- paint the menu's border
  210. void PopDown::Border()
  211. {
  212.     if (isopen && isVisible())    {
  213.         int fg = colors.ffg;
  214.         int bg = colors.fbg;
  215.         int rt = Width()-1;
  216.         ListBox::Border();
  217.         for (int i = 0; i < wlines; i++)    {
  218.             if (selections[i]->type == SEPARATOR)    {
  219.                 WriteWindowChar(LEDGE, 0, i+1, fg, bg);
  220.                 WriteWindowChar(REDGE, rt, i+1, fg, bg);
  221.             }
  222.         }
  223.     }
  224. }
  225.  
  226. // ------- test for a menu selection accelerator key
  227. Bool PopDown::AcceleratorKey(int key)
  228. {
  229.     for (int i = 0; i < wlines; i++)    {
  230.         MenuSelection &ms = **(selections+i);
  231.         if (key == ms.accelerator)    {
  232.             SetSelection(i);
  233.             Choose();
  234.             return True;
  235.         }
  236.     }
  237.     return False;
  238. }
  239.  
  240. // ------- test for a menu selection shortcut key
  241. Bool PopDown::ShortcutKey(int key)
  242.     {
  243.     key = tolower(key);
  244.     for (int i = 0; i < wlines; i++)    
  245.         {
  246.         MenuSelection &ms = **(selections+i);
  247.         if (ms.label != 0)  
  248.             {
  249.             int off = ms.label->FindChar(SHORTCUTCHAR);
  250.             if (off != -1)    
  251.                 {
  252.                 String &cp = *ms.label;
  253.                 int c = cp[off+1];
  254.                 if (key == tolower(c))    
  255.                     {
  256.                     SetSelection(i);
  257.                     Choose();
  258.                     return True;
  259.                     }
  260.                 }
  261.             }
  262.         }
  263.     return False;
  264.     }
  265.  
  266. // ----- keystroke while menu is popped down
  267. void PopDown::Keyboard(int key)
  268. {
  269.     if (AcceleratorKey(key))
  270.         return;
  271.     if (ShortcutKey(key))
  272.         return;
  273.     switch (key)    {
  274.         case UP:
  275.             if (selection == 0)    {
  276.                 SetSelection(wlines-1);
  277.                 return;
  278.             }
  279.             if (selections[selection-1]->type == SEPARATOR)  {
  280.                 SetSelection(selection-2);
  281.                 return;
  282.             }
  283.             break;
  284.         case DN:
  285.             if (selection == wlines-1)    {
  286.                 SetSelection(0);
  287.                 return;
  288.             }
  289.             if (selections[selection+1]->type == SEPARATOR)  {
  290.                 SetSelection(selection+2);
  291.                 return;
  292.             }
  293.             break;
  294.         case ESC:
  295.             CloseMenu(ParentisMenu());
  296.             return;
  297.         case FWD:
  298.         case BS:
  299.             CloseMenu();
  300.             if (Parent() != 0)    {
  301.                 Parent()->Keyboard(key);
  302.                 return;
  303.             }
  304.             break;
  305.         default:
  306.             break;
  307.     }
  308.     ListBox::Keyboard(key);
  309. }
  310.  
  311. // ----- shift key status changed
  312. void PopDown::ShiftChanged(int sk)
  313. {
  314.     if (sk & ALTKEY)
  315.         CloseMenu(ParentisMenu());
  316. }
  317.  
  318. // ---------- Left mouse button was clicked
  319. void PopDown::LeftButton(int mx, int my)
  320. {
  321.     if (ClientRect().Inside(mx, my))    {
  322.         if (my != prevmouseline)    {
  323.             int y = my - ClientTop();
  324.             if (selections[y]->type != SEPARATOR)
  325.                 SetSelection(y);
  326.         }
  327.     }
  328.     else if (!rect.Inside(mx, my))    {
  329.         if (Parent() && my == Parent()->Bottom())
  330.             Parent()->LeftButton(mx, my);
  331.     }
  332.     prevmouseline = my;
  333.     prevmousecol = mx;
  334. }
  335.  
  336. // ---------- Left mouse button was double-clicked
  337. void PopDown::DoubleClick(int mx, int my)
  338. {
  339.     if (!rect.Inside(mx, my))    {
  340.         CloseMenu();
  341.         if (Parent())
  342.             Parent()->DoubleClick(mx, my);
  343.     }
  344. }
  345.  
  346. // ---------- Left mouse button was released
  347. void PopDown::ButtonReleased(int mx, int my)
  348. {
  349.     if (ClientRect().Inside(mx, my))    {
  350.         if (prevmouseline == my && prevmousecol == mx)
  351.             if (selections[my-ClientTop()]->type != SEPARATOR)
  352.                 Choose();
  353.     }
  354.     else if (!rect.Inside(mx, my))    {
  355.         DFWindow *Wnd = desktop.inWindow(mx, my);
  356.         if (!(Wnd == Parent() && my == Top()-1 &&
  357.                 mx >= Left() && mx <= Right()))    {
  358.             CloseMenu(ParentisMenu());
  359.             if (Wnd != 0 && Wnd != desktop.InFocus())
  360.                 Wnd->SetFocus();
  361.         }
  362.     }
  363. }
  364.  
  365. // --------- user chose a menu selection
  366. void PopDown::Choose()
  367. {
  368.     MenuSelection &ms = *selections[selection];
  369.     if (ms.isEnabled())    {
  370.         if (ms.type == CASCADER && ms.cascade != 0)
  371.             // -------- cascaded menu
  372.             ms.cascade->OpenMenu(Right(), Top()+selection);
  373.         else    {
  374.             if (ms.type == TOGGLE)    {
  375.                 // ---- toggle selection
  376.                 ms.InvertToggle();
  377.                 const char *cp = TextLine(selection);
  378.                 int off = cp - (const char *)*text;
  379.                 if (*cp == CheckMark())
  380.                     (*text)[off] = ' ';
  381.                 else
  382.                     (*text)[off] = CheckMark();
  383.                 DisplayMenuLine(selection);
  384.             }
  385.             if (ms.cmdfunction != 0)    {
  386.                 // ---- there is a function associated
  387.                 DFWindow *wnd = (PopDown *) this;
  388.                 // --- close all menus
  389.                 while (wnd &&
  390.                         wnd->WindowType() == PopdownWindow) {
  391.                     ((PopDown *)wnd)->CloseMenu();
  392.                     wnd = (wnd->Parent());
  393.                 }
  394.                 if (wnd && wnd->WindowType() == MenubarWindow){
  395.                     if (wnd == desktop.InFocus())
  396.                         wnd->Keyboard(ESC);
  397.                     wnd = (wnd->Parent());
  398.                 }
  399.                 if (wnd)
  400.                     // ---- execute the function
  401.                     (((Application *)wnd)->*ms.cmdfunction)();
  402.             }
  403.         }
  404.     }
  405.     else
  406.         desktop.speaker().Beep();    // disabled selection
  407. }
  408.  
  409. inline Bool isMenu(DFWindow *wnd)
  410. {
  411.     if (wnd != 0)    {
  412.         WndType wt = wnd->WindowType();
  413.         return (Bool) (wt==MenubarWindow || wt==PopdownWindow);
  414.     }
  415.     return False;
  416. }
  417.  
  418. // ----- test for the parent as menu or menubar
  419. Bool PopDown::ParentisMenu(DFWindow &wnd)
  420. {
  421.     return isMenu(wnd.Parent());
  422. }
  423.  
  424.