Tag: firemonkey

TeeChart Firemonkey LineSeries NULL Points

I am creating a Firemonkey (FMX) application. I use TChart with a Line Series (TLineSeries). I need to draw a chart line that is not continuous. The x-axis is date time and I want the line to be drawn say between 10 and 11AM and then again between 3PM and 4PM. I tried the following but it always draws a continuous line from the first point (X1) to the last point (X4): Series.TreatNulls:=TTreatNullsStyle.tnDontPaint; Series.AddXY(X1,1); Series.AddXY(X2,1); Series.AddNullXY(X2,1); Series.AddNullXY(X3,1); Series.AddXY(X3,1); Series.AddXY(X4,1); Where X1=date value at 10AM X2=date value at 11AM X3=date value at 3PM X4=date value at 4PM I tried various combinations of TreatNulls but still no success. I am using TeeChart Version 2021.32 Any suggestions would be appreciated.
Read More

Bearer Tokens in C++Builder/FMX REST Functionality?

I have a server application running in node.js/Mongoose/MongoDB with a REST interface. My client application is built in Embarcadero C++Builder/Firemonkey(FMX) and so far all is good with interacting with the node server using the embarcadero REST features (TRESTClient/TRESTRequest/TRESTResponse). I recently added authentication to my server using JSON Web tokens and the user registration/login is working successfully, giving me back a bearer token using the following code: const token = jwt.sign({sub: user.id}, process.env.JWT_SECRET, {expiresIn: '30d' }) Accessing data is implemented via express-jwt by sending a REST request with the bearer token. Postman makes it easy to send a request for data using a Bearer token (https://learning.postman.com/docs/sending-requests/authorization/#bearer-token), however I cannot find out how to do this seemingly simple task using Embarcadero's REST features. I have tried using the Embarcadero REST OAUTH/OAUTH2/SIMPLE/BASIC authentication methods with the bearer token in the Access-Token and Request-Token fields and nothing seems to work. How can this be done? I am sure this is something simple I am missing but there is next to no documentation I can find.
Read More

Problem writing file on IOS in Firemonkey

I have a login screen in our mobile app in Firemonkey. I use the following routine to save the entered parameters and read them again when the user launches the application again. The code works fine on Android, but in IOS I get an error that the directory does not exist. procedure TLoginFrm.Save_login_parameters; var SettingsIniFile: TMemIniFile; LDirectory: string; LFileName: string; begin LDirectory := TPath.GetHomePath + TPath.DirectorySeparatorChar + 'Settings'; TDirectory.CreateDirectory(LDirectory); LFileName := TPath.GetHomePath + TPath.DirectorySeparatorChar + 'Settings' + TPath.DirectorySeparatorChar + 'WaveDeskSettings.ini'; SettingsIniFile := TMemIniFile.Create(LFileName); SettingsIniFile.WriteString('Login', 'Account', EditAccount.Text); SettingsIniFile.WriteString('Login', 'Login', EditLogin.Text); SettingsIniFile.UpdateFile; end;
Read More

Freeing components in a background thread in Delphi Android

I'm developing an android app in delphi, where i dynamically create and populate Listviews. As the listviews can contain thousands of entries, freeing all the components can be time consuming, so to keep responsetime down, i want to do it in a background thread, but this causes access violations to occur. type TLVDirectory = record FListView: TListViewEx; .. end; var LVDir: TLVDirectory; LVDirectories: TList<TLVDirectory>; begin LVDir.FListView := TListViewEx.Create(Self); LVDir.FListView.Parent := Layout2; LVDir.FListView.Align := TAlignLayout.Client; LVDirectories.Add(LVDir); . end; Freeing LVDir := LVDirectories[LVDirectories.Count - 1]; LVDirectories.Delete(LVDirectories.Count - 1); TThread.CreateAnonymousThread(procedure() begin LVDir.FListView.Parent := nil; LVDir.FListView.DisposeOf; end).Start; How can i make this work?
Read More

Delphi FMX debugging Android 64-bit apps shows strings as pointer

I use Delphi 10.4.2 for development of an Android app. The 32-bit version of my app is running without problems. But the 64-bit version cannot find my deployed application files. To get the path of the data the following statement is used: path := TPath.GetDocumentsPath + PathDelim; // --> '/data/user/0/solutions.kloss.ZaxxonV2/files/' <-- on 32-bit system When I try to check the content of path in the 64-bit environment with the integrated debugger, it shows only the 64-bit address of the string, not the content. So debugging is impossible. 32-bit debugger works fine. How can the debugger be configured for 64-bit? Does the path variable point to different places when using 64-bit?
Read More

Firemonkey Timage.bitmap.LoadFromStream Fails

I'm developing an app with firemonkey. We are trying to send images through a JSON encoded with Base64 encodign. We cant make it work. We use a TImage to display the images and we use a Image.Bitmap.LoadFromStream to load the image. Just to test we try to do this: We put 2 Timages (Image1 and Image2) with images loaded, and tryed to transfer the image from one TImage to the other with this code: LInput := TMemoryStream.Create; Image1.Bitmap.SaveToStream(linput); Linput.Position := 0; image2.Bitmap.LoadFromStream(linput); But it doesn't work. The last line generates a "Loading bitmap failed" exception. Any clue will be appreciated. :( Thanks in advance...
Read More

Exception parsing XML File

I'm developing a Delphi Firemonkey routine (version 10.4.2) to parse an incoming XML file as a way to export data from my application in multiple file formats. Here is the relevant code I'm using: procedure ParseFieldList(FieldsNode: IXMLNode); var i: integer; FieldNode: IXMLNode; FieldRec: TDataDescRecClass; RecFieldID, FieldName, DataType, MaxLength, Precision: string; begin for i := 0 to FieldsNode.ChildNodes.Count-1 do begin FieldNode := FieldsNode.ChildNodes.Get(i); if FieldNode.NodeType = ntElement then begin RecFieldID := FieldNode.Attributes['db_id']; FieldName := FieldNode.ChildValues['field_name']; DataType := FieldNode.ChildValues['datatype']; MaxLength := FieldNode.ChildValues['maxlength']; Precision := FieldNode.ChildValues['precision']; FieldRec := RecDesc.TDataDescRecClass.Create(RecFieldID, FieldName, DataType, MaxLength, Precision); DataDescList.AddDescRec(FieldRec); end; end; end; procedure ParseDataList(DataRecsNode: IXMLNode); var i, j: integer; idx: integer; DataNode: IXMLNode; DataFieldNode: IXMLNode; FieldRec: TDataDescRecClass; begin for i := 0 to DataRecsNode.ChildNodes.Count-1 do begin DataNode := DataRecsNode.ChildNodes.Get(i); if DataNode.NodeType = ntElement then begin if DataNode.HasChildNodes then begin for j := 0 to DataNode.ChildNodes.Count-1 do begin DataFieldNode := DataNode.ChildNodes.Get(j); if (DataFieldNode.NodeType = ntElement) or (DataFieldNode.IsTextElement) then begin idx := StrToInt(DataFieldNode.Attributes['db_id']); FieldRec := DataDescList.DataDescRec[idx-1]; if (FieldRec.DataType = 'C') and (DataFieldNode.Text > '') then FieldRec.Value := Format('%m', [StrToFloat(DataFieldNode.Text)]) else FieldRec.Value := DataFieldNode.Text; FieldRec.ValueSet := True; FieldRec.DetailField := DetailData; end; end; end; DataDescList.ClearDataValues; end; end; end; function FileExport(FieldStream: TMemoryStream; var ErrorStr: PChar): integer; var FileName: string; ErrStr: string; TitlesIncluded: boolean; i: Integer; fXML: TXMLDocument; BaseNode: IXMLNode; GroupNode: IXMLNode; begin Result := 0; ErrStr := ''; { Initialize the XML parsing parameters } DataDescList := TDataDescListClass.Create; { Parse the memory stream to get the fields and data to be exported } FieldStream.Position := 0; fXML := TXMLDocument.Create(nil); try try { Initialize the XML parsing parameters } fXML.DOMVendor := GetDOMVendor(sAdom4XmlVendor); DefaultDOMVendor := fXML.DOMVendor.Description; fXML.Active := True; fXML.Options := fXML.Options - [doNodeAutoCreate]; fXML.Version := '1.0'; fXML.LoadFromStream(FieldStream); { Parse the memory stream to get the fields and data to be exported } BaseNode := fXML.Node.ChildNodes.Get(1); if BaseNode <> nil then begin for i := 0 to BaseNode.ChildNodes.Count-1 do begin GroupNode := BaseNode.ChildNodes.Get(i); if GroupNode.IsTextElement then begin if GroupNode.NodeName = FileNameTag then begin FileName := GroupNode.Text; try AssignFile(OutputFile, FileName); ReWrite(OutputFile); except on E: EInOutError do begin raise EExportImportError.Create(Format(CannotOpenExportFile, [Filename, EInOutError(E).Message])); end; end; end end else if GroupNode.NodeType = ntElement then begin if GroupNode.NodeName = FieldRecsTag then begin TitlesIncluded := GroupNode.Attributes[TitlesAttr] = 'Y'; ParseFieldList(GroupNode); end else if GroupNode.NodeName = DataRecsTag then ParseDataList(GroupNode); end; end; CloseFile(OutputFile); end; except on E: EExportImportError do begin Result := cCannotOpenExportFile; // MessageDlgCtr('Data Export error', E.Message, mtWarning, [mbOK], 0); end; end; finally ErrorStr := PChar(ErrStr); FreeAndNil(DataDescList); FreeAndNil(fXML); end; end; The XML file is generated by the app and passed as a Memory Stream to the routine, where it is parsed and then re-written in a specific file format. The issue is that if the a node in the XML file data contains a hyphen, it causes the following node to fail with an "access violation at 0x00000000: read of address 0x00000000." The exception comes from ParseDataList - DataFieldNode := DataNode.ChildNodes.Get(j);. I've traced it to the CreateChildList function - NodeList.List.Add(CreateChildNode(DOMNode.childNodes[I])) step in the XML.XMLDoc unit (and the call to _IntfClear in the System unit). Here is a sample XML file I've been testing with: <?xml version="1.0" encoding="UTF-8"?> <transfer transfertype="Export"> <filename>E:\Temp\Test.csv</filename> <field_records delimiter="" quotechar="" titles_included="N"> <field_record db_id="1"> <field_name>Name of Item</field_name> <datatype>S</datatype> <maxlength>0</maxlength> <precision>0</precision> </field_record> <field_record db_id="2"> <field_name>Item Description</field_name> <datatype>S</datatype> <maxlength>0</maxlength> <precision>0</precision> </field_record> <field_record db_id="3"> <field_name>Serial Number</field_name> <datatype>S</datatype> <maxlength>0</maxlength> <precision>0</precision> </field_record> </field_records> <data_records> <data_record> <field db_id="1">Chest of Drawers</field> <field db_id="2">Chest, 3 Drawer, Copper</field> <field db_id="3">CD23</field> </data_record> <data_record> <field db_id="1">Chest of Drawers</field> <field db_id="2">Chest, 4 Drawer, Copper</field> <field db_id="3">CD25-1</field> </data_record> <data_record> <field db_id="1">Comforter</field> <field db_id="2">FQ Blanket</field> <field db_id="3">HB23</field> </data_record> </data_records> </transfer> I'm not clear why a hyphen in the data would cause this problem and how to work around it. (There is also another failure when trying to free the fXML object, but I'd like to get past the parsing issue before I get to that.) Any assistance would be greatly appreciated.
Read More

Delphi FMX project going into the background

How can my Delphi 10.4.2 project detect that it is going into the background? I've tried using TMainWindow.FormDeactivate() but this leaves the floating tool window staying on top of all other application windows as the project goes into the background. To solve that I've also tried... procedure TMainWindow.FormDeactivate(Sender: TObject); begin FloatingToolWindow.FormStyle := TFormStyle.Normal; end; ...but this does not always work. For example, if the floating tool window has the focus (rather than the main window), then the main window's FormDeactivate() does not get called when the user clicks in another application - which leaves the tool window floating over all other application windows. Here is a project that duplicates the problem. Run the project in Windows or in MacOS, click the button to show the tool window, click in the tool window (to give it focus), and finally click some other application's window to put this project in the background. The tool window continues to stay on top and the main window's FormDeactivate() is not called. unit MainForm; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls; type TMainWindow = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormDeactivate(Sender: TObject); procedure FormActivate(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainWindow: TMainWindow; implementation {$R *.fmx} uses FloatingForm; procedure TMainWindow.Button1Click(Sender: TObject); begin FloatingToolWindow.Show; end; procedure TMainWindow.FormActivate(Sender: TObject); begin FloatingToolWindow.FormStyle := TFormStyle.StayOnTop; end; procedure TMainWindow.FormDeactivate(Sender: TObject); begin FloatingToolWindow.FormStyle := TFormStyle.Normal; end; end. The main form's DFM: object MainWindow: TMainWindow Left = 0 Top = 0 Caption = 'Form1' ClientHeight = 480 ClientWidth = 640 FormFactor.Width = 320 FormFactor.Height = 480 FormFactor.Devices = [Desktop] OnActivate = FormActivate OnDeactivate = FormDeactivate DesignerMasterStyle = 0 object Button1: TButton Position.X = 280.000000000000000000 Position.Y = 232.000000000000000000 Size.Width = 137.000000000000000000 Size.Height = 22.000000000000000000 Size.PlatformDefault = False TabOrder = 0 Text = 'Show Floater' OnClick = Button1Click end end The settings for the floating form: object FloatingToolWindow: TFloatingToolWindow Left = 0 Top = 0 BorderIcons = [] BorderStyle = ToolWindow Caption = 'Form2' ClientHeight = 316 ClientWidth = 491 FormStyle = StayOnTop FormFactor.Width = 320 FormFactor.Height = 480 FormFactor.Devices = [Desktop] DesignerMasterStyle = 0 object Text1: TText Position.X = 80.000000000000000000 Position.Y = 88.000000000000000000 Size.Width = 297.000000000000000000 Size.Height = 113.000000000000000000 Size.PlatformDefault = False Text = 'Click on this tool window (to give it focus) and then click on a' + 'ny OTHER application'#39's window to send this project into the back' + 'ground. This window will continue to stay on top of the other ap' + 'plications.' end end Source for the tool window: unit FloatingForm; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects; type TFloatingToolWindow = class(TForm) Text1: TText; private { Private declarations } public { Public declarations } end; var FloatingToolWindow: TFloatingToolWindow; implementation {$R *.fmx} end.
Read More

How to handle non-English accented characters using a physical keyboard in an Android app written in Delphi?

Non-English languages often have diacritics (glyphs added to letters to alter the letters), such as the circumflex accent in French as inâ or ê. Those characters are generally obtained by first hitting a dead key on the keyboard, such as ^, followed by the letter on which we want to apply the diacritic. So, to obtain â, one has to press ^ followed by a to see â in the edit control. When writing an Android app in Delphi using FireMonkey, the handling of those diacritics has always been problematic when a physical keyboard is attached to the Android device, whether it is via USB or Bluetooth. For example, prior to Delphi 10.4.x, in a TEdit or a TMemo, pressing ^ followed by a would give ^a instead of the expected â. Note that applications not written in Delphi are not exhibiting this peculiar problem. As of Delphi 10.4.x, the problem has been fixed for TEdit, but unfortunately TMemo is still plagued with the problem. Of course, if one is not using a physical keyboard, everything works perfectly. However, as I am trying to write an application that will support a physical keyboard, particularly in Samsung DeX mode, this poses a serious problem. I am therefore asking for help in perhaps overriding the key handling of the TMemo, perhaps using the OnKeyDown event, to fix this problem that prevents my app from being used by non-English users. Knowing that when pressing ^, in the OnKeyDown event we get Key = $0 and KeyChar = 710, I managed to intercept the diacritic and insert the accented letter, but I have not been able to remove the diacritic so pressing ^ followed by a gives ^â in the TMemo. I can't figure out how to remove the ^ in front of the â. ... private { Private declarations } IsDeadKey: Boolean; DeadKey: Char; ... procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState); begin if IsDeadKey then begin if (Ord(DeadKey) = 710) then begin case KeyChar of 'a': KeyChar := 'â'; 'e': KeyChar := 'ê'; end; IsDeadKey := False; end else begin KeyChar := DeadKey; IsDeadKey := False; end; end else if (Key = $0) and (Ord(KeyChar) = 710) then begin IsDeadKey := True; DeadKey := KeyChar; end; end;
Read More

MapView marker show previous on iOS

With Firemonkey 10.4.2. I had a problem when I click on marker. If I do that Click on Marker A Click on Marker B Click on Map Click on Marker A => I see info of Marker B, the MapMarker of MapViewMarkerClick is bad I find a problem on FMX.Maps.iOS the last Marker is keep in memory and never set to nil, that's why I always have the same. When you click on a marker, it's look like you go to MapClick and then MapMarkerClick. That's not really clear. When the map is shown with all marker. If click on Marker A, I first go to MapClick, I need to click a second time on the Marker to see it. Then I can naviguate to each marker, but if I click on map. The next time I click on marker I need to do it 2x. All is good on Android, I think it's a problem in FMX.Maps.iOS again. Any idea ? Here is the original code with my edit : procedure TMapKitMapView.MapEvent(const Event: TMapEvent); // added var SelectedMarker: TMapMarker; // end added begin // added SelectedMarker := FSelectedMarker; // end added case Event.Kind of TMapEventKind.CameraChanged: begin SyncCameraPosition; FHostControl.DoCameraChanged; end; TMapEventKind.MarkerClick: begin // Marker has been selected FSelectedMarker := Event.Marker; FHostControl.DoMarkerClick(Event.Marker); end; TMapEventKind.MarkerDeselect: FSelectedMarker := nil; TMapEventKind.MarkerDragStart: FHostControl.DoMarkerDragStart(Event.Marker); TMapEventKind.MarkerDrag: FHostControl.DoMarkerDrag(Event.Marker); TMapEventKind.MarkerDragEnd: FHostControl.DoMarkerDragEnd(Event.Marker); TMapEventKind.MapClick: if FDragState = MKAnnotationViewDragStateNone then if FSelectedMarker = nil then FHostControl.DoMapClick(Event.LatLng) else FHostControl.DoMarkerClick(FSelectedMarker); TMapEventKind.MapLongClick: if FDragState = MKAnnotationViewDragStateNone then if FSelectedMarker = nil then FHostControl.DoMapLongClick(Event.LatLng); TMapEventKind.MapDoubleClick: if FSelectedMarker = nil then FHostControl.DoMapDoubleClick(Event.LatLng) else FHostControl.DoMarkerDoubleClick(FSelectedMarker); end; // added if FSelectedMarker = SelectedMarker then FSelectedMarker := nil; // end added end;
Read More

How to disable FMX ComboBox Mouse Wheel scroll

How can I disable the mouse wheel scroll on a FMX TComboBox when hovering over it? I've tried overriding the MouseWheel method without any luck. I'm most likely doing it wrong as I'm not experienced with overriding. REF: MouseWheel I've gone ahead and removed Inherited: type TComboBoxOverride = class(TComboBox) procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); override; end; // I've tried changing the control on the class overriding the method. No luck cbbServerMap: TComboBoxOverride; { TComboBoxOverride } procedure TComboBoxOverride.MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); begin Handled := True; end; I've found the following SO post for VCL and DevEx but I'm struggling to convert it to FMX, Ref: How to suppress mouse wheel in TcxComboBox When setting a breakpoint on the TCustomComboBox.MouseWheel method I can see that it ignores my override.
Read More

FMX TEdit is unable to accept the ASCII single quote on iOS

I'm using Delphi 10.4 in an FMX application and, although there is no problem on Windows and Android targets, it is not possible, for the user, to enter a single quote character ' into any TEdit field on the iOS platform... Note that if the field is initialised by program, ' is properly displayed. Is there a reason for this or is this a bug ?
Read More