In my last Delphi article, I programmed
a configurable application launchpad. This demonstrated
how to create and delete controls on the fly and give
them OnClick event-handlers. I shall now develop this
further in order to create program launchers with a more
compact user interface based on combo boxes and list
views. By the time you’ve worked through this month’s
projects you will know how to display graphics and icons
in lists and combo boxes. You will also have all the
techniques needed for creating a completely customised
program launcher.
Load up the Lpad.dpr project from this month’s
source code. This is based on the application launchpad
I developed previously. The original project took the
form of a button bar in which a new file-launching button
was created whenever the user dragged a file name or
icon from the Windows Explorer or Desktop onto the launchpad.
This new version substitutes a TComboBoxEx for the buttons.
This has the advantage that it takes up much less screen
space.
Countdown to Launch...
My
new launchpad is seen here above the Windows Explorer.
To add an application (or a file such as one of
the JPG graphics shown here) I drag it right out
of Explorer and drop it onto the launchpad. The
file name is then added to the combo box.
Here you can see I’ve added a number of different
files. Note that each file is preceded by its associated
icon. I can click the combo box to display the
drop-down list. The selected file name will then
appear in the ‘text field’ of the combo
box
Finally, to open the
file I click the Launch button. If the file is
an EXE it will be launched. If it is a data file
(such as this JPG graphic) it will be launched
into its associated application which, in this
case, happens to be the Microsoft Office Picture
Manager
|
The TForm1.AddFile() method is called by the RespondToMessage()
method when a new item is dropped onto the form. The
AddFile() method adds a small icon to an ImageList component
using ImageList1.AddIcon() which returns the index of
the new icon in the image list. My code then adds the
file name and icon to the TComboBoxEx control named CBox.
Delphi’s Help shows that AddItem() takes these
arguments:
function AddItem(const Caption: String; const ImageIndex,
SelectedImageIndex, OverlayImageIndex, Indent: Integer;
Data: Pointer): TComboExItem;
Here, Caption is the text to display for the new item
in the combo; ImageIndex is the index into the ImageList
of the image to display next to the text; SelectedImageIndex is the image to display when the item is selected; OverlayImageIndex is the optional index of a monochrome image which can
overlay the displayed image to implement transparency;
Indent indicates the indentation level of the item and
it is –1 for no indentation; Data is a pointer
to some arbitrary piece of data that you might want to
associate with this item or nil for no data item. This
is my code:
CBox.ItemsEx.AddItem( ExtractFileName(fname), IconIndex,
IconIndex, 0,
-1, ItemList.Objects[ItemList.Add(fname)] );
Here the final Data argument adds the pathname to the
file as an Object in the TStringList object named ItemList.
You will find this declared in the private part of the
form definition. To display the most recently added item
in edit box of the TComboBoxEx named CBox I set its ItemIndex to its last item (Count-1):
CBox.ItemIndex := CBox.Items.Count-1;
In order to delete a selected item, I delete the object
(the string path) from the non-visual ItemList and from
the ItemsEx property of CBox. You will find the relevant
code in our DeleteMIClick() method.
Launch Box
Naturally, this being an application launchpad, it needs
to have some event that starts the file launching process.
In my earlier project I used the OnClick event-handlers
of a set of buttons to do this. In a ComboBox it might
seem that simply selecting an item should be sufficient
to launch it in much the same way that an application
is run when selected from the Windows Start Menu. In
order to do that, you just need to add this single line
of code to the CBoxChange() event-handling method:
LaunchFile(ItemList.Strings[CBox.ItemIndex]);
Here, LaunchFile() is the name of a method which you
will find earlier in my code. The only trouble with launching
an application just by selecting it is that sometimes
you might search for an application in the dropdown list
but then change your mind and decide that you don’t
want to run it after all. For that reason, I’ve
provided a toggle that lets the user decide whether or
not to run an application immediately on selecting its
name. The toggle takes the form of a check-mark against
the ‘Launch On Select’ menu item on the
File menu. The user can select this menu item to check
or uncheck it. This toggle is implemented in the LaunchonSelectMIClick()
method:
LaunchonSelectMI.Checked := not(LaunchonSelectMI.Checked);
Here the use of the not operator returns the opposite
of the LaunchonSelectMI menu item’s current checked
state. If it is checked, then False is returned. If it
is unchecked, True is returned. This opposite value is
then assigned to LaunchonSelectMI.Checked, thereby reversing
its former state.
|
Use the File menu to toggle ‘Launch On Select’.
This menu also lets you save the lauch paths of the
icons in your ‘IconBar’ list or restore
a previously saved list. |
If you choose not to launch an application upon selection,
you can use the Launch button to do it. I’ve assigned
a popup menu to the form and this can be use to
delete or display information on the selected file. I’ve
also added a ‘Show All Files’ item to the
File menu to display a list of all the file names and
paths currently loaded. This is mainly useful as a debugging
aid to check file paths are added and deleted as and
when you expect them to be.
On The List
There are other components that might also be used to
implement a launchpad. For example, if you don’t
care for the drop-down list of a combo box, you could
display a list in a ListView. Load up the Lpad2.dpr project
which uses a List View. If you run it you will see that
the list view displays the file names and precedes each
with the appropriate icon.
My
second launchpad uses a ListView. This has the advantage
of letting you see numerous files without having to navigate
through items in a dropdown list.
Remarkably little code is needed to do this. I simply
created a new TListItem object for each item in the list,
added this object to the ListView control, set its caption
to the file name and its icon to the appropriate index
in the ImageList. Here is the code as it appears in my
AddFile() method:
NewItem := ListView.Items.Add;
NewItem.Caption := ExtractFileName(fname);
NewItem.ImageIndex
:= IconIndex;
To launch an item in the list when it is double-clicked
I obtain the ItemIndex of the ListView to index into
ItemList, a TStringList of paths, as before. This code
is found in the ListViewDblClick() method. Deleting an
item is done in DeleteMIClick() by first deleting the
item from the non-visual ItemList and then deleting the
selected item from the ListView.
By default, the ViewStyle of my ListView is vsList.
This displays items in a fixed order with the icon before
the caption. However, this ViewStyle has one unfortunate
side-effect: when a new item is added the column width
automatically resizes to fit the new caption. If this
is shorter than previous captions, they will appear to
be truncated. A simple way around this is to toggle ViewStyles
to vsSmallIcon and back to vsList when a new item is
added. This sizes the column to the widest caption
in the list. To prevent any repaints, which might cause
a flickering effect, I prevent updating of the view by
placing the ‘view toggle’ between the ListView.Items.BeginUpdate
and ListView.Items.EndUpdate methods. You will find this
code in TForm1.AddFile().
I Want To Be Selected!
Finally we need to deal with the selection in the ListView
to ensure that we don’t try to delete an item or
launch an application when no item is selected. In the
DeleteMIClick() method I always select the item at the
same index as the deleted item or at the previous index
if the deleted item was the last entry in the list. If
there are no items left after the deletion, I do not
attempt to select anything but, instead, I display the
string ‘Nothing selected’ in a label above
the ListView. If the ListView was empty when the Delete
menu item was selected, then the ItemIndex is –1
and so I exit the method without doing anything.
As
an added bonus you can right-click to pop up this menu
which lets you delete the selected item or display some
basic file information
We also need to set the Selection when we reload a list
of items from disk. This is done in ReloadItems() with
this code which places the selection on the first item
at index 0:
if ListView.Items.Count > 0 then
ListView.Items[0].Selected
:= true;
There are, of course, many other components which could
be used as the basis of a launchpad. For example, you
could use MainMenu or PopupMenu controls. While I’ve
concentrated on creating a user-configurable launchpad
in these two articles, there are, in fact, many other
ways in which you could use the basic techniques which
I’ve described. For example, if you don’t
care for the Windows Start Menu, you could create your
own, more civilized alternative. Maybe, for example,
you would prefer to have your applications arranged as
icons in overlapping windows - rather in the manner of
the old Windows 3.1 Program Manager. I shall explain
how to do this in a new series starting next month…
(See also Part One of this series)
December 2005 |