Opening Doors: Getting Inside the IDE
By: Allen Bauer
Abstract: Allen Bauer, architect of the Open Tools API for Delphi and C++ Builder, explains docking in the IDE in his third article on OTA
Opening Doors |
|
Getting inside the IDE
by Allen Bauer, Staff Engineer/Delphi&C++Builder R&D Manager |
The recap... |
|
OK, OK.. Before you say it... Yes I
know it has been a while since my last article. I've also received
several e-mails asking when the next article will be
available. If you have been following the flurry of Borland
announcements and press-releases, you're undoubtedly aware of several key
events. Well needless to say, interesting things are happening
here at Borland. The Kylix project is alive and kicking and also
absorbing a large portion of my time (err.... almost all of my
time). Allow me to recap my previous articles.
My first article
was an introduction to the column; an explanation of the philosophy and
design goals of the Open Tools API. The second article
showed how to create a simple IDE Add-in using the OTA. Well,
since I was unable to present the articles I had intended for January
and February, I've decided to leap ahead and go straight to showing how
to create a form that will be able to dock with the rest of the IDE.
|
Mining for Gold |
|
First of all, since this information is
based on some of the true internal pieces of the IDE, it is subject to
change in future releases. In order to properly work within the
IDE as a "first-class" dockable form, one must use one of the
different ancestor dockable forms that the IDE itself uses. The
Delphi IDE is written using Delphi itself, so this process is, in
theory, easy. In past releases of Delphi (or C++Builder), these
forms were buried within the Delphi32.exe (or BCB.exe) or the
CorIdexx.bpl package. Technically, starting with Delphi 4/C++Builder
4, this was possible, however not easily because there was no .dcp file
shipped with the product which allowed one to link with those internal
forms. Starting with Delphi/C++Builder 5, those forms have now
moved to a new hybrid design-time/IDE package, DsnIdexx.bpl. We definitely
ship the .dcp file that corresponds to this package.
Well, what good does just having the .dcp file
do? By itself, not much. However, if you happen to know with
what unit to link and what the declarations for that unit are, then the
fun can start. So in order to get the ball rolling, hop over to
CodeCentral and grab a little zip file I put there that has the files
you'll need. It contains the stubbed out interfaces and .dfm
files for the files used in both docking and desktop save/restore
logic. These files are in raw form (i.e.. I haven't changed
the interfaces) and only have the implementation stubbed out because
these files are only for information and for use while designing your
IDE Add-in. (Oh all right... I did leave a few gems in
there for you to explore... remember, this is actual code used within
the IDE so I cannot guarantee the static nature of those files from
release to release, even point releases). You'll be compiling your Add-in using packages and
specifically using DsnIdexx.dcp where the real implementation
is. These files simply fool the IDE into allowing you to use its Visual Form Inheritance feature.
Another thing to point out is that this is done without
the use of the cleanly sanitized interface based OTA. This is
bypassing any inherent safe-guards that the OTA provides. Once a
dockable form is active, the functionality that form provides will
probably continue use the formal OTA interfaces. While, property
or component editors can certainly create forms that can be docked, I
would not recommend doing this simply because of the ephemeral nature of
property/component editors.
OK. Now that I've dispensed with the disclaimers
and the slight overview, lets dive in and make some noise! |
Sittin' by the dock of the bay... |
|
To get started, open the DummyDockProj.dpr
(you did get the files I mentioned above from CodeCentral, didn't
you?) in Delphi 5. This project should be included in the same
project group as the project on which you're working. Create your
working project by right-clicking on the project group and selecting
"Add New Project..." Next select the Package item in the
dialog.

When you want to create a descendent form from, say
TDockableForm, activate the DummyDockProj, select the File|New...
dialog, then select the DummyDockProj tab, and finally select
DockableForm. This will create a new form unit that inherits from
TDockableForm only in the DummyDockProj. This is not where you
want it, so save and name this new unit and form to, say, TestDock.pas
and TIDETestDockForm, respectively. activate the package project
you just added to the project group above and select Project|Add to
Project... Browse and select the unit. You can remove now the unit
from the DummyDockProj project if you wish. Select the
package manager for the package and right-click on the Requires tree
node. Select Add.., browse for and select the DsnIDE50.dcp file
(should be in Program FilesBorlandDelphi5Lib). This tells the
compiler where to get the real ancestor form units.
What all this amounts to is that you're taking
advantage of how the IDE finds the ancestor form when instantiating a
descendant form. It first looks in the active project, and if it
isn't in there, then it searches all the currently open projects.
So, now you have a form that descends from an internal IDE form!
You can start adding controls to this form just as you would any other
form!
|
Itchin' to see it work! |
|
By now you are ready to see what this all
means, so let's do it! On the package project, select the options
dialog. Now set it to Design time only and enter a description of
your package, press OK. In order to get your form to show, it
needs to be instantiated at the right time. The standard Delphi
design-time Register1 procedure is the ticket. Add the following to your
Form unit:
procedure Register;
implementation {This line should already be there}
procedure Register;
begin
if IDETestDockForm = nil then
IDETestDockForm :=
TIDETestDockForm.Create(Application);
IDETestDockForm.Show;
end;
|
1I
have heard many different speculations as to the reason why the Register
procedure is case-sensitive. Reasons range from compatibility with
C++Builder to just a bug. The actual reason is that with Win32,
all exports from a DLL are case-sensitive. When Delphi loads a
design-time package it looks in a special compiler generated resource in
the package that describes its contents. This resource lists the
unit names within the package. The IDE then scans through this
list creating a string containing the unit name and the Register
procedure then does a GetProcAddress call on that
string. If it locates that entry-point it is called. Sure,
we could try all possible combinations, but that would be 256
combinations checked for each unit in that package!
|
|
Now we need to make sure that the form is destroyed
when the package is unloaded (like when we rebuild it, is manually
removed/unloaded from the Install Packages dialog, or the IDE is
shutting down). Add this to the end of the unit:
initialization
finalization
IDETestDockForm.Free;
end.
As mentioned in my previous article, this is where
the power of Delphi and packages shines. Press the compile button
on the package manager window to make sure it compiles. If all
goes well, there should be no errors. Now, for the real
test; press the install button.
As long as the IDE didn't explode on you because of a code error, you
should now see a blank form where the design form is. This form is
now live and dockable. Try it. Drag the form around to any
of the various dock sites available within the IDE. If you are one
of those daring souls out there that dabble a little with components and
property editors, the Open Tools API, or generally like to browse
around see what's inside the IDE, are probably already thinking ofthe
things you could do with this! Well, let's try something else..
|
Becoming a First Class Citizen |
|
Suppose you wanted a form that
looks and acts just like the project manager or the package manager with
respect to the toolbar and showing/hiding the text labels on the
buttons. Luckily, that is already available. Instead
of descending from TDockableForm, try descending from
TDockableToolbarForm. This will give you a form with a toolbar,
splitter and all the logic to resize any toolbuttons on the toolbar and
showing/hiding the text labels. The behavior should be identical
to the project manager and the package managers. Also note that
these forms are now first-class IDE citizens! As far as the IDE is
concerned, they are just another built-in IDE form.
Take a few moments and look over the interface
sections on the units in the DummyDockProj. You'll find such gems
as desktop saving/restoring, access to controlling the copy/copy/paste
menus and receiving their actions, and so on. Some of these things
I'll try and cover in future articles and some things I'll probably just
ignore for now... (remember these are internal IDE units and
they're rather raw, so many items are not for general consumption.. you
are, however, free to experiment and poke around little).
|
And That Concludes Our Show... |
|
So far I've shown you how to use
"mock-up" units to fool the IDE into letting you inherit from
forms to which you don't have the full source. I showed how
to use these units/forms to create and install into the IDE a first-class
dockable form. When we revisit this topic again, I'll try and cover such
items as desktop saving/restoring, becoming a docking host, and having
the IDE instantiate your singleton2 form from the desktop
file. |
2A
singleton form is a form whereby one and only one instance may
exist. In the IDE the Project Manager, Object Inspector,
Breakpoints View are all examples of singleton forms. |
Where to now? |
|
I'm sure I've left you with
probably more questions than answers by this point, but that is what a
good cliffhanger is all about! Also, I'll probably jump back to my
planned articles for the next installment and cover one of the newest
areas of the Open Tools API, the dynamic editor keybinding
interfaces. In preparation, you should download the wizard and
components I wrote for my presentation at the 1999 BorCon. Look
them over and get a feel for what they do and how all the keybinding
stuff fits together.
Another future article will cover some of the debugger
APIs. These allow you gain access to a lot of the internals of
IDE's integrated debugger. Things like process control, expression
evaluation, breakpoints, and module lists. Virtually all the IDE's
built-in debugger views are implemented on very similar interfaces as
the ones exposed by the OTA.
That's all for this installment, but be sure to visit Borland's
community site often for news, articles, patches and more.
Also, be sure to use the threaded comments and post your thoughts and
suggestions. I will endeavor to read them all and respond where
appropriate. However, I usually will not answer direct e-mail.
|
|
Server Response from: BDN9A
|
|