Author : Unknown
Page : << Previous 7 Next >>
following lists the necessary steps for allocating memory blcok and fill it with our data: 1) Allocate enough buffers by calling function ::GlobalAlloc(...). 2) Lock the memory by calling function ::GlobalLock(...), which will return a pointer that can be used to access the memory buffers. 3) Fill these buffers with data. 4) Call ::GlobalUnlock(...) to unlock the memory.
We need to use a series of functions in order to put the data to the clipboard: 1) First we need to call function CWnd::OpenClipboard(...), which will let the clipboard be owned by our application (Only the window that owns the clipboard can modify the data contained in the clipboard, any other application is forbidden to access the clipboard during this period). 2) Before putting any data to the clipboard, we must call ::EmptyClipboard() to clear any existing data. 3) We can call ::SetClipboardData() to put new data to the clipboard. 4) Finally we need to call ::CloseClipboard() to close the clipboard, this will let the clipboard be accessible to other windows.
When calling function ::SetClipboardData(...), besides passing the handle of the global memory, we also need to specify the data format. There are many standard clipboard data formats such as CF_TEXT, CF_DIB, which represent text data and bitmap data respectively. We can also define our own data format by calling function ::RegisterClipboardFormat(...).
To copy data from the clipboard, we need to open the clipboard first, then call function ::GetClipboardData(), which will return a global memory handle. With this handle, we can call ::GlobalLock(...) to lock the memory, copy the data from the global memory to our own buffers, call ::GlobalUnlock(...) to unlock the memory, and close the clipboard. We can not free the global memory obtained from the clipboard because after the clipboard is closed, some other applications may also want to access it.
Deleting Selected Text
Sample 9\GDI is based on sample 8\GDI, it allows the user to cut or copy the selected text to the clipboard, and paste the data from clipboard.
When we cut data to the clipboard, we also need to delete the selected text. So first a new function DeleteSelection() is declared in class CGDIDoc, it can be called to delete the currently selected text:
class CGDIDoc : public CDocument
{
......
public:
......
BOOL DeleteSelection();
......
}
Function CGDIDoc::DeleteSelection() is implemented as follows:
(Code omitted)
When there is no currently selected text, the function does nothing. Otherwise we proceed to delete the selected text.
Because the ending selection index may be less than the beginning selection index, first we set the value of local variable nSel to the smaller selection index, and set the number of selected characters to another local variable nNum. Then the unselected text is combined together, and the caret index is adjusted. Next the caret and the client window are updated. Finally, both selection indices are set to -1, this indicates that currently there is no text being selected.
We can call this function when DELETE key is pressed to delete the selected text, also we can call it when the selected text is being cut to the clipboard. In the sample, function CGDIDoc::DeleteChar() is modified as follows:
(Code omitted)
Since this member function may be called when either BACK SPACE or DELETE key is pressed, we need to delete the selected text in both cases. If deleting the selected text is successful, the function will return. Otherwise it means there is no currently selected text, so we go on to delete a single character.
Message Handlers for Cut, Copy Paste Commands
In the sample, both WM_COMMAND and UPDATE_COMMAND_UI message hanlders are added for command ID_EDIT_CUT, ID_EDIT_COPY and ID_EDIT_PASTE in class CGDIDoc. We need to enable commands Edit | Copy and Edit | Cut if there is selected text. So functions CGDIDoc::OnUpdateEditCopy(...) and CGDIDoc::OnUpdateEditCut(...) are implemented as follows:
(Code omitted)
Two functions are implemented exactly the same. For function CGDIDoc::OnUpdateEditPaste(...), we need to check if there is data available in the clipboard, if so, the command will be enabled. This checking can be implemented by calling function ::IsClipboardFormatAvailable(...) with appropriate data format passed to it. The function will return FALSE if there is no data present in the clipboard for the specified data format. The following is the implementation of funcition CGDIDoc::OnUpdateEditPaste(...):
(Code omitted)
Command Edit | Copy is implemented as follows:
(Code omitted)
First, we assign the smaller of the two selection indicies to variable nSel, and the number of selected characters to variable nNum. Then we copy the selected text to a CString type variable szStr. Next, we allocate a memory block, lock it, copy the string from szStr to the new buffers. Then we unlock the memory, open the clipboard, clear it, and copy the data to the clipboard. Finally we close the clipboard.
The implmentation of Edit | Cut command is almost the same except that we must delete the selected text after copying the data to the clipboard. So function CGDIDoc::OnEditCut() is implemented as follows:
(Code omitted)
For Edit | Paste command, everything is the reverse. We need to open the clipboard, obtain data from the clipboard, lock the global memory, copy the data to local buffers, unlock the global memory, and insert the new string to the text at the current caret position. The following is the implementation of this command:
(Code omitted)
Now the application can exchange data with another application that supports clipboard.
10 One Line Text Editor, Step 7: Getting Rid of Flickering
The editor is almost finished except for one annoying feature: every time the user inputs a character, makes selection or moves the caret, the text will flicker. This is because whenever the text is being updated, we call function CDocument::OnUpdateAllViews(...) to cause the whose view window to be updated. By default, before the client window is redrawn, it will be erased using white color. This is the cause of flickering. To get rid of it, we need to update only the area that has changed (instead of updating the whole window).
Function CDocument::UpdateAllViews(...)
Function CDocument::UpdateAllViews(...) has three parameters, two of which have default values:
void CDocument::UpdateAllViews(CView *pSender, LPARAM lHint=0L, CObject *pHint=NULL);
By default, the update message will be sent to view, this will cause function CView::OnUpdate(...) to be called:
void CView::OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint);
All parameters passed to CDocument::UpdateAllViews(...) will be passed to this function. This provides us a way to know what part of the client window needs to be updated. The updating hint can be passed through either parameter lHint or pHint.
By default, CView::OnUpdate(...) will update the whole client area. If we want only a portion of the client window to be updated, we need to bypass the default implementation. Within the overridden funciton, we can use the hint to form a rectangle indicating the area needs to be updated, and use it to call function CWnd::InvalidateRect(...).
Function CWnd::InvalidateRect(...) will cause only the specified rectangular area to be updated.
Defining Hints
Our next task is to divide the updating events into different categories and calculate the rectangle for each situation. The following is a list of situations when only a portion of the client window needs to be updated:
(Table omitted)
The last two situations are a little complicated. When the user makes selections, the newly selected area may be smaller or larger than the old selected area. In either case, we only need to update the changed area to avoid flickering (Figure 9-5).
Because of this, we need to add new variables to remember the old selection indices. In the sample, two new variables and some functions are declared in class CGDIDoc as follows:
(Code omitted)
Variables m_nSelIndexBgnOld and m_nSelIndexEndOld are used to remember the old selection indices, functions GetSelIndexBgnOld() and GetSelIndexEndOld() are used to obtain their values outside class CGDIDoc. Because we also need to know the value of m_nCaretIndex when updating the client window, another function GetCaretIndex() is also added for retrieving its value.
The value of m_nSelIndexEndOld is initialized in the constructor:
CGDIDoc::CGDIDoc()
{
......
m_nSelIndexEndOld=-1;
}
In the sample, some macros are defined as follows to indicate different updating situations when function CDocument::UpdateAllViews(...) is called:
#define HINT_DELCHAR_AFTER 100
#define HINT_DELCHAR_BEFORE 101
#define HINT_DELETE_SELECTION 102
#define HINT_PASTE 103
#define HINT_SELECTION 104
#define HINT_UNSELECTION 105
#define HINT_INPUT 106
Calling Function CDocument::UpdateAllViews(...)
We must modify all the function calls to CDocument::UpdateAllViews(...). The following shows the modifications made to function CGDIDoc::SetCaret(...):
Old Version:
(Code omitted)
New Version:
(Code omitted)
In this function, the value of m_nSelIndexEnd is first assigned to m_nSelIndexEndOld before it is updated. Flag HINT_SELECTION will cause the difference between the newly selected area and the old one to be updated. The area can be calculated from the four selection indices.
The following shows the modifications made to function CGDIDoc::AddChar(...):
Old Version:
(Code omitted)
New Version:
(Code omitted)
Flag HINT_INPUT will cause all the characters after the caret to be updated.
The following shows the modifications made to funciton CGDIDoc::DeleteChar(...):
Old Version:
(Code omitted)
New Version:
(Code omitted)
Flag HINT_DELCHAR_AFTER will cause all the characters after the caret to be updated, and HINT_DELCHAR_BEFORE will cause the character before the caret along with all the characters after the caret to be updated.
The following shows the modifications made to function CGDIDoc::DeleteSelection():
Old Version:
(Code omitted)
New Version:
(Code omitted)
Flag HINT_DELETE_SELECTON will cause the selected text and the characters after the selection to be updated.
The following shows the modifications made to function CGDIDoc::OnEditPaste():
Old Version:
(Code omitted)
New Version:
(Code omitted)
Flag HINT_PASTE will cause all the characters after
Page : << Previous 7 Next >>