problems with old c code with new ncurses version (ldat struct) - c

I have a problem with some code using curses after upgrading to a new server and thus also new software like libs, headers and such.
The problem is the use of the ldat struct fields "firstchar", "lastchar" and "text" which in the the newer versions of curses.h is hidden in the curses.priv.h and therefore they are not resolved.
I could really use some pointers as to how I might be able to resolve these issues.
The code below indicates the use of the struct fields, but it just a part of the complete code as it several thousand lines...
If there is need for additional code I can add this.
I might also add that I have not made this program myself, I'm just responsible for making it work with our new server...
int
update_window(changed, dw, sw, win_shared)
bool *changed;
WINDOW *dw; /* Destination window */
window_t *sw; /* Source window */
bool win_shared;
{
int y, x;
int yind, nx, first, last;
chtype *pd, *ps; /* pd = pointer destination, ps = pointer source */
int nscrolls; /* Number of scrolls to make */
if(! sw->changed) {
*changed = FALSE;
return(0);
}
/****************************************
* Determine number of times window is
* scrolled since last update
****************************************/
nscrolls = sw->scrollcount; if(nscrolls >= sw->ny)
nscrolls = 0;
sw->scrollcount = 0L;
dw->_flags = _HASMOVED;
dw->_cury = sw->cury;
dw->_curx = sw->curx;
if(nscrolls > 0) {
/* Don't copy lines that is scolled away */
for(y = nscrolls; y < sw->ny; y++) {
yind = GETYIND(y - nscrolls, sw->toprow, sw->ny);
if(sw->lastch[yind] != _NOCHANGE) {
first = dw->_line[y].firstchar = sw->firstch[yind];
last = dw->_line[y].lastchar = sw->lastch[yind];
ps = &sw->screen[yind][first];
pd = (chtype *)&dw->_line[y].text[first];
nx = last - first + 1;
LOOPDN(x, nx)
d++ = *ps++;
if(! win_shared) {
sw->firstch[yind] = sw->nx;
sw->lastch[yind] = _NOCHANGE;
}
}
}
} else {
LOOPUP(y, sw->ny) {
yind = GETYIND(y, sw->toprow, sw->ny);
if(sw->lastch[yind] != _NOCHANGE) {
first = dw->_line[y].firstchar = sw->firstch[yind];
last = dw->_line[y].lastchar = sw->lastch[yind];
ps = &sw->screen[yind][first];
pd = (chtype *)&dw->_line[y].text[first];
nx = last - first + 1;
LOOPDN(x, nx)
*pd++ = *ps++;
if(! win_shared) {
sw->firstch[yind] = sw->nx;
sw->lastch[yind] = _NOCHANGE;
}
}
}
if(! win_shared)
sw->changed = FALSE;
}
*changed = TRUE;
return(nscrolls);
}
I appreciate all the help I can get!

The members of struct ldat were made private in June 2001. Reading the function and its mention of scrolls hints that it is writing a portion of some window used to imitate scrolling (by writing a set of lines to the real window), and attempting to bypass the ncurses logic which checks for changed lines.
For a function like that, the only solution is to determine what the developer was trying to do, and write a new function which does this — using the library functions provided.

Related

Passing a void (*fn) from a class

I'm writing an auto display turn-off function with ESP32 on Arduino framework with PIO.
I have a Screen class for handling all of the screen functions.
void Screen::turn_off_screen(){
digitalWrite(SCREEN_ENABLE, LOW);
}
void turn_off_screen_wrapper()
{
Serial.println("turn_off_screen_wrapper called");
if (c_screen_Instance != nullptr)
{
c_screen_Instance->turn_off_screen();
}
}
void Screen::auto_display_power_off(int timeout){
Serial.println("auto_display_power_off called");
c_screen_Instance = this;
auto_off_timer = timerBegin(0, 80, true);
Serial.println("auto_off_timer ran");
timerAttachInterrupt(auto_off_timer, &turn_off_screen_wrapper, true);
Serial.println("timerAttachInterrupt ran");
//Converts given seconds from us to seconds
timerAlarmWrite(auto_off_timer,timeout*1000000,false);
timerAlarmEnable(auto_off_timer);
}
The code compiles however I get this when I run it on the board.
auto_display_power_off called
[E][esp32-hal-cpu.c:93] addApbChangeCallback(): duplicate func=400811F8 arg=3FFBDC54
auto_off_timer ran
The screen never gets turned off of course since the callback never runs. Any ideas why this is happening?
is c_screen_Instance global?
is auto_off_timer global?
Consider providing a bit more of your code.
But anyway.
bool addApbChangeCallback(void * arg, apb_change_cb_t cb){
initApbChangeCallback();
apb_change_t * c = (apb_change_t*)malloc(sizeof(apb_change_t));
if(!c){
log_e("Callback Object Malloc Failed");
return false;
}
c->next = NULL;
c->prev = NULL;
c->arg = arg;
c->cb = cb;
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
if(apb_change_callbacks == NULL){
apb_change_callbacks = c;
} else {
apb_change_t * r = apb_change_callbacks;
// look for duplicate callbacks
while( (r != NULL ) && !((r->cb == cb) && ( r->arg == arg))) r = r->next;
if (r) {
log_e("duplicate func=%8p arg=%8p",c->cb,c->arg);
free(c);
xSemaphoreGive(apb_change_lock);
return false;
}
else {
c->next = apb_change_callbacks;
apb_change_callbacks-> prev = c;
apb_change_callbacks = c;
}
}
xSemaphoreGive(apb_change_lock);
return true;
}
This is addApbChangeCallback's declaration.
Your error comes from this line :
while( (r != NULL ) && !((r->cb == cb) && ( r->arg == arg))) r = r->next;
Where r it's a struct to hold all the callbacks.
This error indeed indicates this callback function was already assigned somewhere in your code. r is global, so your code is re-assigning the same callback twice.
Try to either only assign it once, or to unassign the function before assigning it again with removeApbChangeCallback(void * arg, apb_change_cb_t cb) or timerDetachInterrupt
I've also found a reported issue related to timerAttach on the current version here: https://github.com/espressif/arduino-esp32/issues/6730
Try to roll back the Platform PIO's version to a more stable one:
# instead of espressif32
platform = https://github.com/platformio/platform-espressif32.git#<tag-version>
Check on the git link for the available tags you can use.
Problem was that I was attaching the interrupt in the void loop(). Which would run way faster than the actual timer. After moving it to setup (Setup being a placeholder) I plan on having it on a Hardware interrupt it worked as expected.

Excel Add-in: Assignment in for loop causes segmentation fault but line-by-line assignments work. Why?

I just tested a toy Excel add-in project, cross building the XLL with mingw32 tool chains.
Here is my code:
//testXLL.c
#include "windows.h"
#include "xlcall.h"
#define MEMORYSIZE 65535000
char vMemBlock[MEMORYSIZE];
int vOffsetMemBlock =0;
LPSTR GetTempMemory(int cBytes){
LPSTR lpMemory;
if(vOffsetMemBlock + cBytes > MEMORYSIZE)
return 0;
else{
lpMemory = (LPSTR) &vMemBlock + vOffsetMemBlock;
vOffsetMemBlock += cBytes;
if(vOffsetMemBlock & 1) vOffsetMemBlock++;
return lpMemory;
}
}
LPXLOPER TempStr(LPSTR lpstr){
LPXLOPER lpx;
int chars;
lpx = (LPXLOPER)GetTempMemory(sizeof(XLOPER));
if(!lpx) return 0;
chars = lstrlen(lpstr);
if(chars>255) chars=255;
lpx->val.str=(char*)GetTempMemory((sizeof(char)*chars+1));
if(!lpx->val.str) return 0;
strncpy(lpx->val.str, lpstr,chars);
lpx->val.str[0]=(BYTE) chars;
//lpx->val.str[chars]='\0';
lpx->xltype = xltypeStr;
return lpx;
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) double __stdcall myadd2(double a1,double a2){
return a1+a2;
}
static char functionTable[11][255] =
{" myadd2", // procedure
" BBB", // type_text
" add", // function_text
" add1,add2", // argument_text
" 1", // macro_type
" category", // category
" ", // shortcut_text
" some help topic", // help_topic
" Adds toy", // function_help
" 1st.", // argument_help1
" 2nd" // argument_help2
};
__declspec(dllexport) int __stdcall xlAutoOpen(){
LPXLOPER pxDLL;
Excel4(xlGetName,pxDLL,0);
XLOPER xlRegArgs[11];
for(int i = 0; i < 11; i++){
xlRegArgs[i] = *TempStr(functionTable[i]);
}
Excel4(xlfRegister, 0, 12,
pxDLL,
&xlRegArgs[0], &xlRegArgs[1], &xlRegArgs[2],
&xlRegArgs[3], &xlRegArgs[4], &xlRegArgs[5],
&xlRegArgs[6], &xlRegArgs[7], &xlRegArgs[8],
&xlRegArgs[9], &xlRegArgs[10]);
return 1;
}
__declspec(dllexport) LPXLOPER __stdcall xlAddInManagerInfo(LPXLOPER xlAction) {
static XLOPER xlReturn, xlLongName, xlTemp;
xlTemp.xltype = xltypeInt;
xlTemp.val.w = xltypeInt;
Excel4(xlCoerce, &xlReturn, 2, xlAction, &xlTemp);
if(1 == xlReturn.val.w) {
xlLongName = *TempStr(" xll-name");
} else {
xlLongName.xltype = xltypeErr;
xlLongName.val.err = xlerrValue;
}
return &xlLongName;
}
#ifdef __cplusplus
}
#endif
I built this testXLL.c file in Ubuntu:
>i686-w64-mingw32-gcc -shared -Wl,--kill-at testXLL.c -o win.xll -L. -lxlcall32
This generates the "win.xll" successfully but, when loading this win.xll, Excel crashes.
In Windows 10, I tried to use gdb to debug it, but I can't catch break point in the xll file – it got disabled automatically when loading. But I can see in the gdb output, it is a segmentation fault when Excel crashes.
XLOPER xlRegArgs[11];
for(int i = 0; i < 11; i++){
xlRegArgs[i] = *TempStr(functionTable[i]);
}
What's weird is that, if I substitute the above for loop with the following line-by-line assignments in the xlAutoOpen function, the compiled XLL file works fine in Excel:
XLOPER xlRegArgs[11];
xlRegArgs[0] = *TempStr(functionTable[0]);
xlRegArgs[1] = *TempStr(functionTable[1]);
xlRegArgs[2] = *TempStr(functionTable[2]);
xlRegArgs[3] = *TempStr(functionTable[3]);
xlRegArgs[4] = *TempStr(functionTable[4]);
xlRegArgs[5] = *TempStr(functionTable[5]);
xlRegArgs[6] = *TempStr(functionTable[6]);
xlRegArgs[7] = *TempStr(functionTable[7]);
xlRegArgs[8] = *TempStr(functionTable[8]);
xlRegArgs[9] = *TempStr(functionTable[9]);
xlRegArgs[10] = *TempStr(functionTable[10]);
Please enlighten me. What's the difference between these two assignment approaches?
Although I don't (yet) have a full explanation for this behaviour, I'm posting this as a possible 'workaround', which I have used in a very similar case I encountered in one of my projects.
The issue appears to be some form of 'stack corruption' caused by the use of the function-local variable (i) used as the loop index; converting this to a global/static variable will likely fix the issue. The following code snippet is a suggested fix (I have changed the name of the index variable to avoid possible name clashes elsewhere in the code):
///...
static int regloop; // Used as the loop index, below...
__declspec(dllexport) int __stdcall xlAutoOpen(){
LPXLOPER pxDLL;
Excel4(xlGetName,pxDLL,0);
XLOPER xlRegArgs[11];
for(regloop = 0; regloop < 11; regloop++){
xlRegArgs[regloop] = *TempStr(functionTable[regloop]);
}
Here's the section of code from my aforementioned project (but note this is C++/MFC) that exhibits the same sort of behaviour – but only in x86 builds (x64 builds work without issue):
static int plin; // NOTA BENE:- We use this in the two functions below, as the use of
// a local 'plin' loop index is prone to induce stack corruption (?),
// especially in MSVC 2017 (MFC 14) builds for x86.
void BasicApp::OnUpdatePICmd(uint32_t nID, void *pUI)
{
//! for (int plin = 0; plin < Plugin_Number; ++plin) { // Can cause problems - vide supra
for (plin = 0; plin < Plugin_Number; ++plin) {
BOOL mEbl = FALSE; int mChk = -1;
if ((Plugin_UDCfnc[plin] != nullptr) && Plugin_UDCfnc[plin](nID, &mEbl, &mChk)) {
CommandEnable(pUI, mEbl ? true : false);
if (mChk >= 0) CmdUISetCheck(pUI, mChk);
return;
}
}
CommandEnable(pUI, false);
return;
}
(The Plugin_UDCfnc is a static array member of the BasicApp class.)
I have, in the years since the above code was written, had occasional 'fleeting insights' into why this is happening but, as of now, I can't offer a more robust fix. I shall revisit the issue and update this post if I should stumble upon a resolution. In the meantime, others are welcome to take this as a 'clue' and post their own explanations/solutions.

Stream sine wave to XAudio2

I am trying to write a very simple sine wave generator that plays out through XAudio2.
Currently there is sound playing, and if I call Win32XAudioInit() and then Win32PlayTone() a tone will play, and the tone will change on subsequent calls to Win32PlayTone(), however there is a noticeable click almost every time the tone changes.
I know there are a few reasons that could cause this:
I am not keeping track of the phase-offset, which means new waves would be misaligned.
I am simply updating the Memory that the buffer is pointing to without regard to what is playing.
Regarding #2, I am not sure if XAudio wants me to create a new XAUDIO2_BUFFER and resubmit that every time I change the tone, or if I am supposed to somehow keep track of where the 'playhead' is (for lack of a better term) and only update bytes that have already been played.
I know if #2 is a problem, I won't be able to hear if I fixed it I am still plagued by problem #1.
I have read through XAudio2 - Play generated sine, when changing frequency clicking sound and I think I could figure out the sin wave problem if I knew XAudio2 was set up correctly.
Any thoughts would be helpful, thanks!
struct win32_audio_buffer
{
real32 Memory[44100 * 1]; // samples per buffer (44100) * channels 1
int BytesPerBuffer;
XAUDIO2_BUFFER XBuffer;
IXAudio2 *XEngine;
IXAudio2SourceVoice *SourceVoice;
WAVEFORMATEX WaveFormat;
};
// NOTE XAUDIO2
internal HRESULT
Win32XAudioInit(win32_audio_buffer *AudioBuffer)
{
// Initialize a COM:
HRESULT HRes;
HRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(FAILED(HRes)) { return(HRes); }
// Init XAUDIO Engine
AudioBuffer->XEngine = {};
if (FAILED(HRes = XAudio2Create(&AudioBuffer->XEngine, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{ return HRes; }
// MASTER VOICE
IXAudio2MasteringVoice* XAudioMasterVoice = nullptr;
if (FAILED(HRes = AudioBuffer->XEngine->CreateMasteringVoice(&XAudioMasterVoice)))
{ return HRes; }
AudioBuffer->WaveFormat = {};
//int32 SamplesPerBuffer = 4410;
int SampleHz = 44100;
WORD Channels = 1;
WORD BitsPerChannel = 32; // 4 byte samples
int32 BufferSize = Channels * BitsPerChannel * SampleHz;
AudioBuffer->BytesPerBuffer = SampleHz * Channels;
AudioBuffer->WaveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; // or could use WAVE_FORMAT_PCM WAVE_FORMAT_IEEE_FLOAT
AudioBuffer->WaveFormat.nChannels = Channels;
AudioBuffer->WaveFormat.nSamplesPerSec = SampleHz;
AudioBuffer->WaveFormat.wBitsPerSample = BitsPerChannel; // 32
AudioBuffer->WaveFormat.nBlockAlign = (Channels * BitsPerChannel) / 8;
AudioBuffer->WaveFormat.nAvgBytesPerSec = SampleHz * Channels * BitsPerChannel / 8;
AudioBuffer->WaveFormat.cbSize = 0; // set to zero for PCM or IEEE float
AudioBuffer->XBuffer.Flags = 0;
AudioBuffer->XBuffer.AudioBytes = SampleHz * Channels * BitsPerChannel / 8;
AudioBuffer->XBuffer.PlayBegin = 0;
AudioBuffer->XBuffer.PlayLength = 0;
AudioBuffer->XBuffer.LoopBegin = 0;
AudioBuffer->XBuffer.LoopLength = 0;
AudioBuffer->XBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
AudioBuffer->XBuffer.pContext = NULL;
AudioBuffer->XBuffer.pAudioData = (BYTE *)&AudioBuffer->Memory;
if(FAILED(HRes = AudioBuffer->XEngine->CreateSourceVoice(&AudioBuffer->SourceVoice, (WAVEFORMATEX*)&AudioBuffer->WaveFormat)))
{ return HRes; }
if(FAILED(HRes = AudioBuffer->SourceVoice->Start(0)))
{ return HRes; }
if(FAILED(HRes = AudioBuffer->SourceVoice->SubmitSourceBuffer(&AudioBuffer->XBuffer)))
{ return HRes; }
return(S_OK);
}
internal HRESULT
Win32PlayTone(win32_audio_buffer *Buffer, int32 Hz)
{
real32 PI2 = (real32)6.28318; //530718;
for(int i = 0;
i < Buffer->BytesPerBuffer;
i++)
{
real32 CurrentSample = sinf(i * PI2 / 44100 * Hz);
Buffer->Memory[i] = CurrentSample;
}
return(S_OK);
}
XAudio2 is entirely asynchronous, so you should not change the memory pointed to by a playing packet until the packet is completed or you will get clicks as you describe.
Also, when the current packet completes, you want to have another one already queued up if you want the sound to be continuous.
See these resources for learning how to program XAudio2:
https://github.com/walbourn/directx-sdk-samples/tree/master/XAudio2
https://github.com/microsoft/Xbox-ATG-Samples/tree/master/UWPSamples/Audio
DirectX Tool Kit for Audio

OpenCL device side enqueue kernel & local memory

I'm trying to use local memory inside a device-side enqueued kernel.
My assumption that any locally-declared array is visible across all work items in the work group.
This is proven to be true when I use local memory on kernels that are called from the host-side, but I'm running into problems when I use a similar setup on device-side enqueued kernels.
Is there something wrong with my assumption?
Edit:
My kernel is below:
My goal is to sort the FIFO pipe into 3 buffers. The problem is that my work items have a limited view scope, and I'm trying to write the buffers into another pipe.
int pivot;
int in_pipe[BIN_SIZE];
int lt_bin[BIN_SIZE];
int gt_bin[BIN_SIZE];
int e_bin[BIN_SIZE];
reserve_id_t down_id = work_group_reserve_read_pipe(down_pipe, local_size);
//while ( is_valid_reserve_id(down_id) == false){
// down_id = work_group_reserve_read_pipe(down_pipe, local_size);
//}
//in_bin[tid] = -5;
if( is_valid_reserve_id(down_id) == true){
int status = read_pipe(down_pipe, down_id, lid, &pipe_out);
work_group_commit_read_pipe(down_pipe, down_id);
pivot = pipe_out;
pivot = work_group_broadcast(pivot, 0);
work_group_barrier(CLK_GLOBAL_MEM_FENCE);
work_group_barrier(CLK_LOCAL_MEM_FENCE);
in_pipe[tid] = pipe_out;
//in_bin[lid] = in_pipe[tid];
int e_count = 0;
int gt_count = 0;
int lt_count = 0;
if(in_pipe[tid] == pivot){
e_count = 1;
}
else if(in_pipe[tid] < pivot){
lt_count = 1;
}
else if(in_pipe[tid] > pivot){
gt_count = 1;
}
int e_tot = work_group_reduce_add(e_count);
e_tot = work_group_broadcast(e_tot, 0);
int e_val = work_group_scan_exclusive_add(e_count);
int gt_tot = work_group_reduce_add(gt_count);
gt_tot = work_group_broadcast(gt_tot, 0);
int gt_val = work_group_scan_exclusive_add(gt_count);
int lt_tot = work_group_reduce_add(lt_count);
lt_tot = work_group_broadcast(lt_tot, 0);
int lt_val = work_group_scan_exclusive_add(lt_count);
//in_bin[tid] = lt_val;
work_group_barrier(CLK_GLOBAL_MEM_FENCE);
work_group_barrier(CLK_LOCAL_MEM_FENCE);
if(in_pipe[tid] == pivot){
e_temp[e_val] = in_pipe[tid];
//in_bin[e_val] = e_bin[e_val];
//e_bin[e_Val] = work_group_broadcast(e_bin[e_val], lid);
}
if(in_pipe[tid] < pivot){
lte_temp[lt_val] = in_pipe[tid];
//in_bin[lt_val] = lt_bin[lt_val];
}
if(in_pipe[tid] > pivot){
gt_bin[gt_val] = in_pipe[tid];
//in_bin[gt_val] = gt_bin[gt_val];
}
No, not wrong. Local variables are declared and used across whole work-groups device-side too. They won't be shared with the parent kernels, though.
What exactly are you doing?
The working solution to my question is:
Pipes cannot be created on the device side. What I tried to accomplish was to make a dynamic tree structure, involving branches. OpenCL pipes simply cannot do that, as pipes are still memory objects, created on the host-side. There is no current way in the specifications to create memory objects.
Pipes, however, can be used in a dynamically-recursive method, albeit the recursion cannot deviate, and must occur in a linear fashion. Please consult the sample code found in the AMD APP SDK sample code packs for more details. Specifically, please look at the Device Enqueue BFS example.

only single selection in CListCtrl Checkbox in MFC

In a MFC dialog, I have used a CListCtrl with checkbox. I want to disable multi checkbox selection , so that user can only select a single checkbox at a time. What is the best way to achieve this.I have done this
void SomeClass::OnClickList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
int nSelectedItemIndex = -1;
nSelectedItemIndex = m_ListCtrl.GetNextItem(-1, LVNI_SELECTED);
int nCount = m_ListCtrl.GetItemCount();
for(int nItem = 0; nItem < nCount; nItem++)
{
m_ListCtrl.SetCheck(nItem,false);
}
if(nSelectedItemIndex != -1)
m_ListCtrl.SetCheck(nSelectedItemIndex,true);
*pResult = 0;
}
Somehow I think this method is not so proper and can be made better in other way. All suggesions are welcomed.
EDIT: UPDATE: after writing the code , everything is working but I am facing a new problem.
calling SetCheck() function inside OnItemChanged message handler function, it is calling the same function again, creating a recursion.Thus selection change is somehow slow. How to avoid this.Please help. ????
Finally I have solved it . Now the checkbox and selection works at par. selecting checkbox selects the row and vice versa and one selection is possible. the code:
void SomeClass::ResetAllCheckBox()
{
int nCount = m_ListCtrl.GetItemCount();
for(int nItem = 0; nItem < nCount; nItem++)
{
m_ListCtrl.SetCheck(nItem,false);
}
}
//Handler for ON_NOTIFY(NM_CLICK,...)
void SomeClass::OnClickList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
NMLISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
LVHITTESTINFO hitinfo;
int nPosCB=-1,nPos=-1;
hitinfo.pt = pNMListView->ptAction;
//Make the hit test...
nPosCB = m_ListCtrl.HitTest(&hitinfo);
if(hitinfo.flags != LVHT_ONITEMSTATEICON)
return;
ResetAllCheckBox();
nPos = m_ListCtrl.GetNextItem(-1,LVNI_SELECTED);
m_ListCtrl.SetItemState(nPos, ~LVIS_SELECTED, LVIS_SELECTED);
m_ListCtrl.SetItemState(nPosCB, LVIS_SELECTED, LVIS_SELECTED);
m_ListCtrl.SetSelectionMark(nPosCB);
*pResult = 0;
}
//Handler for ON_NOTIFY(LVN_ITEMCHANGED,...)
void SomeClass::OnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
int nPos = -1;
ResetAllCheckBox();
nPos = m_ListCtrl.GetNextItem(-1,LVNI_SELECTED);
if(nPos != -1)
m_ListCtrl.SetCheck(nPos);
int nCount = m_ListCtrl.GetItemCount();
int nSelectedItemIndex = -1;
for(int nItem = 0; nItem < nCount; nItem++)
{
if(m_ListCtrl.GetCheck(nItem)== 1)
nSelectedItemIndex = nItem;
}
*pResult = 0;
}
When creating the control make sure this style is used LVS_SINGLESEL.
It is passed in the CreateEx/CreateEx function. Also available from the resource editor (if control is added through it).
I found a shorter solution. The idea is to detect if the user checked a checkbox. If it is the case, unchecked all the other checboxes, otherwise do nothing. Contrary to the answer by #egur, this solution does not check the checkbox upon selection of the row.
static void DisableAllItemsExcept(CListCtrl& ctrl, int indexToKeepChecked)
{
for (int nItem = 0; nItem < ctrl.GetItemCount(); nItem++)
if (nItem != indexToKeepChecked)
ctrl.SetCheck(nItem, FALSE);
}
//in begin message map
//ON_NOTIFY(LVN_ITEMCHANGED, IDC_SC_LIST, &CMyDlg::OnLvnItemchangedScList)
void CMyDlg::OnLvnItemchangedScList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
//detect if we checked a new element
if ( (pNMLV->uChanged & LVIF_STATE) && (pNMLV->uNewState & 0x2000) && (pNMLV->uOldState & 0x1000) )
DisableAllItemsExcept(m_lcList, pNMLV->iItem);
*pResult = 0;
}
The detection of state change was based on the following article : Get the notification code from Listview Control checkboxes. The values 0x1000 and 0x2000 do not seems to be defined anywhere in the header file, so maybe I'm using an undocumented feature.

Resources