In addition to defining custom components, you can define and register new standard actions, which will be made available in the Action List component's Action Editor. Creating new actions is not complex. You have to inherit from the TAction class and override some of the methods of the base class.
You must override three methods:
The HandlesTarget function returns whether the action object wants to handle the operation for the current target, which is by default the control with the focus.
The UpdateTarget procedure can set the user interface of the controls connected with the action, eventually disabling the action if the operation is currently not available.
You can implement the ExecuteTarget method to determine the code to execute, so that the user can select the action and doesn't have to implement it.
To show you this approach in practice, I've implemented the three cut, copy, and paste actions for a list box, in a way similar to what VCL does for an edit box (although I've simplified the code a little). I've written a base class, which inherits from the generic TListControlAction class of the ExtActns unit. This base class, TMdCustomListAction, adds some common code shared by all the specific actions and publishes a few action properties. The three derived classes have their own ExecuteTarget code, plus little more. Here are the four classes:
type TMdCustomListAction = class (TListControlAction) protected function TargetList (Target: TObject): TCustomListBox; function GetControl (Target: TObject): TCustomListControl; public procedure UpdateTarget (Target: TObject); override; published property Caption; property Enabled; property HelpContext; property Hint; property ImageIndex; property ListControl; property ShortCut; property SecondaryShortCuts; property Visible; property OnHint; end; TMdListCutAction = class (TMdCustomListAction) public procedure ExecuteTarget(Target: TObject); override; end; TMdListCopyAction = class (TMdCustomListAction) public procedure ExecuteTarget(Target: TObject); override; end; TMdListPasteAction = class (TMdCustomListAction) public procedure UpdateTarget (Target: TObject); override; procedure ExecuteTarget (Target: TObject); override; end;
The HandlesTarget method, one of the three key methods of action classes, is provided by the TListControlAction class with this code:
function TListControlAction.HandlesTarget(Target: TObject): Boolean; begin Result := ((ListControl <> nil) or (ListControl = nil) and (Target is TCustomListControl)) and TCustomListControl(Target).Focused; end;
The UpdateTarget method has two different implementations. The default implementation is provided by the base class and used by the copy and cut actions. These actions are enabled only if the target list box has at least one item and an item is currently selected. The status of the paste action depends on the Clipboard status:
procedure TMdCustomListAction.UpdateTarget (Target: TObject); begin Enabled := (TargetList (Target).Items.Count > 0) and (TargetList (Target).ItemIndex >= 0); end; function TMdCustomListAction.TargetList (Target: TObject): TCustomListBox; begin Result := GetControl (Target) as TCustomListBox; end; function TMdCustomListAction.GetControl(Target: TObject): TCustomListControl; begin Result := Target as TCustomListControl; end; procedure TMdListPasteAction.UpdateTarget (Target: TObject); begin Enabled := Clipboard.HasFormat (CF_TEXT); end;
The TargetList function uses the TListControlAction class's GetControl function, which returns either the list box connected to the action at design time or the target control (the list box control with the input focus).
Finally, the three ExecuteTarget methods perform the corresponding actions on the target list box:
procedure TMdListCopyAction.ExecuteTarget (Target: TObject); begin with TargetList (Target) do Clipboard.AsText := Items [ItemIndex]; end; procedure TMdListCutAction.ExecuteTarget(Target: TObject); begin with TargetList (Target) do begin Clipboard.AsText := Items [ItemIndex]; Items.Delete (ItemIndex); end; end; procedure TMdListPasteAction.ExecuteTarget(Target: TObject); begin (TargetList (Target)).Items.Add (Clipboard.AsText); end;
Once you've written this code in a unit and added it to a package (in this case, the MdPack package), the final step is to register the new custom actions in a given category. This category is indicated as the first parameter of the RegisterActions procedure; the second parameter is the list of action classes to register:
procedure Register; begin RegisterActions ('List', [TMdListCutAction, TMdListCopyAction, TMdListPasteAction], nil); end;
To test the use of these three custom actions, I've written the ListTest example (included with the source code for this chapter). This program has two list boxes plus a toolbar that contains three buttons connected to the three custom actions and an edit box for entering new values. The program allows a user to cut, copy, and paste list box items. Nothing special, you might think—but the strange fact is that the program has no code!
To set up an image for an action (and to define default property values in general) you need to use the third parameter of the RegisterActions procedure, which is a data module hosting the image list and an action list with the predefined values. As you have to register the actions before you can set up such a data module, you'll need a double registration while developing these actions. This issue is quite complex so I won't cover it here, but a detailed description can be found on http://www.blong.com/Conferences/BorCon2002/Actions/2110.htm in the sections "Registering Standard Actions" and "Standard Actions And Data Modules."