Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Handling Cut (CTRL-C) in a CListCtrl dervied control

852 views
Skip to first unread message

Oliver Regenfelder

unread,
May 21, 2010, 11:05:51 AM5/21/10
to
Hello,

I have a CListCtrl derived control which presents some data.

Now, I want to add handling of CTRL-C:
When the user presses CTRL-C the text of the currently selected
item should be copied to the clippboard.

As far as I figured, accelerators are my best option? Am I
right or is there an 'easy' neat whay like some OnCTRLCPressed
messages?

Best regards,

Oliver

David Lowndes

unread,
May 21, 2010, 1:35:23 PM5/21/10
to
>I have a CListCtrl derived control which presents some data.
>
>Now, I want to add handling of CTRL-C:
>When the user presses CTRL-C the text of the currently selected
>item should be copied to the clippboard.
>
>As far as I figured, accelerators are my best option?

Maybe - it depends. If you're doing this from the context of your
application rather than as a stand-alone reusable control, then the
accelerator option to invoke a command in your application may be a
good approach. The alternative is to catch the WM_KEYUP keystroke in a
subclassed (derived MFC class) list control.

Dave

Oliver Regenfelder

unread,
May 21, 2010, 2:06:11 PM5/21/10
to
Hello,

David Lowndes wrote:
>> Now, I want to add handling of CTRL-C:
>> When the user presses CTRL-C the text of the currently selected
>> item should be copied to the clippboard.
>>
>> As far as I figured, accelerators are my best option?

> Maybe - it depends. If you're doing this from the context of your
> application rather than as a stand-alone reusable control, then the
> accelerator option to invoke a command in your application may be a
> good approach.

The control is not a stand-alone reusable contorl but implemented
directly in the applications code base.

> The alternative is to catch the WM_KEYUP keystroke in a
> subclassed (derived MFC class) list control.

What would be the problem with using accelerators in a stand-alone
control?

Is there now abstract way of catching CTRL-C/CTRL-V like things?

Best regards,

Oliver

Joseph M. Newcomer

unread,
May 21, 2010, 2:47:39 PM5/21/10
to
There is no specific mechanism for doing this. And you need to understand what you will
do with the copied (not cut) information.

I tend to do this by subclassing the control and handling WM_KEYDOWN or WM_CHAR messages.
Ctrl+C is character code 0x03.

If what I want is text (suitable for pasting into Word, NotePad, etc.) then I will
typically loop over the selected entries (I usually do this when there is a multi-select
capability); essentially, I just extract the text in some useful form and just
concatenate each item with a CRLF sequence to form a multi-line string, then put that
string in the clipboard. For a list control, I would put a TAB between each column; not
the most elegant solution, but one which can work acceptably in a variety of contexts.
Your Mileage May Vary. You may just want to put spaces between, or do something else that
makes sense for your app. If I want to paste it into another list control in my app, I
might create a new clipboard format, CF_MYLISTCTRL, and register it; and in this, I will
create something interesting, perhaps just a sequence of lines separated by CRLF and
colums separated by tabs, and the PASTE operation will know how to handle this and do the
paste correctly.

Think about "undo" if you implement "cut" rather than just "copy". I've found a one-level
undo is often sufficient (multi-level undo is complex and if you need it, be prepared to
do a lot of work)
joe

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Oliver Regenfelder

unread,
May 21, 2010, 3:19:50 PM5/21/10
to
Hello,

Joseph M. Newcomer wrote:
> There is no specific mechanism for doing this. And you need to understand what you will
> do with the copied (not cut) information.

Yes I meant copy not cut.

> I tend to do this by subclassing the control and handling WM_KEYDOWN or WM_CHAR messages.
> Ctrl+C is character code 0x03.

Is it portable to use character code 0x03. In my solution I use KEYDOWN
and KEYUP to keep track of the CTRL key and when c/C comes in while ctrl
is pressed I call my OnCopy method.

> If what I want is text (suitable for pasting into Word, NotePad, etc.) then I will
> typically loop over the selected entries (I usually do this when there is a multi-select
> capability); essentially, I just extract the text in some useful form and just
> concatenate each item with a CRLF sequence to form a multi-line string, then put that
> string in the clipboard. For a list control, I would put a TAB between each column; not
> the most elegant solution, but one which can work acceptably in a variety of contexts.
> Your Mileage May Vary. You may just want to put spaces between, or do something else that
> makes sense for your app. If I want to paste it into another list control in my app, I
> might create a new clipboard format, CF_MYLISTCTRL, and register it; and in this, I will
> create something interesting, perhaps just a sequence of lines separated by CRLF and
> colums separated by tabs, and the PASTE operation will know how to handle this and do the
> paste correctly.

After I got the CTRL-C done I found out (with horror) about the
clipboard. But I only need to cut text for now and although it is a
ListCtrl the requirement is to only copy the items name so I do not
have to deal with the columns (yet). But it is good to know about the
problems in advance.

I used the following code to access the clipboard:

::OpenClipboard(NULL);

ItemData *item_data = reinterpret_cast<ItemData*>(GetItemData(index));

HANDLE hMem = ::GlobalAlloc(0
, (item_data->epc.GetLength() + 1)* sizeof(TCHAR));

if(hMem == NULL) {
::CloseClipboard();
return;
}

LPVOID lptstrCopy = ::GlobalLock(hMem);
::memcpy(lptstrCopy, item_data->epc, (item_data->epc.GetLength() + 1) *
sizeof(TCHAR));
::GlobalUnlock(hMem);

::SetClipboardData(CF_TEXT, hMem);

::CloseClipboard();


It bothers me just now, when I am using CF_TEXT has this to be ASCII or
unicode?

> Think about "undo" if you implement "cut" rather than just "copy". I've found a one-level
> undo is often sufficient (multi-level undo is complex and if you need it, be prepared to
> do a lot of work)

Luckily I do not need any cut so there is (not yet) any need for an
undo.

Best regards,

Oliver

David Lowndes

unread,
May 21, 2010, 6:52:08 PM5/21/10
to

>What would be the problem with using accelerators in a stand-alone
>control?

You may not have the opportunity to use the accelerator mechanism -
which usually is done by calling TranslateAccelerator in the
application's message loop.

Dave

Joseph M. Newcomer

unread,
May 22, 2010, 5:38:36 AM5/22/10
to
See below...

On Fri, 21 May 2010 21:19:50 +0200, Oliver Regenfelder <oliver.re...@gmx.at> wrote:

>Hello,
>
>Joseph M. Newcomer wrote:
>> There is no specific mechanism for doing this. And you need to understand what you will
>> do with the copied (not cut) information.
>
>Yes I meant copy not cut.
>
>> I tend to do this by subclassing the control and handling WM_KEYDOWN or WM_CHAR messages.
>> Ctrl+C is character code 0x03.
>
>Is it portable to use character code 0x03. In my solution I use KEYDOWN
>and KEYUP to keep track of the CTRL key and when c/C comes in while ctrl
>is pressed I call my OnCopy method.

>****
Probably unnecessary. I have no idea what you mean by "portable" because a Windows app
can only run on Windows and Windows-like clones (e.g. WINE) so there is no portability
issue. Whenever you suggest the work "portable" you imply that the application will run
on a non-Windows platform, and this is not going to happen.

By definition, character code 01 is Ctrl+A, 02==Ctrl+B, etc. These are not optional; this
is the DEFINITION of what these characters do. So I'd ignore trying to simulate the
action of the control key.
****

>> If what I want is text (suitable for pasting into Word, NotePad, etc.) then I will
>> typically loop over the selected entries (I usually do this when there is a multi-select
>> capability); essentially, I just extract the text in some useful form and just
>> concatenate each item with a CRLF sequence to form a multi-line string, then put that
>> string in the clipboard. For a list control, I would put a TAB between each column; not
>> the most elegant solution, but one which can work acceptably in a variety of contexts.
>> Your Mileage May Vary. You may just want to put spaces between, or do something else that
>> makes sense for your app. If I want to paste it into another list control in my app, I
>> might create a new clipboard format, CF_MYLISTCTRL, and register it; and in this, I will
>> create something interesting, perhaps just a sequence of lines separated by CRLF and
>> colums separated by tabs, and the PASTE operation will know how to handle this and do the
>> paste correctly.
>
>After I got the CTRL-C done I found out (with horror) about the
>clipboard. But I only need to cut text for now and although it is a
>ListCtrl the requirement is to only copy the items name so I do not
>have to deal with the columns (yet). But it is good to know about the
>problems in advance.
>
>I used the following code to access the clipboard:
>
>::OpenClipboard(NULL);
>
>ItemData *item_data = reinterpret_cast<ItemData*>(GetItemData(index));

****
What is in the ItemData field? You have not said.
****
>
>HANDLE hMem = ::GlobalAlloc(0
****
Why 0? For clipboard data, it is supposed to be GMEM_MOVEABLE!
****


> , (item_data->epc.GetLength() + 1)* sizeof(TCHAR));

****
WHy is the comma for the first parameter the first character of the second line?
****


>
>if(hMem == NULL) {
> ::CloseClipboard();
> return;
>}
>
>LPVOID lptstrCopy = ::GlobalLock(hMem);

****
If it is an LPVOID, why did you call it an lptstrCopy? Nonsense. Don't use Hungarian
Notation if you can't use it correctly! This is an incorrect usage. Just drop it
entirely, it is confusing and in this case misleading.
****


>::memcpy(lptstrCopy, item_data->epc, (item_data->epc.GetLength() + 1) *
>sizeof(TCHAR));
>::GlobalUnlock(hMem);
>
>::SetClipboardData(CF_TEXT, hMem);

****
As long as item_data->epc is a TCHAR array or CString, this will be correct. Otherwise, I
have no idea what you just put into the clipboard.

Where did you call ::EmptyClipboard?
****


>
>::CloseClipboard();
>
>
>It bothers me just now, when I am using CF_TEXT has this to be ASCII or
>unicode?

****
CF_TEXT will store the data in the format used by your app, and read about text
conversions on GetClipboardData. It will widen ANSI text if it needs Unicode and
down-convert (with possible errors) Unicode text if that's what is in the clipboard and it
needs ANSI
****


>
>> Think about "undo" if you implement "cut" rather than just "copy". I've found a one-level
>> undo is often sufficient (multi-level undo is complex and if you need it, be prepared to
>> do a lot of work)
>
>Luckily I do not need any cut so there is (not yet) any need for an
>undo.

****
I see this copies just a single selection. Nothing wrong with that, but if you ever allow
multiple select you will have to deal with that issue
joe
****

Oliver Regenfelder

unread,
May 22, 2010, 7:01:54 AM5/22/10
to
Joseph M. Newcomer wrote:
> Probably unnecessary. I have no idea what you mean by "portable" because a Windows app
> can only run on Windows and Windows-like clones (e.g. WINE) so there is no portability
> issue. Whenever you suggest the work "portable" you imply that the application will run
> on a non-Windows platform, and this is not going to happen.

I think portable was a bad word to use. Will CTRL-C==03 hold on any version of windows
and with any locale character set?

As a first line of defense I have to say I copied that code from the MSDN almost
verbatim (for the Clippoard data bards).

>> ItemData *item_data = reinterpret_cast<ItemData*>(GetItemData(index));
> ****
> What is in the ItemData field? You have not said.

ItemData is a struct holding the data of each row.
struct ItemData {
...
CString epc;
...
};

> ****
>> HANDLE hMem = ::GlobalAlloc(0
> ****
> Why 0? For clipboard data, it is supposed to be GMEM_MOVEABLE!

Because MSDN hat it this way? Ok. I will change it. Are there any better
sources than MSDN on using the clipboard correctly?

> ****
>> , (item_data->epc.GetLength() + 1)* sizeof(TCHAR));
> ****
> WHy is the comma for the first parameter the first character of the second line?

In my opinion it is more readable, but that is a personal matter of coding style
I would say.

> ****
>> if(hMem == NULL) {
>> ::CloseClipboard();
>> return;
>> }
>>
>> LPVOID lptstrCopy = ::GlobalLock(hMem);
> ****
> If it is an LPVOID, why did you call it an lptstrCopy? Nonsense. Don't use Hungarian
> Notation if you can't use it correctly! This is an incorrect usage. Just drop it
> entirely, it is confusing and in this case misleading.

I copied it from the MSDN, and when it didn't compile (surprise) I only changed the
type to correct it. I wil remove the hungarian notation.

> ****
>> ::memcpy(lptstrCopy, item_data->epc, (item_data->epc.GetLength() + 1) *
>> sizeof(TCHAR));
>> ::GlobalUnlock(hMem);
>>
>> ::SetClipboardData(CF_TEXT, hMem);
> ****
> As long as item_data->epc is a TCHAR array or CString, this will be correct. Otherwise, I
> have no idea what you just put into the clipboard.

Sorry yes. item_data->epc is a CString.

> Where did you call ::EmptyClipboard?

I can not. I use
::OpenClipboard(NULL)
in that case the task itself holds the clipboard and you are not supposed to call
::EmptyClipboard(). This is a bad idea?

> CF_TEXT will store the data in the format used by your app, and read about text
> conversions on GetClipboardData.

So if I compile it as MBCS application it will automatically behave correctly and
if I later change it to be UNICODE then it will also automatically behave correctly.

Thanks,

Oliver

Joseph M. Newcomer

unread,
May 22, 2010, 12:31:45 PM5/22/10
to
See below...

On Sat, 22 May 2010 13:01:54 +0200, Oliver Regenfelder <oliver.re...@gmx.at> wrote:

>Joseph M. Newcomer wrote:
>> Probably unnecessary. I have no idea what you mean by "portable" because a Windows app
>> can only run on Windows and Windows-like clones (e.g. WINE) so there is no portability
>> issue. Whenever you suggest the work "portable" you imply that the application will run
>> on a non-Windows platform, and this is not going to happen.
>
>I think portable was a bad word to use. Will CTRL-C==03 hold on any version of windows
>and with any locale character set?

****
Yes, because it is a character set standard. Ctrl+C=0x03 on any encoding, including
Unicode.
****


>
>As a first line of defense I have to say I copied that code from the MSDN almost
>verbatim (for the Clippoard data bards).
>
>>> ItemData *item_data = reinterpret_cast<ItemData*>(GetItemData(index));
>> ****
>> What is in the ItemData field? You have not said.
>ItemData is a struct holding the data of each row.
>struct ItemData {
> ...
> CString epc;

****
OK, that makes sense. Presumably this is a string which represents the entire row. I
would typically compute this string at the point I needed it, but then I'm lazy.
****


> ...
>};
>
>> ****
>>> HANDLE hMem = ::GlobalAlloc(0
>> ****
>> Why 0? For clipboard data, it is supposed to be GMEM_MOVEABLE!
>
>Because MSDN hat it this way? Ok. I will change it. Are there any better
>sources than MSDN on using the clipboard correctly?
****

Sadly, I find that many of the MSDN examples seem to have been written by summer interns
right after their "introduction to Windows programming" course. The kinds of errors they
contain are often newbie errors. I have no idea why 0 would be used.

All the examples I found under "Using the Clipboard" show GMEM_MOVEABLE

****
No; typically you always specify a window, even if it is the main application window. It's
safer that way; I never opened a clipboard with a NULL handle. The problems arise if you
try to paste from another app, and I've long since forgotten the conditions under which
this fails (it is more widespread than failing to know who to call for WM_RENDERFORMAT and
friends, but it is Just One Of Those Things To Avoid)

But if you do OpenClipboard(NULL), you should not call EmptyClipboard, for the reasons
stated in the MSDN.
****


>
>> CF_TEXT will store the data in the format used by your app, and read about text
>> conversions on GetClipboardData.
>
>So if I compile it as MBCS application it will automatically behave correctly and
>if I later change it to be UNICODE then it will also automatically behave correctly.
>

****
Yes. The conversion table is under SetClipboardData, not GetClipboardData.
joe
****
>Thanks,

0 new messages