Programming for the OS/2 PM in C:The Clipboard

by Rick Papo

<< Printing -- Index -- Drag & Drop >>

Part VIII. - The Clipboard
After what seems like quite a long rest, here we are again working on this little painter program. Since it is one of the areas left obviously unfinished, we will turn to making the clipboard functions work this month.

Overview
On the surface, the system clipboard seems a simple idea. You select what you want to copy or move to another program (or within the current program) and give the Copy or Cut command. Then you move to where you want the information to be copied/moved to and give the Paste command. The information appears at the destination. Simple, right? It looks simple, but in reality it involves a lot of work inside the system. Some of this work is done by the system, some of it is done by the programs, both source and destination.

The first problem is sharing. Under OS/2 or any true memory-protected multitasking system, individual programs cannot normally access memory belonging to other programs. It is possible to share memory between programs, but such sharing must be strictly controlled.

The second problem is data format. At different times the user might want to place simple text, formatted text with special attributes, graphic images, spreadsheet cells or any number of other things. Programs functioning as data sources might not understand all the possible formats. Likewise, programs serving as data destinations might understand a different subset of the formats. A mechanism needs to be provided to at least allow the provider and the receiver to agree on a common format. Where possible, automatic conversion of formats might be provided.

OS/2 has implemented one solution to this problem, one which has proved more than adequate for most programs. To my knowledge, no operating system implements a complete solution yet.

The OS/2 Clipboard
There are only a few functions provided by the system for handling the clipboard. The main functions are as follows: In addition to these functions there are a few more for more advanced usage of the clipboard: WinQueryClipbrdOwner, WinQueryClipbrdViewer, WinSetClipbrdOwner, WinSetClipbrdViewer, WinEnumClipbrdFmts and WinQueryClipbrdFmtInfo.
 * WinOpenClipbrd - Open the clipboard.
 * WinCloseClipbrd - Close the clipboard.
 * WinEmptyClipbrd - Clear the clipboard.
 * WinQueryClipbrdData - Check the clipboard contents.
 * WinSetClipbrdData - Place data on the clipboard.

To implement the Cut or Copy commands, the program must first prepare the information for sharing. Bitmaps and Metafiles are shared by their own nature, but for anything else the program must allocate a block of shared memory with the 'giveable' attribute set. The data must then be copied to that block. Once the data is ready for sharing, the program must open the system clipboard, empty it, and set either the address of the shared data or the handle for the bitmap or metafile on the clipboard. The program can place multiple entries on the clipboard, one entry for each clipboard data format. Once this is done, the program closes the clipboard so that other programs can access the information on it.

To implement the Paste command, the program must open the clipboard, check to see if a format it can use is there, obtain the pointer or handle to the information, copy the information out of the clipboard and finally close the clipboard.

The Clear command does not really use the clipboard, but has come to be associated with the other three commands by custom. Cut is simply Copy followed by Clear. Some systems also perform another variation: if you have selected text at the time you request a Paste operation, then the selected text is cut to a private area (not the system clipboard), the Paste operation is performed, and then a Copy operation is performed with the saved text, replacing the previous clipboard text with the previously selected text.

Implementation in Tiny Painter
The example program we have been working on is not a text editor, and in fact the way the program works makes implementation of the clipboard functions rather difficult. We need to be able to select a rectangular area for cut or copy. The selected area might divide some of the line-segments in the image, and the object placed on the clipboard should appear exactly like the selected area, with no overflow. Other applications might want to use the clipboard information, so we need to provide an image on the clipboard too, in bitmap or metafile format. Bitmap format is much easier to do and more generally understood by other programs, so we will do that. For the time being, the only clipboard format that will be understood by this program for paste will be its own private format. We are not ready to handle any other kind of data. That is a project for another month.

The first thing we must do is devise a way to define the area to be cut, copied or cleared from the drawing. I have modified the sample program so that when the user selects any of the four editing commands, it enters a mode in which it waits for the next mouse button press. The location of that button press is recorded, and the mouse is tracked until the user releases the button, drawing a tracking rectangle as this happens so that the user can see the area he is marking. When the mouse button is released, then the rectangle defined is used for the selected edit operation. No provision for this mode to be aborted is made at this time. This is left as an exercise for the reader.

With the rectangle defined, the current picture's line list must be converted into two lists: what would remain after cutting the rectangle out, and what lies within the rectangle. This operation turns out to be rather complex, and I have isolated it within the function 'ClipLine'. The private clipboard data will contain the original clipping rectangle, the number of clipped lines, and the clipped lines themselves, each of them converted so as to make the paste operation easier later. The clipped data will look as if the clipped rectangle started at the origin x=0, y=0.

The Clear operation is the simplest. We simply replace the current list of lines with the list that remained after clipping and repaint the window.

The Copy operation is more complex. We create a bitmap of the clipping rectangle's contents (more on that another month), and then allocate shared memory to hold the list of clipped lines and the points defining their rectangle. We open the clipboard, discard its contents and set the new contents to be the bitmap and the private clipboard information we have just collected, like this: WinOpenClipbrd (WinQueryAnchorBlock(hwnd)) ; WinEmptyClipbrd (WinQueryAnchorBlock(hwnd)); WinSetClipbrdData (WinQueryAnchorBlock(hwnd), (ULONG)Bitmap, CF_BITMAP, CFI_HANDLE); WinSetClipbrdData (WinQueryAnchorBlock(hwnd), (ULONG)pShared, CF_DSPTEXT, CFI_POINTER); WinCloseClipbrd (WinQueryAnchorBlock(hwnd));

The Copy operation does nothing to the current picture, but the Cut operation combines the Copy operation just described with the Clear operation, in that order. Nothing new appears here.

That leaves us with only the Paste operation to implement. We cannot yet deal with anything on the clipboard except data in the private format that this program sets there, as the program is not yet ready for text or bitmap information. We can and do, however, deal with the private data, but in a special way. Since we have gone to the trouble of defining a rectangle, we will use that rectangle as the location and boundaries for what we will paste on to the drawing. We have already altered the data before placing it on the clipboard so that the clipped data's origin is at (0,0), so this makes what we will be doing much easier. We first need to open the clipboard and check for data in this private format. If it is there, then we will attempt to paste it into the drawing. Whatever happens, we will close the clipboard afterwards. The logic turns out like this: WinOpenClipbrd (WinQueryAnchorBlock(hwnd)); pShared = (struct ClipData *) (WinQueryClipbrdData (WinQueryAnchorBlock(hwnd), CF_DSPTEXT)); if (pShared) { // Convert and add in the clipboard data. } /* endif */ WinCloseClipbrd (WinQueryAnchorBlock(hwnd));

Converting the clipped data is a simple linear transformation, but the results are quite nice. You can clip a portion of the drawing and then paste that portion back onto the drawing at different locations, at different sizes and proportions, all with only a small amount of code.

Adding features to this program as we have been, the 'tiny painter' program has now grown quite a bit. It now has over 1100 lines of course code, though with a little bit of rearranging it could be made quite a bit smaller and more efficient. Enough for now. Next month we will start playing with the Graphics Programming Interface, to see what new features we can add to make the program more useful.

<< Printing -- Index -- Drag & Drop >>