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.
Related
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.
I am looking at using the Windows Threading API and the issue it seems to have is you cannot keep track of when all the threads are completed. You can keep track of when the work item has been completed, assuming you kept track of each one. From my research there is no direct way to query the thread pool to see if the work items submitted has all be completed.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
VOID CALLBACK MyWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK Work) {
DWORD threadId = GetCurrentThreadId();
BOOL bRet = FALSE;
printf("%d thread\n", threadId);
return;
}
int main() {
TP_CALLBACK_ENVIRON CallBackEnviron;
PTP_POOL pool = NULL;
PTP_CLEANUP_GROUP cleanupgroup = NULL;
PTP_WORK_CALLBACK workcallback = MyWorkCallback;
PTP_TIMER timer = NULL;
PTP_WORK work = NULL;
InitializeThreadpoolEnvironment(&CallBackEnviron);
pool = CreateThreadpool(NULL);
SetThreadpoolThreadMaximum(pool, 1);
SetThreadpoolThreadMinimum(pool, 3);
SetThreadpoolCallbackPool(&CallBackEnviron, pool);
for (int i = 0; i < 10; ++i) {
work = CreateThreadpoolWork(workcallback, NULL, &CallBackEnviron);
SubmitThreadpoolWork(work);
WaitForThreadpoolWorkCallbacks(work, FALSE); // This waits for the work item to get completed.
}
return 1;
}
Here is a simple example. What happens is on the WaitForThreadpoolWorkCallbacks I am able to wait on that specific work item. Which is no problem if I am doing a few things. However, if I am traversing a directory and have thousands of files that I need to have work done on them, I don't want to keep track of each individual work item. Is it possible to query the Thread Pool queue to see if anything is left for processing? Or to find out if any of the threads are still working?
you need keep track of active tasks ( like pendcnt in comment) +1. but this must not be global variable, but member in some struct. and pass pointer to this struct to work item. increment this counter before call SubmitThreadpoolWork and decrement from callback, before exit. but you also need and event - set this event in signal state, when counter became 0. and wait on event from main thread. if your code inside dll, which can be unloaded - you need also reference dll, before SubmitThreadpoolWork and FreeLibraryWhenCallbackReturns from callback. also important that counter value - was 1 (not 0) ininitally - so this is count_of_active_cb + 1, and decrement it before begin wait (if not do this - counter can became 0 early - for instance first callback exit before you activate second)
class Task
{
HANDLE _hEvent = 0;
ULONG _dwThreadId = 0;
LONG _dwRefCount = 1;
public:
~Task()
{
if (_hEvent) CloseHandle(_hEvent);
}
ULONG Init()
{
if (HANDLE hEvent = CreateEvent(0, 0, 0, 0))
{
_hEvent = hEvent;
return NOERROR;
}
return GetLastError();
}
void AddTask()
{
InterlockedIncrementNoFence(&_dwRefCount);
}
void EndTask()
{
if (!InterlockedDecrement(&_dwRefCount))
{
if (_dwThreadId != GetCurrentThreadId())
{
if (!SetEvent(_hEvent)) __debugbreak();
}
}
}
void Wait()
{
_dwThreadId = GetCurrentThreadId();
EndTask();
if (_dwRefCount && WaitForSingleObject(_hEvent, INFINITE) != WAIT_OBJECT_0) __debugbreak();
}
};
VOID CALLBACK MyWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK /*Work*/)
{
// need only if your code in dll which can be unloaded
FreeLibraryWhenCallbackReturns(Instance, (HMODULE)&__ImageBase);
WCHAR sz[32];
swprintf_s(sz, _countof(sz), L"[%x] thread", GetCurrentThreadId());
MessageBoxW(0, 0, sz, MB_ICONINFORMATION);
reinterpret_cast<Task*>(Parameter)->EndTask();
}
void CbDemo()
{
Task task;
if (task.Init() == NOERROR)
{
ULONG n = 2;
do
{
if (PTP_WORK pwk = CreateThreadpoolWork(MyWorkCallback, &task, 0))
{
HMODULE hmod;
// need only if your code in dll which can be unloaded
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (PWSTR)&__ImageBase, &hmod))
{
task.AddTask();
SubmitThreadpoolWork(pwk);
}
CloseThreadpoolWork(pwk);
}
} while (--n);
MessageBoxW(0, 0, L"Main Thread", MB_ICONWARNING);
task.Wait();
__nop();
}
}
I've edited the WDK kbfiltr.c callback routine to intercept the Esc key and replace it with 'E'.
It works except that it always replaces it with 2 'E's.
So pressing Esc will output 'ee'.
Here's the code:
{
PKEYBOARD_INPUT_DATA pCur = InputDataStart;
PDEVICE_EXTENSION devExt;
WDFDEVICE hDevice;
hDevice = WdfWdmDeviceGetWdfDeviceHandle(DeviceObject);
devExt = FilterGetData(hDevice);
while (pCur < InputDataEnd)
{
ULONG consumed = 0;
if (pCur->MakeCode == 0x01) {//Esc
pCur->MakeCode = 0x12; //E
}
else{
pCur++;
continue;
}
// indicate one packet at a time
(*(PSERVICE_CALLBACK_ROUTINE)(ULONG_PTR)
devExt->UpperConnectData.ClassService)(
devExt->UpperConnectData.ClassDeviceObject,
pCur,
pCur+1,
&consumed);
pCur++;
}
// tell the caller you consumed everything
*InputDataConsumed = (InputDataEnd-InputDataStart);
(*(PSERVICE_CALLBACK_ROUTINE)(ULONG_PTR) devExt->UpperConnectData.ClassService)(
devExt->UpperConnectData.ClassDeviceObject,
InputDataStart,
InputDataEnd,
InputDataConsumed);
}
Anyone know what I'm doing wrong?
I think this was coding error.
Changing the code as below seems to make it work.
{
PKEYBOARD_INPUT_DATA pCur = InputDataStart;
PDEVICE_EXTENSION devExt;
WDFDEVICE hDevice;
hDevice = WdfWdmDeviceGetWdfDeviceHandle(DeviceObject);
devExt = FilterGetData(hDevice);
while (pCur < InputDataEnd)
{
ULONG consumed = 0;
if (pCur->MakeCode == 0x01) {//Esc
pCur->MakeCode = 0x12; //E
}
// indicate one packet at a time
(*(PSERVICE_CALLBACK_ROUTINE)(ULONG_PTR)
devExt->UpperConnectData.ClassService)(
devExt->UpperConnectData.ClassDeviceObject,
pCur,
pCur+1,
&consumed);
pCur++;
}
// tell the caller you consumed everything
*InputDataConsumed = (InputDataEnd-InputDataStart);
}
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.
I'm making a board like this
GtkWidget *board[x][y];
If I do an array of buttons, how can I know which button was pressed?
Does
g_signal_connect(G_OBJECT(board[][]), "clicked",
G_CALLBACK(board_button_pressed), NULL);
// I want to know what [][] they pressed, how could I verify/check this?
return which button of the array was pressed? Or do I have to make a separate function for each of the board pieces?
For example:
OOO
OXO
OOO
How to know which button was pressed if all of the buttons are named the same?
One of the simplest way would be to just send the information when you are connecting to the callback as data. Something on these lines:
...
typedef struct _identifier{
int x;
int y;
}identifier;
static void button_clicked_cb(GtkButton *button, gpointer data)
{
(void)button; /*To get rid of compiler warning*/
identifier *id = data;
printf("\n id = %d, %d\n", id->x, id->y);
return;
}
....
identifier id[x*y]; /* Size of x*y of the board*/
unsigned int counter = 0;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
id[counter].x = i;
id[counter].y = j;
board[i][j] = gtk_button_new ();
g_signal_connect(board[i][j], "clicked", G_CALLBACK(button_clicked_cb), &id[counter]);
counter++;
}
}
Please note that "clicked" signal is associated only with GtkButton. If you need to use with GtkWidget then look at "button-press-event" or "button-release-event", in which case the callback signature will also change.
Hope this helps!