Re: Here's a "CASE" statement with strings... sorta.
From: Maarten Wiltink (maarten_at_kittensandcats.net)
Date: 01/15/05
- Next message: Harald Knautzberg: "Re: array of TEdit as a constant"
- Previous message: J French: "Re: Best way to deal with long loops?"
- In reply to: Raptor: "Re: Here's a "CASE" statement with strings... sorta."
- Next in thread: Raptor: "Re: Here's a "CASE" statement with strings... sorta."
- Reply: Raptor: "Re: Here's a "CASE" statement with strings... sorta."
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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
- Next message: Harald Knautzberg: "Re: array of TEdit as a constant"
- Previous message: J French: "Re: Best way to deal with long loops?"
- In reply to: Raptor: "Re: Here's a "CASE" statement with strings... sorta."
- Next in thread: Raptor: "Re: Here's a "CASE" statement with strings... sorta."
- Reply: Raptor: "Re: Here's a "CASE" statement with strings... sorta."
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|
|