Link to home
Start Free TrialLog in
Avatar of Gary040897
Gary040897

asked on

My first Delphi Application - calculator

I have just downloaded Delphi 7 and I want to learn how to build my first application. I installed it using the "Typical" option for components. I have some Perl experience but this is my first with the Delphi program. The application is a calculator that will estimate the weight in carats of a gemstone cut from a particular type of rough in various designs. There is an online version of what I want to do written in Perl found at http://www.amfed.org/faceters/faqs_gem_weight.shtml

The formula for the application is Weight = (Vol./W^3) x (specific gravity) x (width3) / 200.

The "Vol./W^3" will already be known by the user as it it generated by another design program. The value of "Vol./W^3" will be between 0.000 and 1.000.

The specific gravity will be selected from a dropdown list. The stone names and their specific gravity values can be found at http://www.fishhead.com/sg-values.txt . Note that the values of specific gravity may have a range in value so the calculation of the weight will need to also show a range in weight.

The next value of width (in millimeters) will be entered by the user and that value needs to be cubed.

The last calculation is to divide by 200.

What I want is a window with input boxes like the online link above but I don't need the "Notes" box. Under that have a "Calculate" and "Clear Form" button. Under that I need an output text box where the calculation is displayed. Use the online link above and enter .241 and 12mm to see the output calculation.

I'm starting this out at 500 points because what I need is a step by step tutorial as well as code. I'll be happy to award additional points to anyone adding additional tweaks that will improve the final product or more points to any one expert who gives me the complete package. I'm not sure how to exceed the 500 point limit but we can figure that out I'm sure. Please include suggestions on background colors, heading bar, etc...
Avatar of Roza
Roza
Flag of Slovenia image

Hi Gary!

Ok. How would I do it:

Put following objects on a form and set its names as following:
Object - Name
TEdit - VolFactor
TEdit - Width
TComboBox - Material
TButton - Calculate
TButton - ClearForm
TEdit - Result

Ok. I would have specific gravity in file like this:
Create file called Material.ifo in directory where you will save your project to.
---bof
13
achroite (tourmaline),3.03,3.10
actinolite,3.00,3.44
adamite,4.32,4.48
alexandrite (chrysoberyl),3.75
albite,2.62
almandine (garnet),4.10,4.30
amber,1.10
amblygonite,3.00,3.10
amethyst (quartz),2.66
ametrine (quartz),2.66
analcime,2.27
anatase,3.90
andalusite,3.13,3.16
--eof

first line is presenting number of materials in file then next lines name and mingrafity then maxgravity.

Ok.. let's go back to project.

Then create structure representing file:
Go to unit1 and insert following declaration between
uses
  Windows, Messages....
and
type

type TMaterial=RECORD
                 Name: STRING[30];
                 GravityFrom, GravityTo: real;
               END;
it would look like:

uses
 Windows, Messages...

type TMaterial=RECORD
                 Name: STRING[30];
                 GravityFrom, GravityTo: real;
               END;

type TForm1=class(TForm)...

Then add to private declarations:
  private
    { Private declarations }
    MaterialInfo: ARRAY OF TMaterial;

Ok.. now let's load info from file to our array:

Duble click on a form to create OnFormCreate event:
procedure TForm1.FormCreate(Sender: TObject);
VAR F: textfile;
    Line: STRING;
    Materials, i: INTEGER;
begin
  DecimalSeparator:='.'; //usual separator is , but we have . in file so we changa it
  AssignFile(F, 'material.ifo');
  Reset(F); //open file
  ReadLn(F,Materials); //Read number of materials
  Setlength(MaterialInfo,Materials);
  FOR i:=0 TO Materials-1 DO //read materials to our array
  BEGIN
    ReadLn(F,Line);
    MaterialInfo[i].Name:=Copy(Line,1,Pos(',',Line)-1);
    Delete(Line,1,Pos(',',Line));
    IF Pos(',',Line)>0 THEN
    BEGIN
      MaterialInfo[i].GravityFrom:=StrToFloat(Copy(Line,1,Pos(',',Line)-1));
      Delete(Line,1,Pos(',',Line));
      MaterialInfo[i].GravityTo:=StrToFloat(Line);
    END
    ELSE
    BEGIN
      MaterialInfo[i].GravityFrom:=StrToFloat(Line);
      MaterialInfo[i].GravityTo:=0;
    END;
    Material.Items.Add(MaterialInfo[i].Name); //add material name to Material ComboBox
  END;
  CloseFile(F); //Close input file
end;

Then duble click on Calculate button to set on Click event:

procedure TForm1.CalculateClick(Sender: TObject);
VAR res, res2: real;
begin
  res2:=0;
  res:=StrToFloat(Width.Text);
  res:=res*res*res;
  res:=res*StrToFloat(VolFactor.Text);
  res:=res/200;
  if MaterialInfo[Material.ItemIndex].GravityTo>0 THEN
     res2:=res*MaterialInfo[Material.ItemIndex].GravityTo;
  res:=res*MaterialInfo[Material.ItemIndex].GravityFrom;
  Result.Text:=FloatToStr(res);
  IF Res2>0 THEN Result.Text:=Result.Text+' - '+ FloatToStr(res2);
end;

procedure TForm1.ClearFormClick(Sender: TObject);
begin
  VolFactor.Text:='0';
  Width.Text:='0';
  Result.Text:='0';
end;

You can also select material name by Material.ItemIndex:= and number.

Ok.. I think this is it. If you would like any aditional explanation or something more in your project then just ask.

Roza
Avatar of Gary040897
Gary040897

ASKER

I have a few basic questions as this is my first time using this program.

1. Where you say:
Put following objects on a form and set its names as following:
Object - Name
TEdit - VolFactor
TEdit - Width
TComboBox - Material
TButton - Calculate
TButton - ClearForm
TEdit - Result
I'm assuming this is accomplished with the subsequent code you provide and this is not an additional step I need to incorporate in some manner.

2. How sensitive is Delphi code? For example you said:
uses
Windows, Messages...

I used Windows, Messages; as that was the format in the code window.
Is there a difference in "..." and ";"? Remember this is my first attempt.

3.  Is there a difference in case such as "end" or "END"?

4. I have the file Material.ifo complete and in the directory where I'm saving my work. In Delphi I used Save Project As and saved my work in that same directory. What do I do to insert the Material.ifo file or is that automatic when I compile?

5. I'm assuming the the characters // are used for code remarks and that by "changa it" you meant "change it".

6. Where you said "Duble click on a form to create OnFormCreate event:" is this an explanation of subsequent code and not an action I needed to do.

7. To prevent using up a lot of space in this question I'm posting my working code at http://www.fishhead.com/code.txt

8. When I use Project/Syntax check on my working code I get the error "Expected ')" but PROCEDURE found and it points me to the line:
procedure TForm1.FormCreate(Sender: TObject);

9. What do I need to do to see the results of the working code in the Form window?
Roza,

I found the first syntax error. I had
type TForm1=class(TForm
I missed the ending )

Fixed that but then there's another error "Expected '=' but found '(' in the line:
procedure TForm1.FormCreate(Sender: TObject);
I tried to insert the = but then there was another error in the same spot.
Roza,

In the Material.ifo file do I include the lines "---bof" and "--eof"?
Roza,

In the Material.ifo file do I include the lines "---bof" and "--eof"?
Roza,

Ok I think I see what you meant by "Double click on a form to create OnFormCreate event". In the unit edit window on the left hand side I can click on various functions which bring me to the proper place in the code to make additions. What I don't see is where the calculate button is as per your instructions "Then double click on Calculate button to set on Click event:". I've post this version of what I have at http://www.fishhead.com/code2.txt but this doesn't have the calculate part inserted yet.

Thanks,
Gary
Ok.. i wrote an essay, but i unfortunatly closed window and I must now start from beginning :(
Just a thaught.. when you will have some time read
this delphi lessons... you wil find some usefull tips, but not relevant now.
http://www.svn.net/ffortino/

When i sad:
Put following objects on a form and set its names as following:

You select a component from componet bar at the delphi window where File menu is...

So you click on icon representing component and then click on a form and you will see now that component on a form.

Ok. If i go from the beginning, you start delphi, then you create new application by pressing File menu and New->Application. Now you see blank form where you will place your objects and there is window with code where you will add some code, you will not delete or change any code already in that window OK?

When i sad name it.. you select that component and then go to object inspector (window on the left) and search name and then change that text with that name.. Ex when you place TEdit on a form it will be named Edit1 and then you change it's name to VolFactor.
Than you place and name all other components.
Now you can already run your program by pressing F9.

If you placed TButton and name it Calculate you can already press on it, but it would not do notheing, because we didn't write any code yet ok?

So now close program which just run and continue with code.

2. delphi is not case sensitive

4. material.ifo will be a file outside of delphi code... your compiled application will read from it. So at the end you will need only material.ifo file and exe file.

5. I'm assuming the the characters // are used for code remarks and that by "changa it" you meant "change it".
Yes //are remarks...  changa=change yes...

-- bof and --eof was just to show begining and end.. so don't include that in file...


>Ok I think I see what you meant by "Double click on a >form to create OnFormCreate event". In the unit edit >window on the left hand side I can click on various >functions which bring me to the proper place in the code >to make additions. What I don't see is where the >calculate button is as per your instructions "Then double >click on Calculate button to set on Click event:". I've >post this version of what I have at >http://www.fishhead.com/code2.txt but this doesn't have >the calculate part inserted yet.

Ok.. if you understood me till now you see that you actualy duble click on a form (which loks like some program with notheing in it)... and if you place button you can actualy duble click with your mouse on that button and then delphi will throw you to code..

So leave all code in which delphi prepares to you when you start new application and just add your code.

So if you duble click on Button you placed in a form and named it calculate, delphi will throw you to code window with this code:

procedure TForm1.CalculateClick(Sender: TObject);
begin
end;

Then you just insert missing lines I wrote for you.
And then you will have something like this:

procedure TForm1.CalculateClick(Sender: TObject);
VAR res, res2: real;
begin
 res2:=0;
 res:=StrToFloat(Width.Text);
 res:=res*res*res;
 res:=res*StrToFloat(VolFactor.Text);
 res:=res/200;
 if MaterialInfo[Material.ItemIndex].GravityTo>0 THEN
    res2:=res*MaterialInfo[Material.ItemIndex].GravityTo;
 res:=res*MaterialInfo[Material.ItemIndex].GravityFrom;
 Result.Text:=FloatToStr(res);
 IF Res2>0 THEN Result.Text:=Result.Text+' - '+ FloatToStr(res2);
end;

Same with Form OnCreate event.

Where i quoted:
uses  Windows, Messages; there were 3 dots.. so leave that code as it is

and same with

type TForm1=class(TForm)...
                        ___

Don't delete what is following.

Note when you place some object (click it and drop it on form), delphi automaticaly adds some code in unit file.

Where i quoted:
uses  Windows, Messages; there were 3 dots..
uses  Windows, Messages ...
so that means there are other things in uses sentence leave them and just insert your code before

type TForm1=class(TForm)...
                        ___

Don't delete what is following.
There are also some lines folowing which delphi creates by himself.

Note when you place some object (click it and drop it on form), delphi automaticaly adds some code in unit file.

Hope now you understand what you should do.

Roza
Roza,

Thanks for the explanations. I lost one of my messages too. I found out the hard way you can't go to another page and then come back to this page and expect your comment to still be there.

Ok I see that the form is where the work get's done and the code is created from that, not the other way around as I was attempting.

I don't see TEdit as an option in my Object Inspector so I'm assuming that I need to add it from the available components. I have all the packages installed.  When I go to the Component\New Component\Ancestor Type I see the other options. Is this were I can add it to the Object Inspector? I see that there are more than one TEdit. There's TEdit [QStdCtrls] and TEdit [StdCtrls]. Which do I use?

Is there a way to compile the Material.ifo into the exe So there's just one visible file when I'm done?

Thanks,
Gary
You add TEdit from components tool bar... litle icons down where it writes standard. Don't go to component new menu.. just see where some icons are... in your tool bar.. then you have tab Additional where additional components are.. (little icons) you click on that icon and then you put it on a form and you will se component on a form. There is only one TEdit under Standard Tab...

I'll think about material.ifo how to solve that problem differently.
Roza,

Ok I see the tabs "Standard, Additional, Win32, etc....  When I insert the function into the form I can rename it in the Object Inspector. Be patient, I'm on that first huge learning curve. ;-)

Gary
Roza,

Ok I see the tabs "Standard, Additional, Win32, etc....  When I insert the function into the form I can rename it in the Object Inspector. Be patient, I'm on that first huge learning curve. ;-)

Gary
Rosa,

Any problem using "Volume Factor" instead of " VolFactor" as long as I change the reference in the code to match the name? Same with "ClearForm" and "Clear Form".

Thanks,
Gary
Roza,

Do the private declarations go inside the { }?

 { Private declarations }
 MaterialInfo: ARRAY OF TMaterial;
Roza,

I think I've done everything you told me correctly but there are 25 syntax errors in what I did. The current code is posted at http://www.fishhead.com/code.txt

The error file is at http://www.fishhead.com/error.txt

I've done this over several times but still have the same problems. Before I try to change things based on the error messages please look at were I'm at now.

Thanks,
Gary
Hi!

I think we are making progress... code looks very close to the final... ok.

First of all.. you didn't rename components yet.
You have probably changed Button's caption and Edit text's.

You must find Name property in object inspector... name can not have space, but you may use _ instead... as you sad if you also change it in your code. Note: You should only change in code you created. For other code Delphi will do it for you when you change name in object inspector.

You probably changed code delphi created to you.
When you duble click on your button you got

procedure TForm1.Button1Click(Sender: TObject);
begin
end;

That's because you didn't rename it to Calculate... and you have changed that Button1Click to CalculateClick... that's a mistake. Rename button to Calculate and then duble click on it and then you will get:

procedure TForm1.CalculateClick(Sender: TObject);
begin
end;

And now just insert missing code. Don't change old one.


private    { Private declarations }
    MaterialInfo: ARRAY OF TMaterial;
it is ok and should be so.

Roza,

I'll review what I've done to this point but I was renaming as I was initially making the elements. Screen shot here.
http://www.fishhead.com/gemweight1.jpg
Yes.. as I sad.. you've changed caption... I forgot to write this.. caption is text it will be visible on button or on label or something else. Text is property which you will se at TEdit's or TComboBoxes Text is editable, you have also Name property in object inspector...
if you look at object inspector you still have Button1       TButton, that's because you didn't change name, but caption/text.. :)

That's why you see all text's on a form properly. Set name not caption. If you change new component's name Delphi will also automaticly change caption or text property.
Roza,

I have it running now. There's a slight problem in the calculation output. I just ran a test through it. The result should have been. 5.54 carats. I got .55387584 which is the right calculation except that I need it to round off to two places to the right of the decimal point. Can the word "carats" be inserted after the calculation in the Result box? Like "5.54 carats".

Can I change the name of the Form1 to "Gem Weight Calculator" with out any problem like the button names?

Can I change the button caption name (or leave it blank) as long as I don't change the Name?

What about the icon in the title bar? How would I insert my own.

How would I add "Help" in the title bar that would drop down with "Info" and "About"?

If I add text around the boxes will Delphi automatically add that to the code too?

Getting Close!
Gary
Ok.. nice that you accomplished to run ;)

1.(  result:=FloatToStrF(res2,ffFixed,digits,precision);
where you replace digits and precision with numbers (2,2)?

see help for details.. pres ctrl+f1 on floattostrf text.

2.) You can change name of form same as button's name... But you can not have space characters in form.. so you probably want to change caption property of form anyway.
2a.)Yes you can freely change captions or leave it blank.

3.)Title bar icon->Select Form and In object inspector go to icon and select desired icon.
Yo can also change program's icon in project->options->application->icon menu.

4.)You drop main menu on a form and duble click it and then insert menus...

5.)You can add labels just like you did with edits.. i suggest that you set name properties like LabelVolumeFactor and LabelWidth etc.
Captions of labels is text displayed on a form so captions can be: Volume factor, Width etc. No coding needed. Delphi does it for you. Just drop labels on a form and compile.

Roza
Can the word "carats" be inserted after the calculation in the Result box? Like "5.54 carats".

Guess how:

result:=result+' carats';
Roza,

result:=FloatToStrF(res2,ffFixed,digits,precision);

I'm assuming this goes in the Calculate section of code. Is that line meant to replace an existing line, an addition to or modification of an existing line? I've tried it many ways and tried to run but get the same error messages.

 Undeclared identifier: 'digits'
 Incompatible types: 'TEdit' and 'String'

I'm having the same problem with
result:=result+' carats';

The Calculate section now reads (working)

procedure TGemCalculator.CalculateClick(Sender: TObject);
var res, res2: real;
begin
 res2:=0;
 res:=StrToFloat(Width.Text);
 res:=res*res*res;
 res:=res*StrToFloat(VolFactor.Text);
 res:=res/200;
 if MaterialInfo[Material.ItemIndex].GravityTo>0 then res2:=res*MaterialInfo[Material.ItemIndex].GravityTo;
 res:=res*MaterialInfo[Material.ItemIndex].GravityFrom;
 Result.Text:=FloatToStr(res);
 if Res2>0 then Result.Text:=Result.Text+' - '+ FloatToStr(res2);
end;

Roza,

With regard to the decimal place in the gem weight. I need the result to be able to read over 100 carats with a two places after the decimal point. Not sure if that changes anything you are suggesting.

Gary
Can the word "carats" be inserted after the calculation in the Result box? Like "5.54 carats".

Guess how: (my mistake.. sorry)

result.text:=result.text+' carats';

I sad see help for details:
function FloatToStrF(Value: Extended; Format: TFloatFormat; Precision, Digits: Integer): string;
Extended=real type...

Format:
ffFixed     Fixed point format. The value is converted to a string of the form "-ddd.ddd...". The resulting string starts with a minus sign if the number is negative, and at least one digit always precedes the decimal point. The number of digits after the decimal point is given by the Digits parameter--it must be between 0 and 18. If the number of digits to the left of the decimal point is greater than the specified precision, the resulting value will use scientific format.

So FloatToStrF(res,ffFixed,3,2) will do just what you want.


procedure TForm1.CalculateClick(Sender: TObject);
VAR res, res2: real;
begin
  res2:=0;
  res:=StrToFloat(Width.Text);
  res:=res*res*res;
  res:=res*StrToFloat(VolFactor.Text);
  res:=res/200;
  if MaterialInfo[Material.ItemIndex].GravityTo>0 THEN
     res2:=res*MaterialInfo[Material.ItemIndex].GravityTo;
  res:=res*MaterialInfo[Material.ItemIndex].GravityFrom;
  Result.Text:=FloatToStrF(res,ffFixed,3,2);
  IF Res2>0 THEN Result.Text:=Result.Text+' - '+ FloatToStrF(res2,ffFixed,3,2);;
end;

Pleas read my instructions carefully I wrote:

1.(  result:=FloatToStrF(res2,ffFixed,digits,precision);
where you !!!replace!!! ->digits and ->precision with numbers (2,2)? Ok.. I was wrong about 2,2...it's 3,2, but :-)

I hate repeating myself :)

Ok.. extra about menus.

You will probably want that when user clicks on about.. about box will open.

First create new blank form file->new->form or choose about box from templates:
file->new->other->forms->about box

Ok if you choose about box, then just duble click on about item in your menu and

procedure TForm1.About1Click(Sender: TObject);
begin

end;

will show. Now we will code that when user clicks on that menu item, about box will show and application will wait until about box is closed:

procedure TForm1.About1Click(Sender: TObject);
begin
  AboutBox.ShowModal;
end;

Now if you choose compile delphi will ask you if you want to include unit2 or if you saved it in any other name into uses list.. you just clik ok. And again press f9. Now you have about box. And simmilar you can do with info form (you can choose blank form and then add components by your choice)...
Roza,

As I figured this question would take a lot of hand holding for my first time and you've already earned the current point value. We can start a new question or I when we are done with it here I can post a question "Extra Points for Roza". You tell me how you want to do it.

Unfortunately I was have introduced an error and saved it. I was try to insert the Menu. First I repositioned my elements to make room for the menu bar. I thought I only saved the reposition but not the case.

When I try to run right now I get
[Error] Project1.dpr(11): Undeclared identifier: 'TForm1'
and it opens to the Project1 tab and points to the line:
 Application.CreateForm(TForm1, Form1);

the complete code from Project1tab code is:

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

I don't remember seeing the Project1 tab before, only Unit 1 but I guess that tab appears when you try to run.

Can you tell what I need to undo? Sorry!
Roza,

Current Unit1 code
http://www.fishhead.com/code.txt
When I started having the current problem I also renamed the Form like all the other items. I thought I tested it before I saved it but somehow there's a conflict between the Unit code and the Project code. I think...
Hm.. Ok I hope you renamed Form in object inspector..

If you did.. just replace Form1 in project1 tab with  GemCalculator like this

uses
 Forms,
 Unit1 in 'Unit1.pas' {GemCalculator};

{$R *.res}

begin
 Application.Initialize;
 Application.CreateForm(TGemCalculator, GemCalculator);
 Application.Run;
end.

Hope this helps...

project1 code is somehow main unit.. usualy you don't change anything in this unit (delphi does it for you), but there are exceptions like yours now :)

Oh.. Set autosave when you compile your app
Tools->environment options->Autosave options-> and check Editor files and Project desktop...

Then you also check Show compiler progress at compiling and running this is usefull.

About points... let's continue here and
you will post question... "Extra Points for Roza".


Roza,

I had to rebuild it to fix it.
I have the decimal point working like it should now. Still stuck on the "carats" thing. When I add the line
result.text:=result.text+' carats';
it works fine when there's just one value but when there's two values it prints out like
6.02 carats - 6.21 instead of 6.02 - 6.21 carats. I'm thinking it needs to be tied to also to the "if" and "then" statements.

Did you think of a way to compile the Material.ifo file into the exe?

I'll need some sort of Exception Notification when user tried to Calculate without having made a selection (or completed selections). When I do that here it hangs up the program sometimes.

I'm updating http://www.fishhead.com/code.txt

Gary
Roza,

I fixed the carat statement. I put it after the "if and "then" statement.

I'll work on the Help and About drop downs now.

Gary
Roza,

If I'm going to allow the Weight calculation to be 4 digits to the left of the decimal. How do I get a coma after the third numer? On rare occasions users will want to calculate for stones larger than 999.99 carats. So I changed to this
Result.Text:=FloatToStrF(res,ffFixed,4,2);
This allows for calculations up to 9999.99 with out an error. I want it to read 9,999.99.

BTW, one of the error messages I will need is if someone tries to calculate for sizes larger than 9,999.99 carats.

Gary
Roza,

Before I try to put the About Box in my working project I'm testing on a blank form. I opened a form and named it and saved it. The I tried to add the about box. I got the about box open and edited it and insert the
AboutBox.ShowModal;
but when I run it there's no About there. Also Delphi isn't asking me if I want to include it. Is there some basic code I need to practice on a basically blank form?

Gary
Roza,

On my test form I can get the About box right when I press F9 but there is no link to it visible on the test form. I tired what you said above. I don't get
procedure TForm1.About1Click(Sender: TObject);
I get
procedure TAboutBox.FormCreate(Sender: TObject);
so I've not set it up properly yet but I'm note sure where I went wrong right now. The code I have for Unit1 and Unit2 of the Test form is at
http://www.fishhead.com/aboutbox.txt

Where am I going wrong?

Gary
Roza,

About the thousands format, I've experimented with cThousandSeparator but no luck getting 9,999.99
Roza,

Ok I finally figured the thousands thing! I changed "ffFixed" to "ffNumber" and it worked. Making a little progress on my own finally. ;-)
>Roza,

>Before I try to put the About Box in my working project >I'm testing on a blank form. I opened a form and named >it and saved it. The I tried to add the about box. I got >the about box open and edited it and insert the
>AboutBox.ShowModal;
>but when I run it there's no About there. Also Delphi >isn't asking me if I want to include it. Is there some >basic code I need to practice on a basically blank form?

Ok.. start new application... name it as you like,
Then add about box file->new->other->about box and finally
put MainMenu on a main form (not about box).

Duble click on Main menu and menu editor will open, now set caption to Help and press enter, Delphi will add another blank space for new menu item on right of Help and under Help. Go under Help menu item and set it's caption to About. Note: Delphi will set those menuitems names to caption+1 ex: About1, Help1 etc.

Ok now duble click on About menu item and you will see
procedure TForm1.About1Click(Sender: TObject);
begin

end;

Then put in this procedure AboutBox.ShowModal;
That's all you need.

That's about creating menu's and showing another forms.
You can show another form and not wait to user close it with only Show method ex: AboutBox.Show;

About checking the result and calculation:

>BTW, one of the error messages I will need is if someone >tries to calculate for sizes larger than 9,999.99 carats.

>I'll need some sort of Exception Notification when user >tried to Calculate without having made a selection (or >completed selections). When I do that here it hangs up >the program sometimes.

procedure TForm1.CalculateClick(Sender: TObject);
VAR res, res2: real;
begin
  res2:=0;
  res:=StrToFloat(Width.Text);
  res:=res*res*res;
  res:=res*StrToFloat(VolFactor.Text);
  res:=res/200;
  if MaterialInfo[Material.ItemIndex].GravityTo>0 THEN
     res2:=res*MaterialInfo[Material.ItemIndex].GravityTo;
  res:=res*MaterialInfo[Material.ItemIndex].GravityFrom;
  IF (res>9999.99) OR (res2>9999.99) THEN

  Result.Text:=FloatToStrF(res,ffGeneral,4,2);
  IF Res2>0 THEN Result.Text:=Result.Text+' - '+ FloatToStrF(res2,ffGeneral,4,2);
end;



oops. sorry.. here is correct procedure

procedure TForm1.CalculateClick(Sender: TObject);
VAR res, res2: real;
begin
  IF (VolFactor.Text<>'') AND (Width.Text<>'') AND (Material.ItemIndex>-1) THEN
  BEGIN //User completed selections and entred values
    res2:=0;
    res:=StrToFloat(Width.Text);
    res:=res*res*res;
    res:=res*StrToFloat(VolFactor.Text);
    res:=res/200;
    if MaterialInfo[Material.ItemIndex].GravityTo>0 THEN
       res2:=res*MaterialInfo[Material.ItemIndex].GravityTo;
    res:=res*MaterialInfo[Material.ItemIndex].GravityFrom;
    IF (res>9999.99) OR (res2>9999.99) THEN
    BEGIN
      Result.Text:='Error can not calculate sizes larger than 9,999.9 carats!';
    END
    ELSE
    BEGIN //result is in range
      Result.Text:=FloatToStrF(res,ffNumber,6,2);
      IF Res2>0 THEN Result.Text:=Result.Text+' - '+ FloatToStrF(res2,ffNumber,6,2);
    END;
  END;
end;
You would probably want also check for inputs:
Click on VolFactor, then go to object inspector and click on events tab. Now search for OnKeyPress and duble click to create OnKeyPress event:

procedure TForm1.VolFactorKeyPress(Sender: TObject; var Key: Char);
begin
  CASE Key OF '0'..'9',#8: ;
       //if key=number or backspace it's ok
                      '.': IF Pos('.',VolFactor.Text)>0 THEN Key:=#0;
       //if decimal point doesn't exist then it's ok else assign 0 value
       //#0 is just like no key pressed.
           ELSE Key:=#0;
  END;
end;

Roza,

I have the error info working properly now. Thanks!

I've got the About box working  somewhat in my test file. I think I've got more dropdowns in the Menu Editor in place than I need but I will work on that. In doing some reading in the Help files I see very little info on setting up About|Help other than "Use Help|About to display version and copyright information".

What I need to accomplish is a Help|About function. With the Help section I need to put a few basic instructions. Do I tie this to another file like a text file? When I say I need a "Help" file I don't want to open up the traditional Help box just a few simple directions. Maybe the term for what I want is not actually a "Help" file.

Gary
Roza,

To further explain what I want in the Main Menu bar is About and Help. I want them as separate Items side by side on the bar. I see how to do the About part. I'm trying to figure out where to put the contents of the Help part. The contents of the Help information will be just be a few sentences. In the Menu Editor I have Help and About side by side but I also see a dropdown for each of those. Is that normal? I also see and additional empty box yet unnamed in the bar next to Help and About. Is that the way the editor functions normally or is that something I did? I've tried to delete the extra boxes but they wont delete.

Gary
ASKER CERTIFIED SOLUTION
Avatar of Roza
Roza
Flag of Slovenia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hmm ok correct to
FOR i:=0 TO MaterialInfo.Lines.Count-1
in FormCreate procedure..
Ok.. what about help... you can display it in memo or RichEdit In richedit you can diplay rtf files, but you must include rtf file with your project.

Will you like any of this solutions?
Roza,

I put a  Memo on the form and named it as you said. When I double click on the Lines section says (TStrings) it opens the String List Editor. Is this where I put the text from my Material.ifo file? Also when you open that Editor it has

M
a
t
e
r
i
a
l
I
n
f
o

running down the edge like this. Do I delete this? I tried putting my test info in there and when I reopened it all of the contents were running down the edge like that.  Or should I use the button in the String List Editor which opens up the code editor and put it there? I think I followed all of your other directions carefully. I've again posted the current code at http://www.fishhead.com/code.txt

When I try to run it I get an error in the line

Material.Items.Add(Copy(MaterialInfo.Lines[i],1,Pos(',',MaterialInfo.Lines[i])-1)); //add material name to Material ComboBox

with the error:
[Error] Unit1.pas(43): 'DO' expected but identifier 'Material' found

I've only worked on this part not the Help|About yet. I want this to run first. ;-)

Gary

>> tried putting my test info

should be text info  (the contents of Material.ifo)
Roza,

I did the Memo section again just as you said and when I right clicked in the String Editor I was able to open the Material.ifo and the delete the first line with the line count number as you said. I still need to know if I leave the letters of MaterialInfo running one letter at a time vertically down the edge or not. I left it there and just opened the Material.ifo as you said. I still get the same error in

Material.Items.Add(Copy(MaterialInfo.Lines[i],1,Pos(',',MaterialInfo.Lines[i])-1)); //add material name to Material ComboBox

when I try to run.
Roza,

If I leave the original line you gave

FOR i:=0 TO MaterialInfo.Lines.Count DO

it will run but there are no materials available in the Material ComboBox.

I double checked in the string editor and doing the way you said removes the characters of " MaterialInfo" running down the side of the page. All the contents of my Material.ifo is running down the page though.
Hm.. strange about character running down the side.

Ok.. only thing that is possible is that you made memo really small.. try to set WordWrap property to false and load file(or paste it if you prefere) in again. Lines in memo file should be just contens of your text without any other lines. Except deleteing first line from text file...

Ok.
FOR i:=0 TO MaterialInfo.Lines.Count-1 DO

you should leave as it was.. just adding -1 after lines.count... (note: we start form 0 to n so it's n+1 items).
Roza,

I have this version where it will run now. Yes I did have a very small Memo box as I thought all that I needed was to have the properties in the form since we were not going to have it visible. I found that the size of the Memo box was very important as how (or if) the items would appear in the Material ComboBox when you run it.

One question on code. We are now using
(res,ffNumber,6,2)  but we were using (res,ffNumber,4,2)
I've tried them both and they both work. Since we are calculating a max of 9,999.99 wouldn't (4,2) work ok?

Now I'll work on the Help|About now.

The Help info is very small. About all I will say is:
Using the Gem Weight Estimator:

1. Enter the design volume factor (Volume/Width³).
2. Enter the width of the stone in millimeters.
3. Click on the Calculate button.

What do I need to do to make an rtf file with RichEdit? I may also consider just putting this info in a Label but that's sort of ugly.

Color. In the Object Inspector I've played with the color but I've discovered that making changes there relates only to  the operating system default colors and does not fix the color of the application. Can I specify and fix the colors? Like on the background of the form.

Size. I want to fix the size if possible. I've tried Constraints settings for MaxHeight, MaxWidth, MinHeight and MinWidth but that doesn't seem to do what I want when you run it.

Compile. When I compile this will it permanently change anything? Can I compile again and just overwrite the previous exe or name it something different?

Almost done!  ;-)







Roza,

Question on Volume Factor when I run F9. When the run first appears the information in the Volume Factor edit box says ".000". It is also highlighted in blue so you should be able just to start typing your value in there. All values will start with "." unless you want the weight of a cube and then the value is "1". The problem is that when I start to type the Volume Factor of say ".348" I start with the "." but nothing happens in the edit box. Normally when a section of test is highlighted anything you type will start to replace it. However, when I type a number and not a decimal the text replacement works. So I can type in a "3" and it will replace the highlighted ".000" but nothing happens when start by typing ".". Since all the Volume Factors start with a decimal this is an inconvenience as I first have to type a number and them backup and insert the decimal point. Is there something in the code that is preventing this from working? The problem also exists when I Clear Form (back to ".000") and highlight the contents with the mouse.

Updated http://www.fishhead.com/code.txt
FloatToStrF(9999.99,ffNumber,4,2) will return 1E4
FloatToStrF(9999.99,ffNumber,5,2) will return 10,000.00
and FloatToStrF(9999.99,ffNumber,6,2) will return 9,999.99

OK. About RichEdit.. If you got only few lines you can add it runtime. Again we will use OnCreate event, but this time about form's event.

I suppose you managed to set up file menu and showing another form with showmodal command.

So go to that form put richedit control on it, name it let say helptext. Now duble click on form to create OnCreate event. Note: we have already created one event for main form.

So:

procedure TAboutForm.FormCreate(Sender: TObject);
begin
    WITH HelpText DO
  BEGIN
    Lines.Clear;
    Paragraph.Alignment:=taCenter;
    HelpText.SelAttributes.Size:=14;
    HelpText.SelAttributes.Color:=clRed;
    Lines.Add('Using the Gem Weight Estimator:');
    Paragraph.Alignment:=taLeftJustify;
    HelpText.SelAttributes.Size:=10;
    HelpText.SelAttributes.Color:=clBlue;
    Lines.Add('');
    Lines.Add('1. Enter the design volume factor (Volume/Width3).');
  END;
end;
Roza,

On the Volume Factor. I'm thinking there must be something here that is preventing me from typing the decimal first.

begin
  case Key of '0'..'9',#8: ;
  //if key=number or backspace it's ok
  '.': if Pos('.',VolFactor.Text)>0 then Key:=#0;
  //if decimal point doesn't exist then it's ok else assign 0 value
  //#0 is just like no key pressed.
  else Key:=#0;
end;
>>and FloatToStrF(9999.99,ffNumber,6,2) will return 9,999.99
Hmmm...
I'm currently using
Result.Text:=FloatToStrF(res,ffNumber,4,2);  and it returns 9,999.99

I can put it back to (6,2)
I was trying to figure out the format for future reference.
Roza,

I changed
'.': if Pos('.',VolFactor.Text)>0 then Key:=#0;
to
'.': if Pos('.',VolFactor.Text)>0 then Key:='.';

This seems to work. Let me know if there's a problem with that.
Ok.. there are two sollutions
1.)
procedure TForm1.VolFactorKeyPress(Sender: TObject; var Key: Char);
begin
  CASE Key OF '0'..'9',#8,'.': ;
  //if key=number or backspace or .
                ELSE Key:=#0;
  END;
end;

But with first one user can enter ex 1.2312.332 which is not valid number so you would like to prevent entering 2 dots.

procedure TForm1.VolFactorKeyPress(Sender: TObject; var Key: Char);
begin
  CASE Key OF '0'..'9',#8: ;
       //if key=number or backspace it's ok
                      '.': IF ((Pos('.',VolFactor.Text)>0)) THEN
                             IF (Pos('.',Copy(VolFactor.Text,VolFactor.SelStart+1,VolFactor.SelLength))=0) THEN Key:=#0;
       //if decimal point doesn't exist then it's ok else assign 0 value or if decimal will be overwriten then overwrite it
       //#0 is just like no key pressed.
           ELSE Key:=#0;
  END;
end;
Hmmm...
I'm currently using
Result.Text:=FloatToStrF(res,ffNumber,4,2);  and it returns 9,999.99

I can put it back to (6,2)
I was trying to figure out the format for future reference.

Ok.. strange.. i tryed but it worked like I wrote ;)
Oh yea.. I didn't tell you anything about colors.. of course you can set fixed color:

Click on a form, go to object inspector find color,
Press arrow to dropdown, go to top and you will se clBlack, clMaroon, clOlive, clRed, clBlue etc.

You can duble click colors and you wil get color selector... you can even define your custom colors.

Roza
Roza,

One more thing about Volume Factor I just discovered. There are situations were there can be a Volume Factor larger than 1.00. With the code we have right now prior to the ones you just posted above I can enter a Volume Factor larger than 1.0 and it seems to work correctly. Is there anything we have in the code that would present any problems that I just haven't seen yet with Volume Factor larger than 1.0?

Gary
Roza,

I used your second solution, very cool. Won't let me typing more than one decimal.

While testing this I accidentally typed in a "`" (the key next to 1).

So I guess I need prevent user from entering anything but a numbers or one decimal there too.

Can I use the same code?
Roza,

>>OK. About RichEdit.. If you got only few lines you can add it runtime. Again we will use OnCreate event, but this time about form's event.

I must not be doing something right here. I'm not sure I understand you. What I did was to go to the MainMenu editor and under About I named the next box down HelpText and in the caption put Contents. I double clicked on that and it took me to the Unit1 code editor. I didn't see

procedure TAboutForm.FormCreate(Sender: TObject);

I see

procedure TGemWeight.HelpTextClick(Sender:

I did get a form with showmodal command working for the About box. When you said
>>So go to that form put richedit control on it, name it let say helptext.
I'm not doing something right here. How do I put " richedit control on it"?

Gary
About problems if VolFactor>1... There should not be any problems with that.

>While testing this I accidentally typed in a "`" (the key next to 1).
Guess you want that for Width?

Yes you can use same code just replace VolFactor with Width.
Roza,

>>OK. About RichEdit.. If you got only few lines you can add it runtime. Again we will use OnCreate event, but this time about form's event.

>I must not be doing something right here. I'm not sure I >understand you. What I did was to go to the MainMenu >editor and under About I named the next box down >HelpText and in the caption put Contents. I double >clicked on that and it took me to the Unit1 code editor. >I didn't see procedure TAboutForm.FormCreate(Sender: TObject);


You shoud have now two forms in your project. First one
GemWeight and another one AboutForm or AboutBox or some other form...

So you shoud go to that form and add richedit control on it.. not to main menu, to form.. you will display richedit on a form, won't you? If you don't have second form look on my previous posts. I explained how to do it.

>I did get a form with showmodal command working for the >About box. When you said
>>So go to that form put richedit control on it, name it let say helptext.
I'm not doing something right here. How do I put " richedit control on it"?

Find form which is working you as About box... if you don't see it press Shift+F12 and select it.. Now go to Win32 Tab (where Edit, Label, Memo, SrollBox was just third tab called Win32), and fourth component from the left is richedit. Put it on a form which is working you as aboutbox. OK?

Roza
Roza,

Width Key Press.
I double clicked on the Width box which took me to the code box and it created a procedure.

procedure TGemWeight.WidthKeyPress(Sender: TObject);
begin
  case Key of '0'..'9',#8: ;
  //if key=number or backspace it's ok
  '.': if ((Pos('.',Width.Text)>0)) then
  if (Pos('.',Copy(Width.Text,Width.SelStart+1,Width.SelLength))=0) then Key:=#0;
  //if decimal point doesn't exist then it's ok else assign 0 value or if decimal will be overwriten then overwrite it
  //#0 is just like no key pressed.
  else Key:=#0;
end;
end;

I got the errors:
[Error] Unit1.pas(119): Undeclared identifier: 'WidthKeyPress'
[Error] Unit1.pas(119): ';' expected but '(' found
[Error] Unit1.pas(121): Undeclared identifier: 'Key'
[Error] Unit1.pas(123): Undeclared identifier: 'Width'
[Error] Unit1.pas(124): Incompatible types
[Error] Unit1.pas(124): ')' expected but identifier 'SelStart' found
[Error] Unit1.pas(133): Statement expected but end of file found
[Error] Unit1.pas(30): Unsatisfied forward or external declaration: 'TGemWeight.WidthChange'

When I double click on the Width box the event that gets created is
procedure TGemWeight.WidthChange(Sender: TObject);
I must be doing something wrong in the way that I created the event.
Roza,

RichEdit is now working just fine. Thanks. The last questions I have are:

1. The Width box keystroke issue.

2. Size. I want to fix the size if possible. I've tried Constraints settings for MaxHeight, MaxWidth, MinHeight and MinWidth but that doesn't seem to do what I want when you run it. When you run it you can still shrink or strech it. This is a small form and I would like to fix the size.

3. Compile. When I compile this will it permanently change anything? Can I compile again and just overwrite the previous exe  or name it something different?

Gary
I wrote once.. do not change anything delphi has written for you unless you realy know what you are doing.

When I double click on the Width box the event that gets created is
procedure TGemWeight.WidthChange(Sender: TObject);
I must be doing something wrong in the way that I created the event.

Yes you have created OnChange event. You should create onKeyPress event. Select Width edit box, go to object inspector, click on Events tab, find OnKeyPress event an then duble click on it.

2.)You must set Constraints of GemWeight form, not controls on that form.. if I set constraints to MinWidth=200, MaxWidth=200 I can not change width runtime..

Ok.. you can also set Form's BorderStyle to bsSingle and set BorderIcons->biMaximize to false.

3.) You have probably compiled already many times by pressing f9 didn't you? So it does not change anything.. F9 compile just creates exe file and runs it ;) You can also freely rename exe file if you like. You can also save project as.. GemWeightCalculator and Delphi will create GemWeightCalculator.exe instead of project1.exe
Note: You can not save project as GemWeight because you have already form named GemWeight.

You can also pack your exe with upx.exe (get it from internet) and you will get abt 200Kb instead of 500Kb +-.

Roza
Roza,

Everything we've talked about so far is now working like I need. Thank you! I just need to build an Icon now. Any warnings I should know other that image size and number of colors?

When I set Constraints I had to experiment a bit. I see that the Min and Max Constraints are different sizes than the actual form. The compiled app is slightly larger than the form.

When I put my email address in the About box is there a way to link it to the users mail client like and html mailto:?

I downloaded UPX from http://sourceforge.net/projects/upx/
I see there's version 120a.zip, 120d.zip and 120w.zip. The d and w version say i386 and the a says Other. Which one should I use?

Instructions or Tips for UPX? I've briefly looked at the html file included in ther version and I see that there are a lot of switches to use. Recommendation?

About icon.. notheing special.. just try an you will se ;)

Aobut Constraints... could be.. didn't noticed yet.

If you used label to display e-mail adress you can add

  ShellExecute(Handle, nil, 'mailto:s57nob@hamradio.si', nil, nil, SW_SHOWNORMAL);

To your OnClick event of that label (you should now be able to create OnClick event by yourself.. hope so.

About upx.. don't know really I just use upx myfile.exe
Version 120w is ok..
Roza,

I created an OnClick event in the about label and I did use a label for the email address. It didn't run though. I have

procedure TAboutBox.LabelEmailClick(Sender: TObject);
begin
ShellExecute(Handle, nil, 'mailto:gary@jewelcutter.com', nil, nil, SW_SHOWNORMAL);
end;

I get the error:
[Error] Unit2.pas(59): Undeclared identifier: 'ShellExecute'
Roza,

Compile Delhpi first and then run upx.exe?
Ooops... Sorry... forgot to tell you that you should add
ShellApi unit to Uses statement at the begining of unit...

Just add at the end like this (do not copy and paste this code).. append ShellApi to your code.

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,Math, Menus, ComCtrls, ShellApi;


About UPX. Yes! Compile your application, find exe file and then use upx on compiled exe file.

Roza,

Thanks all is working. One last tweak on the email thing. I'm looking in the the Events for something like an OnMouseOver which would show that the email info has a link to it. Is there a way to do that?

If I want to add a link to www.mysite.com do I use the same code as above but use something HREF instead of mailto:

Gary
Roza,

I've made and inserted my icons in About and in the form title bar. What do I need to do for that icon to show as the project.exe icon in the Windows Explorer?

This should wrap this one up!
Use OnMouseEnter (when mouse is on your label) and OnMouseLeave(when mouse moves away from control) events.

ShellExecute(Handle, nil, 'http://something.com', nil, nil, SW_SHOWNORMAL);

About icon... I have already wrote about that one:

>>You can also change program's icon in project->options->application->icon menu.

Roza,

For some reason I don't see the comments I left about the OnMouseEnter and OnMouseLeave suggestion you made. This is forcing to much on the user as it opens an email and changes to that url just by touching it with the mouse. What I was hoping for is just something that would show the user that the text in the label is active. What I'm looking for is something like the effect of JavaScript swapImage. Can I make the email address font color change from black to blue with the mouse over the email address and the website url?

Thanks,
Gary
I didn't mean you will put code for opening e-mail to OnMouseEnter event ;) I was hopeing you will write something like this:

OnMouseEnter:

EmailLabel.Font.Style:=EmailLabel.Font.Style+[fsUnderline];

OnMouseLeave:
EmailLabel.Font.Style:=EmailLabel.Font.Style-[fsUnderline];
Roza,

Thanks, that's exactly what I want to do.

One other thing that I asked in the message that didn't get posted for some reason is I was wondering if when you "Open a Project" is there a setting in Delphi that open all the units connected with that project?

Thanks for the help
One other thing that I asked in the message that didn't get posted for some reason is I was wondering if when you "Open a Project" is there a setting in Delphi that open all the units connected with that project?

No.. because that's solved in other way:

See:
view->project manager

Or icons representing view unit&view form just under new, open&save icon...
Roza,

I wrote:
>>I'm currently using
>>     Result.Text:=FloatToStrF(res,ffNumber,4,2);  and it returns 9,999.99

Actually it only returns 9,998.xx. When the value is 9,999.xx it returns 1E04. I forgot to change it back to 6,2. You were absolutely right, of course. I was using 9,999.99 as a format rather than actual output.

All is running well.

Thanks,
Gary
Roza,

I want to make a slight change in the way the Material dropdown looks. I want to display the specific gravity values as well as the material name. I will post another 50 points for you to clarify this one point for me if this is easy. More if it's not.

In the beginning you said:

>>You can also select material name by Material.ItemIndex:= and number.

I think this is where you were going with this but I'm not sure how to get it done. I want it to display slightly different than the way it is in the Material List. For example the list says:

achroite (tourmaline),3.03,3.10

I would like the dropdown to say

achroite (tourmaline) 3.03 - 3.10

Remember there are Materials with only one gravity value.

I've posted the current code I'm running at http://www.fishhead.com/code.txt

Thank you,
Gary

procedure TGemWeight.FormCreate(Sender: TObject);
VAR i: INTEGER;
    Line: STRING;
begin
  DecimalSeparator:='.'; //usual separator is , but we have . in file so we changa it
  FOR i:=0 TO MaterialInfo.Lines.Count-1 DO
  BEGIN
    Line:=MaterialInfo.Lines[i];
    Line:=StringReplace(Line,',',' ',[]);
    Line:=StringReplace(Line,',',' - ',[]);
    Material.Items.Add(Line);
  END;
end;

This should do it.
Roza,

That works just fine. Thank you! Be sure to pick up the points.
Roza,

I've had some requests from users that I want to implement concerning the Material ComboBox. As it is you can use the dropdown list and scroll down to what you want or just start typing and it will jump down to a listing that matches what you are typing. That part is fine. One problem I see is that you can change the values that are displayed and that might give you the impression you are also changing the values that are calculated. The user doesn't know that the calculated values are in the MaterialInfo Memo box and that is not visible. What I would like to do is if you use the dropdown to get a material or type letters into the box which takes you to a matching listing then the numbers that are displayed that way should be disabled from being changed. Perhaps using a KeyPress event? The second part to this and perhaps the more difficult is that I would like to have the option of just typing in a number value only and have it calculate and not use the data in the MaterialInfo Memo list. It would be nice if the number(s) that are typed in could also be either one value or a range in values like 3.03 - 3.10. Is it possible to say if starting with number or decimal then use that number(s) to calculate the result else if starting with a letter (like the names listed) then use the value in MaterialInfo Memo and if using MaterialInfo disable any change to the displayed value in Material ComboBox? If we can do the range in value typed in would the fomat be critical? For example would "3.03 - 3.10" be equal to "3.03-3.10"?

I just updated http://www.fishhead.com/code.txt

Of course there's more points for this!

Thanks,
Gary
Hi!

Don't worry I will answer it today/tomorow morning. Just came from skiing and I'm going to theatre now :)

Hi Roza,

Thanks for the response. Don't worry, I would pick fresh powder over programming any day! Off the topic but do you work with ProTools?

Gary
Hi Gary!

Explain ProTools. Don't know what do you mean?

About our application:

First of all.. it is not good programming style to use combobox to display if you have large amount of data in it. Why not consider replaceing ComboBox with ListBox... It is easyly done just delete Material combobox and place ListBox on a form instead, then name it Material. Application will work ok then. There is another advantage in that. You can not enter some other data.

Then we can create one checkbox and edit for custom input. Then if user checks this checkbox program will calculate info from custom input edit.

Will that be ok? There is alternative for checkbox that we add "custom" material as first item in listbox...

But if you prefer ComboBox other than ListBox we can do as you wanted to do.
Roza,

ProTools is a digital sound recording program and hardware. I see that you are in a band and it looks like you were working on some sort of recording program. I was just curious. http://www.digidesign.com

I exchanged the ComboBox with ListBox. I had to remove the ClearFormClick statement Material.Text:='Material';
to get it to run. I have the height of my boxes and buttons at 21. I noticed that in the ListBox you could see about 1.5 lines of the list so I made the height 17 and then you could only see one line. That made the up and down scroll buttons in the box very small. I could probably live with that except that the other boxes and button were bigger so I made them all 17 high. When I run it though the TEdit boxes will not go that small. The buttons do though. I also tried making the ListBox 30 high to display two lines cleanly. That looks ok for the ListBox but the other elements are not that size and I'm trying to keep this as small as possible. If I kept the size at 21 could I put a blank space in between each of my listings in the MemoBox or does that create problems. Is there a way  to fix this cosmetic problem? Just wondering what my options are.

In the ComboBox I could start typing consecutive letters and it would zero in on the particular listing that most closely matched what I'm typing. For example I could type "T" and it would just to a listing that starts with "ta". If I continue to type another letter say "o" it would jump down to "topaz". With the ListBox I can only type one letter. For example when I type "t" it goes to the highest alphabetical listing in the t's. If I continue to type "o" it jumps to the "o" listings and not the "to" listings. Is there a way to make the ListBox behave like the ComboBox in this respect?

Thanks,
Gary
Roza,

I can live with making the ListBox 30 high and it only jumping to the first letter of the listing unless there's an easy fix.
Roza,

As to whether to use the ComboBox or ListBox I am relying on your judgement and experience to point me in the right direction as to what will work the best and most reliably. So with the addition of a check box we can either have an additional Edit box  or use the first line in the existing ListBox as the place to enter a custom value, correct? If we use the first line in the ListBox would the user just type over the "custom value" and only that line would be changeable? If we use an additional Edit box by putting a check in the CheckBox it would disable the ListBox? If we have two boxes can placing a check in the CheckBox make a message appear in the ListBox like "Enter value in custom box"?

I'm trying to visualize the look of the application and find out what our options are before I ask to to spend any time making code. I know you don't want me to change my mind later. ;-)
Roza,

One more combination I need your opinion on. Can we use the existing ComboBox but disable the changing of the values but still have it jump down to the listing while typing in the first few letters and then add a CheckBox and Edit box for the custom value? Or would that be poor programming style too? What's the best way?

Thank you!
Hi!

Ok.. we will use combobox.

Why and how would I use ListBox?
First of all list box should be used if it displays at least 5 items. Since you want to shrink your form to minimum possible size it's ok to use ComboBox.

>Can we use the existing ComboBox but disable the >changing of the values but still have it jump down to >the listing while typing in the first few letters and >then add a CheckBox and Edit box for the custom value?

Ok.. To disable custom

procedure TGemWeight.MaterialKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  IF Material.ItemIndex<0 THEN Calculate.Enabled:=FALSE
  ELSE Calculate.Enabled:=TRUE;
end;
Hi!

Ok.. we will use combobox.

Why and how would I use ListBox?
First of all list box should be used if it displays at least 5 items. Since you want to shrink your form to minimum possible size it's ok to use ComboBox.

>Can we use the existing ComboBox but disable the >changing of the values but still have it jump down to >the listing while typing in the first few letters and >then add a CheckBox and Edit box for the custom value?

Ok.. To disable custom input this should be enough. Specify OnKeyUp event for Material combo box:

procedure TGemWeight.MaterialKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  IF Material.ItemIndex<0 THEN Calculate.Enabled:=FALSE
  ELSE Calculate.Enabled:=TRUE;
end;
Forget my first idea - it would be very complicated at the end and I am not sure It would really work properly..

OK... here how would I do it:

When user clicks on Calculate button we will check if text in Material is range of Gravity values ex:like 0.12-1.32 or 0.412 - 1.12 or like 0.231 or if text matches one of items in Material. Then it's ok and we will calculate. Otherwise will display error message... ex not correct values or sth. I think this is pretty close to your idea how should it be?

About ProTools.. no.. I don't use it, but I will check for it.. might be interesting. About sth like recording software: It does nothing with playing bass ;) Just some part of other software I write, recording and playback is just minor function implemented in it.
Roza,

Can you have a line say the first in the ComboBox that would allow a custom input of numbers only and not jump down to anything already in the list? Then also disable the changing of any of the existing values in the list and still have it jump down to matching items only by letters only? So if the user types numbers only  in the custom line it will calculate whatever they input on that line only. Or they can use the dropdown and select or start typing letters and have it jump to the matching listing.
Roza,

If doing everything with the ComboBox is too problematic we can go to an Edit box for the custom value part if we need to.
Getting close...

I'll post my results today.
Thanks for your efforts!
Ok. Here we are:

Material=ComboBox

Assign OnKeyPress event:

procedure TGemWeight.MaterialKeyPress(Sender: TObject; var Key: Char);
VAR i:INTEGER;
    MaterialText: STRING;
begin
  IF (Key<>#8) THEN
  BEGIN
    IF (Material.Tag=0) OR (Length(Material.Text)=0) OR (Length(Material.Text)=Material.SelLength) THEN
    BEGIN
      IF (Length(Material.Text)=0) OR (Length(Material.Text)=Material.SelLength) THEN Material.Tag:=0;
      MaterialText:=Copy(Material.Text,1,Material.SelStart)+Lowercase(Key);
      i:=0;
      WHILE (Pos(MaterialText,Material.Items[i])<>1) AND (i<Material.Items.Count) DO
      BEGIN
        INC(i);
      END;
    END
    ELSE i:=Material.Items.Count;
    IF i=Material.Items.Count THEN
    BEGIN
      IF Material.Tag=1 THEN
      BEGIN
        IF (Key<'0') OR (Key>'9') THEN
        BEGIN
          IF Key='-' THEN
          BEGIN
            IF ((Pos('-',Material.Text)>0)) THEN
            IF (Pos('-',Material.SelText)=0) THEN Key:=#0;
          END
          ELSE IF Key='.' THEN
          BEGIN
            IF (Pos('.',Material.SelText)>0) THEN
            ELSE
            BEGIN
              IF (Pos('.',Copy(Material.Text,Pos('-',Material.Text)+1,Length(Material.Text)))>0) THEN Key:=#0;
            END;
          END
          ELSE Key:=#0;
        END
      END
      ELSE
      IF (Length(Material.Text)=0) OR (Material.SelLength=Length(Material.Text)) THEN
      BEGIN
        IF (Key>='0') AND (Key<='9') THEN Material.Tag:=1
        ELSE Key:=#0;
      END
      ELSE Key:=#0;
    END;
  END
  ELSE
end;

OnCalculate button Click event changes to:

procedure TGemWeight.CalculateClick(Sender: TObject);
VAR res, res2, GravityFrom, GravityTo: real;
    Line: STRING;
begin
  IF (VolFactor.Text<>'') AND (Width.Text<>'') THEN
  BEGIN //User completed selections and entred values
    IF (Material.ItemIndex>-1) THEN
    BEGIN
      Line:=MaterialInfo.Lines[Material.ItemIndex];
      Delete(Line,1,Pos(',',Line));
      IF Pos(',',Line)>0 THEN
      BEGIN
        GravityFrom:=StrToFloat(Copy(Line,1,Pos(',',Line)-1));
        Delete(Line,1,Pos(',',Line));
        GravityTo:=StrToFloat(Line);
      END
      ELSE
      BEGIN
        GravityFrom:=StrToFloat(Line);
        GravityTo:=0;
      END;
    END
    ELSE
    BEGIN
      IF Pos('-',Material.Text)>0 THEN
      BEGIN
        GravityFrom:=StrToFloat(Copy(Material.Text,1,Pos('-',Material.Text)-1));
        GravityTo:=StrToFloat(Copy(Material.Text,Pos('-',Material.Text)+1,Length(Material.Text)));
      END
      ELSE
      BEGIN
        GravityFrom:=StrToFloat(Material.Text);
        GravityTo:=0;
      END;
    END;
    res2:=0;
    res:=StrToFloat(Width.Text);
    res:=res*res*res;
    res:=res*StrToFloat(VolFactor.Text);
    res:=res/200;
    if GravityTo>0 THEN
       res2:=res*GravityTo;
    res:=res*GravityFrom;
    IF (res>9999.99) OR (res2>9999.99) THEN
    BEGIN
      Result.Text:='Error can not calculate sizes larger than 9,999.9 carats!';
    END
    ELSE
    BEGIN //result is in range
      Result.Text:=FloatToStrF(res,ffNumber,6,2);
      IF Res2>0 THEN Result.Text:=Result.Text+' - '+ FloatToStrF(res2,ffNumber,6,2);
    END;
  END;
end;
Roza,

That works pretty well! A few things I've noticed so far though.

When you try to type in a custom value the space key wont work. Is it possible to have the ability to type "3.03 - 3.10" as well as "3.03-3.10"? Allowing the space would keep the format the same as the Material listing.

I also noticed that if you try to copy a value from the Material list by using Control/C that it erases the displayed value. Also you cannot paste Control/V to the box. Are these things possible?
 
Roza,

Also, is it possible to disable the changing of the existing values in the Material list. Say if the line in the Material box starts with a letter, like when they are using the existing list, then you can't type a new value in that is associated with an existing listing. Have it only calculate a new value when the line is overwritten and starts with a number?
Roza,

Using the new code I've been able to lock up my machine twice that only a reboot would fix. Even Control/Alt/Delete would not end  the running of our program. What I did was to keep using the items in the Material list "albite 2.62" and try to keep changing the value of 2.62. On the first few tries when I deleted the 2.62 I couldn't type anything but eventually it let me type in a new value. I got a 5 typed in there. When I hit Calculate the error message came up ".....exe raised exception class EConvertError with message "albite 5" is not a valid floating point value." After I cleared the error message I tried again and the system locked up. I did this twice. I do know that we aren't supposed to change the value in this manner but I'm trying to simulate a persistent user.
Hi!

I've changed code a bit.

Got two new events:

procedure TGemWeight.MaterialKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  IF (ssCtrl in Shift) and ((Key=ORD('c')) or (Key=ORD('C'))) THEN Material.Perform(WM_COPY,0,0);
  IF (ssCtrl in Shift) and ((Key=ORD('v')) or (Key=ORD('V'))) THEN Material.Perform(WM_PASTE,0,0);
  IF (ssCtrl in Shift) and ((Key=ORD('x')) or (Key=ORD('X'))) THEN Material.Perform(WM_CUT,0,0);
end;

procedure TGemWeight.MaterialChange(Sender: TObject);
begin
  IF Material.Text='' THEN Material.Tag:=0;
end;

Changed material keypress
procedure TGemWeight.MaterialKeyPress(Sender: TObject; var Key: Char);
VAR i:INTEGER;
    MaterialText: STRING;
begin
  IF (Key<>#8) THEN
  BEGIN
    IF (Material.Tag=0) OR (Length(Material.Text)=0) OR (Length(Material.Text)=Material.SelLength) THEN
    BEGIN
      IF (Length(Material.Text)=0) OR (Length(Material.Text)=Material.SelLength) THEN Material.Tag:=0;
      MaterialText:=Copy(Material.Text,1,Material.SelStart)+Lowercase(Key);
      i:=0;
      WHILE (Pos(MaterialText,Material.Items[i])<>1) AND (i<Material.Items.Count) DO
      BEGIN
        INC(i);
      END;
    END
    ELSE i:=Material.Items.Count;
    IF i=Material.Items.Count THEN
    BEGIN
      IF Material.Tag=1 THEN
      BEGIN
        IF (Key<'0') OR (Key>'9') THEN
        BEGIN
          IF Key='-' THEN
          BEGIN
            IF ((Pos('-',Material.Text)>0)) THEN
            IF (Pos('-',Material.SelText)=0) THEN Key:=#0;
          END
          ELSE IF Key='.' THEN
          BEGIN
            IF (Pos('.',Material.SelText)>0) THEN
            ELSE
            BEGIN
              IF (Pos('.',Copy(Material.Text,Pos('-',Material.Text)+1,Length(Material.Text)))>0) THEN Key:=#0;
            END;
          END
          ELSE Key:=#0;
        END
      END
      ELSE
      IF (Length(Material.Text)=0) OR (Material.SelLength=Length(Material.Text)) THEN
      BEGIN
        IF (Key>='0') AND (Key<='9') THEN Material.Tag:=1
        ELSE Key:=#0;
      END
      ELSE Key:=#0;
    END;
  END;
end;


Changed Calculate procedure

Note!! Invaild floating point value will still occur if you run your program in delphi, but if you run it outide of delphi user will not see anything.

procedure TGemWeight.CalculateClick(Sender: TObject);
VAR res, res2, GravityFrom, GravityTo: real;
    Line: STRING;

  FUNCTION IsNumber(Number: STRING):BOOLEAN;
  BEGIN
    Result:=True;
    try
      StrToFloat(Number);
    except
      Result:=False;
    end;
  END;

begin
  IF (VolFactor.Text<>'') AND (Width.Text<>'') THEN
  BEGIN //User completed selections and entred values
    IF (Material.ItemIndex>-1) THEN
    BEGIN
      Line:=MaterialInfo.Lines[Material.ItemIndex];
      Delete(Line,1,Pos(',',Line));
      IF Pos(',',Line)>0 THEN
      BEGIN
        GravityFrom:=StrToFloat(Copy(Line,1,Pos(',',Line)-1));
        Delete(Line,1,Pos(',',Line));
        GravityTo:=StrToFloat(Line);
      END
      ELSE
      BEGIN
        GravityFrom:=StrToFloat(Line);
        GravityTo:=0;
      END;
    END
    ELSE
    BEGIN
      IF Pos('-',Material.Text)>0 THEN
      BEGIN
        IF IsNumber(Copy(Material.Text,1,Pos('-',Material.Text)-1)) THEN GravityFrom:=StrToFloat(Copy(Material.Text,1,Pos('-',Material.Text)-1))
        ELSE Exit;
        IF IsNumber(Copy(Material.Text,Pos('-',Material.Text)+1,Length(Material.Text))) THEN GravityTo:=StrToFloat(Copy(Material.Text,Pos('-',Material.Text)+1,Length(Material.Text)))
        ELSE Exit;
      END
      ELSE
      BEGIN
        IF IsNumber(Material.Text) THEN GravityFrom:=StrToFloat(Material.Text)
        ELSE Exit;
        GravityTo:=0;
      END;
    END;
    res2:=0;
    res:=StrToFloat(Width.Text);
    res:=res*res*res;
    res:=res*StrToFloat(VolFactor.Text);
    res:=res/200;
    if GravityTo>0 THEN
       res2:=res*GravityTo;
    res:=res*GravityFrom;
    IF (res>9999.99) OR (res2>9999.99) THEN
    BEGIN
      Result.Text:='Error can not calculate sizes larger than 9,999.9 carats!';
    END
    ELSE
    BEGIN //result is in range
      Result.Text:=FloatToStrF(res,ffNumber,6,2);
      IF Res2>0 THEN Result.Text:=Result.Text+' - '+ FloatToStrF(res2,ffNumber,6,2);
    END;
  END;
end;


My thoughts: All we can do is that we eliminate possibly program crashes if user enters invalid data and display some our error messages to let user know what has done wrong. Peristant user will always find a way to enter invalid value... when allowing cuting/pasting text or some other tricks there is almost impossible to eliminate all strange entering that might happen. So best we can do is that we prevent program to crash on such occasions.

What do you think?
PS: I'll work on spaces between numbers entered in.
Thanks for your work! I will be gone most of today but will try to test it all tonight.
Hi Roza,

I've compiled it and testing outside of Delphi. I like the way you can only type in a value that matches the Material values. that should go a long way to prevent most errors. I did manage to create a false error message once. I can't reproduce yet the sequence of inputs that created the false error. What I finally did that made the error was that I had a Material line selected from the dropdown. The I deleted just the value(s) with only the name in the Material box. When I calculated it gave me the error message of "Error can not calculate sizes larger than 9,999.9 carats!" and then I couldn't input any thing else that would calculate. It kept giving me the error message but the calculation was within the range of valid values so I shouldn't have gotten the error message. I'm trying to reproduce it now.

Would it be possible to add something like if there's no numeric value being displayed in the Material box then give it's own specific error message like "No gravity value selected!" Hopefully this error message would still show up if the user first makes a valid calculation either by the dropdown list or a typed input and then tries to make an invalid change.

Any solution for spaces between numbers entered in?

Thanks for your work on this. I realize this is a major overhaul of the procedures!

Gary
Roza,

I see that I can alter a value in the Material list say "achroite (tourmaline) 3.03 - 3.10" to "achroite (tourmaline) 3" and it will still calculate with a result of xx.xx - xx.xx which uses the correct value assigned to achroite in the Memo box but may give the impression that it is calculating the modified value that you are actually seeing in the Material box. Can we further restrict what you see in the Material box as it relates to what is calculated? Perhaps there's a way to tie in my suggestion of a new error message in the comment above to this. So that if the user partially alters an existing value listing in the Material box which doesn't match the actual value in the Memo box it will give the same error message. But still allow the custom value as it is with the exception of allowing the spaces in the custom.

I hope I'm not frustrating you too much!
Roza,

Have you had the time to look at this or is there a problem with how I want this to work?

Thanks,
Gary
Hi Gary.. sorry for not responding, but new year is comeing and I have little time between parties :)

Just get home from one now (1:10 am local time now)

Will try to solve tomorrow.

Sorry again.
Yeah me to! I'm about partied out. Don't work on this stuff with a hangover.  ;-)

What part of the globe are you located? You are seven hours ahead of me in Texas, USA.
Finally something... :)

Waiting for lunch and haveing some time
I am from Slovenia, Europe.
About hangover:I avoid haveing it..

Hope this will solve wrong "achroite (tourmaline) 3" calculation and entering spaces in custom input.

procedure TGemWeight.MaterialKeyPress(Sender: TObject; var Key: Char);
VAR i:INTEGER;
    MaterialText: STRING;

    FUNCTION Has2Spaces(Str:STRING):BOOLEAN;
    BEGIN
      Result:=FALSE;
      Delete(Str,1,Pos(' ',Str));
      IF Pos(' ',Str)>0 THEN Result:=TRUE;
    END;

begin
  IF (Key<>#8) THEN
  BEGIN
    IF (Material.Tag=0) OR (Length(Material.Text)=0) OR (Length(Material.Text)=Material.SelLength) THEN
    BEGIN
      IF (Length(Material.Text)=0) OR (Length(Material.Text)=Material.SelLength) THEN Material.Tag:=0;
      MaterialText:=Copy(Material.Text,1,Material.SelStart)+Lowercase(Key);
      i:=0;
      WHILE (Pos(MaterialText,Material.Items[i])<>1) AND (i<Material.Items.Count) DO
      BEGIN
        INC(i);
      END;
    END
    ELSE i:=Material.Items.Count;
    IF i=Material.Items.Count THEN
    BEGIN
      IF Material.Tag=1 THEN
      BEGIN
        IF (Key<'0') OR (Key>'9') THEN
        BEGIN
          IF Key='-' THEN
          BEGIN
            IF ((Pos('-',Material.Text)>0)) THEN
            IF (Pos('-',Material.SelText)=0) THEN Key:=#0;
          END
          ELSE IF Key='.' THEN
          BEGIN
            IF (Pos('.',Material.SelText)>0) THEN
            ELSE
            BEGIN
              IF (Pos('.',Copy(Material.Text,Pos('-',Material.Text)+1,Length(Material.Text)))>0) THEN Key:=#0;
            END;
          END
          ELSE IF Key=' ' THEN
          BEGIN
            IF ((Material.Text[Material.SelStart]='-') OR (ISNumber(Material.Text[Material.SelStart]))) AND ((NOT Has2Spaces(Material.Text)) OR (Pos(' ',Material.SelText)>0)) THEN
            BEGIN
              IF (ISNumber(Material.Text[Material.SelStart])) THEN
              BEGIN
                IF (Pos(' ',Material.Text)>0) THEN IF Pos(' ',Material.SelText)>0 THEN
                ELSE Key:=#0;
              END;
            END
            ELSE Key:=#0;
          END
          ELSE Key:=#0;
        END
      END
      ELSE
      IF (Length(Material.Text)=0) OR (Material.SelLength=Length(Material.Text)) THEN
      BEGIN
        IF (Key>='0') AND (Key<='9') THEN Material.Tag:=1
        ELSE Key:=#0;
      END
      ELSE Key:=#0;
    END;
  END;
end;

  FUNCTION IsNumber(Number: STRING):BOOLEAN;
  BEGIN
    Result:=True;
    try
      StrToFloat(Number);
    except
      Result:=False;
    end;
  END;


PROCEDURE ShowError;
BEGIN
  ShowMessage('Please enter valid material!');
END;

procedure TGemWeight.CalculateClick(Sender: TObject);
VAR res, res2, GravityFrom, GravityTo: real;
    Line: STRING;
begin
  IF (VolFactor.Text<>'') AND (Width.Text<>'') THEN
  BEGIN //User completed selections and entred values
    IF (Material.ItemIndex>-1) THEN
    BEGIN
      Line:=MaterialInfo.Lines[Material.ItemIndex];
      Delete(Line,1,Pos(',',Line));
      IF Pos(',',Line)>0 THEN
      BEGIN
        GravityFrom:=StrToFloat(Copy(Line,1,Pos(',',Line)-1));
        Delete(Line,1,Pos(',',Line));
        GravityTo:=StrToFloat(Line);
      END
      ELSE
      BEGIN
        GravityFrom:=StrToFloat(Line);
        GravityTo:=0;
      END;
    END
    ELSE
    BEGIN
      IF Pos('-',Material.Text)>0 THEN
      BEGIN
        IF IsNumber(Copy(Material.Text,1,Pos('-',Material.Text)-1)) THEN GravityFrom:=StrToFloat(Copy(Material.Text,1,Pos('-',Material.Text)-1))
        ELSE
        BEGIN
          ShowError;
          Exit;
        END;
        IF IsNumber(Copy(Material.Text,Pos('-',Material.Text)+1,Length(Material.Text))) THEN GravityTo:=StrToFloat(Copy(Material.Text,Pos('-',Material.Text)+1,Length(Material.Text)))
        ELSE
        BEGIN
          ShowError;
          Exit;
        END;
      END
      ELSE
      BEGIN
        IF IsNumber(Material.Text) THEN GravityFrom:=StrToFloat(Material.Text)
        ELSE
        BEGIN
          ShowError;
          Exit;
        END;
        GravityTo:=0;
      END;
    END;
    res2:=0;
    res:=StrToFloat(Width.Text);
    res:=res*res*res;
    res:=res*StrToFloat(VolFactor.Text);
    res:=res/200;
    if GravityTo>0 THEN
       res2:=res*GravityTo;
    res:=res*GravityFrom;
    IF (res>9999.99) OR (res2>9999.99) THEN
    BEGIN
      Result.Text:='Error can not calculate sizes larger than 9,999.9 carats!';
    END
    ELSE
    BEGIN //result is in range
      Result.Text:=FloatToStrF(res,ffNumber,6,2);
      IF Res2>0 THEN Result.Text:=Result.Text+' - '+ FloatToStrF(res2,ffNumber,6,2);
    END;
  END;
end;

Hi Roza,

My Grandparents were born in Prague.

I replaced the two procedures:

procedure TGemWeight.MaterialKeyPress(Sender: TObject; var Key: Char);
procedure TGemWeight.CalculateClick(Sender: TObject);

But I get the following errors:

[Error] Unit1.pas(236): Undeclared identifier: 'ISNumber'
[Error] Unit1.pas(236): Operator not applicable to this operand type
[Error] Unit1.pas(237): Operator not applicable to this operand type
[Fatal Error] Gem_estimator.dpr(6): Could not compile used unit 'Unit1.pas'

Did I miss something?

Thanks,
Gary
Move function IsNumber to place before first procedure... just after

{$R *.dfm}

FUNCTION IsNumber(Number: STRING):BOOLEAN;
 BEGIN
   Result:=True;
   try
     StrToFloat(Number);
   except
     Result:=False;
   end;
END;

That should help.
When I do that I get:

[Error] Unit1.pas(107): Undeclared identifier: 'ShowError'

I've updated http://www.fishhead.com/code.txt
Also put ShowError function after IsNumber function.
Well actualy it's procedure ShowError not function :)
Roza,

Once again you've done a great job. It all seems to work the way I imagined. I'm posting another question for you the points.

Thank you,
Gary