[+] indicates a continued line
standard disclaimer: some mail servers eat underscores.
Intro
-----
A long time ago, I wanted to include functions from the DragManager in one
of my applications. I downloaded the docs and immedaitely balked. At that
time Mel Patrick came to my rescue and hand-holded me through _his_ code
until I got things together. Thanks Mel.
Staz also released his PG/FB filter around the same time and for a while it
seemed as if all FB apps were going to get these wonderful functionnalities.
For some reason this didn't come about, which is strange as we all use and
appreciate this in the apps that we use. I will agree that it may not be
easy sailing. However it is part of the Mac experience [and it is carried
over in Carbon] so we should be using it.
This is thus the first part of a six-part series destined to walk you
through adding DragManager functionnalities to your FB apps. I will try and
get others to reveiw this with a view of verifying that it is Appearance
Manager compliant and so that a finalised and corrected version can appear
in the docs for that Runtime, although I will try and keep things backward
compatible so that if you use the Standard Runtime you can still do so.
A few words of warning: if you program in PG then I would say that the PG
DND filter is the way to go rather than taking the macramé path that I will
follow. Even in FB, if your needs are limited to 'pict's and 'text', then
you should follow the PG DND filter path also, Staz has designed it so that
it autoloads in FB Standard Runtime [although the docs that were delivered
with the filter are not on the FB3 cd - or if they are they are so cleverly
hidden that even in 5 releases I still haven't found them!], this can mean
that you may have problems knowing how to use the available functions. Staz?
So who is this for? First of all, for anyone who is curious enough to want
to know what is going on behind the scenes. Secondly, the DragManager is not
limited to text and picts and you may need to implement this feature for
proprietary data. In this case you're out on your own... No more! Finally,
by understanding how the DragManager works and is implemented, you can find
your way around Staz's code more easily and customise it to your needs - for
example, it you just need text you can cut all references to images and slim
your code a little.
On a personal level, I am currently including DragManager support in
'LochNess' ready for the beta. These postings will parallel that
development. And yes, I am implementing drags for a proprietary format. So I
thought to share my fumblings.
Part One
--------
In order to follow the different episodes you will need the revised
DragManager Include file that alain has prepared by translating all of the
latest Apple Header. This file is called "Tlbx DragMgr.Incl" and contains
the following mention in line 16 of the header:
"Adapted for FB^3 - Alain { 17/08/01 }" [alain, where is this file
available?]
The aim of the different episodes will be to create a single generic include
file that you then adapt to your needs.
Before we start, you should remind yourself about the DragManager, its use
and functions. You can do worse than download the "DragManager Programmer's
Guide" and the "Drag and Drop HIGuidelines" from Apple's site [urls provided
at the end], and at least read the presentation of the first.
Simply put, the DragManager extends the capacity inherent in the Finder's
desktop metaphor giving the ability to 'drag' discreet GUI elements, rather
than executing this function through the means of a command-line interface.
We think nothing of 'copying' a file to another drive by dragging the icon
to the disk, nor of launching a file in an application by dropping its icon
on that of an application. So much so that we forget that these are visual
metaphors and not the real thing when talking about 'dropping a doc on the
app.'
The DragManager, which first appeared in System 7 Pro, and in later systems
through a series of extensions, before becoming assimilated into the System,
extends this metaphore to running applications. It allows you to drag
content, an icon or a clipping, to the open window of your application.
However, unlike the Finder behaviour, what happens afterwards depends on
you, and can get complex.
The first thing to do is to determine if theDragManager is present. If you
determine elsewhere in your application the minimum System requirements,
System 8.5 for example, you may see this step as pretty useless as the
DragManager is embedded in the System more tightly than IE in Windows 98.
Plese bear with me.
You know by now that Apple has provided an elegant mechanism called the
Gestalt Manager that you can call in order to know if certain System
features are available. So we will use that here. Code follows.*
clear local
local fn isDragManagerAvailable
dim @ myReturn as ptr
'
long if fn gestalt( _gestaltDragMgrAttr,myReturn) =_noErr
myReturn = ( myReturn and( 1 << _gestaltDragMgrPresent))
end if
end fn = myreturn
This should be called when initialising your application and the result
stored and tested before invoking any DragManager routines. Let us examine
what it does:
"dim @ myReturn as ptr"
Here we are preparing a variable that will hold the result that the
GestaltManager will send us. Quite a few Toolbox calls can't accept a
register-based value so we have to use the '@' to force it to be memory
based. it isn't really important to type it as a pointer, any form of
'neutral' long value [ie. 4 bytes] will do, but I believe that it is
worthwhile cultivating good habits and to stick to them.
"fn gestalt( _gestaltDragMgrAttr,myReturn)"
This is the call to the GestaltManager. "_gestaltDragMgrAttr" is what we are
asking it to tell us about; this is a predefined constant from the Header
files. As with quite a few calls, the answer is returned in the passed
variable, in this case 'myReturn', but the call also returns an error code
that we need to check.
"long if fn gestalt( _gestaltDragMgrAttr,myReturn) =_noErr"
Here we are checking the value of the returned error code, and effectively
blocking access to our IF/END IF code, if the returned value is anything but
"_noErr". Even though _noErr has a value of zero, it is much clearer to use
the constant as we can understand the logic at work here even 6 years later.
"myReturn = ( myReturn and( 1 << _gestaltDragMgrPresent))"
If we get here we are sure that all has gone OK [so far], but what is this
code? At this point, on my machine, myReturn has a value of &h3F and this
certainly doesn't correspond to a simple '_true', '_pTrue' or '_zTrue'
value. In fact the GestaltManager has set certain bits of the returned value
according to what features of the DragManager are available. For the moment
we are only concerned with one of those bits; the one indicating that the
DragManager is present or not. Apple has provided a constant
'_gestaltDragMgrPresent' for this, and its value is zero. Hold on! you may
say, I thought that zero meant nothing, zilch, noErr and whatever. Yes, but
here we are looking at bit flags, so we need to see if bit zero [the one
farthest to the right] is set. So we need a way to translate
_gestaltDragMgrPresent to a means of detecting this. That is the purpose of
'(1 << _gestaltDragMgrPresent)'. '<<' is here a bit-shifting device; it is
saying: shift to the left the value of '1' and do this by the number of
moves corresponding to the value of _gestaltDragMgrPresent [more technically
'x << n' means multiply x by 2^n]. As we have already seen that
_gestaltDragMgrPresent equals zero, all this does is create a value of one.
So (1 << _gestaltDragMgrPresent) equals one. In the expression 'x and n',
the 'and' is a boolean operator meaning "return 'true' if a bit [or bits] in
x are the same as the corresponding bit [or bits] in n, else return
'false'". This boils down to, on my machine '&h3F and 1', and by quickly
counting on my fingers I find that the result is indeed 'true'. [&h3F -
hexadecimal notation - for decimal 63 or binary &x0000001111111111, - and
'&x0000000000000001' = '&x0000000000000001'.]
Instead of creating and using a new variable we reuse myReturn, for which we
no longer have a use, to collect the result, ready to return it to our
calling function.
A quick note: you will often see this function called 'fn DNDAvailable', I
have seized the occasion to correct this. 'Drag and Drop' [DND] is the
functionnality provided in the FInder to drop an icon on another, and by
extension the functionnality that the DragManager brings to your
application. However the implementation is your responsability, so at this
point 'Drag and Drop' is not available, only the DragManager.
Let's try this. I can't include a project file, but you should be able to
follow things from this:
INCLUDE "Tlbx DragMgr.Incl"
'--- myDragManager.INCL -----------------------
'----------------------------------------------
' is DM available - return 'true' or 'false'
'----------------------------------------------
clear local
local fn isDragManagerAvailable
dim @ myReturn as ptr
'
long if fn gestalt( _gestaltDragMgrAttr,myReturn) =_noErr
myReturn = ( myReturn and( 1 << _gestaltDragMgrPresent))
end if
end fn = myReturn
'----------------------------------------------
'--- myMain.MAIN ------------------------------
clear local
local fn inits
long if( fn isDragManagerAvailable)
print "drag manager available"
xelse
print "no drag manager present"
end if
end fn
'----------------------------------------------
window 1
fn inits
do
handleevents
until _nil
This is a bit boring and hardly merits all the time that it has taken you to
read up to here, so let's take things a bit further. Rather than just seeing
if the DragManager is there, let's see what it can tell us...
INCLUDE "Tlbx DragMgr.Incl"
'--- myGlobals.GLBL ---------------------------
begin record dmRecord
dim dmPresent as boolean
dim dmFloatingWnd as boolean
dim dmPPCDragLib as boolean
dim dmImageSupport as boolean
dim dmStartInFloat as boolean
dim dmSetDragImage as boolean
end record
'
dim gDMrecord as handle to dmRecord
end globals
'--- myDragManager.INCL -----------------------
'----------------------------------------------
' is DM available - return 'true' or 'false'
'----------------------------------------------
clear local
local fn isDragManagerAvailable( theDMrecord as handle to dmRecord)
dim @ myReturn as ptr
'
long if fn gestalt( _gestaltDragMgrAttr,myReturn) =_noErr
theDMrecord..dmPresent =_zTrue
theDMrecord..dmFloatingWnd = myReturn and _gestaltDragMgrFloatingWind%
theDMrecord..dmPPCDragLib = myReturn and _gestaltPPCDragLibPresent%
theDMrecord..dmImageSupport = myReturn and _gestaltDragMgrHasImageSupport%
theDMrecord..dmStartInFloat = myReturn and
_gestaltCanStartDragInFloatWindow%
theDMrecord..dmSetDragImage = myReturn and _gestaltSetDragImageUpdates%
xelse
theDMrecord..dmPresent =_false
end if
'
end fn
'----------------------------------------------
'--- myMain.MAIN ------------------------------
clear local
local fn inits
'
gDMrecord = fn newhandleclear( sizeof( dmRecord))
'
long if( gDMrecord)
fn isDragManagerAvailable( gDMrecord)
long if( gDMrecord..dmPresent)
print "drag manager present "
if( gDMrecord..dmFloatingWnd) then [+]
print "and supports floating windows "
if( gDMrecord..dmPPCDragLib) then [+]
print "and PPC DragLib is present "
if( gDMrecord..dmImageSupport) then [+]
print "and allows SetDragImage call"
if( gDMrecord..dmStartInFloat) then [+]
print "and supports starting a drag in a floating window"
if( gDMrecord..dmSetDragImage) then [+]
print "and supports drag image updating in SetDragImage"
xelse
print "no drag manager"
end if
end if
'
end fn
'----------------------------------------------
window 1
fn inits
do
handleevents
until _nil
We will now walk through this code and note the changes.
Defining a record to contain the results is a compromise between compacity
and legibility. It would of course have been possible to just copy the
result [myResult] passed by the Gestalt call and check the bits on that
before calling different parts, but this solution provides a clearer more
readable solution with booleans [bytes] bearing the smallest variables that
we have available.
In the isDragManagerAvailable function instead of returning the previous
'yes/no' value, we now store the results for later. You shouldn't blindly
follow this. Just test for those features that you intend to use. I will not
be dealing with these in this series as they are beyond basic dragging, the
current objective, but alain has put together a demo of image dragging with
a translucide preview I believe.
The isDragManagerAvailable function also demonstrates an alternative means
of getting a bit-flag result; by adding '%' to the end of a constant you
transform it to a bit mask for that value. Functionally it is equivalent to
the '(1 << _constant)' manipulation that we saw earlier, and in my opinion,
a little easier to read.
In the inits function we create the handle for the record with the toolbox
function 'newhandleclear'. This has the advantage over the more usual
'newhandle' of setting all the contents to zero [false], which means that we
don't have to provide that result in the isDragManagerAvailable function; it
is there by default.
This is the end of Part One. In Part Two we will examine the situation "OK,
I've got a drag... what do I do with it?" Until then, read over the docs,
and if you see any faults or problems in this, please let me know.
:-j
----
* I will use my standard coding conventions in all code. While these are
slightly different from what you see in the Apple sample code there should
be no major difficulty in understanding both. I use my conventions for 2
purposes;
- they allow me to determine where my variables come from;
- the consistant use of spaces allows me to do search and replaces with more
precision.
I will also use FB^3 definitions for variables as alain has persuaded me
that this is 'a good thing'. It is also assumed that you have prepared FB
with the following set to true in the prefs:
- use only DIMensioned variables
- do not allow re-DIMensioned variables
- use register based variables for speed
Unless otherwise stated I am using the Standard Runtime.
----
reference:
<http://developer.apple.com/techpubs/macos8/pdf/DragMgrProgrammersGuide.pdf>
<http://developer.apple.com/techpubs/macos8/pdf/DragDropHIGuidelines.pdf>**
<http://developer.apple.com/technotes/tn/pdf/tn1043.pdf>
<http://developer.apple.com/technotes/tn/pdf/tn1085.pdf>
** Apple notes that this document is not readable in acrobat 4 and that you
should load acrobat reader version 3 from the adobe website
<http://www.adobe.com/>.
if you wish to download these I would advise going to the following page:
<http://developer.apple.com/techpubs/macos8/InterproCom/ [+]
DragManager/dragmanager.html>
and then doing a control-click on the links to download them to your hard
drive, this avoids them being opened in your browser.
[END OF PART ONE]