Re: Here's a "CASE" statement with strings... sorta.

From: Maarten Wiltink (maarten_at_kittensandcats.net)
Date: 01/15/05


Date: Sat, 15 Jan 2005 10:42:14 +0100


"Raptor" <bogus@none.com> wrote in message
news:cs97gl0125l@news2.newsguy.com...
> Maarten Wiltink <maarten@kittensandcats.net> wrote in message
[...]
>> Now the problem becomes that you have to store the generic name
>> somewhere. It's no longer in the caption and there is no ready
>> place to put it.

Of course, when reading this back just such a place suggested itself.

Name.

[...]
> Precisely the reason I'm using the strings
> is that using the tags for case statements make harder-to-read
> and hard-to-change code.

const
  MNU_FILE_NEW = 100;
  MNU_FILE_OPEN = 101;

> To me, this is easy:
>
> s := GetAlphaNumPlus(s, ' _'); // Strip . and &
> {case Caption of} if
> 'File' =s then begin end else if
> 'New' =s then DoMenuNew else if
> 'Open_SQL' =s then DoMenuOpenSQL else if
> 'Open_Debug' =s then begin end else if
> 'As Text' =s then DoMenuOpenDebugText else if
>
> Easy to read, and easy to change.
>
> This is hard:
>
> case tag of
> 1: DoMenuNew;
> 2: DoMenuOpenSQL;
> 3: DoMenuOpenDebugText;
>
> with good procedure names,it is fairly readable--though not nearly so
> as a case statement which uses the actual captions or caption_forms.
>
> The worst part is changing! Adding a new menu item at number 33 means
> that all the case statements from 34 to (say) 54 are off by one and
> have to be manually changed.

case (Tag) of
  32: DoToolsSyntaxCheck;
  33: DoToolsConsistencyCheck;
  55: DoToolsLogoff;
  34: DoToolsOptions;
  ...
end;

>> But that's a "push" style system. You can pull instead. In the
>> OnIdle handler, walk the entire menu and for every item, determine
>> if it should be visible, enabled, checked, whatever. There are
>> probably a limited number of different rules for how a menu item
>> should be affected by application state. Some may be disabled if
>> an item isn't selected in a listview, or checked if the selected
>> item (if there is one) denotes a read-only object. The configuration
>> for a menu item might include the rules that determine its being
>> enabled/disabled, shown/hidden, and checked/unchecked.
>
> This seems a different paradigm, and I don't fully grasp it. To me the
> most natural approach was to write a procedure called SetContext. I'm
> using pages and tabs for the functional divisions of my application,
> and when I click a tab it calls

It is a different paradigm, and it would be useful to learn about it,
I think. It has _different_ efficiencies, not the least of which is
that it is *much* less convoluted and interstricted. Every menu item
knows about the application conditions that affect it, instead of all
application conditions knowing about *every single* menu item. Adding
menu items becomes a self-contained operation; admittedly changing
the composition of application state becomes harder. But the former
is done more often in my experience.

Going back to the push paradigm, Context could be a computed property
that takes a different enumerated value corresponding to the active
tab. Setting it would take the tab selection along with it; changing
the tab selection would call its event dispatcher.

type TContext = (ctxGeneral, ctxSharing, ctxSecurity);

property Context: TContext read GetContext write SetContext;

function TDemo.GetContext: TContext;
begin
  Result:=TContext(pageContexts.ActivePageIndex);
end;

procedure TDemo.SetContext(const Value: TContext);
begin
  if (Context!=Value) then begin
    pageContexts.ActivePageIndex:=Ord(Value);
    DoContextChanged();
  end;
end;

procedure TDemo.DoContextChanged;
begin
  UpdateMenus();
end;
{ When the selected tab has changed by itself, have the page control
  notify us, too }
procedure TDemo.ContextTabSelected(Sender: TObject);
begin
  DoContextChanged;
end;

You can build an elaborate system of bits of application state where
every change updates exactly the GUI bits that need it. If you do this
well, application state decomposes along a tree and every internal node
notifies its children of the change, and leaf nodes update the GUI. The
problems start when the tree becomes a graph and things start getting
updated twice.

> SetContext changes the state of my menu items writ large, disabling
> and hiding some, and the focus/state events of certain things within
> will also change them as well, as when a keystroke event creates a
> Changed (dirty) state for an editor and enables a previously disabled
> Save menu item.

The Dirty flag is indeed one of those parts into which application state
as a whole decomposes. Other things I've seen are a ReadOnly flag, a
Selection object, what you call the Context, a Document object, flags
for visibility of toolbar, statusbar, debug pane, contents of titlebar,
structural view, contents view, statusbar, etcetera.

>> The usual approach is extensive enrolling, in the form of every
>> menu item pointing directly to DoNew, DoOpen, DoSave, etcetera.
>
> I'm not sure what "enrolling" means. The remainder, I assume, means
> the the New menu item has in it's onClick even DoNew, etc.

Curses. _Un_rolling. Yes, that's exactly what it means.

>> Note that you _do_ have all these procedures and you _do_ have a
>> place to store the pointers (TMenuItem.OnClick).
>
> Sure. I did the obvious at first, placing the menu control on the
> form, adding submenu items one at a time with names like
> mnuMainFileSaveAs, setting the caption, placing DoFileSaveAs in
> the OnClick event (in this case). I found that by the time my menu
> had grown to 40 items, the whole process of managing them this way
> became a burden. Enabling and disabling clusters of these by name
> became more ponderous as things got larger.

How is

EnableMenuItems
 ([mnuFileNew,
   mnuFileOpen,
   mnuFileSave,
   mnuFileSaveAs
  ],
  True
 );

more cumbersome than

EnableMenuItemsByName
 (MenuItems,
  ['New',
   'Open',
   'Save',
   'SaveAs'
  ],
  True
 );

?

> Generating the whole menu at run time from a master list feels MUCH
> easier. As items are generated each is set with
>
> MenuItems[i].Caption := aStr;
> MenuItems[i].onClick := EventHandlers.menuOnClickEvent;

Well, as I said you have a procedure for every menu item anyway. All
you've done is route dispatching through a single case statement that
has to identify the menu items first.

Groetjes,
Maarten Wiltink



Relevant Pages

  • Re: Query combo box on tab question
    ... the fact that a control is positioned on a tab control makes no ... > have a name field do I use the caption name? ... > name of form tab name Combo box where selection is made. ...
    (microsoft.public.access.macros)
  • Re: java component focus lost event and tabbedPane design problem
    ... >> doing whatever will not prevent the change of tab if the selection is ... does not change the selectedIndex if the current focus owner is a child ... has an InputVerifier and objects to the ...
    (comp.lang.java.programmer)
  • [ANN] Intelli-Dolphin 1.1 - yet more fun for Christmas
    ... So here's another bulge in your Christmas stocking - Intelli-Dolphin ... This extension allows the tab key to easily jump to the next compiler ... line, next to a tab, or have a muliple selection). ... sequence, if you are running Unit Tests, or using the Method Explorer, ...
    (comp.lang.smalltalk.dolphin)
  • Re: Excel 2007 - cursor down & left after entry - How?
    ... "Roger Govier" wrote: ... To the OP, are you sure you are using the TAB key, not the right ... I finally found the options and the selection is "Down" after ...
    (microsoft.public.excel.setup)
  • Re: Moving the cursor (insertion point) in an input (text) field
    ... which will make the cursor to point at the ... end of selection. ... starting and ending sequence. ... will enter the starting sequence, and when they tab to the next field, ...
    (comp.lang.javascript)