Category: StackOverflow

Error in FMX.Treeview function TTreeViewContent.GetLastVisibleObjectIndex

I am migrating a VCL application to Firemonkey. It features a couple of TTreeview controls which display folder trees. In the main form's OnCreate event handler the paths of the folders are read from a .Ini file and the treeviews are set up. At some point before the main form appears on screen, an exception occurs in the FMX.TreeView TTreeViewContent.GetLastVisibleObjectIndex function, which is: function TTreeViewContent.GetLastVisibleObjectIndex: Integer; var Item: TTreeViewItem; begin if (FTreeView.FGlobalList.Count > 0) and (FTreeView.FLastVisibleItem < FTreeView.FGlobalList.Count) then begin Item := FTreeView.FGlobalList[FTreeView.FLastVisibleItem]; {etc.} end else Result := ControlsCount; end; I have inspected the values of the variables and found that FTreeView.FGlobalList.Count =1 and FTreeView.FLastVisibleItem = -1. The error occurs in the statement Item := FTreeView.FGlobalList[FTreeView.FLastVisibleItem]; since the array index is invalid. This code seems to be related to determining which treeview items are visible within the scrolling window of the treeview control. Since the error occurs before the form has been displayed, I tried making the treeview invisible during the update of the treview, as in the following code: procedure TFormMain.UpdateTreeview(var Folder: TFolder; Treeview: TTreeview); begin if Folder= FInputFolder then begin if not FTreeviewInputFolderValid then begin Treeview.Visible:= False; Treeview.BeginUpdate; FolderToTreeView(Folder, Treeview); //Treeview.InvalidateRect(Treeview.ContentRect); Treeview.ExpandAll; Treeview.EndUpdate; Treeview.Visible:= True; FTreeviewInputFolderValid:= True; end; end; {Ditto for FOutputFolder} end; If the program is run without setting up the treeview controls before the form is shown, i.e. by not reading the folder paths from a .ini file and not updating the treeview controls, then the error does not occur. Any suggestions about how to avoid what seems to be a coding error in function TTreeViewContent.GetLastVisibleObjectIndex? In answer to Tom, the code of FolderToTreeview is: procedure TFormMain.FolderToTreeview(Folder: TFolder; Treeview: TTreeview); var TreeviewOwner: TComponent; RootNode: TTreeViewItemFolderCpt; procedure AddFolderChildCpts(ParentTreeNode: TTreeViewItemFolderCpt; Folder: TFolder); var i: integer; FolderCpt: TFolderCpt; FileCpt: TFileCpt; SubFolder: TFolder; ChildTreeNode: TTreeViewItemFolderCpt; Found: Boolean; begin {Add all cpts of folder to child nodes of ParentTreeNode} for i:= 0 to Folder.CptCount-1 do begin FolderCpt:= Folder.Cpts[i]; ChildTreeNode:= TTreeViewItemFolderCpt.Create(ParentTreeNode); ParentTreeNode.AddObject(ChildTreeNode); ChildTreeNode.Parent:= ParentTreeNode; ChildTreeNode.FolderCpt:= FolderCpt; ChildTreeNode.OnPaint:= TreeViewItemPaint; if FolderCpt is TFileCpt then begin FileCpt:= FolderCpt as TFileCpt; ChildTreeNode.ImageIndex:= 1; end else if FolderCpt is TFolder then begin SubFolder:= FolderCpt as TFolder; ChildTreeNode.ImageIndex:= 0; {Recursively add subfolder:} AddFolderChildCpts(ChildTreeNode, SubFolder); end; end; end; begin if not Folder.IsSorted then Folder.Sort(True); {Delete all existing nodes in tree:} Treeview.Clear; {Create a new root node and add to tree:} RootNode:= TTreeviewItemFolderCpt.Create(Treeview); Treeview.AddObject(RootNode); RootNode.Parent:= Treeview; {Link folder object to root tree node:} RootNode.FolderCpt:= Folder; RootNode.ImageIndex:= 0; RootNode.OnPaint:= TreeViewItemPaint; {Now install child folder cpts:} AddFolderChildCpts(RootNode, Folder); end; TFolder, TFolderCpt, TFileCpt are elements of a separate class hierarchy to store in a memory tree structure the names and metadata of all files and folders under the root folder. The root object of class TFolder has a method Read(Path: string) which generates all its nodes by visiting the files and directories under the root directory using FindFirst and FindNext procedures. Folder.Read is called before FolderToTreeview is called. Because of this extra complication I don't think that you be able to run the FolderToTreeview method.
Read More

How to delete TChart (FMX, C++)

I can't figure out how to delete a TChart. This has to be simple, but I'm not seeing it. In the TListView ButtonClick code below, I delete a series from the active TChart and, if the series happens to be the last one, then I want to delete the entire TChart. TComponent *T; // find chart and delete the selected series for (int i = 0; i < (Form1->ComponentCount); i++) { T = Form1->Components[i]; if (T->ClassName() == "TChart") { int test = T->Tag; if (test == TappedChartTag) { //TappedChartTag is global if (TChart *TC = dynamic_cast<TChart *>(T)) { TC->RemoveSeries(AItem->Index); // this removes the series if (TC->SeriesCount()==0) { // if this was last series, delete chart TC->CleanupInstance(); } } } } } I can't find a way to completely get rid of the TChart. The TC->CleanupInstance(); wipes it completely (nothing visible), but the TChart is still there. I'm working in Embarcadero Rad Studio (C++ Builder 10.3 Update 1). UPDATE: I tried delete TC; in place of that TC->CleanupInstance(); and that works on Win32 but not on iOS.
Read More

How to reference NFCNDEFReaderSession from IOSapi.CoreNFC in Delphi FMX App

I try to include some NFC reading functionality in my Delphi IOS 64bit App. (RAD Studio 10.3.2). But I cannot find the right way to reference NFCNDEFReaderSession. All results in the message ObjectiveC class NFCNDEFReaderSession could not be found. uses System.SysUtils, Macapi.ObjectiveC, iOSapi.Foundation, iOSapi.CoreNFC, FMX.Helpers.iOS; type //..... //from IOapi.CoreNFC only for information NFCNDEFReaderSessionClass = interface(NFCReaderSessionClass) ['{8F8EFAB7-FEC0-4DC8-ABD2-669A639617E4}'] end; NFCNDEFReaderSession = interface(NFCReaderSession) ['{11F096EA-B10B-4566-909F-5C72A930084A}'] procedure setReadingAvailable(readingAvailable: Boolean); cdecl; function readingAvailable: Boolean; cdecl; function initWithDelegate(delegate: Pointer; queue: dispatch_queue_t; invalidateAfterFirstRead: Boolean): Pointer { instancetype }; cdecl; end; //..... TNFCNDEFReaderSession = class(TOCGenericImport<NFCNDEFReaderSessionClass, NFCNDEFReaderSession>) end; TNFCNDEFReaderSessionDelegate = class(TOCLocal,NFCNDEFReaderSessionDelegate) public procedure readerSessionDidInvalidateWithError(session: NFCNDEFReaderSession; didInvalidateWithError: NSError); cdecl; procedure readerSessionDidDetectNDEFs(session: NFCNDEFReaderSession; didDetectNDEFs: NSArray); cdecl; end; TIOS_NFCUtil = class private sessionDelegate: TNFCNDEFReaderSessionDelegate; session : NFCNDEFReaderSession; public constructor create; end; var IOS_NFCUtil: TIOS_NFCUtil; implementation uses FMX.DialogService; { TIOS_NFCUtil } constructor TIOS_NFCUtil.create; var rsession: NFCReaderSession; begin try sessionDelegate := TNFCNDEFReaderSessionDelegate.Create; // Create OK rsession := TNFCReaderSession.Create; // Session := TNFCNDEFReaderSession.Create; session := TNFCNDEFReaderSession.Wrap(TNFCNDEFReaderSession.Alloc.initWithDelegate(sessionDelegate,nil, false)); except on e: Exception do begin TDialogService.showmessage(e.Message); end; end; end; How to initialize the NFC session? ---> session:=NFCNDEFReaderSession.Wrap(TNFCNDEFReaderSession.Alloc.initWithDelegate(sessionDelegate,nil, false)); Checked out to initialize NFCNDEFReaderSession inside TNFCNDEFReaderSessionDelegate class, but ObjectiveC class is still not found type TNFCNDEFReaderSessionDelegate = class(TOCLocal,NFCNDEFReaderSessionDelegate) private session: NFCNDEFReaderSession; public procedure InitReaderSession; procedure readerSessionDidInvalidateWithError(session: NFCNDEFReaderSession; didInvalidateWithError: NSError); cdecl; procedure readerSessionDidDetectNDEFs(session: NFCNDEFReaderSession; didDetectNDEFs: NSArray); cdecl; end; procedure TNFCNDEFReaderSessionDelegate.InitReaderSession; var step: Integer; begin try step := 0; //session := TNFCNDEFReaderSession.Create; TNFCNDEFReaderSession.Wrap(TNFCNDEFReaderSession.Alloc.initWithDelegate(self,nil,false)); step := 1; (*if assigned(session) then begin step := 2; session.initWithDelegate(self,nil,false); step := 3; session.beginSession; step := 4; end; *) except on e: Exception do begin TDialogService.showmessage(e.Message+ ' ' + step.ToString); end; end; end; MessageBox: ObjectiveC class not found (step 0) also in case of session := TNFCNDEFReaderSession.Create;
Read More

iOS Notification sound playing in app, although this hasn’t been initiated by me (iOS 12 only)

Ever since updating devices to iOS 12, my Delphi application has started playing sound notifications when a specific operation is done in the application. This has not been coded or configured by me. When using the application in iOS 11 or lower, the sound notification does not play. The project is built and compiled in Delphi 10.3.1. I cannot find any mentions of this phenomena anywhere in the RAD Studio documentation, or online. Disabling notifications for the application in the iOS settings does work, however this solution has its drawbacks obviously. Edit: This is the documentation I have been reading: http://docwiki.embarcadero.com/RADStudio/Rio/en/Mobile_Tutorial:_Using_Notifications_(iOS_and_Android) This operation, when initiated, starts an animation of a spinning wheel, indicating waiting (not TAniIndicator). A TTimer is also started at this point, indicating to the user how long the wait has been. However, I cannot see how the event handler in the timer could make this happen: procedure MainForm.timer_loadingTimer(Sender: TObject); begin tt_loadingText.Tag := tt_loadingText.Tag + 1; tt_loadingText.Text := tt_loadingText.Tag.ToString + ' seconds.'; end; Has anyone ever run into the same issue, and has any idea what could cause it? I suspect there is some configuration somewhere, or maybe it is a side effect hidden deep in my code.
Read More

Prevent firing events while scrolling TVertScrollBox

Normally, while scrolling the contents of a "scroll box", no event functions are fired from the sub-components of a scroll box, e. g. in native apps. But in FireMonkey, if a TVertScrollBox contains sub-elements like TRectangle (which I want to use as menu entries for a custom menu), scrolling the TVertScrollBox on Android with a finger sometimes triggers the event functions (like OnClick) of the sub-elements and this is very confusing for me and our customers - They don't want to tap a specific element while scrolling. In native apps this never happens. I couldn't figure out how to prevent this behaviour. I tried to set the HitTest property to FALSE for all sub-elements in the OnMouseEnter and OnMouseLeave (I also tried other events) with something like this: procedure TframeCornerMenu.VertScrollBox1MouseEnter(Sender: TObject); var list: TRectangle; i: Integer; begin list := FindComponent('rectMenuList') as TRectangle; for i := 0 to list.ChildrenCount - 1 do begin if list.Children[i] is TRectangle then TRectangle(list.Children[i]).HitTest := false; end; end; But this obviously doesn't (and can't) work, because the user taps the sub-elements first which are lying on top of the TVertScrollBox. Is this a bug / not implemented feature in FireMonkey? I appreciate all ideas solving this scrolling problem. If possible, without third-party components. I am using Delphi Community Edition 10.3.2 (26.0.34749.6593).
Read More

How to check a BLE device still connected with Delphi 10.3

I've successfully connected my Delphi(FM) app to a ESP32 device and I can send commands/receive data over BLE (I am showing a message for connection status after connection is successful/unsuccessful). But problem is that, if user close/power off the ESP32 device then mobile app still shows connection established. I've tried both System.Bluetooth.TBluetoothConnectionState and System.Bluetooth.TBluetoothLEManager.ConnectionState with a TTimer but no success. if not (BLEDevice1.ConnectionState = TBluetoothConnectionState.Connected) then begin Showmessge('Connection not established!'); // this type of control doesn't change anything. end;
Read More

Proper way to detect on button click on a Dynamic Listview Appearance

My listview is set to dynamic appearance. It has 2 TListItemTextButtons on it. I want to assign a sort of "OnClick" event to each of those buttons on each items. The thing is Almost every listview items needs a different proc to run on it and I know exactly how to do it. When I normally create a dynamic TButton I can assign a custom proc to it using AnonProc2NotifyEvent from TProc<TObject> to TNotifyEvent. I'm aware of the OnItemClickEx a normal listview has but it seems a little buggy. Example: lets say Listviewitem has a itemindex of 3 and you as a user click on the button in listviewitem 1 then the button from listviewitem 3 will activate. Embarcadero Docs says that there's a OnClick event however I cannot find one: http://docwiki.appmethod.com/appmethod/1.15/libraries/en/FMX.ListView.Types.TListItemTextButton_Events How can I go about assigning a OnClick for a TListItemTextButton? Example of what I want to do: LItem.Objects.FindObjectT<TListItemText>('Username').Text := 'Some username'; LItem.Objects.FindObjectT<TListItemText>('Info').Text := 'Some extra info'; LItem.Objects.FindObjectT<TListItemTextButton>('Kick').OnClick := AnonProc2NotifyEvent(lvPlayers, procedure(Sender: TObject) begin SendRconCommand('removeconnection ' + jplayers['userid'].AsString); end); However this provides a Undeclared Identifier for OnClick.
Read More

How to use curl for Dropbox token in Firemonkey

I’m trying to get Dropbox token base on Dropbox documentation. For that I have to use (from Dropbox documentation): https://www.dropbox.com/developers/documentation/http/documentation#oauth2-token URL: https://api.dropboxapi.com/oauth2/token METHOD: POST EXAMPLE: curl https://api.dropbox.com/oauth2/token \ -d code=<AUTHORIZATION_CODE> \ -d grant_type=authorization_code \ -d redirect_uri=<REDIRECT_URI> \ -u <APP_KEY>:<APP_SECRET> But I don't know how to do that in Firemonkey. I was trying to do something like that but without success. My code: HTTP := TIdHTTP.Create(nil); try HTTP.HandleRedirects := True; HTTP.ConnectTimeout := FHTTPTimeout; HTTP.Request.BasicAuthentication := False; HTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP); HTTP.Request.Accept := 'application/x-www-form-urlencoded'; HTTP.Request.ContentType := 'application/json'; sTemp:= '{ "code"="<' + Settings.SynchroDropBoxCode + '>" ' + keyCrLf + ' "grant_type"="authorization_code" ' + keyCrLf + ' "redirect_uri"="<http://127.0.0.1:8888/>" ' + keyCrLf + ' "<' + cloudDropBoxMobileAppKey + '>":"<'+cloudDropBoxMobileAppKeySecret+'>" }'; DataToSend := TStringStream.Create(sTemp, TEncoding.UTF8, True); try sHTTPResponse := HTTP.Post('https://api.dropbox.com/oauth2/token', DataToSend); finally DataToSend.Free; end; finally HTTP.Free; end;
Read More

Getting parameters from IdHTTPServer – Firemonkey

I’m trying to get Dropbox token from call-back parameters but the parameters are always empty. Here is my code: sURL := https://www.dropbox.com/oauth2/authorize' + '?response_type=token' + '&client_id=' + MyAppID + '&redirect_uri=http://127.0.0.1:8888/'; ShellExecute(0, 'OPEN', PChar(sURL), '', '', SW_SHOWNORMAL); Next I have IdHTTPServer set to listen http://127.0.0.1:8888/ address. On executing the browser with Dropbox log-in popup. When I made log-in the redirection is made and the “IdHTTPServerCommandGet” events is called. So far so good. In the browser I can see the next URL: http://127.0.0.1:8888/#access_token=123&token_type=bearer&uid=1234&account_id=dbid%1234 But in “IdHTTPServerCommandGet” events the parameters are always empty: ARequestInfo.UnparsedParams; // Empty ARequestInfo. QueryParams; // Empty if ARequestInfo.Params.Count > 0 then // Empty sToken := ARequestInfo.Params[0]; And here is my question. How can I get the whole URL with parameters from IdHTTPServer?
Read More

Pick columns from csv (FMX, C++)

I am reading one column of data out of a big CSV file (column 3). I want to know if there is a more efficient way of doing this. Right now i read each entire row, and then pick out my column. String S; TStringList *List = new TStringList; List->LoadFromFile(myCSV); // load the CSV file std::auto_ptr<TStringList>pListA(new TStringList); for (int i = 0; i < 400; i++) { // for testing just getting first 400 rows. pListA->Text = StringReplace(List[0].Strings[i], ",", "\r\n", TReplaceFlags() << rfReplaceAll); S = pListA->Strings[3]; // do something with this value from column 3 } This code works fine, but it has to read the entire row of CSV data just to let me get that one column. Below is sample of the first 2 rows from the CSV data: 1,0,-6856,12830,8458,-16666,98717,-10718,-80874,49999,-99998,98569,99998,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 2,8948,-6856,12830,8458,-16666,39641,63125,-99973,99998,-99998,39385,99998,1,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 UPDATE: Based on Remy's code I ended up with this below. TStreamReader* Reader; Reader = new TStreamReader(myCSV); while (!Reader->EndOfStream) { String S = Reader->ReadLine(); int start = PosEx(_D(","), S, 1); start = PosEx(_D(","), S, start+1) + 1; int end = PosEx(_D(","), S, start); S = S.SubString1(start, end-start); // use S } Reader->Close(); delete Reader->BaseStream; delete Reader;
Read More

Is there a way to write in an locale database?

i'm learning to use firemonkey, and i made an app that i'm using on my phone (it works like a reminder if u want) so basically there's a database file (SQLite) in my phone's internal storage, and in the form i put a TMemo + FDConnection + FDPhysSQLiteDriverLink so the app can read the database and display it on the TMemo, now i did put a TEdit and i added a button i want what i write on the TEdit to be added to the database. i'm new to SQL delphi and stack overflow in general. thanks PS: i'm trying to do it without using livebindings. var query: TFDQuery; begin query := TFDQuery.Create(nil); try // Define the SQL Query query.Connection := FDConnection1; query.SQL.Text := 'SELECT * FROM Employee'; query.Open(); outputMemo.Text := ''; // Add the field names from the table. outputMemo.Lines.Add(String.Format('|%8s|%-25s|%-25s|', [' ID ', ' NAME >', ' DEPARTMENT '])); // Add one line to the memo for each record in the table. while not query.Eof do begin outputMemo.Lines.Add(String.Format('|%8d|%-25s|%-25s|', [query.FieldByName('ID').AsInteger, >query.FieldByName('Name').AsString, query.FieldByName('Department').AsString])); query.Next; end; finally query.Close; query.DisposeOf; end; end; this is the code to display the database, i want to know if there's a way to write on the memo or a TEdit and press a button to change back the database, i'm wondering if it works both ways.
Read More