Quantcast
Channel: TMS Software
Viewing all 1093 articles
Browse latest View live

TMS Day followup: TMS FlexCel

$
0
0

Back from TMS day

I am personally very happy with everything about the TMS day. It took 26 hours flying plus 6 hours in a car trip to Germany so I could speak for an hour, but it was completely worth it. There is still something magical about face to face communications, something that is lost in written words. I speak daily with tens of customers via email, I also write posts like this very one, and still there is something missing when you don't see the face of the human you are speaking to.

I also was happy about how the sessions worked: Instead of someone standing there and reading a powerpoint, the sessions were interactive and people in the room asked many interesting questions. This was how it was intended to be, and I'd like to think that we are all -the tms team and the people who assisted- happy about it. But of course we are also aware that things are never perfect, and a lot of stuff can be made better for the next time. You only get better if you correctly identify what can be made better first.

So let's stop with the good parts (which were a lot) and let's focus for a little in what can be improved. The main weak point, from my own impressions and from the impressions I got from the people I spoke in the breaks, seems to be that the sessions didn't go too much in depth in the technical part. This is kind of reasonable if you think about it: To cover an item in depth one hour isn't too much, and there was a *lot* of stuff that we wanted to cover. I could have spoken for a week about FlexCel and there would be still stuff left to be said.

The fact that the sessions were interactive didn't help either: It is hard to plan a presentation when you don't know in advance how much questions will be asked. In this point I must say that even if I was aware that the time in the actual presentation is much shorter than the time when you plan it, I still grossly miscalculated how much stuff I could fit in an hour, and how many interesting questions would appear. I presented just half of the stuff I had prepared and I feel bad about that.

And the last problem is about the technical differences between attendees. Being a TMS day, some people came there to hear in depth stuff about some specific TMS products and have an overview of the others. So some people were heavy FlexCel users while others hadn't heard about it. If you make the presentation too technical, the guys who came for the "overview" will get a good nap. If you make it too general, then the guys who eat FlexCel <#tags> and __ranges__ for breakfast will be bored.

So now, about the hard part: Solutions.

As said the main issue was time, and of course the obvious solution would be to do a "TMS week" not a "TMS day", but this is not really viable. We could show less stuff and focus more, and I think this is something to consider for next time. I also would like to have larger breaks because I think the breaks are the best part of any event. In them you get to speak casually with the guys doing the sessions, and also with other customers which are using the same solutions as you. And we get to speak casually with you. But it is a compromise: the larger the breaks you make, the less sessions you can make in one tms day. Even so, I would vote for longer breaks.

About the unpredictability of the sessions, we could make the sessions not interactive and leave all the questions for the end. But I don't think that is a great idea; in my opinion the greatest thing about the tms day is the interactivity. To watch a 1-hour lecture about something we could just upload a video to youtube and you could watch it from the comfort of your home. You could then ask the questions in the comments of the video. It does work, but it loses the magic. I much prefer to cover half of the themes and have a nice talk with the guys in the room, than to cover everything and not interact.

About the diversity of experience in the room, well, I don't see too much solution about that. I tried to do a mix of both general information and technical, but I ended up speaking more of the general parts than of the more technical stuff. And one thing I didn't know in advance is that most of the people in the room were using FlexCel so I could have skipped the introductory parts. I was surprised by that: I knew which products the people were using, but most people coming were using VCL subs or TMS all access, which doesn't gave me extra information. I honestly wasn't expecting so many FlexCel users, and even if that makes me very happy, I need to apologize: I'll try to make the presentation more technical the next time.

And finally, about all the stuff I couldn't say or expand because of the limited time: That bothers me a lot. There is so much stuff that I wanted to say and I couldn't! But at least I got a solution for this: I will be using the rest of this post to write some of the things I would have said if I had the time to expand even more in the questions that were asked. So without further delay, let's go down to business:

That Excel yellow warning

One of the questions raised when I was showing how to create Excel files with FlexCel is why when I opened them in Excel, Excel wasn't showing a warning that the file wasn't created with Excel. What I answered is that Excel doesn't do any check that the file is a "genuine" Excel file or anything like this. As long as the file conforms to the spec (and even many files which don't conform) Excel will be happy to open it.

There are two types of warnings that Excel might show when opening a file:

1)The "Red" warnings:

This is not a very common warning, and it happens when the file contains invalid data. You should never get this warning in files created with FlexCel, and if you do, you should contact us so we fix it.

2)The "Yellow" warnings:

This might be because the file was downloaded from the internet, because it has macros, or in general when the file is valid but there are some security concerns. The particular question was about the "file is downloaded from the internet" warning, and so this is what I will cover here. First of all, note that the warning isn't related to FlexCel, it just happens whenever you donwload a file from the internet, no matter if it was created with Excel, FlexCel or whatever, and it looks like this:


In the session I commented that the flag that triggers this warning is not really stored in the file, but in a separate hidden NTFS stream. But I didn't had the time to expand more into it. So today, I'll take the opportunity to dive a little bit deeper.

Firs of all, you can see the streams in a file by issuing this command in a command prompt:

dir /r
For a file downloaded from the internet, you should see something like this:



The tools to manipulate the stream in DOS are a little bit limited, but you can see the stream content in notepad by doing:

notepad  test_yellow_box.xlsx:Zone.Identifier
And there is also a "streams" utility by Mark Russinovich which you can use to explore NTFS streams: https://technet.microsoft.com/en-us/sysinternals/bb897440.aspx

But of course, this was a Delphi session, so we care more about on how to do it from Delphi itself. And it turns out it isn't difficult, as you can just delete the stream:

  DeleteFile('test_yellow_box.xlsx:Zone.Identifier');
This should get rid of the stream and the yellow warning on the file. Of course this isn't too useful since the files you generate locally don't have that stream, and you don't want to blindly remove the warning in the files you download from the internet. But it might be useful for some workflows, and actually, sometimes you really need to remove that stream in order to work with the files in certain apps.

The "Do you want to save changes?" warning and Excel 2016

Unable to leave a good thing alone, I spoke about other warning that I know many users care about (and you can already start to understand why 1 hour was never going to be enough to cover everything I wanted to cover). The warning dialog shows when you close the file immediately after opening it and without modifying it. The exact text of the warning varies with the type of file (xls or xlsx) and the Excel version, but normally looks like this:



Or sometimes like this:



It happens whenever you have formulas in your file and the Excel version the file declares it was saved with is different from the Excel version that you open the file with. What happens under the hood is that Excel recalculates the file when it was saved by an older version because the older version might have bugs in the calculated values. So then it offers to save the new recalculated values in the file, even if nothing did change. And it will also identify the new saved file as "Saved with the new Excel version" so it doesn't need to be recalculated again when you reopen it.

Again this is not specific to FlexCel: If you save a file with formulas in Excel 2010 and open it in Excel 2016, you will get a warning when closing the file.

Of course, FlexCel is not Excel 2010 or 2016, but we do have a property that allows us to "identify" the file as being created by an specific Excel version. So I showed first how the dialog appears in a simple file created with FlexCel. This happens because by default FlexCel identifies the file as created by an unknown Excel version, so it is always recalculated when you open it.

But then, I went to show on how you could make the warning disappear by setting the property:
xls.RecalcVersion := TRecalcVersion.Excel2016;
So I rerun the code and... as we could expect in a live event, the dialog stubbornly showed up. By that time we were already late in our schedule, so I had to move on to the next thing, but now I want to expand in what happened.

In short: There was a "January Update" of Excel 2016 which happened to introduce a bunch of new functions. (see https://support.office.com/en-us/article/What-s-new-in-Excel-2016-for-Windows-5fdb9208-ff33-45b6-9e08-1f5cdb3a6c73?ui=en-US&rs=en-US&ad=US

We were aware of this update, and since the functions introduced are probably the first useful new functions introduced since Excel 2007, we already had implemented recalculation for them all at the tms day timeframe (even if we hadn't yet released an update to the public). But what I hadn't realized is that this "January Update" also changed the recalculation id that must be saved with the file.

Files created with a "Pre-January-Update" Excel 2016 (or with FlexCel reporting as Excel 2016) would have a diffefent recalculation ID and trigger the save dialog when you opened them in "Post-January-Update" Excel 2016.

This was the reason the demo failed: FlexCel was still writing the "Pre-January-Update" ID into the file, but the Excel I used in the presentation had silently updated some days ago to the "January Update" and was recalculating the "old Excel 2016" file.

So we just released an update to FlexCel (6.7.16) which will:

1)Add full support for all the new functions in the Excel 2016 january update.

2)Identify the files saved with a RecalcID of Excel2016 with the "Post-January-2016" Id.

3)Add a new member "LatestKnownExcelVersion" to the TRecalcVersion enum. Now you can set:
xls.RecalcVersion := TRecalcVersion.LatestKnownExcelVersion;
And FlexCel will identify the file as saved with the latest version of Excel it knows about. So when Excel 2018 or whatever else comes and you update FlexCel, the new files you create will all automatically get the latest Recalc ID.

Virtual Datasets

This is one of the slides I had planned to show in the presentation, but which I didn't had time to:



And it was a shame, since many people contacted me after the session to inquire about this specific topic. So again, I will use the time I have in this blog post to expand on it:

As you might know if you are a FlexCelReport user, FlexCel can use any TDataSet, any TList and any TArray as datasources for the reports. But those 3 types aren't hardcoded anywhere, and you might have your own data containers which you want to use directly instead of copying the data to TList or a TDataSet. And it is not difficult to do so.

As you can see on the slide, FlexCel actually gets all of its data from 2 abstract classes: TVirtualDataTable and TVirtualDataTableState. FlexCel comes with specialized classes derived from them which implement it for a TDataSet, TList and TArray, but nothing is stopping you from creating your own specializations of TVirtualDataTable and TVirtualDataTableState.

Also mentioned in the slide there was an example project which creates specializations that allow you to use a TStringList as a datasource in your FlexCel Reports. You can get it here: http://www.tmssoftware.biz/flexcel/samples/stringlisttable.zip

To create a new datasource of FlexCel, you need to answer some questions like "how many records my datasource has?", or "what are the names of the columns for my datasource?". To do so, you need to create 2 different classes, descending from TVirtualDataTable and TVirtualDataTableState. Why 2 different classes? The difference between TVirtualDataTable and TVirtualDataTableState is that the first contains information which is static and can be used in different threads or different datasources in the same report without worries, while the second contains state information which changes for each dataset used, even if it the dataset is the same. FlexCel can keep a single copy of the TVirtualDataTable in memory and use it for all similar tables in a report, but it needs to create different TVirtualDataTableState classes for each table.

For example, a DataTable will have always the same columns and column names, no matter how many times it is used inside the same report. So the question: "what are the names of the columns for my datasource?" is answered in the TVirtualDataTable abstract class, by overriding the Get_ColumnCount, GetColumn, GetColumnName and GetColumnCaption methods.

For example, the TStringList datasource has a single column with the data (as it is just a list of strings). We will name this column "Data" and so the methods look like this:
function TStringListProvider.GetColumn(const columnName: UTF16String): Int32;
begin
  if not SameText(columnName, 'DATA') then raise Exception.Create('Unknown Column: ' + columnName);
  Result := 0;
end;

function TStringListProvider.GetColumnCaption(
  const columnIndex: Int32): UTF16String;
begin
  Result := GetColumnName(columnIndex);
end;

function TStringListProvider.GetColumnName(
  const columnIndex: Int32): UTF16String;
begin
  Result := 'Data';
end;

function TStringListProvider.Get_ColumnCount: Int32;
begin
  Result := 1;
end;

On the other hand, the question "how many records my datasource has?" depends in the master-state relationship and must be answered by a TVirtualDataTableState. For example, a detail table in a master-detail relationship might have 3 records for the first master record, but 5 for the second. In our TStringList example, we just override the Get_RowCount to return the number of items in the string list:
function TStringListStateProvider.Get_RowCount: Int32;
begin
  Result := FStringList.Count;
end;
If you now look at the source for our TStringList example, you will see that we have already covered almost everything. The only thing missing is to provide FlexCel with the actual values for a row and column. You do this by overriding the methods:
function TStringListStateProvider.GetValue(const column: Int32): TReportValue;
begin
  Result := FStringList[Position];
end;

function TStringListStateProvider.GetValue(const row,
  column: Int32): TReportValue;
begin
  Result := FStringList[row];
end;
And that is mostly it. The only method in the example that we did not cover is a method that will create a state from a TVirtualDataTable:
function TStringListProvider.CreateState(const sort: UTF16String;
  const masterDetailLinks: TMasterDetailLinkArray;
  const splitLink: TSplitLink): TVirtualDataTableState;
begin
   Result := TStringListStateProvider.Create(FStringList, self);
end;

And that is used by FlexCel to create the multiple VirtualDataTableState instances it needs from the single VirtualDataTable which you add with FlexCelReport.AddTable.

Of course this only shows the simplest virtual datatable wrapper that you can create. There are many other methods available for overriding in TVirthalDataTable and TVirtualDataTableState, which allow for advanced functionality. For example, if you want to do a lookup in the datasource with the <#lookup> tag, you might want to override the Lookup method (even when the default implementation is good). Some other functionality like DISTINCT might not be available if you don't override the corresponding method. But for most reports, what we covered here is enough.

The report designer

Another thing I got asked about is the report designer I had installed in Excel during the presentation. This is an Excel addin which appears as a new tab in the ribbon:



It allows you to design a report template in a simpler way, by dragging and dropping fields from a tag pane. The bad news is that it has been in its current state (not yet finished) for more than a year already, and I never seem to get the time to finish it. There are always more important things to do. But as I've been asked by many people in the tms day about the possibility to get a beta, I will do my best to get a beta released in a couple of weeks. Not all functionality will be working, but the basics should be there.


At TMS, ALL-ACCESS means ALL-SPOILED!

$
0
0

In October 2015 we introduced TMS ALL-ACCESS, our 1 year subscription to the entire product range.

This no-nonsense subscription was created due to the numerous requests we received from our customers. They simply wanted ALL products with no restrictions or limitations. At tmssoftware.com all products means all products we offer now but also all new products we'll release in the year of the subscription. Hereby a small overview of the new products we added to already large range of products in TMS ALL-ACCESS since the announcement in October 2015:


Nov 12, 2015 : TMS LCL Cloud Pack
Set of LCL components to offer easy access from Windows, Raspberry Pi, Mac OS-X, Linux applications to cloud services like Amazon, OneDrive, Box, DropBox, Facebook, Flickr, FourSquare, Picasa, Google Calendar/Drive/Tasks/Places, LinkedIn, Twitter, Windows Live Calendar/Contacts, Wunderground weather, YouTube, ...

Dec 3, 2015: TMS Logging
Compact cross-platform logging framework offering informative log output to a flexible number of targets with a minimum amount of code.

Jan 21, 2016: TMS FNC Chart
The TMS FNC Chart is a fully cross-platform component designed to display different kinds of data such as financial and marketing data, monthly business sales, graphical and math data and much more as a chart. The Chart supports types such as bar, area, line, marker and variants such as stacked bar, stacked area, stacked percentage area, stacked percentage bar XY-line, XY-scatter and digital line.

Feb 26, 2016: TMS Echo
Delphi framework for data replication. It allows you to have two or more databases and synchronize data between them. Changes you make to one client database (inserts, updates, deletes) can be sent to another database. It's bidirectional, so you can send and receive changes between databases.

Mar 31, 2016: myCloudData.net service
myCloudData.net offers an extremely simple, fast and flexible way to have data from desktop, web, mobile and IoT apps in the cloud. myCloudData.net offers a secure use of cloud data storage with API based metadata configuration. It uses OAUTH 2.0 and a full REST/JSON/HTTP API, and is accessible from all programming environments

Apr 5, 2016: TMS FNC UI Pack
Powerful, feature-rich UI controls in 1 component set for 3 frameworks and 5+ operating systems. Use one source-code base, one UI component set to create feature-rich applications for all popular operating systems that exist on the planet.


So basically, our TMS ALL-ACCESS customer were treated monthly with a new components set! ( Not sure if we can keep up this speed of new product releases but we have a long list of new component, new component features & new product ideas) So, with TMS ALL-ACCESS, you never have to worry again if you will miss something or if you have to take budget in account for future products. No more excuses... TMS ALL-ACCESS is just our best product you can get at tmssoftware.com! We invite you to have a look here at the list of products available now in TMS ALL-ACCESS.

A single-source TV-guide for 6 operating systems

$
0
0

Introduction

With the first release of our brand new TMS FNC UI Pack we are venturing into a new way of designing and creating components. A way that allows developers to easily switch between 3 frameworks (FMX, VCL and LCL). As the TMS FNC UI Pack targets these three frameworks it automatically comes with support for a multitude of operating systems. As we wanted to take "easily switching between 3 frameworks" to the test we have created a TV-guide application that uses the planner component, parses JSON retrieved with our TMS Cloud Components and made it running on 6! major operating systems: Windows 10, Mac OS X Yosemite, iOS 9.0, Android Lollipop, Ubuntu and Raspbian.

Click image for more screenshots.

Cross-platform, cross-framework code

Creating our business logic

After installing the TMS FNC UI Pack the TTMSFNCPlanner component is available on FMX, VCL and LCL and we are ready to start developing applications. Now, to start using it, it would be wise to think a few moments on how to write an application that is running on multiple frameworks, multiple operating systems. If we want to start with VCL and want to move to FMX in a couple of months, it would not be very resource and time friendly to write an application that does not use the power of FNC. Therefore we want to create a single source universal business logic unit that will be used in three different projects, one for every framework. To create a single source unit and use it in different projects, which is compatible with FMX, VCL and LCL we need to add a conditional define to our project to identify each framework, if only because unit names for FNC components must be different (requirement in the Delphi IDE hosting both FMX & VCL).





To initialize the planner and retrieving data from our service, we start by adding a TTVGuideLogic class that is instantiated in each separate project main form unit and contains the business logic for the app.
  TTVGuideLogic = class
  private
    FPlanner: TTMSFNCPlanner;
    FChannels: TTVChannels;
    FAccess: TCloudAccess;
  public
    destructor Destroy; override;
    function GetJSONArray(URL: string; AID: String = ''): TJSONArray;
    function FindChannelByName(AName: String): TTVChannel;
    procedure InitPlanner(APlanner: TTMSFNCPlanner);
    procedure InitChannels;
    procedure UpdateResources(AChannel: TTVChannel; AResource: Integer);
  end;
Each framework has its own set of units in order to compile succesfully. We use the conditional defines added to our project to make the difference between each framework.
uses
  {$IFDEF VCL}
  Classes, SysUtils, VCL.TMSFNCPlanner, VCL.TMSFNCCustomControl, VCL.TMSFNCPlannerBase, VCL.TMSFNCPlannerData, CloudBase,
  Generics.Collections, JSON, VCL.TMSFNCGraphics, VCL.TMSFNCUtils, DateUtils;
  {$ENDIF}

  {$IFDEF FMX}
  Classes, SysUtils, FMX.TMSFNCPlanner, FMX.TMSFNCCustomControl, FMX.TMSFNCPlannerBase, FMX.TMSFNCPlannerData, FMX.TMSCloudBase,
  Generics.Collections, JSON, FMX.TMSFNCGraphics, FMX.TMSFNCUtils, DateUtils;
  {$ENDIF}

  {$IFDEF LCL}
  Classes, SysUtils, LCLTMSCloudBase, LCLTMSFNCPlanner, LCLTMSFNCPlannerBase, LCLTMSFNCPlannerData, LCLTMSFNCGraphics, LCLTMSFNCUTils, DateUtils,
  fgl, fpjson, jsonparser;
  {$ENDIF}

Initializing the planner

The initialization code for the planner look & feel is added to the InitPlanner method, which is called after creating an instance of the TTVGuideLogic class in your project. When comparing this to our unit section, you will notice it doesn't require any conditional defines in order to succesfully compile. With the TMS FNC UI Pack we have added a few helper units to set the font size, set the color and have also created our own fill and stroke classes that are used in every FNC component.
procedure TTVGuideLogic.InitPlanner(APlanner: TTMSFNCPlanner);
var
  I: Integer;
begin
  FPlanner := APlanner;
  FPlanner.BeginUpdate;
  FPlanner.Items.Clear;
  FPlanner.Positions.Count := 6;
  FPlanner.OrientationMode := pomHorizontal;

  FPlanner.DefaultItem.TitleColor := gcSlategray;
  FPlanner.DefaultItem.TitleFontColor := gcWhite;
  FPlanner.DefaultItem.Color := gcWhitesmoke;
  FPlanner.DefaultItem.ActiveColor := gcSlateGray;

  FPlanner.Interaction.ReadOnly := True;

  FPlanner.Resources.Clear;
  for I := 0 to FPlanner.Positions.Count - 1 do
    FPlanner.Resources.Add;

  FPlanner.ItemsAppearance.Stroke.Color := gcWhite;
  FPlanner.ItemsAppearance.Stroke.Kind := gskSolid;
  FPlanner.ItemsAppearance.Stroke.Width := 2;
  FPlanner.ItemsAppearance.TitleStroke.Assign(FPlanner.ItemsAppearance.Stroke);

  FPlanner.GridCellAppearance.InActiveFill.Assign(FPlanner.GridCellAppearance.Fill);
  FPlanner.PositionsAppearance.Layouts := [pplTop, pplBottom];

  FPlanner.ModeSettings.StartTime := Now;
  FPlanner.ModeSettings.EndTime := Now;
  FPlanner.Mode := pmDay;

  FPlanner.TimeLineAppearance.Layouts := [ptlLeft, ptlRight];
  FPlanner.TimeLineAppearance.RightVerticalTextAlign := gtaTrailing;
  FPlanner.TimeLineAppearance.RightSubVerticalTextAlign := gtaLeading;
  FPlanner.TimeLine.CurrentTimePosition := pctpOverItems;
  FPlanner.TimeLine.DisplayUnitType := pduMinute;
  FPlanner.TimeLine.DisplayUnit := 5;
  FPlanner.TimeLine.DisplayStart := 0;
  FPlanner.TimeLine.DisplayEnd := (MinsPerDay div FPlanner.TimeLine.DisplayUnit) - 1;

  TTMSFNCUtils.SetFontSize(FPlanner.ItemsAppearance.TitleFont, 14);
  TTMSFNCUtils.SetFontSize(FPlanner.PositionsAppearance.BottomFont, 14);
  TTMSFNCUtils.SetFontSize(FPlanner.PositionsAppearance.TopFont, 14);

  FPlanner.EndUpdate;
  FPlanner.TimeLine.ViewStart := IncHour(Now, -2);

  InitChannels;

  UpdateResources(FindChannelByName('MTV'), 0);
  UpdateResources(FindChannelByName('Eurosport 1'), 1);
  UpdateResources(FindChannelByName('BBC 1'), 2);
  UpdateResources(FindChannelByName('TLC'), 3);
  UpdateResources(FindChannelByName('Disney XD'), 4);
  UpdateResources(FindChannelByName('CNN'), 5);
end;

Using the cloud to access information

A TV-guide application would only be a TV-guide application if it would show some TV-channels and the TV-shows that are playing at a specific time range. In the previous code snippet we have initialized the planner to show a time range of 24 hours, and the service that is used to retrieve the TV-shows of a specific TV-channel is parameterized to always return the TV-shows of today. To keep a reference to TV-channels and TV-shows we additionally add the classes needed to retrieve and persist information. In this code snippet, we have just conditional defines because of a small difference in handling generic lists between the Delphi compiler and the FPC compiler and the TMS Cloud access classes that have a different class name for VCL, FMX and LCL.
  TTVChannel = class;
  TTVShow = class;

  {$IFDEF VCL}
  TCloudAccess = class(TCloudBase);
  TTVShows = TObjectList<TTVShow>;
  TTVChannels = TObjectList<TTVChannel>;
  {$ENDIF}

  {$IFDEF FMX}
  TCloudAccess = class(TTMSFMXCloudBase);
  TTVShows = TObjectList<TTVShow>;
  TTVChannels = TObjectList<TTVChannel>;
  {$ENDIF}

  {$IFDEF LCL}
  TCloudAccess = class(TTMSLCLCloudBase);
  TTVShows = specialize TFPGObjectList<TTVShow>;
  TTVChannels = specialize TFPGObjectList<TTVChannel>;
  {$ENDIF}

  TTVShow = class
  private
    FGenre: string;
    FStartTime: TDateTime;
    FTitle: string;
    FID: string;
    FEndTime: TDateTime;
    FKind: string;
  public
    property ID: string read FID write FID;
    property Title: string read FTitle write FTitle;
    property Genre: string read FGenre write FGenre;
    property Kind: string read FKind write FKind;
    property StartTime: TDateTime read FStartTime write FStartTime;
    property EndTime: TDateTime read FEndTime write FEndTime;
  end;

  TTVChannel = class
  private
    FName: string;
    FID: string;
    FShows: TTVShows;
  public
    constructor Create;
    destructor Destroy; override;
    property ID: string read FID write FID;
    property Name: string read FName write FName;
    property Shows: TTVShows read FShows;
  end;
After defining the necessary classes, we create an instance of our cloud base component for FMX (TTMSFMXCloudBase) and we use that instance to retrieve our TV-channels in JSON.
procedure TTVGuideLogic.InitChannels;
var
  i: integer;
  arr: TJSONArray;
  o: TJSONObject;
  c: TTVChannel;
begin
  FChannels := TTVChannels.Create;

  FAccess := TCloudAccess.Create(nil);
  arr := GetJSONArray('http://www.tvgids.nl/json/lists/channels.php');
  if Assigned(arr) then
  begin
    for i := 0 to GetArraySize(arr) - 1 do
    begin
      o := GetArrayItem(arr, i) as TJSONObject;
      c := TTVChannel.Create;
      c.ID := FAccess.GetJSONProp(o,'id');
      c.Name := StringReplace(FAccess.GetJSONProp(o,'name'), '&eacute;', 'é', [rfReplaceAll]);
      FChannels.Add(c);
    end;
  end;
end;
And we call InitChannels in our planner initialization code
  InitChannels;
After retrieving the TV-channels, the TV-shows are also retrieved, parsed and added as items to our TTMSFNCPlanner.
procedure TTVGuideForm.UpdateResources(AChannel: TTVChannel; AResource: Integer);
var
  c: TTVChannel;
  s: TTVShow;
  arr: TJSONArray;
  i: Integer;
  o: TJSONObject;
  it: TTMSFNCPlannerItem;
  dt: TDateTime;
begin
  dt := Now;
  TMSFNCPlanner1.BeginUpdate;
  for I := TMSFNCPlanner1.Items.Count - 1 downto 0 do
  begin
    if TMSFNCPlanner1.Items[I].Resource = AResource then
      TMSFNCPlanner1.Items[I].Free;
  end;

  TMSFNCPlanner1.Resources[AResource].Text := 'No Channel Selected';
  if Assigned(AChannel) then
  begin
    c := AChannel;
    c.Shows.Clear;
    TMSFNCPlanner1.Resources[AResource].Text := c.Name;
    arr := GetJSONArray('http://www.tvgids.nl/json/lists/programs.php?channels='+c.ID+'&day=0', c.ID);
    if Assigned(arr) then
    begin
      for i := 0 to GetArraySize(arr) - 1 do
      begin
        o := GetArrayItem(arr, i) as TJSONObject;
        s := TTVShow.Create;
        s.ID := FAccess.GetJSONProp(o,'db_id');
        s.Title := FAccess.GetJSONProp(o,'titel');
        s.Genre := FAccess.GetJSONProp(o,'genre');
        s.Kind  := FAccess.GetJSONProp(o,'soort');
        s.StartTime := FAccess.IsoToDateTime(FAccess.GetJSONProp(o,'datum_start'));
        s.EndTime := FAccess.IsoToDateTime(FAccess.GetJSONProp(o,'datum_end'));
        c.Shows.Add(s);
        it := TMSFNCPlanner1.AddOrUpdateItem(s.StartTime, s.EndTime, s.Title, s.Kind);
        it.Resource := AResource;
        it.Hint := it.Title + ' [' + TimeToStr(Frac(s.StartTime)) + ' - ' + TimeToStr(Frac(s.EndTime)) + ']';
        if (dt >= it.StartTime) and (dt <= it.EndTime) then
        begin
          it.Color := gcYellowgreen;
          it.FontColor := gcWhite;
        end;
      end;
    end;
  end;

  TMSFNCPlanner1.EndUpdate;
end;
The UpdateResources call is also added to our planner initialization code and retrieves the channel of choice and adds an item for each show to the planner.
  UpdateResources(FindChannelByName('MTV'), 0);
  UpdateResources(FindChannelByName('Eurosport 1'), 1);
  UpdateResources(FindChannelByName('BBC 1'), 2);
  UpdateResources(FindChannelByName('TLC'), 3);
  UpdateResources(FindChannelByName('Disney XD'), 4);
  UpdateResources(FindChannelByName('CNN'), 5);
To starting using the code from this separate unit, we add the unit to our project and use the following code, after dropping an instance of the TTMSFNCPlanner on the form.
procedure TTVGuideForm.FormCreate(Sender: TObject);
begin
  FTVGuideLogic := TTVGuideLogic.Create;
  FTVGuideLogic.InitPlanner(TMSFNCPlanner1);
end;

procedure TTVGuideForm.FormDestroy(Sender: TObject);
begin
  FTVGuideLogic.Free;
end;
After succesfully retrieving our TV-channels and TV-shows, the application runs on Windows 10, but it could very well be iOS 9, or MAC OS X Yosemite, or Raspbian, or ...

The full source code is available for download

Click image for more screenshots.

Free IDE Tools for Delphi 10 Seattle

$
0
0

For easily installing our free IDE plugins we have created a bundled installer with support for the latest Delphi 10 Seattle IDE.

TMS Project Manager

Free IDE plugin to easily ZIP and upload projects.



TMS Rich Clipboard

Free IDE plugin to copy source code with syntax highlighting to the clipboard.



TMS Presentation Tool

Free IDE plugin to monitor clipboard, track clipboard history and to save & load code snippets for giving presentations with Delphi.



TMS What's New

Free IDE plugin to keep current with the latest releases, blogs & tweets from TMS software.



The bundled free IDE plugins can be downloaded here.

TMS rollout of components with RAD Studio 10.1 Berlin support started

$
0
0


This is a short notice for our customers who are eager to get started with RAD Studio 10.1 Berlin: we've started rolling out our bi-yearly wave of product updates with official built-in support for this new release. Starting from today, following product updates now have built-in support for RAD Studio 10.1 Berlin:

TMS Component Pack: Our suite of 400 feature-packed, sophisticated user interface controls, including advanced grids, ribbon, syntax highlighting memo, rich text editor, visual scheduling/planning control and much more... TMS Component Pack exists and helps software developers all over the world for almost 20 years now and has grown into a stable, vast, powerful, intuitive to use set of user interface controls you cannot miss.

TMS Pack for FireMonkey: Our suite of FMX controls for full cross-platform development targeting Windows, Mac OS-X, iOS, Android. TMS Pack for FireMonkey is a set of powerful controls that help you building advanced desktop or mobile applications including grid, planner, navigational controls, instrumentation controls, rich text editor and much more... Simply the most comprehensive bundle of cross-platform UI controls.

TMS FNC UI Pack: This is our brand new set of framework neutral UI controls allowing you to build single source code business logic based applications for VCL, FMX and LCL frameworks. Whether you'll write traditional Windows desktop VCL applications, cross-platform desktop or mobile applications with the FMX framework or use the free Lazarus IDE and FPC compiler to build Windows, Linux, Raspbian, ... applications, ... there is only one learning curve and only one source code base needed to use these framework neutral controls. With TMS FNC UI Pack, you're in full control, irrespective of the IDE or framework you use.

Our team is working hard to continue to add RAD Studio 10.1 Berlin support to our other wide range of component products. You can track here the progress of our work to cover other products.

Introducing TMS FNC UI Pack

$
0
0

TMS FNC UI Controls, a set of framework neutral controls

Introduction

As your customers use an ever increasing number of devices & operating systems, it is a daily challenge for us, software developers to make our software available for the myriad of target platforms in use. Fortunately, as Pascal developers, we already have a huge benefit that Delphi meanwhile targets 4 platforms: Windows, Mac OSX, iOS and Android. Add Lazarus to that, and Pascal developers can also target Linux and its derivatives such as Raspbian and many more. The 3 main frameworks we have available to create our software for these platforms are: VCL, FMX and LCL. The framework to use will be determined by the target and the IDE used. That implies that the controls that can be used are also typically determined by this choice and might limit your abilities when some controls or control features are not available for one of these frameworks. Addressing that limitation is exactly one of the primary goals of the TMS FNC UI Controls. It offers you a set of powerful & feature-rich UI controls that you can use in Delphi's VCL framework, FMX framework and Lazarus LCL framework. It allows to create Win32, Win64, Mac OS-X, iOS, Android, Linux, Raspbian, ... applications with a single codebase and a single learning curve.

Concept

FNC controls enable you to write and use 100% identical Pascal code, be it in a VCL app, FMX app or LCL app. The FNC components methods, properties and events are therefore 100% identical regardless of the framework being used. As an example, the following code creates a new event in our FNC Planner control:

var
  plIt: TTMSFNCPlannerItem;
begin
  plIt := TMSFNCPlanner1.Items.Add;
  plIt.StartTime := Date + EncodeTime(8,30,0,0);
  plIt.EndTime := Date + EncodeTime(11,0,0,0);
  plIt.Title := 'New event';
  plIt.Text := 'Content';
end;
and from this code, it is impossible to tell whether it will be from a VCL, FMX or LCL app. In the application UI, it will also look exactly the same regardless of framework or operating system:


This means that if you properly separate your logic or adopt an MVC approach, you can easily share .PAS files between VCL and FMX projects, between VCL and LCL projects etc... There are in this respect actually only two limitations. First limitation is the design-time form fileformat that is different between VCL, FMX and LCL. VCL uses the .dfm file, FMX uses the .fmx file and LCL uses the .lfm file. For applications for different devices with different form factors, it typically already requires to design the form separately for separate frameworks, so this isn't too much of a limitation. For other applications, a solution is to create the controls at runtime. A second limitation is the namespaces (unit names). To be able to register identical classnames for different framework controls in Delphi, it is required that these live in different namespaces. As such, the FNC VCL controls unit names have the prefix VCL., the FNC FMX controls unit names have the prefix FMX. and the FNC LCL controls use prefix LCL (without dot, to be able to support FPC versions older than v3.0)

In practice, this means that for the example above with the TMSFNCPlanner, the unit clauses for the different frameworks would be as below. To keep using a single source file, a solution is to set a define at project level depending on the framework and write:

uses 
{$IFDEF VCL}
  VCL.TMSFNCPlannerBase, VCL.TMSFNCPlannerData, VCL.TMSFNCPlanner, VCL.TMSFNCCustomControl;
{$ENDIF}

{$IFDEF FMX}
  FMX.TMSFNCPlannerBase, FMX.TMSFNCPlannerData, FMX.TMSFNCPlanner, FMX.TMSFNCCustomControl;
{$ENDIF}

{$IFDEF LCL}
 LCLTMSFNCPlannerBase,  LCLTMSFNCPlannerData, LCLTMSFNCPlanner, LCLTMSFNCCustomControl;    
{$ENDIF}
In the same way, when used, we could include the resource of our form file that is different in each framework via a conditional define:

{$IFDEF VCL}
{$R *.dfm}
{$ENDIF}

{$IFDEF FMX}
{$R *.fmx}
{$ENDIF}

{$IFDEF LCL}
{$R *.lfm}
{$ENDIF}
These are of course the things you need to take in account when you want to create a single codebase to build projects with multiple frameworks. In other cases, you do not need to take care of this and you can enjoy the exact same feature set of this UI component library irrespective of the IDE and platform you target.

Another important core concept is the introduction of the TMS FNC Graphics library that is included. This enables you to write graphics code that is framework independent. This includes framework neutral colors, fill, stroke, alignment, font, path types and the TTMSFNCGraphics class using this to draw everything you need. This is a sample code snippet of framework neutral drawing:

var
  gr: TTMSFNCGraphics;
begin
  gr := TTMSFNCGraphics.Create(PaintBox1.Canvas);

  gr.Fill.Color := gcYellow;
  gr.Stroke.Color := gcGray;

  gr.DrawRectangle(0,0,100,20);

  gr.Font.Color := gcRed;
  gr.DrawText(2,0,100,20,'Hello world',false)
  gr.Free;
end;
The result is:


and is exactly the same on every framework, target, device, ...

Controls

In TMS FNC UI Pack v1.0, there are already 29 controls included. On the tool palette this looks like:

VCL, FMX



LCL



This includes a grid, planner, richeditor, treeview, various color, font, fontsize, bitmap ... picker , toolbar, ... and more.

Introducing the TMS FNC Grid

The TMS FNC Grid is a high-performance, not data-bound grid capable of dealing with hundreds of thousands of rows, has a wide range of cell types and inplace editors, offers built-in sorting, filtering and grouping and can import and export data in several file formats. To illustrate some of the capabilities of the TMS FNC Grid, here is some framework code initializing the grid to show several cell capability features:

begin
  TMSFNCGrid1.FixedRows := 2;
  TMSFNCGrid1.ColumnCount := 7;
  TMSFNCGrid1.MergeCells(1,0,2,1);
  TMSFNCGrid1.MergeCells(3,0,2,1);
  TMSFNCGrid1.MergeCells(5,0,2,1);

  TMSFNCGrid1.Cells[1,0] := 'Monday';
  TMSFNCGrid1.HorzAlignments[1,0] := gtaCenter;
  TMSFNCGrid1.Cells[1,1] := 'AM';
  TMSFNCGrid1.Cells[2,1] := 'PM';

  TMSFNCGrid1.Cells[3,0] := 'Tuesday';
  TMSFNCGrid1.HorzAlignments[3,0] := gtaCenter;
  TMSFNCGrid1.Cells[3,1] := 'AM';
  TMSFNCGrid1.Cells[4,1] := 'PM';

  TMSFNCGrid1.Cells[5,0] := 'Wednesday';
  TMSFNCGrid1.HorzAlignments[5,0] := gtaCenter;
  TMSFNCGrid1.Cells[5,1] := 'AM';
  TMSFNCGrid1.Cells[6,1] := 'PM';
  TMSFNCGrid1.AutoNumberCol(0);

  TMSFNCGrid1.AddCheckBox(1,2,false);
  TMSFNCGrid1.AddRadioButton(1,3,1);

  TMSFNCGrid1.AddProgressBar(3,2,50);
  TMSFNCGrid1.Cells[3,3] := 'Hello world';

  TMSFNCGrid1.AddBitmapFile(5,2,'e:	mscalendar.png');
  TMSFNCGrid1.AddBitmapFile(5,3,'e:	msmail.png');

  TMSFNCGrid1.Cells[1,4] := 'Red';
  TMSFNCGrid1.Colors[1,4] := gcRed;
  TMSFNCGrid1.Cells[3,4] := 'Yellow';
  TMSFNCGrid1.Colors[3,4] := gcYellow;
  TMSFNCGrid1.Cells[5,4] := 'Lime';
  TMSFNCGrid1.Colors[5,4] := gcLime;

  TMSFNCGrid1.FontNames[1,4] := 'Courier New';
  TMSFNCGrid1.FontStyles[3,4] := [fsBold];
  TMSFNCGrid1.FontSizes[5,4] := 12;

  TMSFNCGrid1.AddNode(2,2);
end;


As another quick introduction to the grid, this 2 line snippet demonstrates how data from a CSV file can be loaded and automatic filtering via a drop down in the column header is enabled:

  TMSFNCGrid1.LoadFromCSV('e:	mscars.csv');
  TMSFNCGrid1.Options.Filtering.DropDown := true;

Of course, this is just a very brief introduction to the TMS FNC Grid. Just the FNC grid alone could deserve multiple articles to cover it in detail. You can familiarize yourself with the TMS FNC Grid by reading the full PDF developers guide you can find at http://www.tmssoftware.biz/download/manuals/TMSFNCGridDevGuide.pdf or use the trial or full version of the component that comes with several samples.

Introducing the TMS FNC Planner


Our TMS FNC Planner is a scheduling component with various built-in time-axis options, i.e. a day, week, month, period, half-day period, timeline as well as custom time-axis mode where you can fully control the duration of each timeslot in the Planner. The Planner supports single and multi resource views and can have the time-axis horizontal or vertical. When targetting the Planner to a mobile device, it will automatically use a touch-friendly approach to select, insert, delete, pan in the Planner. In a previous blog, we have presented a fully framework neutral sample TVGuide application that uses our TMS FNC Planner in this blog article: http://www.tmssoftware.com/site/blog.asp?post=335

Introducing the TMS FNC RichEditor

With the TMS FNC Rich Editor you can assemble a WordPad-style editor or Outlook style mail application in a matter of minutes. TMS FNC Rich Editor comes with capabilities to do WYSIWYG editing of rich text with images, URLs, bullet lists, custom graphics, mail merging etc... To make development even faster, there is a pre-built toolbar for rich editor editing and formatting and non-visual components to facilitate the import and export from HTML & RTF files and that of course in all frameworks, operating systems and target devices supported.

In this introduction sample, drop the TTMSFNCRichEditor on the form as well as the TTMSFNCRichEditorFormatToolbar and assign the TTMSFNCRichEditor to TTMSFNCRichEditorFormatToolbar.RichEditor. Also add a TTMSFNCRichEditorHTMLIO and TTMSFNCRichEditorRTFIO non-visual component on the form and also assign the TTMSFNCRichEditor to TTMSFNCRichEditorHTMLIO.RichEditor and TTMSFNCRichEditorRTFIO.RichEditor.

The rich editor content can be initialized with following code to perform a mail-merge that uses here two merge fields : NAME and EMAIL.

begin
  TMSFNCRichEditor1.AddText('Dear Mr. NAME');
  TMSFNCRichEditor1.AddLineBreak;
  TMSFNCRichEditor1.AddText('CC: EMAIL');
  TMSFNCRichEditor1.SelStart := 9;
  TMSFNCRichEditor1.SelLength := 4;
  TMSFNCRichEditor1.SetSelectionMergeField('NAME');

  TMSFNCRichEditor1.SelStart := 21;
  TMSFNCRichEditor1.SelLength := 5;
  TMSFNCRichEditor1.SetSelectionMergeField('EMAIL');
end;
When the app is started, the text can be further decorated by editing & formatting via the toolbar. When it is ready, following code performs the merge with the NAME and EMAIL field and is exported to RTF via TTMSFNCRichEditorRTFIO and after this, the merge is undone:


var
  sl: TStringList;
begin
  sl := TStringList.Create;

  try
    sl.Add('NAME=Elon Musk');
    sl.Add('EMAIL=elon@tesla.com');
    TMSFNCRichEditor1.Merge(sl);
  finally
    sl.Free;
  end;

  TMSFNCRichEditorRTFIO1.Save('e:	msmerge.rtf');

  TMSFNCRichEditor1.UnMerge;
end;

Introducing the TMS FNC TreeView

Finally, another large feature-packed control from the TMS FNC UI Controls set we want to introduce is the TMS FNC TreeView, TTMSFNCTreeView. This is a multi-column treeview control with regular mode and virtual mode and designed for and capable of using millions of nodes. In addition, the nodes support rich information, text atttributes can be customized per node cell, HTML formatted text in node cells is possible, images, checkboxes can be added and optional inplace editing is available.

In this introduction we'd like to demonstrate the difference in regular (node collection) based mode and virtual mode when using the TTMSFNCTreeView. The first code snippet demonstrates an initialization of a 2 column treeview:

var
  tn,cn: TTMSFNCTreeViewNode;
begin
  TMSFNCTreeView1.BeginUpdate;

  TMSFNCTreeView1.Columns.Clear;
  TMSFNCTreeView1.Nodes.Clear;

  TMSFNCTreeView1.Columns.Add.Text := 'Country';
  TMSFNCTreeView1.Columns.Add.Text := 'Capital';

  tn := TMSFNCTreeView1.AddNode(nil);
  tn.Text[0] := 'Europe';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'Germany';
  cn.Text[1] := 'Berlin';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'France';
  cn.Text[1] := 'Paris';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'United Kingdom';
  cn.Text[1] := 'London';

  tn := TMSFNCTreeView1.AddNode(nil);
  tn.Text[0] := 'Asia';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'Japan';
  cn.Text[1] := 'Tokyo';

  cn := TMSFNCTreeView1.AddNode(tn);
  cn.Text[0] := 'China';
  cn.Text[1] := 'Peking';

  TMSFNCTreeView1.EndUpdate;
end;



Important to note here is that the text in the multiple columns of the treeview can be simply accessed with an array indexed Node.Text[]: string property.

In a next step, we'll use the TTMSFNCTreeView in virtual mode and insert 1 million nodes! Columns are not virtual, so this must be initialized and to keep it simple, this will be initialized to one column:

begin
  TMSFNCTreeView1.Columns.Clear;
  TMSFNCTreeView1.Columns.Add;
  TMSFNCTreeView1.Columns[0].Text := 'Large treeview';
end;

To use the TTMSFNCTreeView in virtual mode, two events are crucial: the OnGetNumberOfNodes() event and the OnGetNodeText() event. The first is triggered to know how many nodes at root level or child level should be added. The latter is used to retrieve the column text of the node. Let's start with the OnGetNumberOfNodes event. This event has parameters ANode and a var parameter ANumberOfNodes. ANode is either a node with ANode.Level set to -1 indicating the number of root level nodes is requested or it contains the node for which the number of child nodes is requested. With the ANode.Level property, you can know how many hierarchical levels deep the node is. In this example, we'll insert 1 million (100x100x100) nodes by inserting 100 root level nodes that have each 100 childs and each child has again 100 subchilds.

This event takes care of this:

procedure TForm1.TMSFNCTreeView1GetNumberOfNodes(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; var ANumberOfNodes: Integer);
begin
    if ANode.Level = -1 then
    ANumberOfNodes := 100
  else  
  if ANode.Level = 0 then
    ANumberOfNodes := 100
  else  
  if ANode.Level = 1 then
    ANumberOfNodes := 100;
end;

Then, the other event for virtual node handling, OnGetNodeText is used to return the text for node columns. Note that this event will be triggered for each node and for each column for this node. The column for which the event is triggered is indicated with the AColumn parameter. As we have only 1 column in this example, this is ignored and the node text is directly returned:

procedure TForm1.TMSFNCTreeView1GetNodeText(Sender: TObject;
  ANode: TTMSFNCTreeViewVirtualNode; AColumn: Integer;
  AMode: TTMSFNCTreeViewNodeTextMode; var AText: string);
begin
  if ANode.Level = 0 then
    AText := 'Root node '+inttostr(ANode.Index)
  else
  if ANode.Level = 1 then
    AText := 'Child node '+inttostr(ANode.Index)
  else
  if ANode.Level = 2 then
    AText := 'Subchild node '+inttostr(ANode.Index);
end;




We hope this brief introduction of the major controls in the TMS FNC UI Pack whetted your appetite to start exploring the components, discovering the benefits and efficiency of having one UI component set to cover all the target operating systems you want to target and perhaps cook up your first Linux GUI apps with LCL. We're eager to learn how your experience is going and to hear your feedback, comments and further wishes and needs in this direction.

Revisiting persisting app settings in the cloud

$
0
0

Back in 2013, I wrote a blog on persisting application settings in the cloud like Google Chrome for example does. The solution presented at that time was using the DropBox cloud service. The technique was quite simple. Persist settings in an INI file and automatically store and retrieve this INI file from a DropBox account.

Now, 3 years later, I wanted to revisit this technique and present a new solution that is more flexible and fine-grained and can be used from apps on Windows, Mac OSX, iOS, Android and Linux. The new technique will use the TMS structured data cloud storage service myCloudData.net.

As a brief introduction to myCloudData, think of it as configurable tables in the cloud, accessible with a REST API and using OAUTH 2 for authentication & authorization. If you want to learn more about myCloudData.net, check its API doc or another blog article written about it.

Time to get down to the nitty gritties! As an example, a VCL app was created with a left and right aligned panel and inbetween a memo. The user can customize the left panel and right panel width with a splitter control and by right-clicking the panel, the panel color can be changed. Further, there is a checkbox for both panels to toggle the visibility of the panel. This is a typical and basic skeleton of what many user configurable apps could be.



We'll now use the myCloudData.net service to store not only how the user configures this left and right panel, but also the last form's size and position on the screen. Whenever the user restarts the app, it will bring back the app as he originally left it on whatever machine the user starts this app. He will always find it back as he left it.
We'll drop a TAdvmyCloudData component on the form and in the form's constructor, we'll initialize it with the application key & secret obtained to use the myCloudData.net service. Further, this component is initialized to persist its application token & secret and finally, the call to AdvmyCloudData.Connect will test validity of possible existing tokens or will perform a new authentication & authorization when no valid tokens are found.

procedure TmCDDemo.FormCreate(Sender: TObject);
begin
  FConnected := false;
  AdvMyCloudData1.App.Key := MYCLOUDDATAKEY;
  AdvMyCloudData1.App.Secret := MYCLOUDDATASECRET;
  AdvMyCloudData1.App.CallBackPort := 8888;
  AdvMyCloudData1.App.CallBackURL := 'http://127.0.0.1:8888';

  AdvMyCloudData1.PersistTokens.Location := plIniFile;
  AdvMyCloudData1.PersistTokens.Key := '.myCloudData.ini';
  AdvMyCloudData1.PersistTokens.Section := 'tokens';
  AdvMyCloudData1.Connect;
end;
Once this step is performed, the AdvMyCloudData component triggers the OnConnected event from where we call the form's ReadSettings method.

In the ReadSettings method, it is checked whether the table to hold the settings exists for the user account on the service or not. When found, the first record from the table is read to retrieve the settings. When not found, the metadata for the table is created. Of course when the new table is created with its metadata, it's not yet possible to retrieve stored settings. This table will only be used the first time then when the user closes the app.
procedure TmCDDemo.ReadSettings;
var
  table: TMyCloudDataTable;

begin
  table := AdvMyCloudData1.TableByName('SETTINGS');

  if not Assigned(table) then
  begin
    table := AdvMyCloudData1.CreateTable('SETTINGS');
    table.MetaData.Add('PNLVISL', ftBoolean);
    table.MetaData.Add('PNLVISR', ftBoolean);
    table.MetaData.Add('PNLWL', ftInteger);
    table.MetaData.Add('PNLWR', ftInteger);
    table.MetaData.Add('PNLCLRL', ftInteger);
    table.MetaData.Add('PNLCLRR', ftInteger);
    table.MetaData.Add('FORMW', ftInteger);
    table.MetaData.Add('FORMH', ftInteger);
    table.MetaData.Add('FORML', ftInteger);
    table.MetaData.Add('FORMT', ftInteger);
    table.SetMetaData;
  end
  else
  begin
    table.Query;
    if table.Entities.Count > 0 then
    begin
      Self.Left := table.Entities[0].Value['FORML'];
      Self.Top := table.Entities[0].Value['FORMT'];
      Self.Width := table.Entities[0].Value['FORMW'];
      Self.Height := table.Entities[0].Value['FORMH'];
      LeftPanel.Visible := table.Entities[0].Value['PNLVISL'];
      RightPanel.Visible := table.Entities[0].Value['PNLVISR'];
      LeftPanel.Width := table.Entities[0].Value['PNLWL'];
      RightPanel.Width := table.Entities[0].Value['PNLWR'];
      LeftPanel.Color := TColor(table.Entities[0].Value['PNLCLRL']);
      RightPanel.Color := TColor(table.Entities[0].Value['PNLCLRR']);

      LeftCheckBox.Checked := LeftPanel.Visible;
      RightCheckBox.Checked := RightPanel.Visible;
    end;
  end;
end;
It was a choice to create this metadata programmatically. It could as well have been created in advance online via the metadata editor. When checking the myCloudData.net account, after this programmatic creation of metadata, it is shown as:

To store the settings, all we need to do is call WriteSettings from the form's OnClose event. This of course is only done when TAdvmyCloudData was able to succesfully connect to an account on the service:
procedure TmCDDemo.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if FConnected then
    WriteSettings;
end;
Persisting the settings in the WriteSettings procedure becomes:
procedure TmCDDemo.WriteSettings;
var
  table: TMyCloudDataTable;
  entity: TMyCloudDataEntity;
  ins: boolean;
begin
  table := AdvMyCloudData1.TableByName('SETTINGS');

  table.Query;

  ins := table.Entities.Count = 0;

  if not ins then
    entity := table.Entities[0]
  else
    entity := table.Entities.Add;

  entity.Value['FORML'] := Self.Left;
  entity.Value['FORMT'] := Self.Top;
  entity.Value['FORMW'] := Self.Width;
  entity.Value['FORMH'] := Self.Height;
  entity.Value['PNLVISL'] := LeftPanel.Visible;
  entity.Value['PNLVISR'] := RightPanel.Visible;
  entity.Value['PNLWL'] := LeftPanel.Width;
  entity.Value['PNLCLRL'] := integer(LeftPanel.Color);
  entity.Value['PNLWR'] := RightPanel.Width;
  entity.Value['PNLCLRR'] := integer(RightPanel.Color);

  if not ins then
    entity.Update
  else
    entity.Insert;
end;
So, with this small amount of code and a one-time authentication and authorization of the myCloudData account, the user will always have the last settings of the app he used, on any machine from where he is using the application.
I hope this serves as a small example of uses for the myCloudData.net service and actually only scratches the surface of possibilities. The use of myCloudData.net for the functionality demonstrated here is completely free, as in free beer. Only when using large amounts of data or blob fields, a subscription to myCloudData.net is required.
Our TMS Cloud Pack component TAdvmyCloudData makes it completely abstract and dead-simple to use the underlying REST API from the service for VCL applications and this component is also available with exactly the same interface for FireMonkey applications and Lazarus applications.

We are eager to learn about your creative and innovative applications using these components and service and in what further directions this can be developed to satisfy your needs!

The full source code of this sample application can be downloaded here.

Rest Server Authentication using JWT (JSON Web Token)

$
0
0

When building HTTP Servers with TMS Business frameworks - more specifically Rest/JSON servers with TMS XData - you can use the builtin authentication and authorization mechanism to protect server resources/requests. Such mechanism is actually implemented in our lower-level framework TMS Sparkle and can be used for any types of HTTP server, not only XData. This blog post will show you how to use authorization and authentication using JSON Web Tokens.

JSON Web Token (JWT)

From Wikipedia: "JSON Web Token (JWT) is a JSON-based open standard (RFC 7519) for passing claims between parties in web application environment". That doesn't say much if we have never heard about it before. There is plenty of information out there to read more in details about JWT, so here I'm going directly to the point in a very summarized practical way.

A JWT is a string with this format:

aaaaaaaaaaa.bbbbbbbbbb.cccccccccc

It's just three sections in string separated by dots. Each section is a text encoded using base64-url:

(base64url-encoded header).(base64url-encoded claims).(base64url-encoded signature)

So a real JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidG1zdXNlciIsImlzcyI6IlRNUyBYRGF0YSBTZXJ2ZXIiLCJhZG1pbiI6dHJ1ZX0.
pb-4JAajpYxTsDTqWtgyIgpoqCQH8wlHl4RoTki8kpQ

If we decode each part of the JWT separately (remember, we have three parts separated by dots), this is what we would have from part one (spaces and returns added to make it more readable). It's the header:

{
  "alg":"HS256", 
  "typ":"JWT"
}

And this is part two decoded, which is the payload or claims set:

{
  "name":"tmsuser",
  "iss":"TMS XData Server",
  "admin":true
}

Finally the third part is the signature. It makes no sense to decode it here since it's just a bunch of bytes that represent the hash of three things: the header, the payload, and the secret that only the generator of the JWT knows.

The payload is the JSON object that "matters", it's the actual content that end-user applications will read to perform actions. The header contains meta information of the token, mostly the hashing algorithm used to generate the signature, also present in the token. So, we could say that a JWT is just an alternative way to represent a JSON object, but with a signature attached to it.

What does it has to do with authentication and authorization? Well, you can think of the JWT as a "session" or "context" for an user accessing your server. The JSON object in the payload will contain arbitrary information that you are going to put in there, like permissions, user name, etc. This token will be generated by your server upon some event (for example, an user "login"), and then the client will resend the token to the server whenever he wants to perform any operation. This would be the basic workflow:

1. Client performs "login" in the server by passing regular user credentials (user name and password for example)
2. The server validates the credentials, generates a JWT with relevant info, using the secret, and sends the JWT back to the client
3. The client sends the JWT in next requests, passing the JWT again to the server
4. When processing each request, the server checks if the JWT signature is valid. If it is, then it can trust that the JSON Object in payload is valid and can proceed normally

Since only the server has the secret, there is no way the client can change the payload, adding false information to it - for example, change the user name or permissions. When the server receives the modified JWT, the signature will not match and the token will be rejected by the server.

For more detailed information on JSON Web Tokens (JWT) you can refer to https://jwt.io, the Wikipedia article or just the official specification. It's also worth mentioning that for handling JWT internally, either to create or validate the tokens, TMS XData uses under the hood the open source Delphi JOSE and JWT library.

JWT Authentication with TMS XData

Enough of theory, the next steps will show you how to implement authentication/authorization using JWT in TMS XData. This is just a suggestion of implementation, and it's up to you to define with more details how your system will work. In this example we will create a login service, add the middleware and use XData server-side events to implement authorization.

User Login and JWT Generation

We're going to create a service operation to allow users to perform login. Our service contract will look like this:

  [ServiceContract]
  ILoginService = interface(IInvokable)
  ['{BAD477A2-86EC-45B9-A1B1-C896C58DD5E0}']
    function Login(const UserName, Password: string): string;
  end;

Users will send user name and password, and receive the token. Delphi applications can invoke this method using the TXDataClient, or invoke it using regular HTTP, performing a POST request passing user name and password parameters in the body request in JSON format. Nevertheless, be aware that you should always use a secure connection (HTTPS) in your server to protect such requests.

The implementation of such service operation would be something like this:

uses {...}, Bcl.Jose.Core.JWT, Bcl.Jose.Core.Builder;
 
function TLoginService.Login(const UserName, Password: string): string;
var
  JWT: TJWT;
  Role: string;
  IsAdmin: Boolean;
begin
  { check if UserName and Password are valid, retrieve User data from database, 
   add relevant claims to JWT and return it. In this example, we will only
   add two claims: Role and IsAdmin.  }
  
  // Now that application specific logic is finished, generate the token 
  JWT := TJWT.Create(TJWTClaims);
  try
    JWT.Claims.SetClaimOfType<string>('role', Role);
    JWT.Claims.SetClaimOfType<string>('isadmin', IsAdmin);
    JWT.Claims.Issuer := 'XData Server';
    Result := TJOSE.SHA256CompactToken('secret', JWT);
  finally
    JWT.Free;
  end;
end;

Now, users can simply login to the server by performing a request like this (some headers removed):

POST /loginservice/login HTTP/1.1
content-type: application/json
 
{
  "UserName": "tmsuser",
  "Password": "tmsuser"
}

And the response will be a JSON object containing the JSON Web Token (some headers removed and JWT modified for better readibility):

HTTP/1.1 200 OK
Content-Type: application/json
 
{
    "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6InRtc3VzZXIiLCJpc3MiOiJYRGF0YSBTZXJ2ZXIifQ.CAxxa3aizZheG3VXmBoXtfdg3N5jN9tNAZHEV7R-W4Q"
}

For further requests, clients just need to add that token in the request using the authorization header by indicating it's a bearer token. For example:

GET /customers?$orderby=Name HTTP/1.1
content-type: application/json
authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6InRtc3VzZXIiLCJpc3MiOiJYRGF0YSBTZXJ2ZXIifQ.CAxxa3aizZheG3VXmBoXtfdg3N5jN9tNAZHEV7R-W4Q

Adding TJwtMiddleware to process tokens in requests

The second step is to add the TJwtMiddleware to our XData server module. It's done just once, before starting up the server with the module. All we need to inform is the secret our middleware will use to validate the signature of the received tokens:

uses {...}, Sparkle.Middleware.Jwt;
 
{...}
  Module.AddMiddleware(TJwtMiddleware.Create('secret'));

That's it. What this will do? It will automatically check for the token in the authorization header. If it does exist and signature is valid, it will create the IUserIdentity interface, set its Claims based on the claims in the JWT, and set such interface to the User property of THttpRequest object. Regardless if the token exists or not and the User property is set or not, the middleware will forward the processing of the request to your server anyway. It's up to you to check if user is present in the request and perform the correct actions. The only situation where the middleware will return immediately is if the token is present and is invalid (bad format or wrong signature). In this case it will return an error to the client immediately and your server code will not be executed.

Authorizing the requests

Finally, in your application code, all you have to do is check for such User property and take actions based on it. For example, suppose you have a service operation DoSomething that runs some arbitrary code. You don't want to allow anonymous requests (not authenticated) to execute that operation. And you will also only execute it if authenticated user is an administrator. This is how you would implement it:

uses {...}, Sparkle.Security, XData.Sys.Exceptions;
 
procedure TMyService.DoSomething;
var
  User: IUserIdentity;
begin
  User := TXDataOperationContext.Current.Request.User;
  if User = nil then
    raise EXDataHttpUnauthorized.Create('User not authenticated');
  if not (User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean) then
    raise EXDataHttpForbidden.Create('Not enough privileges');
  
  // if code reachs here, user is authenticated and is administrador
  // execute the action
end;

Server-side events

The above code is valid for service operations. But you can also use server-side events to protect the entity sets published by XData. For example, you can use the OnEntityDeleting event to not allow non-admin users to delete resources. The event handler implementation would be pretty much the same as the code above (Module refers to a TXDataServerModule object):

  Module.Events.OnEntityDeleting.Subscribe(procedure(Args: TEntityDeletingArgs)
    var User: IUserIdentity;
    begin
      User := TXDataOperationContext.Current.Request.User;
      if User = nil then
        raise EXDataHttpUnauthorized.Create('User not authenticated');
      if not (User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean) then
        raise EXDataHttpForbidden.Create('Not enough privileges');
    end
  );

That applies to all entities published by the server. Of course, if you want to restrict the code to some entities, you can check the Args.Entity property to verify the class of object being deleted and perform actions accordingly.

Finally, another nice example for authorization and server-side events. Suppose that every entity in your application has a property named "Protected" which means only admin users can see those entities. You can use a code similar to the one above to refuse requests that try to modify, create or retrieve a protected entity if the requesting user is not admin.

But what about complex queries that return multiple entities? In this case you can use the OnEntityList event, which will provide you with the Aurelius criteria (TCriteria) that will be used to retrieve the entities. You can then modify such criteria depending on user permissions:

  Module.Events.OnEntityList.Subscribe(procedure(Args: TEntityListArgs)
    var 
      User: IUserIdentity;
      IsAdmin: Boolean;
    begin
      User := Args.Handler.Request.User;
      IsAdmin := (User <> nil) and User.Claims.Exists('isadmin') and User.Claims['isadmin'].AsBoolean;
      if not IsAdmin then
        Args.Criteria.Add(TLinq.EqualsTo('Protected', false));
    end
  );

The code above simply checks if the requesting user has elevated privileges. If it does not, then it adds an extra condition to the criteria (whatever the criteria is) which filters only the entities that are not protected. So non-admin users will not see the protected entities in the server response.


For more information about the subject, here is a summary of the links related to the topics covered in this post:
TMS Business Subscription Page
TMS XData Product Page
TMS Sparkle Product Page
TMS XData Online Documentation
TMS Sparkle Online Documentation
JSON Web Token Web Site
Wikipedia article about JSON Web Token
JSON Web Token Specification - RFC-7519
Delphi JOSE and JWT library




Developing your first FNC custom control

$
0
0

Some weeks ago, we released the TMS FNC UI Pack, a set of Framework Neutral Components (FNC), i.e. UI controls that can be used from VCL Windows applications, FireMonkey (FMX) Windows, Mac OS-X, iOS, Android applications and LCL framework based Lazarus applications for Windows, Linux, Mac OS-X,..
The TMS FNC UI Pack contains highly complex & feature-rich components such as grid, planner, rich editor, treeview, toolbars. To create such complex components that work under 3 frameworks and a myriad of operating systems is not a trivial excercise and requires intricate knowledge about the VCL, FMX and LCL frameworks as well as the operating systems the controls need to work under.
To help ourselves and the users of the TMS FNC UI Pack, we have introduced several abstractions that facilitate creating framework neutral components and this is what we want to cover in this brief introduction to developing FNC custom controls.

FNC custom control basics

The structure of the FNC custom control we want to present is this of a classic UI control. The control is responsible for painting itself and interacts with keyboard and/or mouse. The control has several properties to control its appearance and behavior. If we look at this concept from the perspective of implementing this for 3 different frameworks, the biggest challenges faced are:

1) abstractions in the code for dealing with graphics: especially VCL and FMX are quite different in this respect, so abstraction is welcome.
2) abstractions in keyboard and mouse handling: also here there are differences, although subtle, between VCL, FMX and LCL.
3) abstractions in types: types such as font, color, rectangles, points are different in FMX and VCL.

So, we'll cover what is included in FNC to facilitate dealing with this.

Unit organisation

Perhaps the biggest stumbling block is unit organisation. As it is desirable to create components that can be installed in the IDE, (which means for Delphi for both the VCL and FireMonkey framework simultanously) we'll need units for VCL and units for FireMonkey. Although we can use the same class name, the class hierarchy for the VCL control and the FMX control will be different. The VCL FNC control will descend from the VCL TCustomControl and the FMX FNC control will descend from the FMX TControl. In Lazarus, the FNC control will descend from the LCL TCustomControl. In a nutshell, to solve this, we create 3 units for a component that will be nearly identical and we provide a conversion step to allow you to write the code in one unit and automatically generate the other units for the other frameworks. For this example, in total, we'll have 6 units: 3 units with the code for the control for the 3 supported frameworks and 3 units for the component registration in the IDE:

// Units for the VCL variant of the FNC control
VCL.TMSFNCCust.pas
VCL.TMSFNCCustReg.pas

// Units for the FMX variant of the FNC control
FMX.TMSFNCCust.pas
FMX.TMSFNCCustReg.pas

// Units for the LCL variant of the FNC control
LCLTMSFNCCust.pas
LCLTMSFNCCustReg.pas
We'll also use 3 packages: a package for VCL, a package for FMX and a package for LCL. We can install the VCL & FMX package simultanously in the Delphi IDE and the LCL package in the Lazarus IDE. The package for the custom control will have a dependency to the framework plus to the TMS FNC UI Pack for the framework. The structure of the packages in the Delphi 10.1 Berlin IDE is:


and in Lazarus, this is:

Getting to grips

Ok, now that the unit structure is setup, we can focus on writing the code. To write this code, we'll use 3 TMS FNC UI Pack units with abstractions: xxx.TMSFNCCustomControl, xxx.TMSFNCGraphics and xxx.TMSFNCTypes (with xxx = the framework). In this example, we'll write the code in VCL and automatically generate the FMX & LCL equivalents from it, so the uses list becomes:

for FMX
uses
  Classes, Types, FMX.TMSFNCCustomControl, FMX.TMSFNCGraphics, FMX.TMSFNCTypes;
for LCL
uses
  Classes, Types, LCLTMSFNCCustomControl, LCLTMSFNCGraphics, LCLTMSFNCTypes;
The FNC control we'll write here is very rudimentary for the sake of focusing on the abstractions. It is basically a gauge control that can be clicked with the mouse to set the gauge value, has a gauge value property to set it and can use the key up & down to change the gauge value. So, we need painting of the gauge, we need some gauge line color & font settings and we need handling of the mouse click and keyboard.


Control initialization

The control descends from the abstract FNC class TTMSFNCCustomControl and exposes one extra color property for the gauge line color (note that this are of the abstract type TTMSFNCGraphicsColor), a font (of the abstract type TTMSFNCGraphicsFont, that has also a font color in all frameworks) and a gauge value property of the type TControlValue. Note that the Stroke & Fill properties are published. This contains the control border & control background color and even enables things such as setting a background gradient, a border with a specific pen style etc...

  TControlValue = 0..100;

  TTMSFNCCustomControlSample = class(TTMSFNCCustomControl)
  private
    FLineColor: TTMSFNCGraphicsColor;
    FFont: TTMSFNCGraphicsFont;
    FValue: TControlValue;
  protected
    procedure Draw({%H-}AGraphics: TTMSFNCGraphics; {%H-}ARect: TRectF); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Stroke;
    property Fill;
    property Font: TTMSFNCGraphicsFont read FFont write SetFont;
    property LineColor: TTMSFNCGraphicsColor read FLineColor write SetLineColor default gcRed;
    property Value: TControlValue read FValue write SetValue default 0;
  end;
In the constructor, initialization of the line color property value is done as well as the border color and the font is created. This is of the type TTMSFNCGraphicsFont and the main purpose is to have a font with font color in all frameworks. There is one more thing particular in the constructor and that is the assignment of the Font.OnChange event handler. In Delphi Pascal, we can simply assign the object method but for the FPC compiler, this needs to be prefixed with the @ symbol. Fortunately, the Lazarus environment has the LCL define we can use to handle this.

{ TTMSFNCCustomControlSample }

constructor TTMSFNCCustomControlSample.Create(AOwner: TComponent);
begin
  inherited;
  Stroke.Color := gcBlack;
  FLineColor := gcRed;
  FFont := TTMSFNCGraphicsFont.Create;
  FFont.OnChanged := {$IFDEF LCL}@{$ENDIF}FontChanged;
  Width := 100;
  Height := 100; 
end;

destructor TTMSFNCCustomControlSample.Destroy;
begin
  FFont.Free;
  inherited;
end;
Painting

The control descends from the abstract FNC class TTMSFNCCustomControl and exposes 3 color properties, the border, background and gauge line color (note that these are of the type TTMSFNCGraphicsColor), a font (of the type TTMSFNCGraphicsFont, that has also a font color in all frameworks) and a gauge value property of the type TControlValue.

Painting is done in the override of the Draw() method that has 2 parameters: AGraphics: TTMSFNCGraphics, a framework neutral graphics library and the rectangle of the control via ARect of the type TRectF. In VCL and LCL only the not fractional part of the floating point numbers is used but of course in the FireMonkey framework, this can use the fractional parts as well.

The painting code itself is:

procedure TTMSFNCCustomControlSample.Draw(AGraphics: TTMSFNCGraphics;
  ARect: TRectF);
var
  angle: double;
  lf,tf: TPointF;
  w: single;
begin
  inherited;

  angle := Value/High(Value)*2*PI;

  w := Min(ARect.Right - ARect.Left, ARect.Bottom - ARect.Top) / 2;

  lf.X := (ARect.Right - ARect.Left)/2;
  lf.Y := (ARect.Bottom - ARect.Top)/2;

  tf.X := lf.X + Cos(angle) * w;
  tf.Y := lf.Y - Sin(angle) * w;

  AGraphics.Stroke.Color := LineColor;

  AGraphics.DrawLine(lf,tf);

  AGraphics.Font.Assign(Font);
  AGraphics.DrawText(ARect, InttoStr(Value),false, gtaCenter, gtaCenter);
end;

Mouse and keyboard handling

The mouse and keyboard handling is via the HandleKeyDown and HandleMouseDown virtual method overrides:

  TTMSFNCCustomControlSample = class(TTMSFNCCustomControl)
  protected
    procedure HandleKeyDown(var {%H-}Key: Word; {%H-}Shift: TShiftState); override;
    procedure HandleMouseDown({%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Single); override;
  end;
The implementation is straightforward. One thing that is noteworthy, is that the unit xxx.TTMSFNCTypes has a list of keys in a framework and operating system neutral way, i.e. we can check for the arrow up and arrow down keys with the constants KEY_DOWN and KEY_UP.

procedure TTMSFNCCustomControlSample.HandleKeyDown(var Key: Word;
  Shift: TShiftState);
begin
  inherited;

  if Key = KEY_DOWN then
  begin
    if Value > Low(Value) then
      Value := Value - 1;
  end;

  if Key = KEY_UP then
  begin
    if Value < High(Value) then
      Value := Value + 1;
  end;
end;
In the mouse down handler, we set the focus to the control when possible and then calculate the value of the gauge that matches with the mouse click position. Note that the X,Y values are of the type single to accomodate FMX and will contain non fractional values in VCL and LCL.

procedure TTMSFNCCustomControlSample.HandleMouseDown(Button: TTMSFNCMouseButton;
  Shift: TShiftState; X, Y: Single);
var
  angle: single;
  dx,dy: single;

begin
  inherited;

  if AllowFocus then
    SetFocus;

  dx := x - (Width/2);
  dy := - y + (Height/2);

  if dx = 0 then
    angle := sign(dy) * PI / 2
  else
    angle := ArcTan(dy/dx);

  if dx < 0 then
    angle := PI + angle;

  if angle < 0 then
    angle := angle + 2 * PI;

  Value := Round((angle / 2 / PI) * High(Value));
end;
Creating the units for FMX and LCL

Now that we have the VCL framework FNC component ready that contains 100% framework neutral code, let's create automatically the FMX and LCL units from this. 3 steps are needed:

1) Rename the unit VCL.TMSFNCCust.pas to FMX.TMSFNCCust.pas and LCLTMSFNCCust.pas
2) Change in the unit .PAS file the unit name, i.e. replace VCL.TMSFNCCust by FMX.TMSFNCCust and LCLTMSFNCCust respectively
3) Change the unit references in the uses list from
VCL.TMSFNCCustomControl, VCL.TMSFNCGraphics, VCL.TMSFNCTypes;
to
FMX.TMSFNCCustomControl, FMX.TMSFNCGraphics, FMX.TMSFNCTypes;
or
LCLTMSFNCCustomControl, LCLTMSFNCGraphics, LCLTMSFNCTypes;

To accomplish this, we call a simple powershell script that performs text replacement from VCL.TMS to FMX.TMS or LCLTMS respectively:

powershell -command "(gc VCL.TMSFNCCust.pas) -replace 'VCL.TMS','LCLTMS' |Out-file LCLTMSFNCCust.pas -Encoding utf8"
powershell -command "(gc VCL.TMSFNCCust.pas) -replace 'VCL.TMS','FMX.TMS' |Out-file FMX.TMSFNCCust.pas -Encoding utf8"
With these units created, we can compile the packages and install our FNC custom control for use from VCL, FMX and LCL applications in Lazarus.


The full source of this sample FNC custom control can be downloaded here. This sample can be used with the latest version of the TMS FNC UI Pack.

We hope this already whets your appetite for exploring FNC and the power of writing code for UI controls once for use in 3 frameworks. See also this blog article for a more general coverage of what is available in the TMS FNC UI Pack. In a next article, we'll go deeper in compound control creation and also the TTMSFNCGraphics library that offers a vast range of functions, going from drawing text, polygons, polylines, images in various formats, controls like checkboxes, radiobuttons, buttons, ... and much more.

Presenting the TMS software website redesign

$
0
0

Since 1995, the year tmssoftware.com started focussing on offering components & tools for developers, it was time for a fifth major overhaul of our website. Since the beginning of this year, a lot work went into this overhaul behind the scenes for this overhaul. Some of the main goals of this website overhaul we set at the beginning of this project were:

  • Improved product discoverability
  • Graphically evolve into a more modern style, influenced by Apple, Google material design, Windows 10 style
  • Improved organisation of products by technologies and overview of product structure
  • Find sufficient differentiating and recognizable style elements
  • Move to responsive design for optimal UX for mobile, tablet, desktop
  • Improved readability of information with more space, more contrast, more consistent and better structure of product pages
  • Make the site more consistent with common web practices
Taking all these goals in consideration, you will understand the project was quite challenging. What's more, we wanted to respect the motto: evolution instead of revolution to also keep a high degree of familiarity for customers who know and use our website for all these years. Fortunately, we got the help from the company Well Communications. Here's a break-down of how all these goals were achieved:

Improved product discoverability

There is product discoverability from website top down navigation and from entering the website via search. Instead of showing the latest product releases, the home page is now centered around products. Products can be discovered from the main "Products" menu with a popup showing an overview of our most popular products organised by product type.



. At the same time, there is an overview of technologies/frameworks our products are based on and from there, the visitor is guided to the products per technology. Within the technology boxes on the main page, we have also featured our most popular products within this technology and the visitor can navigate to it directly by clicking it.



Equally important is discoverability from entering the website via searches. Here we have on product pages immediately references to equivalent products for different technologies. If you'd search for a cross platform scheduling/planner components via Google and were directed to the VCL product page, you can immediately see in the top right corner that we have the product also available for other technologies/frameworks.




Graphically evolve into a more modern style

Where the previous website was still more influenced by the Office 2007 style, early iOS style, it was a choice to move to a more minimal style typically used these days. We removed the old DVD box analogy representing the software, we removed gradients, grayish & soft elements and now have a more sharp, slightly harder graphical design.

Improved organisation of products

There is a strong focus on organisation of products by technology or framework. This is already visible on the main page where we have currently 8 boxes representing 8 technologies/frameworks we have products for. Each technology or framework is represented by a specific color and these colors should help increase the awareness under what technology a product can be used.



Some products support multiple technologies/frameworks. Whereas this was not possible in the former website, now, products supporting multiple frameworks at the same time will be listed under each technology/framework these support. As such, our ORM product TMS Aurelius will appear under VCL, FMX and the business logic category as it falls into each of these categories.

Differentiating and recognizable style elements

Our logo has been slightly restyled with the underlining of the part 'tmssoftware' and ending the line with a corner under the dot. Where a square logo is needed like for avatars, this is reduced to 'tms' underlined ending under the dot.






This line & dot element is repeated for various boxes on our website and subtle animations stress this line & dot element.



We also like to think of the dot on the line a kind of similar to putting the dot on the i, hinting at our drive and continuous striving for delivering our products and support with the highest possible quality.

Responsive design

We had noticed that about 20% of our website traffic was coming from mobile devices & tablets, so we considered this important enough to also focus on offering a good UX for users on mobile phones and tablets. Our website will now automatically adapt to have a good usability on all these different browser form factors.



Improved readability

Where in our former website, the product information or blogs were often squeezed between the left sidebar and the right-side download box or blog history & selector, we removed the boxes at the right side of these pages and we add the capability to hide the left sidebar. This way, the full page can be used to read our product information and blogs and have this space available for bigger screenshots as well.

Consistency with common web practices

We paid attention to keep our website consistent with typical & common web practices these days. You'll find the website sign-in button in the top right corner of the page. You find the company information, social media links, website sitemap and commonly used page references always the bottom of the website in the footer area. In a similar way, after login the website will always display your user account name on top as well as the main menu in a fixed non-scrolling area. A search functionality across our product pages, web forum and FAQs is also available and always visible in the top right corner.




In a nutshell, we hope you will not only find our v5.0 website more visually pleasing but also easier to navigate and discover products. While v5 was tested and approved by a small group of people in the past couple of weeks, as of today, it is available for everyone. This means that from today, we're eager to learn from you how you experience the v5 website and more importantly, if you have more ideas and suggestions to improve it even further. With your inputs and help, we can continue to strive for perfection or "to put the dot on the line" in our case. But at the same time,keeping the famous words of painter Salvador Dali in mind: "Have no fear of perfection, you will never reach it".



TMS software teams up with Malcolm Groves' new Code Partners company

$
0
0

  -  
It's with a lot of enthusiasm that we'd like to announce today that TMS software teams up with Malcolm Groves' new Code-Partners company. With this partnership, Code Partners becomes our exclusive representative for Australia, Japan and East-Asia. Code Partners will offer a full scale of services to assist you with your needs for application development and in relationship to TMS software products specifically, this includes:

  • help you choosing the TMS components and tools for your tasks at hand
  • offer expert-level consulting for integrating our components in your applications with a direct line to our R&D team
  • handle selling of our products in the Australia, Japan and East-Asia region
  • offer training on our components and tools
  • make extra information, insights, tips & tricks available on TMS components online via blog, videos, webinars,..

We're convinced that Malcolm's deep technical product knowledge, long-time experience with Delphi and software development in general and strong motivation is an excellent choice for a successful & long-term partnership that strives for a win³ goal:

  • a win for our customers
  • a win for Code Partners
  • a win for us
Visit Code Partners today and stay tuned!

Almost 8 years at tmssoftware

$
0
0



I remember my first day at tmssoftware. Some guys behind their computer, busy toggling on their keyboard, all focused on that screen.
The first job Bruno gave me: here is our website, can you put all our products in a database, together with supported IDE's, frameworks, Windows versions. OK, not the nicest job... but afterwards I understood Bruno just wanted me to learn as fast as possible the entire product range. And yes, our product range is quite large.

The second job was to go through all product demo projects ... here we go again, this kept me busy for a few weeks. After this 'product' introduction, I finally was ready to take the challenge to enter the world of 'support'.

Now 8 years later, support is a daily routine, most of our customers know me, and more than that, most of the emails in the info@tmssoftware.com inbox are starting with 'hi Nancy'.

What also keeps fascinating me in this Delphi world is how everything evolves so incredibly fast. 8 years ago we were not even talking about FireMonkey, cross-platform development, components for accessing cloud services, testing on multiple devices like iPad, Android, Linux, Raspbian, ... It also seems like the Delphi world has a small group of real die-hard fans , always the same people you see in the Delphi Google communities, on forums, at conferences, ... Or is it just my imagination?

Beside all these new products we released over the 8 years, we also moved to a new office this year and last month we had a major redesign of our website (thanks again for all the positive comment we received!).

Can you imagine how curious I am about what new technologies, new challenges, new products, new exciting things I would write about in a similar blog post after another 8 years at tmssoftware?!

After these 8 years I can say I'm still glad to help our customers as good as I can and as fast as I can.

---
Kind regards,
Nancy Lescouhier
TMS software team


TMS teams up with Roman Yankovsky to bring TMS FixInsight, the static code analysis tool

$
0
0

We're pleased and honoured to announce that Roman Yankovsky joins the TMS family and the excellent static code analysis tool FixInsight is from now on available as TMS FixInsight product. Roman Yankovsky is the architect and developer of the highly respected FixInsight tool for Delphi. We're proud and excited that the high expertise and talent of Roman is now part of the TMS family! First of all, some details about what FixInsight can do for you:

FixInsight is an IDE plugin from where you can start a static code analysis or you can configure FixInsight to run as part of the build process. This code analysis helps you identify all potential issues in the code and coding convention compliance. This let's you trace bugs before you or your customers do. At this time, FixInsight has 29 specific code warnings, like for example a warning about objects created in the Try block or destructors without override directive... It has 10 code convention warnings and 2 code optimization hints.



FixInsight is all about watching over the quality of the code from early on in the development process and by that reducing the number of bugs and reduce support.

With FixInsight being part of the TMS family now, we'll work together with Roman and we look forward to bring future versions of TMS FixInsight that make it even more feature-rich than today. We of course also welcome & look forward to all feedback, comments, needs from you about FixInsight.

FixInsight is available in two editions: TMS FixInsight Personal and TMS FixInsight Pro. The difference between the two is that TMS FixInsight Pro comes with a command-line tool to integrate running TMS FixInsight from various automation / build processes.
Discover FixInsight now by loading the trial version from here

New cryptography solutions from TMS software in partnership with Cyberens

$
0
0

In the past couple of months, TMS software formed a partnership with the company Cyberens with the goal to jointly develop cryptography solutions for Delphi, C++, C users. With this partnership, Bernard Roussely and Marion Candau from Cyberens bring in the high expertise in cryptography algorithms with the expertise of TMS software to bring this in intuitive and easy to use classes for Delphi and C++Builder users. Bernard Roussely and Marion Candau have years of experience with cryptography, developed cryptography based solutions for various high-profile security projects in companies and governments and have completed all the administrative work to satisfy EU export control requirements.

The first result coming forth out of this partnership is the TMS Cryptography Pack. This is a bundle of advanced and up-to-date cryptography algorithms such as AES, SPECK, SALSA20, ECIES encryption, SHA2 and SHA3 hash generation, RSA, EdDSA signing ...



You can discover the TMS Cryptography Pack here and download a fully functional trial version for Delphi XE2 to Delphi 10.1 Berlin.

We're excited to work together with Bernard Roussely & Marion Candau to build out a strong line of cryptography based solutions for software developers in the coming years and we're eager to learn about your needs, comments, wishes in this area.

TMS X-Cloud Todolist with FNC

$
0
0

It's almost three months since we released the first version of the TMS FNC UI Pack, a set of Framework Neutral Components (FNC), and have meanwhile released 2 major updates which include the TTMSFNCTabSet/TTMSFNCPageControl (v1.1), the recently introduced TTMSFNCListBox/TTMSFNCCheckedListBox and significant improvements / new features to the TTMSFNCTreeView such as filtering, sorting, keyboard lookup, clipboard support, ... (v1.2).

As already explained in previous blog posts (http://www.tmssoftware.com/site/blog.asp?post=335 and http://tmssoftware.com/site/blog.asp?post=346), the TMS FNC UI Pack is a set of UI controls that can be used from VCL Windows applications, FireMonkey (FMX) Windows, Mac OS-X, iOS, Android applications and LCL framework based Lazarus applications for Windows, Linux, Mac OS-X, ... . The TMS FNC UI Pack contains highly complex & feature-rich components such as grid, planner, rich editor, treeview, toolbars. So, with a single set of controls, you have the freedom of choice to use Delphi, C++Builder or the free Lazarus to create applications for a myriad of operating systems, you have a single learning curve to these components and as demonstrated here, you can use a single source to create apps for multiple targets.

This blog post will cover the TTMSFNCCheckedListBox, which is one of the new components that are added in the latest release (v1.2) of the TMS FNC UI Pack, show how to use myCloudData.net to store data and demonstrate how easy it is to switch between FMX, VCL and LCL with one shared source code. myCloudData is an easy to use & flexible service to make use of structured storage in the cloud from Windows, web, mobile or IoT apps and is offered by tmssoftware.com. myCloudData is OAUTH/JSON REST based and our TMS Cloud Pack includes a component to access the service and thus your data seamlessly.

Click image for more screenshots.

A single shared source

As with our TV-guide sample we have created a single shared source file that is used in a FMX, VCL and LCL project. The unit starts by defining the business logic class that will be instantiated in our application main form unit.
  TTODOListLogic = class
  private
    FTable: TMyCloudDataTable;
    FListBox: TTMSFNCCheckedListBox;
    FMyCloudDataAccess: TMyCloudDataAccess;
    FOnConnected: TNotifyEvent;
    FBitmapContainer: TTMSFNCBitmapContainer;
  protected
    procedure DoConnected(Sender: TObject);
  public
    destructor Destroy; override;
    procedure InitListBox(AListBox: TTMSFNCCheckedListBox);
    procedure InitMyCloudData;
    procedure Refresh;
    procedure InitializeTable;
    procedure AddNewItem(AText: string; ADate: TDateTime; APriority: TPriority);
    procedure DeleteItem;
    procedure Connect;
    procedure DoBeforeDrawItem(Sender: TObject; AGraphics: TTMSFNCGraphics; ARect: TRectF; AItem: TTMSFNCListBoxItem; var AAllow: Boolean; var ADefaultDraw: Boolean);
    procedure DoItemCheckChanged(Sender: TObject; AItem: TTMSFNCCheckedListBoxItem);
    procedure DoItemCompare(Sender: TObject; Item1, Item2: TTMSFNCListBoxItem; var ACompareResult: Integer);
    property OnConnected: TNotifyEvent read FOnConnected write FOnConnected;
    property BitmapContainer: TTMSFNCBitmapContainer read FBitmapContainer write FBitmapContainer;
  end;
Each framework has its own set of units in order to compile succesfully. We use the conditional defines added to our project to make the difference between each framework.
uses
  Classes, SysUtils, DB
  {$IFDEF VCL}
  ,VCL.TMSFNCListBox, VCL.TMSFNCCheckedListBox, CloudMyCloudData, CloudBase, VCL.TMSFNCUtils,
  CloudCustomMyCloudData, VCL.TMSFNCGraphics, VCL.Dialogs, VCL.TMSFNCTypes, Types, VCL.TMSFNCBitmapContainer;
  {$ENDIF}

  {$IFDEF FMX}
  ,FMX.TMSFNCListBox, FMX.TMSFNCCheckedListBox, FMX.TMSCloudMyCloudData, FMX.TMSCloudBase,
  FMX.TMSFNCUtils, FMX.TMSCloudCustomMyCloudData, FMX.TMSFNCGraphics, FMX.TMSFNCTypes, FMX.Dialogs, Types, FMX.TMSFNCBitmapContainer;
  {$ENDIF}

  {$IFDEF LCL}
  ,LCLTMSFNCListBox, LCLTMSFNCCheckedListBox, LCLTMSCloudMyCloudData, LCLTMSCloudBase,
  LCLTMSFNCUtils, LCLTMSCloudCustomMyCloudData, LCLTMSFNCGraphics, Dialogs, LCLTMSFNCTypes, LCLTMSFNCBitmapContainer;
  {$ENDIF}

myCloudData

As our todolist is storing its todo items in the cloud we take advantage of our own service, that can virtually store anything we want. The initialization is done programmatically.
procedure TTODOListLogic.InitMyCloudData;
begin
  FMyCloudDataAccess := TMyCloudDataAccess.Create(nil);
  FMyCloudDataAccess.PersistTokens.Location := plIniFile;
  {$IFDEF FMX}
  FMyCloudDataAccess.PersistTokens.Key := TTMSFNCUtils.GetDocumentsPath + PthDel + 'myclouddatafmx.ini';
  FMyCloudDataAccess.OnConnected := DoConnected;
  {$ENDIF}
  {$IFDEF VCL}
  FMyCloudDataAccess.PersistTokens.Key := TTMSFNCUtils.GetDocumentsPath + PthDel + 'myclouddatavcl.ini';
  FMyCloudDataAccess.OnConnected := DoConnected;
  {$ENDIF}
  {$IFDEF LCL}
  FMyCloudDataAccess.PersistTokens.Key := TTMSFNCUtils.GetDocumentsPath + PthDel + 'myclouddatalcl.ini';
  FMyCloudDataAccess.OnConnected := @DoConnected;
  {$ENDIF}
  FMyCloudDataAccess.PersistTokens.Section := 'tokens';
  FMyCloudDataAccess.App.Key := MYCLOUDDATAKEY;
  FMyCloudDataAccess.App.Secret := MYCLOUDDATASECRET;
  FMyCloudDataAccess.App.CallBackPort := 8888;
  FMyCloudDataAccess.App.CallBackURL := 'http://127.0.0.1:8888';
end;
You might notice 3 things here. First is the TMyCloudDataAccess class which is common between FMX, VCL and LCL. This is defined earlier in our business logic as the unit names are different for FMX, VCL and LCL.
type
  {$IFDEF VCL}
  TMyCloudDataAccess = class(TAdvMyCloudData);
  {$ENDIF}

  {$IFDEF FMX}
  TMyCloudDataAccess = class(TTMSFMXCloudMyCloudData);
  {$ENDIF}

  {$IFDEF LCL}
  TMyCloudDataAccess = class(TTMSLCLCloudMyCloudData);
  {$ENDIF}
Second, is the event handler assignment, that we also need to wrap with conditional defines because LCL works with an additional @. Third is the ini file that is also created with a framework suffix, as the token and token encryption are unique per application and not shareable. After defining our business logic, it's time to setup our GUI. The form unit is shared between FMX, VCL and LCL and there you will also notice the uses list and the form file is separated with defines. After designing our form (using the TTMSFNCCheckedListBox, some tool bar buttons (TTMSFNCToolBarButton) we are ready to connect to our business logic and create a working todo list that stores its items in the cloud.
procedure TTODOListForm.DoConnected(Sender: TObject);
begin
  Panel1.Enabled := True;
  Panel2.Enabled := True;
  TMSFNCToolBarButton2.Enabled := False;
  TMSFNCCheckedListBox1.Enabled := True;
  TMSFNCToolBarButton4.Enabled := True;
  TMSFNCToolBarButton5.Enabled := True;
  TMSFNCToolBarButton6.Enabled := True;
  TMSFNCToolBarItemPicker1.Enabled := True;
  FTODOListLogic.InitializeTable;
  FTODOListLogic.Refresh;
end;

procedure TTODOListForm.FormCreate(Sender: TObject);
begin
  FTODOListLogic := TTODOListLogic.Create;
  FTODOListLogic.InitListBox(TMSFNCCheckedListBox1);
  FTODOListLogic.InitMyCloudData;
  {$IFDEF LCL}
  FTODOListLogic.OnConnected := @DoConnected;
  {$ELSE}
  FTODOListLogic.OnConnected := DoConnected;
  {$ENDIF}
  TMSFNCCheckedListBox1.BitmapContainer := TMSFNCBitmapContainer1;
  TMSFNCToolBarItemPicker1.BitmapContainer := TMSFNCBitmapContainer1;
  TMSFNCToolBarItemPicker1.Bitmaps.Clear;
  TMSFNCToolBarItemPicker1.Bitmaps.AddBitmapName('low');
  TMSFNCToolBarItemPicker1.DisabledBitmaps.Assign(TMSFNCToolBarItemPicker1.Bitmaps);
  TMSFNCToolBarButton2.DisabledBitmaps.Assign(TMSFNCToolBarButton2.Bitmaps);
  TMSFNCToolBarButton4.DisabledBitmaps.Assign(TMSFNCToolBarButton4.Bitmaps);
  TMSFNCToolBarButton5.DisabledBitmaps.Assign(TMSFNCToolBarButton5.Bitmaps);
  TMSFNCToolBarButton6.DisabledBitmaps.Assign(TMSFNCToolBarButton6.Bitmaps);

  dt := TMyDateTimePicker.Create(Self);
  {$IFDEF FMX}
  dt.Position.X := 5;
  dt.Position.Y := 40;
  {$ELSE}
  dt.Left := 5;
  dt.Top := 40;
  {$ENDIF}
  dt.Date := Now;
  dt.Parent := Panel1;
  dt.Width := 105;
end;

procedure TTODOListForm.FormDestroy(Sender: TObject);
begin
  FTODOListLogic.Free;
end;

procedure TTODOListForm.TMSFNCCheckedListBox1ItemSelected(Sender: TObject;
  AItem: TTMSFNCListBoxItem);
begin
  TMSFNCToolBarButton6.Enabled := True;
end;

procedure TTODOListForm.TMSFNCToolBarButton1Click(Sender: TObject);
begin
  FTODOListLogic.Refresh;
end;

procedure TTODOListForm.TMSFNCToolBarButton2Click(Sender: TObject);
begin
  FTODOListLogic.Connect;
end;

procedure TTODOListForm.TMSFNCToolBarButton3Click(Sender: TObject);
begin
  FTODOListLogic.DeleteItem;
end;

procedure TTODOListForm.TMSFNCToolBarButton4Click(Sender: TObject);
begin
  FTODOListLogic.AddNewItem(Edit1.Text, dt.Date, TPriority(TMSFNCToolBarItemPicker1.SelectedItemIndex));
end;

procedure TTODOListForm.TMSFNCToolBarItemPicker1ItemSelected(Sender: TObject;
  AItemIndex: Integer);
begin
  TMSFNCToolBarItemPicker1.Bitmaps.Clear;
  TMSFNCToolBarItemPicker1.Bitmaps.AddBitmapName(TMSFNCBitmapContainer1.Items[AItemIndex].Name);
  TMSFNCToolBarItemPicker1.DisabledBitmaps.Assign(TMSFNCToolBarItemPicker1.Bitmaps);
end;
When starting the application, and clicking the connect button, our business logic unit will do the work. It will create a table in myCloudData, send a notification to our GUI, which will then allow to add items to our listbox, refresh or delete existing items, and this is done with one source code, available on multiple frameworks, multiple platforms.

The full source code is available for download

Click image for more screenshots.


Windows 10 funnies

$
0
0

This isn't the first funny Windows 10 behavior I encounter that makes a developer scratch his head but I thought to share this particular one.

As it happens, here at TMS software, we quite often deal with grid related code, be it with our VCL TAdvStringGrid or FMX TTMSFMXGrid or FNC TTMSFNCGrid and when doing some testing code for our grid, why not create a new Delphi project Grid.dproj. To my surprise, compiling & running this app from the Delphi 10.1 Berlin IDE shows a funny black notification window for a moment on startup. The notification changes randomly and always informs about some shortcut keys.
My initial thought was that our grid code somehow erratically triggered something in the new Win 10 toast notification code in the Delphi 10.1 Berlin VCL. But no, even after removing our grid component, the notification kept coming. The app reduced to its minimum of an empty form with a single label shows the notification on startup.


Form at design time


Form at runtime

Then we created a new application, called Project1.dproj and surprise,... no such shortcut notification. Next logical step .. compare the grid.dproj file and the project1.dproj file to see if there was some difference in the .dproj file that could trigger this behavior. But no, no suspicious differences found.
Next we suspected a virus and therefore tested the app source on a different Windows 10 machine and the magic notification popped up again.

At that point, Google is your friend and after a quite long search, it leaded to suspect the XBox games Grid and Grid2 where Windows shows a notification of shortcuts for games. Searching the XBox store indeed reveals such game Grid and Grid2.



And yes, renaming the Delphi project to grid2.dproj will also make the notification appear for grid2.exe but not so with grid3.dproj or any other combination.

Being still intrigued by this Windows behavior, we thought that Windows 10 had perhaps some registry key that contains apps for which to show these notifications on startup, but nowhere grid.exe or grid2.exe could be found in the registry. That leaves us with the assumption that somewhere hidden in the Windows 10 source code, there must be a hardcoded reference to grid.exe and grid2.exe.
Conclusion in a nutshell: do not ship Delphi apps named grid.exe or grid2.exe to your customer if you want to avoid your customer asking questions about a weird notification coming up. :)

Delphi Cookbook 2ed for TMS FixInsight Customers

$
0
0



Delphi Cookbook 2ed by Daniele Teti is finally available. It is a best seller for PacktPub in its category, you can find more information about the book on its author blog.

I am honored to be the reviewer of the second edition. It was an exciting adventure to take a look behind the curtains of book publishing. And I have to say that Daniel did a great job.

Like any cookbook, this book is a collection of recipes. It is exactly what a "cookbook" should be. Recipes are very different - from the use of VCL styles to the drawing on a FMX ListView component, and from examples of functional programming in Delphi to importing a Java-library interface in Android. Over 60 recipes, according to the cover :)

Personally for me, the most interesting part of this book is not the essence of the recipes themselves, but the fact that the author uses all Delphi innovations when it's possible. This was very interesting to watch. Delphi has been developing very quick in recent years, and we are not always aware of how much is new in the recent versions.

I therefore recommend Delphi Cookbook primarily for those who still works with older versions of Delphi and wants to take a closer look at the innovations. Although those who work with the latest version will definetely find something to expand their horizons too. And it particularly useful as a reference book.

PacktPub was so kind to offer 50% discount on the ebook for FixInsight customers. If you order a FixInsight license in July you will get a discount coupon for the book on PacktPub website.


Introducing tag based search on TMS software website

$
0
0

Since 1995, the year Delphi was born, TMS software has been creating components and tools for developers for 21 years now and in all these years, our offerings have grown significantly. From the beginning, we have focused on VCL UI controls but soon controls for IntraWeb and .NET were added. As soon as Embarcadero introduced the cross-platform FireMonkey (FMX) framework, we started to create UI controls for this framework as well. Around the same time, a product line to help you with your business logic, the TMS Business Subscription set of products was created. Then the explosion of cloud services came and we jumped on that bandwagon with our series of cloud components to make it dead-easy to connect & consume these cloud services. Recently, our newest line of UI controls, the FNC (Framework Neutral Components) was launched where the focus is on offering you one set of components, one learning-curve and the freedom to use the controls (simultaneously or not with a single source base) for VCL Windows applications, FMX cross platform applications with Delphi or C++Builder or take a different route with the free Lazarus IDE/FPC compiler and the LCL framework to target Windows, Linux, macOS, ...

We have long lost count of the number of components we have on offer and we have been wrestling a lot with trying to present our products as logically structured, easy to find and to discover as possible. With the introduction of our new website v5.0 end of May this year, a strong focus was on organisation per technology/framework with color coding of technologies for easy recognition. In the product popup accessible from the top menu, the organisation is more semantically, grouping by themes like grids, charts, cloud, ...

But long on our todolist was also a system to tag our components and make tag based search possible this way. Well, today, I'm happy to introduce our tag based search on our website. Our tag based search is not replacing regular text based search or navigation mechanisms per technology/framework or theme. It is just one more way, one more tool to help you find what you are looking for.

Note that from now on, our products display their tags at the bottom of the product page and tag based search can be started from clicking on these tags but is also possible from the product popup or regular search started from the top right corner.

To see the tag search in action, here is a sample where a search for a treeview control is done. The word treeview can be typed or picked from the tag list. The search returns the different products, nicely color coded per technology in what products we offer a treeview control:



We hope this additional tool on our website will help you finding what you need faster. As time evolves and with your feedback, we'll further fine-tune & extend the tags. Let us know what you think about it and how you like it!

From the team with love: the new TMS Component Pack 8.3

$
0
0

We're excited to present the newest edition of the TMS Component Pack v8.3, our pack of over 400 VCL UI controls to make powerful, feature-rich Windows applications with Delphi & C++Builder.

The TMS Component Pack first version dates back from 1998 when our customers started asking for a bundle of our grid component and growing number of additional VCL UI controls developed by then.

TMS Component Pack is as such 18 years in the making and it's a relentless job to keep fine-tuning existing controls to changing needs, changing UI requirements, new UI paradigms & themes, new feature requests and add create brand new controls. Both surprising and exciting is that during all these 18 years, almost on a weekly basis, new ideas come in from you and from our team for component features and new components. A lot of our customers using TMS Component Pack UI controls are as passionate as our team about getting exactly the right look & feel, behavior and capabilities from the controls.

With this new version v8.3 we have added the usual list of new features, improvements and fixes. You can always consult this list here. One especially bigger extension was drag & drop support, sorting & filtering in our supercharged multicolumn treeview control. But in this blog, I wanted to draw your attention to new controls added in this release.

First of all, there is the new TAdvSearchList / TAdvSearchEdit.

The design of this component is inspired by new paradigms in search controls featured mostly on websites. Websites like Amazon, AliExpress, Google, Facebook, Booking.com, Trivago, ... all help the user in search by filtered suggestion lists as you type. TAdvSearchList is the basis for such list and offers a multicolumn list with optional categorized items, pictures, text and description with items, a place to show a number of search results and various criteria to find a match from first character, anywhere in a word, in text only or text and description etc...

Where the list control can be easily hooked up to a separat edit control and perform its search & filtering, the TAdvSearchEdit has the list embedded in a dropdown and it features additional optional buttons for directly filtering per categories when categories are used. It can as such be configured as a simple list to more complex and attractive looking lookup lists like this example:



Second new control added as is TAdvResponsiveList (preview).

The TAdvResponsiveList is a VCL UI control inspired by responsive design techniques and bringing this paradigm to Windows controls.As is the case with browsers, where we want to offer the best experience regardless of the browser client area size, Windows applications can also benefit from adapting the control's look & feel and behavior to the control's size to ensure the experience with your application is optimal regardless of the size of the screen of the user or regardless of a user running your application in a small window just in a corner of his desktop.



In a nutshell, TAdvResponsiveList offers a variable number of conditions that can be set for rendering its content depending on the size of the control. This can range from the number of columns depending on the width, the number or rows depending on the height, the absolute or relative size of cells when the control is resizing etc... As each cell offers rendering of HTML formatted text and this HTML formatted text can be the result of a template and in the conditions, variable templates can be set depending on the condition, i.e. size of the control. This way, more or less detail can be shown in the control item depending on its size.
But the sheer flexibility of this control becomes apparent when TAdvResponsiveList instances can be used as a child control in a TAdvResponsiveList and both parent and child can have their set of conditions. Or when you can create your own control descending from TAdvResponsiveList that can have fully custom drawn items.
A first developers guide to TAdvResponsiveList is available now. With TMS Component Pack 8.3, you can explore and start using the control from today in your applications. It is for now still marked as being in preview as this is all new to VCL application developers and we're eager to learn about your thoughts, comments, feedback, ideas & suggestions for next iterations of TAdvResponsiveList.

September ... a busy 'event' month at tmssoftware

$
0
0

We're pleased that in the month September we have again the chance to meet our customers, to show our products, to meet new developers who wishes to learn about our components & tools, ... on several upcoming events in Europe.

Bruno Fierens has also been invited as speaker, Bruno will talk about the challenges of creating cross-framework Pascal components. He will bring a session that discusses the differences between Pascal UI frameworks, how to build UI controls that can be used in the different UI frameworks and an introduction to the complex TMS FNC UI Controls that we have released so far. We will also show a sneak preview of a few brand new FNC UI controls that are currently in development.
An overview of the events:

SDN event - 2 September

Achmea Conference Center
Handelsweg 2, 3707 NH ZEIST
The Netherlands



Delphi Congrès 2016 - 15 September

FIAP
30 rue Cabanis, 75014 Paris
France



Delphi-Tage 2016 - 17 September

Mercure Hotel
Friesenstrasse 44-48, 50670 Köln
Germany



Pascal Delphi Festival 2016 - 20 September

Congrescentrum
Blokhoeve 1, 3438 LC Nieuwegein
The Netherlands



We're looking forward to see you at one of these events!

Viewing all 1093 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>