For an application to survive in the GUI world today, it must support at least the ever-present "Cut, Copy, and Paste" functions to which users have grown so attached. GEOS makes this very simple through the Clipboard and Quick-Transfer mechanisms.
All applications should support and use these mechanisms in order to maintain consistency throughout the system. In addition, these mechanisms provide certain functionality for data transfer that can be easily used.
To understand this chapter fully, you should have a working knowledge of Virtual Memory management as well as some familiarity with the concepts of mouse input. However, the concepts of the Clipboard and quick-transfer mechanisms are simple and may be understood without having read those sections.
1 Overview
1.1 Cut, Copy, and Paste
1.2 Quick-Transfer
2 Transfer Data Structures
2.1 The Transfer VM File Format
2.2 ClipboardItemFormatInfo
2.3 Transfer Data Structures
2.4 Clipboard Item Formats
3 Using The Clipboard
3.1 Registering with the Clipboard
3.2 Managing the Edit Menu
3.3 The GenEditControl
3.4 Handling Cut and Copy
3.5 Handling Paste
3.6 Unregistering with the Clipboard
3.7 Implementing Undo
3.8 Transfer File Information
3.9 Undoing a Clipboard Change
4 Using Quick-Transfer
4.1 Supporting Quick-Transfer
4.2 Quick-Transfer Procedure
4.3 Quick-Transfer Data Structures
4.4 Source Object Responsibility
5 Shutdown Issues
The user understands two ways to cut and paste information: The first is to use the Edit menu and the Cut , Copy , and Paste functions. The second is to use the mouse by selecting and dragging text, graphics, or objects with the quick-copy and quick-move functions. For a full description of how these two operations are accomplished, see the user's guide provided in the Geoworks retail products.
Both these functions use similar data structures; a single flag determines whether the operation is a quick-transfer or a normal Clipboard operation. However, the procedures and rules of usage have significant differences.
You should be familiar with the features and uses of both the Clipboard and the quick-transfer mechanism. If you are, you can skip this section; however, if you are not familiar with the features and rules governing the Edit menu and the quick-transfer operations, you most likely will want to read this section.
Even the simplest, text-using applications provide an Edit menu with the
Cut
,
Copy
, and
Paste
options.
The Edit menu works with a hidden data area called the Clipboard . The Clipboard has no visual representation, and it can contain any format of data from text to graphics strings to custom formats defined by applications. The Clipboard's structure and implementation are completely invisible to the user beyond the functions Cut , Copy , and Paste .
The most common use of the Edit menu is within a word processor or draw program. For example, when a user wants to place a graphic from GeoDraw into GeoWrite, or when he wants to rearrange text in his NotePad, he can use the cut, copy, and paste operations.
When a user selects an item such as the word "puddle," the Cut option becomes enabled. This indicates that the user can remove the selection from the document and it will be placed on the Clipboard (anything already on the Clipboard will be replaced with the selection). When nothing is selected, the Cut option is disabled.
In order to enable the Copy option, the user must select an item, just as with Cut . Copying, however, will not remove the selection from the document--it will simply create a copy of the selection and place it on the Clipboard, removing whatever had previously been on the Clipboard.
Pasting is allowed only when something in a compatible data format is already sitting on the Clipboard. The Paste operation copies what is on the Clipboard into a document at the current insertion point (in the case of text, this is where the cursor was last placed); if something is already selected, the selection will be replaced with the pasted material. If there is nothing on the Clipboard, then there is nothing to paste; therefore, the option is disabled.
The user can copy and move selected items without using the Edit menu; he simply drags the item using the quick-transfer button of his mouse, and the item will be either copied or moved.
There are several rules which govern whether a move or copy is taking place. The user can override these rules with a certain set of keypresses to force either a copy or move. Copy is equivalent to using the Copy and Paste functions in succession, and move is equivalent to using the Cut and Paste functions in succession.
The operation of a quick transfer (move or copy), however, depends on whether the transfer is across documents or internal to a single document. If the transfer is internal, it should become a move. If the transfer occurs across documents, it should become a copy. (Note that, for the purposes of quick-transfer, the term document is used to refer to anything the user will conceptually view as a single data holder--for example, a GeoManager "document" consists of one disk.)
Because not all documents will support a given transfer operation (for example, a text file editor can not receive a GeoDraw graphic), the user is provided with immediate feedback about the type and validity of the transfer.
For example, if a GeoDraw ellipse was selected and a quick-transfer was begun, immediately the mouse pointer would show the transfer as a move operation. As the user moves the mouse outside the GeoDraw document and into another GeoDraw document, the cursor changes to indicate a copy operation. If the user then moves the mouse over a text-only document, a "no operation," or "invalid," cursor would be shown, indicating that the ellipse could not be received.
Some applications may implement an additional feature that provides feedback to the user: modifying the source object as the type of transfer changes. For example, a drawing program may wish to alter the shape or color of an object while it is being quick-moved but leave it normal during a quick-copy or no-operation transfer.
Both the Clipboard and quick-transfer mechanisms use similar structures to accomplish data transfer. These data structures are owned and managed by the GEOS User Interface; however, they are accessible to all geodes in the system through a number of routines and messages.
The Clipboard consists of a VM file known as the Transfer VM File. The quick-transfer mechanism uses the same file for its data transfer. Typically, this is a file designated and managed by the User Interface.
In addition, certain formats of data are supported automatically--GEOS text and graphics strings are special formats that are described below. Geodes can create their own specific formats; for example, GeoManager uses a special format for quick-transfer of files between disks and directories.
The Transfer VM File is a normal VM file, managed by the UI. It contains several components, each of which is accessed through special routines that take care of nearly all the synchronization issues involved with the Clipboard and quick-transfer mechanisms. Because both the Clipboard and the quick-transfer mechanism use this file and its data structures, these structures are detailed here; the following section ( Using The Clipboard ) details how to use them for either the Clipboard or the quick-transfer mechanism.
The Transfer VM File has one header block for the Clipboard transfer item and another for the quick-transfer transfer item. These headers have the same structure, and they contain all the information necessary to work with the file's contents. They contain pointers to and information about each format available for both the Clipboard and the quick-transfer functions. The transfer item's header is shown in ClipboardItemHeader .
Code Display 7-1 ClipboardItemHeader
/* The ClipboardItemHeader structure details what formats, if any, exist in the * Transfer VM File for both the Clipboard and quick-transfer mechanisms.*/
typedef struct {
/* The CIH_owner field is the optr of the object that put
* the current transfer item into the Transfer VM File. */
optr CIH_owner;
/* The CIH_flags field determines whether the transfer item is used by
* the quick transfer mechanism or by the normal Clipboard. If this
* field is equal to the constant CIF_QUICK, then the transfer item
* belongs to the quick transfer mechanism. If it is any other value,
* the transfer item belongs to the normal Clipboard. */
ClipboardItemFlags CIH_flags;
/* The ClipboardItemFlags type has one predefined value:
* CIF_QUICK 0x4000
* If this flag does not apply, use TIF_NORMAL, which is 0x0000. */
/* The CIH_name field is a 33-character buffer that should contain a
* null-terminated character string representing the name of the given
* transfer item. */
ClipboardItemNameBuffer CIH_name;
/* The CIH_formatCount field indicates the number of formats of the
* transfer item currently available. For example, this field would be
* 2 if CIF_TEXT and CIF_GRAPHICS_STRING formats were supported and
* available in the Transfer VM File. */
word CIH_formatCount;
/* The CIH_sourceID field contains information about the source
* document of the transfer. Typically, this will be the optr of the
* parent GenDocument object. */
dword CIH_sourceID;
/* The CIH_formats field is actually an array of ten
* ClipboardFormatInfo structures, each of which contains the
* important details about a specific format of the transfer item. */
FormatArray CIH_formats;
/* This field is reserved and should not be used. */
dword CIH_reserved;
} ClipboardItemHeader ;
/* The FormatArray type is an array of ClipboardItemFormatInfo structures. The * definition of this field is shown here: */
typedef ClipboardItemFormatInfo FormatArray[CLIPBOARD_MAX_FORMATS]; /* CLIPBOARD_MAX_FORMATS is a constant defining the maximum number of * formats allowed for any given transfer item. It is defined as 10. */
The rest of the Transfer VM File consists of VM blocks containing the data that is to be transferred. Each format supported will have its own VM block or series of VM blocks, pointed to by the
ClipboardItemFormatInfo
structure in the item's header.
The Transfer VM File actually contains two transfer items: One for the Clipboard and one for the quick-transfer mechanism (see the figure below). When calling
ClipboardQueryItem()
, the requesting geode must specify which item it wants. See Using The Clipboard
and Using Quick-Transfer
.
This structure contains information about a specific format of the transfer item. The Transfer VM File will support up to ten formats of a given item at once (for both the Clipboard and quick-transfer); each of these formats is stored in its own VM block or VM chain and is represented in the header by a
ClipboardItemFormatInfo
structure in the array
CIH_formats
.
The
ClipboardItemFormatInfo
structure contains other information about the specific format as well as space for two extra words of data. The structure is shown in ClipboardItemFormatInfo
.
Each element in the
CIH_formats
array contains two items: One word represents the manufacturer ID of the geode responsible for the format (useful if a custom format is used within several applications from a single manufacturer), and the other represents the actual format number. To combine these words into a
ClipboardItemFormatID
record, or to extract either word from the record, use the macros shown after ClipboardItemFormatInfo
.
Code Display 7-2 ClipboardItemFormatInfo
/* The ClipboardItemFormatInfo structure contains information about a given * format of the transfer item. */
typedef struct {
/* CIFI_format contains a format ID as well as the
* manufacturer ID of the geode responsible for the format. */
ClipboardItemFormatID CIFI_format;
/* CIFI_extra1 and CIFI_extra2 are extra words provided for * format-specific use. */ word CIFI_extra1; word CIFI_extra2;
/* CIFI_vmChain is a VM handle pointing to the first block in the * specific format. */ VMChain CIFI_vmChain;
/* CIFI_renderer contains the token of the geode that created * the format. */ GeodeToken CIFI_renderer; } ClipboardItemFormatInfo ;
Two structures are used with specific routines when dealing with the transfer mechanisms. The
ClipboardQueryArgs
structure is returned by
ClipboardQueryItem()
, and the
ClipboardRequestArgs
structure is returned by
ClipboardRequestItemFormat()
. Both routines are used during a Paste operation, and both structures are shown in ClipboardQueryArgs and ClipboardRequestArgs
.
Note that the
CQA_header
field is of type
TransferBlockID
. This type is a dword made up of two word-sized components: a VM file handle and a VM block handle. The three macros listed after ClipboardQueryArgs and ClipboardRequestArgs
can be used to create the
TransferBlockID
argument and extract either of the components from the whole.
Code Display 7-3 ClipboardQueryArgs and ClipboardRequestArgs
/* ClipboardQueryArgs is filled by ClipboardQueryItem(), which is called when
* determining whether a transfer item exists. */
typedef struct {
word CQA_numFormats; /* the total number of formats available */
optr CQA_owner; /* the optr of the originating object */
TransferBlockID CQA_header; /* The combined VM file handle and VM block
* handle of the block containing the
* ClipboardItemHeader */
} ClipboardQueryArgs;
/* ClipboardRequestArgs is filled by ClipboardRequestItemFormat(), which is called
* when the application wants to retrieve the current transfer item. */
typedef struct {
VMFileHandle CRA_file; /* The VM file handle of the transfer file */
VMChain CRA_data; /* The handle of the VM chain containing the
* transfer item */
word CRA_extra1; /* an extra word of data */
word CRA_extra2; /* another extra word of data */
} ClipboardRequestArgs;
There are several built-in transfer formats that many GEOS applications may support; each of these types is an enumeration of
ClipboardItemFormat
. Additionally, custom formats can be defined to allow special data structures to be cut, copied, pasted, or quick-transferred without translation into text or graphics strings. The Transfer VM File may contain up to ten formats of a given transfer item.
ClipboardItemFormat
is shown below.
typedef enum /* word */ {
CIF_TEXT,
CIF_GRAPHICS_STRING,
CIF_FILES,
CIF_SPREADSHEET,
CIF_INK,
CIF_GROBJ,
CIF_GEODEX,
CIF_BITMAP,
CIF_SOUND_SYNTH,
CIF_SOUND_SAMPLE
} ClipboardItemFormat;
A transfer item of CIF_TEXT format is headed by a
TextTransferBlockHeader
structure. The text follows this header in the VM chain. A transfer item of CIF_GRAPHICS_STRING format is simply the entire GString stored in the transfer VM file in the VM chain.
Because every format identifier has two components, it is highly unlikely that two different designers will create overlapping custom formats.
The format is defined as a
ClipboardItemFormatID
type, which is a dword composed of two word-sized pieces. The first piece is a constant representing the format ID number (such as CIF_TEXT or CIF_GRAPHICS_STRING). The second piece is a constant representing the Manufacturer ID number of the manufacturer responsible for creating the format.
To create a custom format, simply define these two items as appropriate (your Manufacturer ID should be set already). Then define your format to fit within the structures used by the Clipboard (shown above).
ClipboardQueryItem(), ClipboardRegisterItem(), ClipboardDoneWithItem()
To use the Clipboard, your application must have an Edit menu (or a GenEditControl object) and an object which can implement the Cut, Copy, and Paste operations. This object is often the application's Process object or some other coordinating object. This object must be able to do each of the things in the following list:
MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED
and
MSG_META_CLIPBOARD_NOTIFY_TRANSFER_ITEM_FREED
.
MSG_META_CLIPBOARD_COPY,
MSG_META_CLIPBOARD_CUT
When the user issues a Cut or Copy order, the object must put the proper data into the Clipboard Transfer VM File.
MSG_META_CLIPBOARD_PASTE
When the user issues a Paste order, the object must query the Clipboard to ensure a proper format is available and then copy the information from the Clipboard.Because the Clipboard is constantly in use by many different threads, you must always gain exclusive access to the transfer VM file when you want to use it. After you're done with the transfer file, you should relinquish exclusive access so other threads can continue to use it.
For operations that involve changing the transfer item (cut and copy, for example), you must register your new transfer item with
ClipboardRegisterItem()
, which also allows other threads to use the file.
For operations that involve looking at but not changing the transfer item, you should use
ClipboardQueryItem()
.
Since you have no changes to register, you must later use
ClipboardDoneWithItem()
to give up your exclusive access to the transfer VM file.
Registering with the Clipboard
Unregistering with the Clipboard
ClipboardAddToNotificationList()
Because the Clipboard is a system entity available to all geodes, another thread may change it without your application noticing. The Clipboard therefore provides notification for this case. Because the Clipboard does not know which geodes are interested in its contents, however, applications must register when they first start up.
Calling
ClipboardAddToNotificationList()
allows an application to add an object to the list of those notified of changes to the Clipboard. This routine should be called by whichever object is going to be handling the Cut, Copy, and Paste operations, typically in the object's
MSG_META_INITIALIZE
handler. If the object handling the Clipboard operations is the application's Process object, however, it may call
ClipboardAddToNotificationList()
in its
MSG_GEN_PROCESS_OPEN_APPLICATION
handler.
MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED, ClipboardTestItemFormat()
The Edit menu is simply a normal menu with several standard triggers. Most applications will simply include a GenEditControl object in their UI, add a menu GenInteraction of type GIGT_EDIT_MENU, and leave the Edit menu construction up to them (see The GenEditControl ). Some, however, may want to create their own menu and triggers. A sample of this type of setup is shown in A Sample Edit Menu .
Code Display 7-4 A Sample Edit Menu
/* Other objects, including a GenPrimary as the parent of the GenInteraction, are * left out for clarity. */
/* The GenInteraction is the menu in which the three triggers will appear. */
@object GenInteractionClass EditMenu = {
GI_visMoniker = `E', "Edit";
GI_comp = EditCut, EditCopy, EditPaste;
GII_visibility = GIV_POPUP;
}
/* The Cut trigger sends a MSG_META_CLIPBOARD_CUT to the Process
* object when pressed. */
@object GenTriggerClass EditCut = {
GI_visMoniker = `t', "Cut";
GTI_destination = process;
GTI_actionMsg = MSG_META_CLIPBOARD_CUT;
}
/* The Copy trigger sends a MSG_META_CLIPBOARD_COPY to the
* Process object when pressed. */
@object GenTriggerClass EditCopy = {
GI_visMoniker = `C', "Copy";
GTI_destination = process;
GTI_actionMsg = MSG_META_CLIPBOARD_COPY;
}
/* the Paste trigger is set up initially disabled. It sends a * MSG_META_CLIPBOARD_PASTE to the Process object when pressed. */
@object GenTriggerClass EditPaste = {
GI_visMoniker = `P', "Paste";
GTI_destination = process;
GTI_actionMsg = MSG_META_CLIPBOARD_PASTE;
GI_states = @default & ~GS_ENABLED;
}
Some Edit menus may also contain other triggers such as "Delete Event," or "Remove Item." These triggers, however, are not standard and must be implemented exclusively by the application.
Two main rules govern the maintenance of the Edit menu:
The first rule must be implemented entirely by the application; the Clipboard will not enable or disable the Copy or Cut triggers. The second rule, however, requires that the application be notified whenever the Clipboard's contents change--it is possible, for example, for the application to copy data to the Clipboard, enable its Paste trigger, and have another application then also copy some custom data to the Clipboard. In this case, the original application must disable its Paste trigger if it can not read the data.
Whenever the Clipboard's contents change, the UI will send notification to all objects that have registered with it. The notification will be in the message
MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED
. Use of this message can be found in the ClipSamp sample application.
Code Display 7-5 Handling Clipboard Changes
/* MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED is sent with no * parameters and requires no return value. */
/* The strategy of this message is to first check whether the CIF_TEXT format, the * only format supported by this sample application, is available on the Clipboard. * If so, the Paste trigger is enabled; if not, the Paste trigger is disabled. */
@method MyClipProcessClass, MSG_META_CLIPBOARD_NOTIFY_NORMAL_TRANSFER_ITEM_CHANGED
{
ClipboardQueryArgs query; /* A structure of arguments */
Boolean endisable = FALSE; /* The trigger is disabled */
/* Call ClipboardQueryItem() to gain exclusive access to and information about
* the current transfer item. Pass it zero indicating we're checking the
* normal transfer item (not the quick-transfer item) and the empty arguments
* structure. */
ClipboardQueryItem(TIF_NORMAL, &query);
/* If there are any formats, then test if CIF_TEXT is one of them. The routine
* ClipboardTestItemFormat() tests a format against all those available and
* returns true if it is supported, false if it is not. Use the macro
* FormatIDFromManufacturerAndType to create the format argument. If CIF_TEXT
* is not supported, then the enDisable argument is set to TRUE. */
if (query.CQA_numFormats) {
if (ClipboardTestItemFormat(query.CQA_header,
FormatIDFromManufacturerAndType(MANUFACTURER_ID_ME, CIF_TEXT))) {
endisable = TRUE;
}
}
/* Because we've found out what we need to know, restore the Clipboard with a
* call to ClipboardDoneWithItem(). This routine takes the transfer item's
* header and returns nothing; it also relinquishes our exclusive
* access to the Clipboard and is therefore very important. */
ClipboardDoneWithItem(query.CQA_header);
/* Now, if endisable is true, set the Paste trigger enabled. If endisable is
* false, set it disabled. These operations are accomplished by sending the
* appropriate message to the trigger object. */
if (endisable) {
@call EditPaste::MSG_GEN_SET_ENABLED(VUM_NOW);
} else {
@call EditPaste::MSG_GEN_SET_NOT_ENABLED(VUM_NOW);
}
}
As stated above, most applications will simply let a GenEditControl object create and maintain their Edit menu.
GenEditControlClass
is a subclass of
GenControlClass
(see the Controllers chapter for usage of controllers in general).
The GenEditControl object can provide triggers and/or tools for Undo, Cut, Copy, Paste, Select All, and Delete operations. These operations must all be handled by your application, of course, just as if you did not use a GenEditControl; using this controller, however, simplifies your UI programming and allows the Edit tools to be used by the GenToolControl.
The features of the GenEditControl are listed below (they are flags of the
GECFeatures
record type):
MSG_META_UNDO
to the application's target.
MSG_META_CLIPBOARD_CUT
to the application's target.
MSG_META_CLIPBOARD_COPY
to the application's target.
MSG_META_CLIPBOARD_PASTE
to the application's target.
MSG_META_SELECT_ALL
to the applications' target.
MSG_META_DELETE
to the application's target.The GenEditControl also provides an equivalent set of tools. Each tool executes the exact same functions as the analogous feature; see GenEditControl Features and Tools for the listing of the features and tools as well as the standard settings.
The GenEditControl handles two different notification types: GWNT_SELECT_STATE_CHANGE, sent when the selection state changes, and GWNT_UNDO_STATE_CHANGE, sent when a state change in the Undo status occurs. In both cases, the GenEditControl will appropriately update the Cut, Copy, Paste, Delete, and Undo triggers (the Select All trigger will always be enabled).
Code Display 7-6 GenEditControl Features and Tools
/* This display shows the features and tools records of GenEditControlClass, as * well as the default settings and instance data. */
/* GenEditControlClass features */ typedef WordFlags GECFeatures; #define GECF_UNDO 0x0020 /* MSG_META_UNDO */ #define GECF_CUT 0x0010 /* MSG_META_CLIPBOARD_CUT */ #define GECF_COPY 0x0008 /* MSG_META_CLIPBOARD_COPY */ #define GECF_PASTE 0x0004 /* MSG_META_CLIPBOARD_PASTE */ #define GECF_SELECT_ALL 0x0002 /* MSG_META_SELECT_ALL */ #define GECF_DELETE 0x0001 /* MSG_META_DELETE */
#define GEC_DEFAULT_FEATURES (GECF_UNDO | GECF_CUT | GECF_COPY | \ GECF_PASTE | GECF_SELECT_ALL | GECF_DELETE)
/* GenEditControlClass tools */ typedef WordFlags GECToolboxFeatures; #define GECTF_UNDO 0x0020 /* MSG_META_UNDO */ #define GECTF_CUT 0x0010 /* MSG_META_CLIPBOARD_CUT */ #define GECTF_COPY 0x0008 /* MSG_META_CLIPBOARD_COPY */ #define GECTF_PASTE 0x0004 /* MSG_META_CLIPBOARD_PASTE */ #define GECTF_SELECT_ALL 0x0002 /* MSG_META_SELECT_ALL */ #define GECTF_DELETE 0x0001 /* MSG_META_DELETE */
#define GEC_DEFAULT_TOOLBOX_FEATURES (GECTF_UNDO | GECTF_CUT | GECTF_COPY | GECTF_PASTE | GECTF_SELECT_ALL | GECTF_DELETE)
/* GenEditControlClass Instance Data Settings */
@default GCI_output = (TO_APP_TARGET); /* Send output to the target */
@default GI_states = (@default | GS_ENABLED);
@default GI_attrs = (@default | GA_KBD_SEARCH_PATH);
MSG_META_CLIPBOARD_CUT, MSG_META_CLIPBOARD_COPY
Cut and Copy are very similar in function; both put data onto the Clipboard. However, Cut causes the data to subsequently be deleted from the document, and Copy does not.
When the user starts either of these operations, the object that handles them must go through a series of specific steps to load the data into the Clipboard's VM file. (For simplicity of example, this chapter will assume that the Process object will handle all Clipboard operations; this may not be the case in complex programs.)
The steps are simple; each is enumerated below, and edited examples from the sample application ClipSamp are provided in MSG_META_CLIPBOARD_CUT and MSG_META_CLIPBOARD_COPY . Note that these examples do not use the default text object handlers for copy and paste; they treat the entire text flow as the current selection.
VMAlloc()
. As an alternative, you may pre-duplicate the item in memory with
MemAlloc()
and then simply attach them to the Transfer VM File with
VMAttach()
.
ClipboardRegisterItem()
.Code Display 7-7 MSG_META_CLIPBOARD_CUT
/* This is the same as MSG_META_CLIPBOARD_COPY except that after copying the * data to the Clipboard, it deletes the data from the document. * * MSG_META_CLIPBOARD_CUT has no parameters and no return value. The strategy * is as follows: First, copy the subject data into the Clipboard with * MSG_META_CLIPBOARD_COPY. Then, delete the data (which, in this case, is a single * memory block containing all the subject text). */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_CUT {
/* Use MSG_META_CLIPBOARD_COPY to copy the data to the clipboard. */
@call self::MSG_META_CLIPBOARD_COPY();
/* Delete the data. The data is contained entirely within a single memory
* block and is just text. The block is referenced by the memory handle
* textHandle. If textHandle is not null, then the block may be freed. */
if (textHandle) { /* If textHandle is valid, */
MemFree(textHandle); /* free the memory block */
textHandle = 0; /* and zero the handle. */
}
/* Redraw the view area to reflect the deleted text. */
ResetViewArea(); /* Custom routine to redraw the view. */
}
Code Display 7-8 MSG_META_CLIPBOARD_COPY
/* This message handler goes through all the steps necessary for a Copy operation * that works with text data only. * MSG_META_CLIPBOARD_COPY has no parameters and requires no return. * * The strategy employed by this handler is as follows: * First, allocate memory for and create the duplicate data block, filling in all * the appropriate fields. * Next, retrieve the Transfer VM File and attach the data block to the file. * Next, allocate and construct the transfer item header VM block. * Finally, register and lock in the transfer item to the Clipboard. * * A single global variable named textHandle refers to the block of text owned and * used by the sample application. All other data structures are defined within the * message handler. */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_COPY {
char *textText; /* temporary string for the text */
int textLength; /* length of string including null */
MemHandle headerMemHandle; /* handle of ClipboardItemHeader block */
VMFileHandle transferVMFile; /* VM file handle of Transfer VM File */
VMBlockHandle dataVMBlock; /* VM handle of attached data block */
VMBlockHandle headerVMBlock; /* VM handle of attached header block */
ClipboardItemHeader *headerMem; /* ClipboardItemHeader for the VM file */
optr textObj; /* temporary text object for transfer */
/* First, lock the text string into memory and get its length, adding one for
* the null character at the end. Then unlock the text string's block. */
textText = (char *) MemLock(textHandle);
textLength = (strlen(textText) + 1);
MemUnlock(textHandle);
/* Next, build the transfer item block by creating a temporary text object and
* copying our text into it. Other formats may simply copy the text directly
* into a VM block. */
textObj = TextAllocClipboardObject(ClipboardGetClipboardFile(), 0, 0);
@call textObj::MSG_VIS_TEXT_REPLACE_ALL_PTR((char *)MemLock(textHandle), 0);
MemUnlock(textHandle);
dataVMBlock = TextFinishedWithClipboardObject(
textObj, TCO_RETURN_TRANSFER_FORMAT);
/* Now get the transfer VM file. */
transferVMFile = ClipboardGetClipboardFile();
/* Now, allocate and fill in the transfer item header block. */
headerVMBlock = VMAlloc( transferVMFile,
sizeof(ClipboardItemHeader),
MY_TRANSFER_ID);
headerMem = (ClipboardItemHeader *)VMLock(transferVMFile, headerVMBlock,
&headerMemHandle);
headerMem->CIH_owner = (optr) (((dword)GeodeGetProcessHandle()<<16) | 0);
headerMem->CIH_flags = 0; /* Normal transfer; no flags. */
(void) strcpy(headerMem->CIH_name, "Sample Text");
headerMem->CIH_formatCount = 1;
headerMem->CIH_sourceID = 0;
headerMem->CIH_formats[0].CIFI_format =
FormatIDFromManufacturerAndType(MANUFACTURER_ID_ME, CIF_TEXT);
headerMem->CIH_formats[0].CIFI_vmChain =
VMCHAIN_MAKE_FROM_VM_BLOCK(dataVMBlock);
headerMem->CIH_formats[0].CIFI_extra1 = 0;
headerMem->CIH_formats[0].CIFI_extra2 = 0;
VMUnlock(headerMemHandle);
/* Now register the transfer item with the Clipboard. This will actually put
* the transfer item and its header into the Clipboard. */
ClipboardRegisterItem(BlockIDFromFileAndBlock( transferVMFile, headerVMBlock), 0); }
ClipboardRequestItemFormat(), MSG_META_CLIPBOARD_PASTE
The Paste operation pulls data off the Clipboard and places it at the insertion point in the application's data. The Clipboard remains unchanged throughout the operation; the data is simply duplicated and passed on to the application.
The steps in handling a
MSG_META_CLIPBOARD_PASTE
are simple; each is enumerated below, and a sample method for pasting is shown in MSG_META_CLIPBOARD_PASTE
.
ClipboardQueryItem()
. You should also call
ClipboardRequestItemFormat()
to make sure that the present clipboard item is pasteable.
ClipboardRequestItemFormat()
. Finally, copy the transfer item into your pre-allocated memory.
ClipboardDoneWithItem()
, relinquish your exclusive access to the Transfer VM File and to the clipboard item itself. The Paste operation can then be completed entirely by your application by assimilating the pasted data and displaying it properly.Code Display 7-9 MSG_META_CLIPBOARD_PASTE
/* This message handler goes through the necessary steps to grab the transfer item * from the Clipboard and copy it into application's memory. This example uses a * single global variable called textHandle, a memory handle of the only data block * owned by the application. The memory block contains text. * MSG_META_CLIPBOARD_PASTE has no parameters and requires no return value. */
@method ClipSampProcessClass, MSG_META_CLIPBOARD_PASTE {
ClipboardQueryArgs query; /* returned by
* ClipboardQueryItem() */
ClipboardRequestArgs request; /* returned by
* ClipboardRequestItemFormat() */
TextTransferBlockHeader *dataBlock /* pointer to block header */
MemHandle dataBlockMemHandle; /* handle of locked block */
word charsAvail; /* number of chars in block */
int textLength; /* length of text */
word transferFlags = 0; /* flags for the transfer
* (normal transfer) */
/* Call ClipboardQueryItem() to be sure that a normal
* transfer item exists in the Clipboard. */
ClipboardQueryItem(transferFlags, &query);
/* Fills ClipboardQueryArgs structure */
/* If a transfer item exists, check for a CIF_TEXT
* version, the only format we support. */
if (query.CQA_numFormats) { /* if more than zero formats available */
if (ClipboardTestItemFormat(query.CQA_header,
FormatIDFromManufacturerAndType(
MANUFACTURER_ID_ME,
CIF_TEXT))) {
/* A CIF_TEXT version exists. Now we grab that transfer item by calling * ClipboardRequestItemFormat(). This routine fills ClipboardRequestArgs, * which contains information about the Transfer VM File and the * ClipboardItemHeader block in that file. */
ClipboardRequestItemFormat(FormatIDFromManufacturerAndType( MANUFACTURER_ID_ME, CIF_TEXT), query.CQA_header, &request);
/* Now we have the VM file handle of the Transfer VM File and the VM block * handle of the ClipboardItemHeader structure. From this we can get the * data in the data block. To speed things up, we will copy the transfer * text directly into our already-allocated memory block; the handle to our * memory block is in textHandle. */
dataBlock = (TextTransferBlockHeader *)VMLock( request.CRA_file, VMCHAIN_GET_VM_BLOCK(request.CRA_data), &dataBlockMemHandle); textHugeArray = VMCHAIN_GET_VM_BLOCK(dataBlock->TTBH_text); VMUnlock(dataBlockMemHandle);
/* Since the data is CIF_TEXT, we know the data block is in the format of
* TextTransferBlockHeader. We get the text by cycling through the
* format's HugeArray. This code has been taken out of this example for
* simplicity; you can look at the ClipSamp sample application source
* code for it. */
}
/* After copying the text into our block, we signal we're done with the
* transfer file. We do this by calling ClipboardDoneWithItem(). After
* that, we update our view and return. */
ClipboardDoneWithItem(query.CQA_header);
ResetViewArea(); /* Routine defined in ClipSamp. */
}
ClipboardRemoveFromNotificationList()
Because the Clipboard sends notification out to all registered geodes, geodes must "unregister" when they are shutting down. Otherwise, the Clipboard will attempt to send a message to a defunct object, and this can cause problems for the operating system. Therefore, in your
MSG_GEN_PROCESS_CLOSE_APPLICATION
handler you should make a call to the routine
ClipboardRemoveFromNotificationList()
, which removes the passed object from the notification list.
For the most part, implementation of Undo is left up to the application. This is due to the fact that operations that may be undone are typically very application-specific. The text objects and the Ink object are the only exceptions to this; they provide their own Undo functions in response to
MSG_META_UNDO
. For more information on Undo and how it works in GEOS, see the MetaClass chapter.
ClipboardTestItemFormat(), ClipboardEnumItemFormats(), ClipboardGetItemInfo(), ClipboardGetNormalItemInfo(), ClipboardGetUndoItemInfo(), ClipboardGetClipboardFile()
With the following routines, you can get information about any of the transfer files in use.
ClipboardTestItemFormat()
ClipboardQueryItem()
to get the transfer item header.
ClipboardEnumItemFormats()
ClipboardQueryItem()
to get the transfer item header.
ClipboardGetItemInfo()
ClipboardQueryItem()
to get the transfer item header.
ClipboardGetNormalItemInfo()
ClipboardGetUndoItemInfo()
ClipboardGetClipboardFile()ClipboardUnregisterItem()
Using
ClipboardUnregisterItem()
, you can revert one level of clipboard changes. Note that this works for only one level; there is no way to back out more than one change to the clipboard. This routine can not "undo" itself; that is, calling this routine twice in a row will leave the clipboard in a state other than the original state.
An application must understand the Clipboard and its structure before being able to support the quick-transfer feature of the UI. However, because quick-transfer is such a convenient feature for users, every appropriate application should support it. To use the quick-transfer mechanism, you will probably want to understand how mouse input is handled.
The quick-transfer mechanism makes extensive use of the Clipboard's data structures. However, this does not mean that when a quick-transfer is initiated, the Clipboard is altered. Instead, the Clipboard maintains a separate (but similar) data structure within the Transfer VM File.
When a transfer is in progress, the distinction between a normal transfer and a quick transfer is made with the flag CIF_QUICK. When passed to the transfer mechanism's routines, this flag indicates that the quick-transfer item should be accessed and the Clipboard data should remain intact.
Quick-Transfer Data Structures
In order for an application to support the quick-transfer mechanism, it must be able to handle several situations. The list below enumerates all the tasks the application must be ready to perform:
Applications should also understand the three rules that govern the behavior of a quick-transfer:
Although applications must handle several situations to support the quick-transfer mechanism, the procedure involved in a quick-transfer is quite simple. The steps of how a quick-transfer operation is performed are outlined below:
MSG_META_START_MOVE_COPY
to the object under the pointer image.
ClipboardStartQuickTransfer()
to initiate the quick-transfer mechanism. It then builds the transfer item just as it would if the user had clicked on the Copy trigger in the Edit menu. It then logs the transfer item with the quick-transfer mechanism.
ClipboardSetQuickTransferFeedback()
. If the source object is a visible object in a GenView, it must also send the message
MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER
to itself to allow the pointer events to be sent to other objects in other windows (because the GenView grabs the mouse on the press).
MSG_META_PTR
should check if a quick transfer is in progress by either checking the passed event flags or by calling
ClipboardGetQuickTransferStatus()
. The object should, in response, provide feedback as to whether it can accept the transfer item or not. It calls
ClipboardSetQuickTransferFeedback()
with the proper feedback signal.
MSG_META_END_MOVE_COPY
from the UI).
ClipboardGetQuickItemInfo()
on the transfer item. If it can handle the item, it calls
ClipboardQueryItem()
, grabs the transfer item, and finally calls
ClipboardEndQuickTransfer()
.
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED
to the source object, informing it about the final outcome of the operation. Some source objects will change shape, shading, or color during a quick-transfer and must know when the transfer is concluded. If the operation is a quick-move, the source must delete the information or object that was moved.The quick-transfer mechanism uses the same structures as the Clipboard. However, there are special data structures that are used exclusively by the quick-transfer mechanism. These data structures are used by individual UI routines and are documented with those routines.
MSG_META_START_MOVE_COPY, ClipboardStartQuickTransfer(), MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK
When the user presses the move/copy button, the UI sends a
MSG_META_START_MOVE_COPY
to the object under the mouse pointer. The selected object can be either a gadget run in the UI thread (such as a GenText object) or a process-run visible object within a view.
If the object is a process-run visible object in a view, both the application object and the object under the mouse pointer will receive the notification message. If the object is UI-run, only it will receive the message.
Receipt of this message tells an object to begin a quick-transfer operation. This operation consists of several steps:
The reason for providing feedback to the quick-transfer mechanism is simple: to indicate to the user what is going on. By giving information to the quick-transfer mechanism, objects allow the user to be informed immediately what type of operation is in progress--a move, a copy, or nothing at all.
Immediately after your object has grabbed the mouse, it should call the routine
ClipboardStartQuickTransfer()
. This routine allows the object not only to indicate which type of operation is in progress but also to attach a special graphical region to the cursor (though not required). This allows the application to provide additional information to the user as to what is going on (e.g. GeoManager attaches an image when a file transfer is initiated).
You must also indicate to
ClipboardStartQuickTransfer()
the object that will receive notification when the transfer has concluded. This is important because when a quick-move has been completed, the source object must ensure that the original copy of the item (usually the source object itself) is deleted.
After calling
ClipboardStartQuickTransfer()
, the source object should duplicate and register the transfer item with the Transfer VM File. To do this, register the item as you normally would for a cut or copy operation (see Registering with the Clipboard
); however, be sure to use the flag CIF_QUICK to ensure that the normal Clipboard data remains unaffected.
Once the transfer item has been registered, the source object becomes a potential destination and should act as such. However, you may wish to continue to provide source-related visual feedback to the user as long as the quick-transfer is going on: During a quick-transfer, the source will receive
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_FEEDBACK
each time the mode of the quick-transfer changes. This message will tell the source object what the current mode of transfer is in order for the source to give extra visual feedback to the user. This behavior is not required of the source object but can be beneficial to your application. It is also supplemental to the destination-related feedback that must be provided.
ClipboardGetQuickTransferStatus(), ClipboardSetQuickTransferFeedback(), MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER, MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER
All objects that can potentially receive a transfer item are considered potential destinations during a quick-transfer operation. During the operation, the user will likely move the mouse pointer across the screen, entering and leaving several different potential destinations.
When the mouse first moves over the object, the object will receive a
MSG_META_PTR
. When your object receives this message, it must provide immediate feedback to the transfer mechanism to indicate whether a move, a copy, or no operation is to be performed (the object should provide this feedback with the understanding that the operation type is what would happen if the transfer were to conclude at that moment).
When the first
MSG_META_PTR
is received, the object should call the routine
ClipboardGetQuickTransferStatus()
. This routine returns whether a quick-transfer is in progress; if so, the object should acquire a mouse grab in order to provide feedback until the mouse pointer leaves its bounds.
The object should then check the quick-transfer Clipboard for supported formats. This is done just as with the Clipboard--with the routine
ClipboardQueryItem()
. If no supported formats are available, the object should provide the "no operation" feedback. However, if one or more is available, the object should determine whether the operation is a move or copy (call
ClipboardGetItemInfo()
) and act accordingly.
To provide feedback, the object must call
ClipboardSetQuickTransferFeedback()
in its method for
MSG_META_PTR
.
This routine sets the mode of the transfer to one of the enumerated type
ClipboardQuickTransferFeedback
. If the format is not supported,
ClipboardSetQuickTransferFeedback()
is passed CQTF_CLEAR.
When the mouse has left an object's bounds, the object must relinquish its mouse grab. Either a
MSG_META_CONTENT_LOST_GADGET_EXCLUSIVE
or a
MSG_META_PTR
with the UIFA_IN flag cleared will indicate this situation to the object. The former occurs when the mouse has moved onto a window or other object that is obscuring your object, and the latter is a result of the mouse moving outside of your bounds altogether.
At this point, the object must do two things: Reset the mouse cursor and re-transmit the last mouse pointer event.
To reset the mouse pointer, call
ClipboardSetQuickTransferFeedback()
and pass it CQTF_CLEAR. This will set the default cursor. To re-send the last pointer event received (you must do this because the last one occurred outside your object's bounds and might have been within another object's bounds), you simply have to return the flag MRF_REPLAY when releasing the mouse grab.
If the source object wishes a quick transfer to be able to be carried outside its view, it must send
MSG_VIS_VUP_ALLOW_GLOBAL_TRANSFER
to itself. Process objects acting as a content must send
MSG_GEN_VIEW_ALLOW_GLOBAL_TRANSFER
to the GenView.
The object then becomes oblivious to future quick-transfer events until the pointer returns to its window (or unless it was registered for notification of quick-transfer conclusion).
MSG_META_END_MOVE_COPY, ClipboardEndQuickTransfer()
If, when the mouse pointer is within your object's bounds, the user releases the Move/Copy button, your object becomes the true destination of the transfer. You will be notified by a
MSG_META_END_MOVE_COPY
.
Upon receipt of this message, the object should first determine the move/copy/no-operation behavior as above, with one exception: If either of the flags CQNF_MOVE or CQNF_COPY is set for the transfer item, then the user has overridden the normal behavior and the destination object should respond with the appropriate operation.
After determining the proper action, the object should retrieve the transfer item (as it would from the Clipboard), passing one of CQNF_MOVE, CQNF_COPY, or CQNF_NO_OPERATION. This flag will cause the proper notification to be sent to the transfer's source and allow it to complete its actions properly.
To finish the transfer, the object should call
ClipboardEndQuickTransfer()
.
In addition to the routines above, you can use one other to retrieve information about a quick-transfer item.
ClipboardGetQuickItemInfo()
returns a set of handles for the transfer VM file and the file's header block.
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED
After the transfer has concluded, the original source of the transfer will receive
MSG_META_CLIPBOARD_NOTIFY_QUICK_TRANSFER_CONCLUDED
if it requested notification when it registered the original transfer. This message will be accompanied by a
ClipboardQuickNotifyFlags
record indicating what type of operation the transfer ended up being. The source object should then follow the rules of quick transfer and act appropriately (e.g. delete the source object on a quick-move operation).
ClipboardClearQuickTransferNotification(), ClipboardAbortQuickTransfer(), ClipboardRemoveFromNotificationList()
It is possible for a quick-transfer source object to shut down (be destroyed) before the completion of a quick-transfer operation. If the object registered to be notified of transfer completion, it must un-register as it is shutting down.
This is done with
ClipboardClearQuickTransferNotification()
or
ClipboardAbortQuickTransfer()
.
Additionally, any application that registers with the Clipboard must un-register when it is shutting down. This is done with the routine
ClipboardRemoveFromNotificationList()
.