How to "refresh" TListBox? - firemonkey

I'm creating an app which shows bill numbers, like the one you see at Mcdonald's. A POS system send bill numbers to my app and the numbers are showed in a TListBox called "ListBoxPrep". Then, when the POS system sends my app the number of a bill to be removed, my app gets rid of it from "ListBoxPrep" and add it to "ListBoxReady". Every communication between the POS and my app is done via TCP connection and I have no problem with it.
The problem I'm facing is that I still see the number remain in "ListBoxPrep" even after deleting it by "pItem->Free();". "pItem" is a pointer of TListBoxItem. I want the numbers disappear as soon as my app receives the "delete signal" from the POS and especially without user's interation such as clicking the panel etc. I think of using TTimer, but I have no idea how to make "ListBoxPrep" refresh by itself. Do you have any idea to do that? Any suggestion would be appreciated. I'm using RAD Studio 10.4.
After my app received the "delete signal" from the POS, I still see the numbers at right side. They are supposed to disappear.
As soon as I click the "ListBoxPrep", the numbers disappear.
void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
{
//We receive data: POS --> Screen(PC)
String sentDataFromPOS = AContext->Connection->Socket->ReadLn();
if(sentDataFromPOS .IsEmpty())
{
ShowMessage("Data sent from POS is empty!");
return;
}
// 1. Find an order number to move to the right (prep -> ready)
int indexOrderToRemove = ListBoxPrep->Items->IndexOf(sentDataFromPOS);
// 2. Add the order number to the "Ready list"
addNumberToReady(sentDataFromPOS);
// 3. Remove the order from the "Prep list"
ListBoxPrep->BeginUpdate();
TListBoxItem* pItem = ListBoxPrep->ItemByIndex(indexOrderToRemove);
pItem->Free(); // HERE I have a problem
// test: To refresh the screen
LayoutLeft->Visible = false;
LayoutLeft->Visible = true;
/*
ListBoxPrep->Enabled = false;
ListBoxPrep->Visible = false;
ListBoxPrep->Enabled = true;
ListBoxPrep->Visible = true;
ListBoxPrep->Repaint();
*/
ListBoxPrep->EndUpdate();
}

TIdTCPServer is a multi-threaded component. Its OnExecute event is called in the context of a worker thread. As such, it MUST synchronize with the main UI thread when accessing UI controls (that goes for ShowMessage() too, BTW). You can use the RTL's TThread::Synchronize() (synchronous) or TThread::Queue() (asynchronous) method for that.
Also, you should not be Free()'ing the TListBoxItem objects directly. You have the index of the desired item, you can use ListBoxPrep->Items->Delete() instead.
If you are using one of the clang-based compilers, try something more like this:
void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
{
//We receive data: POS --> Screen(PC)
String sentDataFromPOS = AContext->Connection->Socket->ReadLn();
if (sentDataFromPOS.IsEmpty())
{
TThread::Synchronize(nullptr,
[](){ ShowMessage("Data sent from POS is empty!"); }
);
return;
}
TThread::Queue(nullptr, // or Synchronize(), your choice...
[=, this](){ this->orderIsReady(sentDataFromPOS); }
);
}
void __fastcall TForm1::orderIsReady(String orderNumber)
{
// 1. Find an order number to move to the right (prep -> ready)
int indexOrderToRemove = ListBoxPrep->Items->IndexOf(orderNumber);
// 2. Add the order number to the "Ready list"
addNumberToReady(orderNumber);
// 3. Remove the order from the "Prep list"
if (indexOrderToRemove != -1)
ListBoxPrep->Items->Delete(indexOrderToRemove);
}
If, on the other hand, you are using the "classic" Borland compiler, then try this instead:
struct orderHelper
{
String orderNumber;
orderHelper(const String &orderNumber)
: orderNumber(orderNumber)
{
}
void __fastcall orderIsReady()
{
Form1->orderIsReady(orderNumber);
}
};
void __fastcall TForm1::orderIsEmpty()
{
ShowMessage("Data sent from POS is empty!");
}
void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
{
//We receive data: POS --> Screen(PC)
String sentDataFromPOS = AContext->Connection->Socket->ReadLn();
if (sentDataFromPOS.IsEmpty())
{
TThread::Synchronize(NULL, &orderIsEmpty);
return;
}
orderHelper helper(sentDataFromPOS);
TThread::Synchronize(NULL, &(helper.orderIsReady));
}
void __fastcall TForm1::orderIsReady(String orderNumber)
{
// 1. Find an order number to move to the right (prep -> ready)
int indexOrderToRemove = ListBoxPrep->Items->IndexOf(orderNumber);
// 2. Add the order number to the "Ready list"
addNumberToReady(orderNumber);
// 3. Remove the order from the "Prep list"
if (indexOrderToRemove != -1)
ListBoxPrep->Items->Delete(indexOrderToRemove);
}
Or this:
struct orderHelper
{
String orderNumber;
orderHelper(const String &orderNumber)
: orderNumber(orderNumber)
{
}
void __fastcall orderIsReady()
{
try {
Form1->orderIsReady(orderNumber);
} __finally {
delete this;
}
}
};
void __fastcall TForm1::orderIsEmpty()
{
ShowMessage("Data sent from POS is empty!");
}
void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
{
//We receive data: POS --> Screen(PC)
String sentDataFromPOS = AContext->Connection->Socket->ReadLn();
if (sentDataFromPOS.IsEmpty())
{
TThread::Synchronize(NULL, &orderIsEmpty);
return;
}
orderHelper *helper = new orderHelper(sentDataFromPOS);
TThread::Queue(NULL, &(helper->orderIsReady));
}
void __fastcall TForm1::orderIsReady(String orderNumber)
{
// 1. Find an order number to move to the right (prep -> ready)
int indexOrderToRemove = ListBoxPrep->Items->IndexOf(orderNumber);
// 2. Add the order number to the "Ready list"
addNumberToReady(orderNumber);
// 3. Remove the order from the "Prep list"
if (indexOrderToRemove != -1)
ListBoxPrep->Items->Delete(indexOrderToRemove);
}

I modified some code in IdTCPServerExecute to remove a few compile errors. Here is the code which has worked well. Thank you again, Remy Lebeau !
void __fastcall TForm1::IdTCPServerExecute(TIdContext *AContext)
{
//We receive data: POS --> Screen(PC)
String sentDataFromPos = AContext->Connection->Socket->ReadLn();
// test
//sentDataFromPos = "";
if(sentDataFromPos.IsEmpty())
{
TThread::Synchronize(nullptr,
[=](){ ShowMessage("Data sent from POS is empty!"); }
);
return;
}
TThread::Synchronize(nullptr,
// Queue doesn't make the numbers disappear. It doesn't display them at right side either .
[&, this]()
{
this->orderIsReady(sentDataFromPos);
}
);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::orderIsReady(String orderNumber)
{
// 1. Find an order to move to the right (prep -> ready)
int indexOrderToRemove = ListBoxPrep->Items->IndexOf(orderNumber);
// 2. Add an order of the same order number to the "Ready list"
addNumberToReady(orderNumber);
// 3. Remove the order from the "Prep list"
if(indexOrderToRemove != -1)
{
ListBoxPrep->Items->Delete(indexOrderToRemove);
}
}

Related

I want to copy, paste and cut content in my CEdit from my Clipboard

This is the event handlers i implemented to the copy, paste and Cut buttons in my MFCRibbonBar:
in the MyRibbonView.cpp:
void CMyRibbonView::OnEditCopy()
{
CWnd *wnd = GetFocus();
if (wnd == pEdit)
pEdit->Copy();
if (!OpenClipboard())
{
AfxMessageBox(_T("Cannot open the Clipboard"));
return;
}
if (!EmptyClipboard())
{
AfxMessageBox(_T("Cannot empty the Clipboard"));
return;
}
HGLOBAL hGlob = GlobalAlloc(GMEM_FIXED, 64);
strcpy_s((char*)hGlob, 64, "Current selection\r\n");
if (::SetClipboardData(CF_TEXT, hGlob) == NULL)
{
CString msg;
msg.Format(_T("Unable to set Clipboard data, error: %d"), GetLastError());
AfxMessageBox(msg);
CloseClipboard();
GlobalFree(hGlob);
return;
}
CloseClipboard();
}
void CMyRibbonView::OnEditPaste()
{
if (OpenClipboard())
{
HANDLE hClipboardData = GetClipboardData(CF_TEXT);
char *pchData = (char*)GlobalLock(hClipboardData);
CString strFromClipboard;
strFromClipboard = pchData;
pEdit->SetWindowText(strFromClipboard);
GlobalUnlock(hClipboardData);
CloseClipboard();
}
}
void CMyRibbonView::OnEditCut()
{
OnEditCopy();
pEdit->SetWindowText(L" ");
}
There is no errors, it's just not working. I tested it by adding the messages to check if it's actually the data or not but they're not popping up.
You need to GlobalLock your hGlob memory before copying your character string into it (this operation converts it into a usable pointer for your process - see here), and then call GlobalUnlock after you've done that (so that the clipboard can access hGlob):
HGLOBAL hGlob = GlobalAlloc(GMEM_FIXED, 64); // Maybe also need GMEM_MOVEABLE here instead?
char* cCopy = (char*)GlobalLock(hGlob);
strcpy_s(cGlob, 64, "Current selection\r\n");
GlobalUnlock(hGlob);
if (::SetClipboardData(CF_TEXT, hGlob) == NULL)
{
//...
And you'll need a similar arrangement for the paste operation.

xamarin android: showing ads in recycleview hide some articles in rss entries

I'm trying to show ads in recycleview and i succeeded to do it using the code below .. the problem is that in every "MspaceBetweenAds" position the ad show up but the article at this replaced with the ad
i tried to fix it by modifying ItemCount() by Mposts.Count + (Mposts.count% MspaceBetweenAds) but i'm getting "IndexOutOfBounds " error
any help please .. this is my code
public class AdsView : ListViewHolder
{
public AdView mAdView { get; private set; }
public AdsView(View view) : base(view)
{
mAdView = view.FindViewById<AdView>(Resource.Id.AdsCard);
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
RecyclerView.ViewHolder vh = null;
switch (viewType)
{
case 1:
View vBig = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.BigCard, parent, false);
vh = new MyView(vBig);
break;
case 2:
View vAds = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.AdsCard, parent, false);
vh = new AdsView(vAds);
break;
}
return vh;
}
public override void OnBindListViewHolder(ListViewHolder holder, int position)
{
var MyHolder = holder as MyView;
switch (holder.ItemViewType)
{
case 1:
// code to show posts articles here
break;
case 2:
var AdHolder = holder as AdsView;
fnc.AddBannerAd(AdHolder.mAdView);
break;
}
}
public override int GetItemViewType(int position)
{
if (position > 0 && position % mSpaceBetweenAds == 0) { return 2; }
else { return 1; }
}
and this is a demo app https://drive.google.com/open?id=1Tk3G8dw9nqIffxmEFNGqIgXNzCJJPxD_
As the demo you posted contains 3rd party package, I cannot run it directly and modify the demo for you. The flowing is my solution to your problem:
Error causes:
The way you do (modifying ItemCount() by Mposts.Count + (Mposts.count% MspaceBetweenAds) cannot change the real length of Mposts, as a result, it results in the
"IndexOutOfBounds " error.
Solution
If you want to insert ads into your recylerview, you need not only modify your adapter to show both item and ads, but also need to modify your layout resource file, that is, you need to insert the ads data into the data list for of your recylerview. Or you can simply add a duplicated item to the list every [MspaceBetweenAds]items.

Matrix conversion OpenGL ES 2.0

I would like to know how I can use this matrix in my custom GL renderer to implement pinch zoom. I keep getting an error that is telling me I cannot set an opengl.Matrix which is type float[16] with the Matrix object I am changing in my activity below. What is the correct way to pass my graphics.matrix through such that it will update my view correctly? As a workaround I am passing the newDist value to my renderer, but then every time I touch the screen with 2 fingers, it resets the eyeZ value, so I think I really need to get this matrix to work so it holds my zoom values.
#Override public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_MOVE:
if (mode == ZOOM) {
final float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
mGLSurfaceView.queueEvent(new Runnable() {
#Override
public void run() {
mGLSurfaceView.setMyMatrix(newDist);
Log.d(TAG, "runnable exists");
}
});
}
}
break;
}
return true; // indicate event was handled
}

alBufferData() sets AL_INVALID_OPERATION when using buffer ID obtained from alSourceUnqueueBuffers()

I am trying to stream audio data from disk using OpenAL's buffer queueing mechanism. I load and enqueue 4 buffers, start the source playing, and check in a regular intervals to refresh the queue. Everything looks like it's going splendidly, up until the first time I try to load data into a recycled buffer I got from alSourceUnqueueBuffers(). In this situation, alBufferData() always sets AL_INVALID_OPERATION, which according to the official v1.1 spec, it doesn't seem like it should be able to do.
I have searched extensively on Google and StackOverflow, and can't seem to find any reason why this would happen. The closest thing I found was someone with a possibly-related issue in an archived forum post, but details are few and responses are null. There was also this SO question with slightly different circumstances, but the only answer's suggestion does not help.
Possibly helpful: I know my context and device are configured correctly, because loading small wav files completely into a single buffer and playing them works fine. Through experimentation, I've also found that queueing 2 buffers, starting the source playing, and immediately loading and enqueueing the other two buffers throws no errors; it's only when I've unqueued a processed buffer that I run into trouble.
The relevant code:
static constexpr int MAX_BUFFER_COUNT = 4;
#define alCall(funcCall) {funcCall; SoundyOutport::CheckError(__FILE__, __LINE__, #funcCall) ? abort() : ((void)0); }
bool SoundyOutport::CheckError(const string &pFile, int pLine, const string &pfunc)
{
ALenum tErrCode = alGetError();
if(tErrCode != 0)
{
auto tMsg = alGetString(tErrCode);
Log::e(ro::TAG) << tMsg << " at " << pFile << "(" << pLine << "):\n"
<< "\tAL call " << pfunc << " failed." << end;
return true;
}
return false;
}
void SoundyOutport::EnqueueBuffer(const float* pData, int pFrames)
{
static int called = 0;
++called;
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
if(tState == AL_STATIC)
{
Stop();
// alCall(alSourcei(mSourceId, AL_BUFFER, NULL));
}
ALuint tBufId = AL_NONE;
int tQueuedBuffers = QueuedUpBuffers();
int tReady = ProcessedBuffers();
if(tQueuedBuffers < MAX_BUFFER_COUNT)
{
tBufId = mBufferIds[tQueuedBuffers];
}
else if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}
void* tConverted = convert(pData, pFrames);
// the fifth time through, we get AL_INVALID_OPERATION, and call abort()
alCall(alBufferData(tBufId, mFormat, tConverted, pFrames * mBitdepth/8, mSampleRate));
alCall(alSourceQueueBuffers(mSourceId, 1, &mBufferId));
if(mBitdepth == BITDEPTH_8)
{
delete (uint8_t*)tConverted;
}
else // if(mBitdepth == BITDEPTH_16)
{
delete (uint16_t*)tConverted;
}
}
void SoundyOutport::PlayBufferedStream()
{
if(!StreamingMode() || !QueuedUpBuffers())
{
Log::w(ro::TAG) << "Attempted to play an unbuffered stream" << end;
return;
}
alCall(alSourcei(mSourceId, AL_LOOPING, AL_FALSE)); // never loop streams
alCall(alSourcePlay(mSourceId));
}
int SoundyOutport::QueuedUpBuffers()
{
int tCount = 0;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &tCount));
return tCount;
}
int SoundyOutport::ProcessedBuffers()
{
int tCount = 0;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_PROCESSED, &tCount));
return tCount;
}
void SoundyOutport::Stop()
{
if(Playing())
{
alCall(alSourceStop(mSourceId));
}
int tBuffers;
alCall(alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &tBuffers));
if(tBuffers)
{
ALuint tDummy[tBuffers];
alCall(alSourceUnqueueBuffers(mSourceId, tBuffers, tDummy));
}
alCall(alSourcei(mSourceId, AL_BUFFER, AL_NONE));
}
bool SoundyOutport::Playing()
{
ALint tPlaying;
alCall(alGetSourcei(mSourceId, AL_SOURCE_STATE, &tPlaying));
return tPlaying == AL_PLAYING;
}
bool SoundyOutport::StreamingMode()
{
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
return tState == AL_STREAMING;
}
bool SoundyOutport::StaticMode()
{
ALint tState;
alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState));
return tState == AL_STATIC;
}
And here's an annotated screen cap of what I see in my debugger when I hit the error:
I've tried a bunch of little tweaks and variations, and the result is always the same. I've wasted too many days trying to fix this. Please help :)
This error occurs when you trying to fill buffer with data, when the buffer is still queued to the source.
Also this code is wrong.
if(tQueuedBuffers < MAX_BUFFER_COUNT)
{
tBufId = mBufferIds[tQueuedBuffers];
}
else if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}
You can fill buffer with data only if it unqueued from source. But your first if block gets tBufId that queued to the source. Rewrite code like so
if(tReady > 0)
{
// the fifth time through, this code gets hit
alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId));
// debug code: make sure these values go down by one
tQueuedBuffers = QueuedUpBuffers();
tReady = ProcessedBuffers();
}
else
{
return; // no update needed yet.
}

javafx, TableView: detect a doubleclick on a cell

Given a TableView, i need to detect the doubleclick on a cell.
tableView.setOnMouseClicked(new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
if(event.getClickCount()>1)
{
System.out.println("double clicked!");
}
}
});
How to determine the cell on which the mouse has been clicked?
Code example.
Run the "Example 12-11: Alternative Solution Of Cell Editing" of official tableview tutorial.
Replace the followings:
table.setEditable(false);
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
TableCell cell = new TableCell<Person, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() > 1) {
System.out.println("double clicked!");
TableCell c = (TableCell) event.getSource();
System.out.println("Cell text: " + c.getText());
}
}
});
return cell;
}
};
No need to EditingCell since your cells are uneditable. Cell factory is used for cell rendering. So one can put any node/control other than default Labeled using cell's setGraphics() method. IMO you cannot access the default cell directly so you should define your own cell factory to be able to put event filter on cell.
JavaFX allows you to set up multiple listeners per cell (I'm not saying that this is good or bad, just that you can). Each listener will execute your code if you have code set to execute a response to the specific listener for the specific column/row. To capture cell mouse clicks, I use the following:
table.setEditable(true);
table.getSelectionModel().setCellSelectionEnabled(true); // selects cell only, not the whole row
table.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent click) {
if (click.getClickCount() == 2) {
#SuppressWarnings("rawtypes")
TablePosition pos = table.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
int col = pos.getColumn();
#SuppressWarnings("rawtypes")
TableColumn column = pos.getTableColumn();
String val = column.getCellData(row).toString(); System.out.println("Selected Value, " + val + ", Column: " + col + ", Row: " + row);
if ( col == 2 ) { ... do something ... }
if ( col == 5 ) { ... do something ... }
if ( col == 6 ) { ... do something ... }
if ( col == 8 ) { ... do something ... }
}
}
});
You can see from the above code, on the columns I want to do something based on a mouse click, I have code:
if ( col == <int> ) { ... do something ... }
I also have those columns set to not allow editing:
thisCol.setEditable(false);
The rows that I want to edit I have .setEditable(true) but don't have a response included with a mouse click.
Cell editing defaults to 2 mouse clicks. You can change the above code to capture different mouse events on a cell, so you can still edit the cell with 2 mouse clicks, or open a URL, dialog box, etc., with any other mouse event determined by you. TableView allows you to determine your own functionality based on your imagination and programming skills. You're not stuck with "I can either edit it, or fire a mouse event with it." You can do both :)
Add the following in the body of your listener, with T the type of your table record :
#SuppressWarnings("rawtypes")
ObservableList<TablePosition> cells = tableView.getSelectionModel().getSelectedCells();
for( TablePosition< T, ? > cell : cells )
{
System.out.println( cell.getColumn());
}// for
Create your cell using a cell factory and in the cell factory which creates the cell node, place an mouse event handler or filter on the node rather than the tableView.
In my case i use next code
tableViewObject.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (t.getClickCount() == 2 && getSelectedItem() != null) {
SMPBLogger.logInfo("Double cliked", Boolean.TRUE);
if (listener != null) {
listener.doubleClicked(tableViewObject.this,getSelectedItem());
}
}
}
});

Resources