Sunday, March 22, 2020

Delphi Tip of the Day - Object Inspector Search

I was watching an excellent video on how to use the batch move capabilities within Delphi to import text data into a database and had one of those "What the heck just happened" moments. The object inspector was only displaying the "Name" property.

What the heck just happened...


I rewound the video by about 20 seconds and paid careful attention to what was going on. The presenter used the "search" box associated with the Object Inspector. What? I had no idea you could do that. Wow! Game Changer!

I've been staring at the object inspector for years and never even saw the search box even though it's right there, plain as day, in my face. Sure beats scrolling up and down through all the properties anytime I wanted to make a change.

Game Changer!




Alternate Game Changer!


I'd like to thank Bruce McGee for the following "Quick Edit" tip:

Right-click on a control at design time and select Quick Edit from the drop-down menu. It works for VCL and FMX controls. The VCL image on DocWiki is not correct. It actually reads "Caption" instead of "Text" in my version of Delphi.

http://docwiki.embarcadero.com/RADStudio/Rio/en/Quick_Edit_(VCL)
http://docwiki.embarcadero.com/RADStudio/Rio/en/Quick_Edit_(FireMonkey)


Enjoy!
Semper Fi,
Gunny Mike

Saturday, March 21, 2020

Delphi - SQLite Sample Projects

I've been exploring SQLite within Delphi. There are several sample SQLite applications that come with Delphi if you selected to install the samples during setup. I've put together the list of SQLite samples that are available.

The common samples file path is located here:
..\Studio\20.0\Samples\Object Pascal\Database\FireDAC\Samples\

FireDAC.SQLiteIniFile Demo Sample 

This sample uses FireDAC to connect to an SQLite database that emulates the structure and the operations of an INI File.

AddOn\SQLiteIniFile\Demo\SQLiteIniDemo.dproj

FireDAC.TFDLocalSQL MegaFMX Sample 

The sample shows different TFDLocalSQL applications.

Comp Layer\TFDLocalSQL\MegaFMX\LSQLMega.dproj

FireDAC.SQLite Encryption Sample 

This sample demonstrates how to encrypt/decrypt an SQLite database.

DBMS Specific\SQLite\Encryption\SQLite_Encrypt.dproj

Controlling Database Access Rights 

The SQLite is an embedded DBMS. This implies that it is a single user DBMS and does not need such concepts as a user, access rights, and so on. Still, some application can benefit from an access right control, for example:

  •  An application can restrict rights depending on an end-user license. A demo license means less possibilities, a full license provides all possibilities. 
  • Multi-tier data access frameworks can use their own user concept and control data access rights using some generic approach. 

 Again, SQLite provides a unique feature allowing you to authorize or not SQL commands. To work with this API, a Delphi application should set the OnAutorize event handler of the TSQLiteDatabase object, which is a database connection wrapping object. Hook this event after a database connection is opened.

DBMS Specific\SQLite\OnAuthorize\SQLite_OnAuthorize.dproj

Hooking Database Updates 

SQLite provides an unique API allowing you to monitor all updates to a database. This feature can be used, for example, to log all updates to a DB. To work with this API, a Delphi application should set the OnUpdate event handler of the TSQLiteDatabase object, which is a database connection wrapping object. Hook this event after a database connection is opened.

DBMS Specific\SQLite\OnUpdate\SQLite_OnUpdate.dproj

Custom Collations 

SQLite stores and handles all character data either in UTF8 or UTF16, depending on the OpenMode connection parameter. When SQLite needs to compare or sort a character data, it has to know what rules to use for this. The rules are known as a collation.

DBMS Specific\SQLite\UserCollation\SQLite_UserColl.dproj

Custom Functions 

SQLite does not support the stored procedure or function concept, as it allows you to use the host language environment to extend the engine functionality. SQLite allows you to register host language functions in the SQLite engine and use them in the SQL commands. FireDAC simplifies this by introducing the TFDSQLiteFunction component.

DBMS Specific\SQLite\UserFunc\SQLite_UserFunc.dproj

The SQLite R*Tree Module 

An R-Tree is a special index that is designed for doing range queries. R-Trees are most commonly used in geospatial systems where each entry is a rectangle with minimum and maximum X and Y coordinates. Given a query rectangle, an R-Tree is able to quickly find all entries that are contained within the query rectangle or which overlap the query rectangle.

This idea is easily extended to three dimensions for use in CAD systems. R-Trees also find use in time-domain range look-ups. For example, suppose a database records the starting and ending times for a large number of events. A R-Tree is able to quickly find all events that were active at any time during a given time interval, or all events that started during a particular time interval, or all events that both started and ended within a given time interval. And so forth. 

DBMS Specific\SQLite\UserRTree\SQLite_UserRTree.dproj

Title: SQLite 

Brief Description: This snippet shows you how to populate a listbox from a SQLite database using FireDAC and LiveBindings. See the section called "Accessing a Database" in the Mobile Tuturials topic in the documentation for more information and licensing requirements.

Platforms supported: iOS, Android

..\Mobile Snippets\FireDACSQLite\FireDAC_SQLite.dproj

Enjoy
Semper Fi,
Gunny Mike

Delphi Tip of the Day - OnShow SetFocus or Form1.ActiveControl

I'm creating a simple database program to explore how SQLite works within Delphi. I want to be able to switch between three databases: Album, Test, and World. So I added a TComboBox and populated the Items property with three values; Album, Test, World. I also set TextHint := 'Choose Database...';

object ComboBox1: TComboBox
    Left = 8
    Top = 8
    Width = 121
    Height = 21
    ParentShowHint = False
    ShowHint = False
    TabOrder = 0
    TextHint = 'Choose database...'
    Items.Strings = (
      'Album'
      'Test'
      'World')

When I ran the program the TextHint for the ComboBox did not display:



That is not what I was expecting. The Combox was the active control which negates being able to see the TextHint "Choose database..."

So I added a little piece of code to the form's onShow event.

procedure TForm1.FormShow(Sender: TObject);
begin
  StatusBar1.SetFocus;
end;

Now when I run the program right from the get-go the StatusBar componet has the focus and the ComboBox displays as intended.


Sometimes it's the small things that make all the difference!

An alternative  to using the OnShow event was suggested in the comments.

Form1.ActiveControl := StatusBar1


I love Delphi! 

Enjoy!
Semper Fi
Gunny Mike

Sunday, February 23, 2020

Delphi Tip of the Day - Autosave Project Desktop

I am not a full-time Delphi guy. So, when I'm learning Delphi by reading OPC (other people's code) I want to make the best of my time. I was studying some code from Cary Jensen that goes with Chapter 6 of his Delphi in Depth: FireDAC book.

When I was done understanding a section of his code, I simply folded it. I figured I beat that code up enough in my mind. I understood it. And I don't need to look at it again, at least not right now. I also didn't want to be distracted by having those code blocks expanded. 

I started a brand new project in Delphi to write some of my own code that was similar but different, just to reinforce what I learned. When I was finished, I reopened Jensen's code and the pieces of code I had folded were no longer folded. 

It turns out this is an easy fix. Simply enable "Save project desktop when closing".
Tools > Options > IDE > Saving and Desktop


Not only did this keep sections of the code I had folded, folded, it remembered whether my last view was Code view or Design view. It also kept track of where in the code view I left off. I was looking at line 522 in the source code when I close the project, Delphi put me right back at line 522 the next time I reopened the project. How cool is that.

Enjoy!
Semper Fi
Gunny Mike


Sunday, February 16, 2020

Delphi Revisited: Database Application Design

I have been creating database applications for over 30 years. Unfortunately, I have never been able to create a single database application in Delphi. That's a very sad fact. One that I'm not very proud of. Because I've been unable to create a database application with Delphi, I considered myself mediocre. And then something happened... Delphi turned 25.

"And then something happened... Delphi turned 25"


I, like so many of you, spent time this past week looking at all the commotion on the internet over Delphi turning 25. Then I looked at the picture of me, and my two boys hanging in my home office. I've been using Delphi since it was Turbo Pascal 3. 


That picture was taken in 1994 for an article that ran in the December issue of Military Lifestyle magazine.


"You don't need to be a programming guru to be successful"


Wow, my Zilch software is 29 years old. It's older than Delphi. I'm not mediocre. I'm living proof you don't need to be a programming guru to be successful. You just need determination.

And then, I had an epiphany regarding my lack of database design skills with Delphi. I realized I have been concentrating on the wrong stuff. I got so bogged down with the "How to do Data Entry" aspects that I totally missed out on everything else I can do with Delphi.

I actually do have one commercial product, Credit Card Math, that uses an ElevateDB database. But you know what, even though this program uses a database, it does not have any "Data Entry" functions.

"Data entry does not a database application make!"


That's it! Data entry does not a database application make! I concentrated on the wrong thing, data entry. I need to focus on the presentation and the analysis, and the reporting of the data and not the data entry. 

All I need is data in the database. I can build the whole presentation layer and reports without having any data entry functions. (light bulb moment)

I have such a renewed spirit. I'm done fiddling around making fancy grids that do all sorts or nifty data entry tricks. I'm going to focus on the meat and potatoes of my application. When the time comes I'll deal with the data entry stuff.

It's time to get ready for Zilch turning 30 in 2021. 

Happy Birthday Delphi!

Semper Fi,
Gunny Mike

Sunday, December 29, 2019

When You Don't Know, What You Don't Know

I've been in a rut for about the past six months. Not just a Delphi programming rut but a "life rut". What's worse, I knew I was in a rut and thought I was trying to get out of it only to realize I wasn't. Then this morning came and... I now know what I didn't know.

"I knew I was in a rut and thought
I was trying to get out of it..."


I was fascinated by a post in the Delphi Developer group on FaceBook written by a long-time VCL developer who is frustrated with making something work on an Android device. Admittedly, this person is not a GUI guy nor an OOP fundamentalist. I can totally relate to this. I have been using Delphi since it was Turbo Pascal 3. I am a traditional "top-down procedural" programmer who has been struggling to wrap my head around OOP fundamentals.

One of the comments on this post stopped me dead in my tracks. It simply stated... "Put your login form in a TFrame" and gave a link to a GitHub repository of FMX Cross-Platform-Samples.

https://github.com/FMXExpress/Cross-Platform-Samples

I've seen some of this FMXExpress stuff before but never knew about this GitHub repository. At the time of this writing there are 99 well organized Cross Platform Samples available. I've been looking for an excuse to rededicate myself to learning FMX cross application development in Delphi and this GitHub repository looks like the perfect spot for me to learn.

"I really don't know what GitHub is or how to use it."


Now, I've heard of GitHub before and I've been to a few different GitHub repositories, but I really don't know what GitHub is or how to use it. This was the my first acknowledgement of YDKWYDK. So I jumped on lynda.com and looked for a course about GitHub and found this one called "Learning GitHub".

About ten minutes into the course the instructor says "Projects allow you to visualize your work with Kanban-style boards." I've never heard of Kanban. What is Kanban? (More YDKWYDK).

"What is Kanban?"


I do another search within lynda.com and discoverd a course called "Stay Lean with Kanban".

Wow, this Kanban stuff is really cool. In it's simplest form Kanban is a way to visualize and prioritize the workload. You visually see what you are doing and what needs to get done. You also eliminate waste by focusing on what's most important. This is done by limiting the work in progress.

I'm about a third of the way through this tutorial and paused it to write this blog because of another YDKWYDK episode.

As I was getting to understand this Kanban stuff it hit me. Why can't I use Kanban for my personal life?

"Why can't I use Kanban for my personal life?"


I can and I intend to do just that. There's a website for "Personal Kanban". There's also a book called "Personal Kanban: Mapping Work | Navigating Life".

It's safe to say I have climbed out of my rut and I'm getting back at it. What a journey today has been...

FB Delphi Developer > FMXExpress > GitHub > Kanban

Semper Fi,
Gunny Mike





Saturday, September 7, 2019

Delphi Tip of the Day - Who's up First?

I'm working on updating my old flagship product and I ran into an issue where a file kept getting deleted on startup. I could not for the life of me figure out where this was happening. I wound up adding vcl.Dialogs to all of the units and then adding code to the Initialization section of each unit hoping I could find out where the damned delete code was.
initialization
begin
  ShowMessage('Inside [Unit Name Here]');
end;
Adding this code to each and every unit was a pain in the butt. And it still didn't help me track down where the file was getting deleted. There had to be an easier way to figure out the order in which each unit was being created.

I turned to the SO community asking for an easy way to generate a list of forms/units in the order they are created. I received the simplest and most elegant solution from Uwe Rabbe. He recommended looking at the C=ICODE segments of the map file.

My first two questions were, "What's a map file? And how do you create one?"

Information on what a *.map file is can be found here.
http://docwiki.embarcadero.com/RADStudio/Rio/en/API_%28%2A.map%29

To create a .map file from inside the IDE click Project > Options > Delphi Compiler > Linking

Set Map file := Detailed


Click Save

Then build your project and you will have a *.map file.

The reason I was having such a hard time tracking down where the file deletion was happening was because it took place within a "dll" file.



Line 47 of the map file shows the first unit of my project is Zglobals (yes the dreaded global unit). Lines 58-60 show the next three units from my project.

Line 59 StdDlls is a precompiled dll unit that I am unable to modify the source code for because it uses "Quick Reports". I do not have Quick Reports installed on my new dev computer. Therefore I cannot actually modify the source code for this unit.

It turns out that the file delete was happening inside the dll.

I just thought that Uwe Rabbe's solution to creating an ordered list of form/units was so simple and elegant I needed to share it with you in case you ever need a quick and easy way to see the order in which each Delphi unit gets created inside one of your projects.

Cheers!
Semper Fi,
Gunny Mike

Sunday, August 11, 2019

Delphi Tip of the Day - Rounding (Again!)

It's been over 7 years since I dove into the mystical world of rounding numbers within Delphi. It's amazing how much code actually gets written in support of the main stuff an application does. I am so glad I wrote my blog... it's the one place I can go back to when I find gaps from my hard drive crash of 2018.

I ran into the old familiar rounding issue - again. And of course, I didn't have the code I used to get it squared away.

My original blog post from 7 years ago, "Rounding the Currency type like they taught us in school", explains the issue in full detail and nothing has changed with regard to how Delphi handles rounding.

And as usual David Heffernan gave...


So I went back to the Stack Overflow post to revisit the answers given to my original question. And as usual David Heffernan gave a very straightforward and succinct answer.

{---------------------------------------------------------------}
{ Added 2019-08-07                                              }
{                                                               }
{ Used to solve the Delphi's rounding function.                 }
{                                                               }
{ https://stackoverflow.com/q/10965759/195983                   }
{ https://stackoverflow.com/a/10970283/195983                   }
{---------------------------------------------------------------}
Function RoundTo2dp (Value: Currency): Currency;
begin
  Result := Trunc(Value*100+IfThen(Value>0, 0.5, -0.5))/100;
end;

I added the code to my StrStuff unit. (Does everyone have a StrStuff unit or is it just me?) And it did not compile. So, when I got done cussing out Heffernan for putting bum code out there, I did some digging.

It turns out there are two IfThen functions. One in the SystemStrUtils unit and the other one in the System.Math unit. And of course, I chose the wrong unit.

This specific IfThen function is from System.Math


This specific IfThen function is from the System.Math unit:
unit Strstuff;

interface

uses System.Sysutils, System.Math;

Function RoundTo2dp (Value: Currency): Currency;
And of course after I put the correct unit in the uses clause it worked like a champ. I must admit, it felt good cussing out Heffernan. But as usual... he's right. Again!

Thank you David.


Enjoy!
Semper Fi
Gunny Mike
https://zilchworks.com


External Reference Update: 2021-06-04



Monday, July 29, 2019

Stop saying "I don't have time" - Say "It's not a priority"

That little switch from saying "I don't have time", to saying "It's not a priority" is a HUGE game changer. I learned about this from a co-worker last week. She didn't invent it... she heard it and passed it on to me. So, I'm passing it on to you.

I started using it immediately. It's my new mantra. I've mostly said it quietly only too myself so far. An I can attest, it's already made a big difference in my life. I thought about making the title of the post click-bait. For example:

Stop saying "I don't have time", say this instead...


Making people click into the site to get the punchline. But that's not what this is about. This is about leveraging the smallest of things, and turning it into the biggest of life changers. As my good friend Bob Wight, would say, "Riley, that's what you call a woomera". A small little device that when used, gains a tremendous advantage over not using it. (R.I.P. Bob, I miss you a lot).

One of my favorite songs is "We will never pass this way again" by Seals and Crofts. It speaks to me in so many ways. And it's true! So while we go through life, why not pick up a little nugget every now, from wherever we find it, and pass it on to the rest of the world. You never know the significance one small little thought will have on someone.

Enjoy the rest of your day and tackle those priorities!

Semper Fi,
Gunny Mike
zilchworks.com

Sunday, July 28, 2019

Delphi Tip of the Day - TODO or not TODO

I tend to be a scatter-brain when it comes to Delphi.

Really? Tend to be? "No Gunny. You are a scatter-brain when it comes to Delphi." I can hear all of you who know me saying this. That's fine.. I fully embrace it. I am a scatter-brain, especially when it comes to everything Delphi. There is so much I want to learn and do.

I have been trying to update my very old, flagship product Zilch Standard for 8 years. I've made zero progress. Why? Two reasons:
  1. I am a scatter-brain.
  2. I was of the mindset I needed to start from scratch and completely redesign everything.

When I was a teenager,
I would go to the ...


When I was a teenager, I would go to the library with a specific idea of what I wanted to read about. I'd head straight to those bookshelves and start eyeballing titles. "Nope." "Nope." "Uh Uh." 'Nah." 'Oh, that looks interesting." "Hey that reminds me of  X."  "Where is the 'X' section." Off I'd go to the "X" section,  and repeat this process until I found something all consuming.

This happens to me all the time with Delphi. My thirst for learning overtakes me and I lose focus on actually making progress inside the IDE. 

How about the other issue, having the mindset of completely redesigning everything from scratch? Well that was squelched when my hard drive crashed and I lost over three-and-a-half years of code. Gone! Poof! It actually turned out to be a blessing. It has forced me to look at things with a whole different perspective. 

That brings me to today's Delphi Tip of the Day.

As I was going through and porting over my old Delphi 5 code, I started doing that "scatter-brain" thing again. I first noticed it when I was updating the Main Menu functionality. "Hey, you know what would be cool, an MRU (Most Recently Used) file list." "Yeah lets add that". And off I went on a quest to learn how to build an MRU list.

Two hours later when I went to make another Nespresso (yeah I'm a coffee snob), it hit me. "Hey Gunny, an MRU list is not the objective. Put it on the back burner."

My back burner items tend to get lost and disappear.


My back burner items tend to get lost and disappear. Then I get frustrated because I can't remember all those "To Do" items I have on the back burner. "Hey wait a minute. Doesn't Delphi have a 'To Do' list function built in? 

Yes, Delphi does indeed have TODO functionality and I started using it the other day. However, in traditional Gunny Fashion it's not your normal TODO.
"If we are going to get wet up to our knees, we might as well get wet up to our waist and get the job done right."
I often use that phrase at my day job. Especially when there are two or three of us gathered around working on an immediate issue. It's different when it's an urgent need that must get done before you go home. 

So when I get ankle deep on a "new" feature I'd like to add, that is not a priority, I add it to my TODO list in Delphi. However, I also include the links to the website articles that will be useful when the time comes to implement them later. For example here is my current TODO list:


If you drill down into the highlighted "TODO" item it brings up the source code:

var
  HWnd: THandle;
  MR_Result : Word;

begin
  {---------------------------------------------------------------}
  { Use this code for retail version                              }
  {---------------------------------------------------------------}
  Hwnd := FindWindow('TMainForm','Zilch Standard - Main Screen');
  {---------------------------------------------------------------}
  { Use this code for trial version                               }
  {---------------------------------------------------------------}
  //Hwnd := FindWindow('TMainForm','Zilch Standard (Evaluation Copy) - Main Screen');

  {---------------------------------------------------------------}
  { TODO 3 -oMJR -cPhase 2 : Set Focus to running application     }
  {---------------------------------------------------------------}
  // Refer to these links when for implementing this TODO item
  // https://codeoncode.blogspot.com/2016/12/get-processid-by-programname-include.html
  // https://stackoverflow.com/questions/47598473/how-can-i-call-getwindowthreadprocessid-in-delphi-10-2
  {---------------------------------------------------------------}
   If Hwnd <> 0 then SetForeGroundWindow(GetForeGroundWindow())
  else
  begin
    Application.Title := 'Zilch';
    Application.CreateForm(TMainForm, MainForm);
    Application.CreateForm(TNoPrintDlg, NoPrintDlg);
    Application.CreateForm(TCreditorInfo, CreditorInfo);
    Application.CreateForm(TDisclaim_Save, Disclaim_Save);
    {---------------------------------------------------------------}
    { Use this code for US Marine Corps distribution                }
    {---------------------------------------------------------------}
    {
    MR_Result := USMC_End_User_License.ShowModal;
    If MR_Result = idYes then
    begin
      Application.ProcessMessages;
      Application.Run;
    end;
    }
    {---------------------------------------------------------------}
    { Use this code for genral public distribution                  }
    {---------------------------------------------------------------}
    Application.ProcessMessages;
    Application.Run;
  end;
end.

This way I can do a little "ankle deep" research and keep it preserved on the back burner for latter use without the risk of loosing it. It allows me to scratch the itch at the time it itches and lets me go knee deep later on when the time comes to dive in.

Going forward I will make extensive use of the TODO feature in Delphi. I hope this has helped you better understand TODO's and how they can help you scratch the itch when it itches.

It's time to publish this and get back to work!

Enjoy,
Gunny Mike
https://zilchworks.com

Saturday, July 27, 2019

Delphi Makes Updating Old Code Very Easy (Hidden Gem)

When my computer hard drive crashed in October 2018 I was really upset. The hard drive was completely shot and unrecoverable. I lost all of the code I had been working on... ARRRGH! The last backup I had was taken in March 2015. Yup, 3 1/2 years of Delphi, Evelated DB, and SQL code GONE!

Intellectually, I accepted this had happened and purchased Carbonite within a few days of getting my computer back this time with a solid state drive. However, I had not emotionally accepted I lost all that work. Well, last weekend I completely came to grips with the situation. Had a good cry. Decided to pickup what I could and start anew updating my old software.

The last time I compiled this project was 2008. So, I opened my Delphi 2005 project with Delphi 10.3 and clicked "Run" and held my breath.

It looked like crap. The default font is the old "System" font. The background colors didn't register properly. I couldn't see some of the stuff because the font color and background color was the same. This was broken. That didn't look right. Then the big "Ah Ha!" moment came...

Hey wait a minute... I just got a clean compile on code written with Delphi 2005 that is hasn't been touched since 2008 . That's amazing!


I'm slowly working my way through enhancing the look and feel. I'll be adding new functionality as well. It's amazing how a few minor changes takes thing to a whole new level.

Before: 2008 (Delphi 5)

After: 2019 (Delphi Rio)

So now I'm updating this code so I looks modern. In the process I will be replacing old functions with better, faster, more readable functions. Not all of them, just those using dumb code. I started swapping out function names in small little pieces. Repeating the Save, Run, Test, Close, Next Change. Save, Run, Test, Close, Next Change for every function I replaced.

Work Smarter Not Harder!


I wanted to see if I had replaced all the old occurrences of one function with the new function.

By pure dumb luck I right-clicked on a function name and found "Search For Usages...". Whoa. This is a great tool. And I'm going to show you how to use it.

Step 1: Click on a function in any unit.

Step 2: Right-Click on the function name and choose Search For Usages

Step 3: Click Search

Step 4: Expand the results



Step 5: Double-Clicking takes you to the source code

Step 6: Repeat the search making sure there are no more changes needed

Step 7: Clean up when done

I am so grateful I found this nice little utility. I just had to stop making code changes and share it with you guys.

Enjoy,
Gunny Mike
zilchworks.com

Sunday, June 9, 2019

Say what you mean and mean what you say!

How often are you or your words misinterpreted? Do you even know? Would someone tell you? Or would you go on with life thinking everything is just honky dory. Only to find out later things were not just fine, you only thought they were.



I attended a Toastmasters Leadership Institute training session and was introduced to a "word exercise". This was the first-time I ever heard this done, or given any thought to how a simple phrase could possibly be misinterpreted. It never dawned on me that someone might take to mean what I said differently than I meant it.

Read this sentence to yourself: "I never said I thought your idea was bad."

Fairy straightforward, right. How did this sound to you? Is this something you may have even said to someone? I know I've said this or something very similar to someone.

Let's see how many different ways this can be interpreted. Read each sentence below putting emphasis on the "bolded" word below.

"I never said I thought your idea was bad."

"I never said I thought your idea was bad."

"I never said I thought your idea was bad."

"I never said I thought your idea was bad."

"I never said I thought your idea was bad."

"I never said I thought your idea was bad."

"I never said I thought your idea was bad."

"I never said I thought your idea was bad."

"I never said I thought your idea was bad."

Interesting right? Yeah that was the same reaction I had. It never occurred to me that someone might see or hear what I say in a different context than they way I mean it.

That explains a lot. Today, we are constantly texting or emailing people. It's not always your fault if you or what you say gets taken the wrong way. It depends on the way it gets interpreted by the recipient.

So even if you say what you mean and mean what you say it might still be taken differently.

Enjoy!

Semper Fi,
Gunny Mike
https://capecodgunny.com

Your feedback, and comments are always welcome. Don't forget to subscribe for email notifications of new posts.

Saturday, March 9, 2019

Delphi Tip of the Day - Forward Declarations. Solving the Chicken or the Egg Problem.

Today's Delphi Tip of the Day is about resolving "which came first, the chicken or the egg". Because Delphi is a strongly typed language you can't refer to something until it's been defined. However, there are situations where you want to refer to something NOW, but unfortunately it hasn't been defined yet.

In other words "I want the egg of that chicken. I know that chicken hasn't laid that egg yet. However, since I know that chicken is going to lay that egg (trust me I know), let me have that egg now."

This is where forward declarations come into play.

I was brought here by something that came up while reading Pawel Glowacki's book Expert Delphi. I'm on page 233 from Chapter 7 - Sensing the world. Pawel discusses the TSensorManager class in the System.Sensors unit.

There is main TSensorManager class that acts as a gateway to all sensor information. It has a Current: TSensorManager class property that is used to reference all sensor information. At the top of the System.Sensors unit, you can find a TSensoryCategory enumerated type that provides the top level categorization of all possible sensors: 

At this point my head was about to explode so I decided to open the System.Sensors unit and have a look at the actual code Pawel was referring to. Then it hit me.

What the hell is this empty class doing here? How can you have an empty class?
TSensorManager = class;
I remember going down this empty class learning path a while ago. But do you think I can remember what it was, or means. No. Obviously, I had not associated a nice, simple "word picture" to the meaning of this empty class. Because if I had, I would have remembered.

So now I have the nice, easy to remember, "Oh that's Delphi's way of resolving the chicken or the egg thing" word picture.

Have a look at this type definition snippet from the System.Sensors unit:"
type

  //
  //other type declarations
  //
  
  TSensorManager = class; //←-- egg reference
  
  TCustomSensor = class abstract
    public type
      TProperty = (UniqueID, Manufacturer, Model, SerialNo, Name, Description);
    private
      FOnDataChanged: TNotifyEvent;
      FOnSensorRemoved: TNotifyEvent;
      FOnStateChanged: TNotifyEvent;
      FManager: TSensorManager; //←-- egg reference
      FStarted: Boolean;
    //
    // other class definitions
    //
  end;
  
  //
  //other type declarations
  //

  TSensorManager = class abstract  //←--- chicken reference
  public type
    TFilters = TDictionary‹string tsensorfilter=""›;
  private class var
    FCurrentManager: TSensorManager;
    class function InternalGetSensorManager: TSensorManager; static;
    class constructor Create;
    class destructor Destroy;
  protected type
    TSensorManagerType = class of TSensorManager;
    //
    // other class definitions
    //
  end;

    
  //
  //other type declarations
  //

 implementation
The problem arises because there's a private field declaration in the TCustomerSensor class that references the TSensorManager class that doesn't exist yet. It's the damn "chicken or the egg" thing.

To get around this Delphi allows what is called a forward declaration.

This is not by any means the complete class definition. The complete class definition must be declared somewhere inside the same type declarations where the forward declaration resides.

Here are a couple useful links:

http://docwiki.embarcadero.com/RADStudio/Rio/en/Classes_and_Objects_(Delphi)#Forward_Declarations_and_Mutually_Dependent_Classes

http://www.delphibasics.co.uk/RTL.asp?Name=Class

Special Characters:
The  "‹" and "›" in the source code sections were created using the Alt + Keypad method.
https://www.keynotesupport.com/websites/special-characters-symbols.shtml


Enjoy!
Semper Fi
Gunny Mike
https://zilchworks.com

Monday, March 4, 2019

Delphi Tip of the Day - What is the "A" prefix I see used on parameters?

Today's Delphi Tip of the Day is all about consistent naming conventions. Consistency in the Delphi and Object Pascal language makes it easier to read and comprehend the code.

I have often wondered why the "A" prefix is used on Delphi parameters. Instead of just accepting it as some esoteric thing as I have done for the past twenty years, I googled around and found an answer. Have a look at the following code snippet:
constructor TPerson.Create(AFirstName, ALastName: string);
begin
  FirstName := AFirstName;
  LastName  := ALastName;
end;
The "A" in "AFirstName" and "ALastName" denotes an Argument as in the above constructor code example.

Typical naming conventions that are used are:
A := Argument
F := Field
T := Type
E := Exception
I := Interface
L := Local Variable
G := Global Variable
The key is to be consistent in all your code. If we as a Delphi community are consistent then it makes it much easier to communicate with each other.

See also:
Object Pascal Style Guide By: Charles Calvert
[Archive]

#delphi #tipoftheday #capecodgunny

Enjoy,
Gunny Mike
https://zilchworks.com

Sunday, February 24, 2019

Expert Delphi - Code Errata

For the past month I have been making my way through Pawel Gloawcki's book Expert Delphi. I love this book. I am having fun and learning so much. I completed the "Game of Memory" application from Chapter 4, and it works pretty good.


I initially ran into an issue running The Game of Memory on my Galaxy S9+. In portrait mode, if I selected "6 Pairs" it rendered two columns of six. However, only the top portion of the bottom two tiles displayed. I changed the TILE_MARGIN constant from 2 to 4 and it fixed the render problem.

I moved onto Chapter 5 - Firemonkey in 3D. I discovered an error from page 162 that took me over an hour to track down. I finally figured it out. The program is supposed to display a red 3D Pyramid. However, I was only getting a blank screen. I discoverd one of the private properties was not getting set.

  TWireframe = class(TControl3d)
  private
    FDrawColor: TAlphaColor;
    FEdges: TEdges;
    FPoints3D: TPoints3D;
    FDisplayed: Boolean;
  public
    constructor Create(AOwner: TComponent) ; override;
    destructor Destroy; override;
    procedure Render; override;
    property DrawColor: TAlphaColor read FDrawColor write FDrawColor;
    property Points3D: TPoints3D read FPoints3D;
    property Edges: TEdges read FEdges;
    property Displayed: Boolean read FDisplayed write FDisplayed;
  end;

It is the code for the constructor where the error is:

The private boolean field FDisplayed was not getting set to true.

constructor TWireframe.Create(AOwner: TComponent);
begin
  inherited;
  FPoints3D := TPoints3D.Create;
  FEdges := TEdges.Create;
  FDrawColor := TAlphaColorRec.Red;
end;



As soon as I added this code to the constructor it worked like it was supposed to.

constructor TWireframe.Create(AOwner: TComponent);
begin
  inherited;
  FPoints3D := TPoints3D.Create;
  FEdges := TEdges.Create;
  FDrawColor := TAlphaColorRec.Red;
  FDisplayed := True; // This is missing on page 162 of the book
end;


It probably wouldn't have taken me so long to figure this out if I was a more seasoned Delphi programmer. Oh well, it is what it is. So, for any of you who are reading or plan to read "Expert Delphi" keep this little tidbit in mind when you get to Chapter 5.

I have submitted an errata to the folks at https://packtpub.com

Enjoy,
Gunny Mike
https://zilchworks.com


Saturday, December 8, 2018

Code Rage 2018 Replay: What's New With RAD Studio 10.3 Rio

I was unable to attend Code Rage 2018 this year. I'd like to thank Embarcadero for making the replays available. In this video, Sarina Dupont, David Millington, and Marco Cantu discuss what's new with RAD Studio 10.3 Rio. I like the new changes to the IDE.

Code Rage 2018: RAD Studio 10.3 Rio Product Address
https://www.embarcaderoacademy.com/courses/441209/lectures/7010714


Q&A Slide 

10.3 Rio is now available!
https://community.idera.com/developer-tools/b/blog/posts/delphi-c-builder-and-rad-studio-10-3-rio-are-now-available

IDE Main Window:
https://community.idera.com/developer-tools/b/blog/posts/new-in-10-3-ide-ui-improvements-in-the-main-window

IDE and Project Options:
https://community.idera.com/developer-tools/b/blog/posts/new-in-rad-studio-10-3-options-dialog-improvements

GetIt, Compile and other dialogs:
https://community.idera.com/developer-tools/b/blog/posts/new-in-rad-studio-10-3-improvements-to-the-getit-new-items-and-compile-dialogs

C++17:
https://community.idera.com/developer-tools/b/blog/posts/new-in-rad-studio-10-3-c-17-asynchronous-c-code-completion-and-more

Delphi inline variables & type inference:
https://community.idera.com/developer-tools/b/blog/posts/introducing-inline-variables-in-the-delphi-language

Delphi RTL Improvements:
https://community.idera.com/developer-tools/b/blog/posts/delphi-rtl-improvements-in-10-3-rio

High DPI perMonitorV2 & GetSystemMetrics:
https://community.idera.com/developer-tools/b/blog/posts/vcl-support-for-per-monitor-v2-and-getsystemmetrics-coming-in-10-3

High DPI Image List:
https://community.idera.com/developer-tools/b/blog/posts/new-in-rad-studio-10-3-high-dpi-image-list-for-windows

Enjoy,
Gunny Mike
http://www.zilchworks.com

Wednesday, November 28, 2018

The 20-Second Hug

program TwentySecondHug;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

begin
  try
    Writeln('The 20-Second Hug');
    Writeln('Copyright (C) 2018 by Michael J. Riley');
    Writeln('(May be freely distributed worldwide)');
    Writeln('#20SecondHug #20SecondHugs #PilotLight ');
    Writeln(#13#10);
    Writeln('+--------------+');
    Writeln(' Instructions ');
    Writeln('+--------------+');
    Writeln(' 1. Squeeze recipient.');
    Writeln(' 2. Don''t let go until this window closes.');
    Sleep (20000); //Stay awake don't miss this part;
      { TODO 1 :
      Translate instructions into other languages.
      Ask Delphi programmers for help by putting
      translations in blog comments. }
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Enjoy,
Gunny Mike
zilchworks.com

Sunday, August 12, 2018

Delphi Book Collection

One week after I wrote about Warren Buffet's 25-5 rule I created my own "Top 5" list. However, this time I put a little twist on it. Instead of creating a to-do list of things I wanted to accomplish in the next six months, I created a list of the top 5 things I have accomplished within my lifetime. It took me about five or six days but I finally came up with my list.

#4 on my list: "Creating a software product back in 1991 that still generates sales today."

If you check the list of Delphi releases you'll notice that Delphi didn't exist in 1991. Turbo Pascal was the tool I used to create that first version of my software. Here is a picture of Zilch v1.11. Unfortunately, I do not have a copy of my very first version 1.0.

Zilch Version 1.11 (Version 1.0 not available)

Learning Turbo Pascal was fun. I owe my passion and desire for computer programming to Jeff Duntemann. He wrote a book called "Complete Turbo Pascal". It was that book that taught me everything I needed to know about computer programming, and the Pascal language. In fact, the reports within my software are pulled using the double-linked list example in his book. Duntemann does a brilliant job of explaining this concept.

That book is by far my absolute favorite programming book. I used several snippets of code, and in some cases entire routines from the examples in his book. Like the linked list routines. I remember getting close to being done. My software was about a month away from being released as shareware. And it dawned on me, "Holy crap. I've used a bunch of code from this book and it's copyrighted. Can I do this?" So I called Jeff and the conversation went something similar to this...

Me: Mr. Duntemann.

Jeff: Yes.

Me: You don't know me. My name is Michael Riley. I'm a US Marine developing software in my spare time.

Jeff: That's good. I like Marines.

Me: Mr. Duntemann, I've...

Jeff: Please call me Jeff not Mr. Duntemann.

Me: Okay. I've been using your book "Complete Turbo Pascal" to learn how to write my software. And I've embedded several of the code snippets into my software. The're all over the place. I've even used the complete linked-list code routines. Am I allowed to do this? Can I use your code in my own software?

Jeff: Of course. That's why I wrote the damn book.

Me: Whew, that's a relief. You have no idea how worried I was. I really like how you explained double linked lists.

Jeff: I get quite a few calls from people telling me they have my book and asking to explain something. I remember the phone ringing at 1 in the morning one time and I was explaining linked lists to this guy. I remember using a kite metaphor. You see it's like a kite with a tail, and attached to that tail is another kite with a tail. And attached to that tail is another kite. Kites and tails with attached kites keep going on and on and on.

I love this guy. That was 1991 and Jeff and I have been friends ever since. One of these days I'm going to have to get Jeff to sign this book. I've completely destroyed the spine. I even had to use clear shipping tape to hold the spine together.

Complete Turbo Pascal Third Edition - Jeff Duntemann

My books have been scattered all over my basement office. When I want one I go searching from pile to pile. Moving stuff, looking around. It's frustrating and not very productive. Arrrgh!

It's been like this for a long time. It's not just books that are unorganized. It's me. It's everything. Besides, the older I get the harder it is to keep track of stuff. Stuff I need to do. When it needs to get done. Who I owe it to. Who owes me stuff. All that stuff.

So, I dusted off my old Franklin Planner that I haven't used since 2010. Inserted the new planner pages on Wednesday night and started using it the next day. Life is so much better now. I set a task for organizing my basement home office on Saturday. Then I decided to gather up all my Delphi books and put them on one single bookshelf. Wow, I have quite the collection.

Click to enlarge image

And guess what? All those books are tied to my #4 lifetime accomplishment: "Creating a software product back in 1991 that still generates sales today".

However, it not about what I did. It's about people. The people who helped me while I was in the midst of doing the thing I did. Like I said, I owe my #4 to Jeff Duntemann. Without him this never would have happened. Look what it's lead to. It's because of Jeff that I stayed enthused, and stayed passionate. Passionate enough to buy 32 Delphi books over the past 28 years. Not to mention the eBooks sitting on my computer. Thank you Jeff.

Don't worry I've told this to Jeff already. He's not hearing this for the first time reading it here. What kind of guy do you think I am.

I have a challenge for you! I want you to create your own "Top 5" list of lifetime achievements.

It doesn't matter if it takes you a week, or two weeks, or longer. Just do it. Then sit and think about your accomplishments. Think long enough about them until you discover who helped you get there. And then, reach out to them and let them know.

Enjoy,
Gunny Mike
zilchworks.com



Sunday, May 20, 2018

Don't Just Code in Delphi, Think in Delphi

I have always struggled with fully understanding the object oriented nature of Delphi. I still struggle with it today. For example, I have tried to read Nick Hodge's book "Coding in Delphi" three times and can't get past page 23. I'm currently on page 96 of Pawel Glowaki's book "Expert Delphi" and had to stop because he talks about using the TTextWriter class which is a class with virtual abstract methods. Virtual Abstract Methods, are you kidding me. What the hell are Virtual Abstract Methods.

(Expert Delphi page 96) "Notice that the TTextWriter class is a class with virtual abstract methods that just define the interface to the text writing functionality so we need to use one of the text writer descendants such as TStringWriter."

Okay, so I get a small glimpse into what Pawel's talking about. These VAM's are just interface definitions. They don't really exist. The real functions exist somewhere else. In this case the TStringWriter class. I'm just barely hanging on here, but I'm hanging on.

I blame my Delphi ignorance on my lack of going back to square one and learning Delphi's OOP think from the beginning. At the time I started using Delphi I just plowed ahead and made the code work. I wanted a Windows program and that is all I cared about. Looking back at it, I'd say I forced Delphi to work like my top-down procedural thinking. I never fully embraced the Delphi OOP think.

Hodges, Glowaki, Cantu, and all the other Delphi authors out there think in Delphi. And if I want to understand them and be proficient in Delphi I need to think in Delphi too.

So, how do you think in Delphi?

You find a resource that explains Delphi in a simple straight-forward manner. Perhaps a resource that teaches you how Delphi came into being. Does such a resource exist? Yes. And I just so happened to have a copy on my bookshelf.


Here are three paragraphs from page 7 and 8. If these paragraphs speak to you like they did me, then I highly recommend you read the entire Turbo Pascal 5.5 Object Oriented Programming Guide.

The challenge of object-oriented programming (OOP) is that it sometimes requires you to set aside habits and ways of thinking about programming that have been considered standard for many years. Once that is done, however, OOP is simple, straight- forward, and superior for solving many of the problems that plague traditional software programs. 

A note to you who have done object-oriented programming in other languages: Put aside your previous impressions of OOP and learn Turbo Pascal 5.5's object-oriented features on their own terms. OOP is not one single way; it is a continuum of ideas. In its object philosophy, Turbo Pascal 5.5 is more like C++ than Smalltalk. Smalltalk is an interpreter, while from the beginning, Turbo Pascal has been a pure native code compiler. Native code compilers do things differently (and far more quickly) than interpreters. Turbo Pascal was designed to be a production development tool, not a research tool. 

And a note to you who haven't any notion at all what OOP is about: That's just as well. Too much hype, too much confusion, and too many people talking about something they don't understand have greatly muddied the waters in the last year or so. Strive to forget what people have told you about OOP. The best way (in fact, the only way) to learn anything useful about OOP is to do what you're about to do: Sit down and try it yourself.

I'm convinced this little 124 page resource will give me the solid foundation of Delphi think that I've  been missing. I was so excited to find this little gem and what it offers, I had to stop reading and tell you guys about it.

A copy is available on archive.org

Enjoy!

Semper Fi
Gunny Mike
zilchworks.com


Saturday, April 28, 2018

What do you, me, Warren Buffet, and his pilot have in common?

Success. Each one of us; you, me, Buffett, his pilot all want success. Whether we acknowledge it or not, we each hunger for success. So, what separates the exceptionally successful people from the rest of us?

Check out this story about Warren Buffet and his long-time pilot then you decide.




Mike Flint was Buffett's personal airplane pilot for 10 years. Flint had flown for 4 different U.S. Presidents before, so he was pretty good at flying. Yet he still felt as though he hadn't achieved all of the career and life goals that he wanted to.

So one day Buffett jokingly says to Flint: "The fact that you're still working for me tells me I'm not doing my job. You should be out going after more of your goals and dreams."

So Flint asks Buffett for his help, and Buffett tells him to go through this 3-step exercise.

Here's how it works (you can play along at home, too)…

STEP 1
Buffett started by asking Flint to write down his top 25 goals - the things that came to mind when he thought of success in his career and life. So, Flint took some time and wrote them down.

STEP 2
Then, Buffett asked Flint to review his list and circle his top 5 goals - the things that were most important to him and that he wanted more than anything else in the world.

This was a lot harder for Flint, since everything on his list was important to him (after all, that's why he wrote them down). But Warren insisted that he could only pick five, so after some time and thought, he made five circles.

"Are you sure these are the absolute highest priority for you?" Warren asked. Steve confidently replied that they were.

STEP 3
At this point, Flint had two lists. The 5 items he had circled were List A and the 20 items he hadn't circled were List B.

Warren now asked Flint when he planned to get to work on these top 5 goals and what his approach would be.

Flint explained, "Warren, these are the most important things in my life right now. I'm going to get to work on them right away. I'll start tomorrow. Actually, no I'll start tonight."

Flint went on to explain his plan, who he would enlist to help him, when he expected to complete each item…

And that's when Buffett asked him about the second list, "And what about these other 20 things on your list that you didn't circle? What is your plan for completing those?"

Flint replied, "Well the top 5 are my primary focus, but the other 20 come in at a close second. They are still important so I'll work on those intermittently as I see fit as I'm getting through my top 5. They aren't as urgent, but I still plan to give them a dedicated effort."

To which Buffett replied:

"No. You've got it wrong, Mike. Everything you didn't circle just became your 'avoid at all cost list.' No matter what, these things get no attention from you until you've succeeded with your top 5."

Semper Fi
Gunny Mike
zilchworks.com