Based in the users selection on the TASK Wizard page, I need to be able to use the answer to create 5-different variables/variant to use in the FILES & ICONS sections.
Examples;
1.- Results would indicate what directory to place files into.
2.- Results would also indicate what the TEXT in the Perameters would be.
Each example above would be a different variable/variant
These variants would essentially replace #define(s) variable that I am currently using.
My application is a multi-state application with each state having different support file contents, I wish to be able to use the TASK option instead of having a separate exe file for each.
Your question is too broad to cover, so I'll try to show you just a principle of getting [Files] entry DestDir parameter from script code, which is what you can apply also for [Icons] parameters. The key is to use the {code:...} constant in which you can specify a getter function declared in the [Code] section of your script. The following example shows, how to install file into 4 different directories based on selected tasks:
#define PathNone "None"
#define PathBoth "Both"
#define PathFirst "First"
#define PathSecond "Second"
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Tasks]
Name: TaskFirst; Description: "First task"
Name: TaskSecond; Description: "Second task"
[Files]
Source: "MyApp.exe"; DestDir: "{code:GetMyAppDir}"
[Code]
function GetMyAppDir(Param: string): string;
begin
// check if both tasks are selected; if yes, then assign a subfolder path defined
// by the PathBoth preprocessor variable to the Result
if IsTaskSelected('TaskFirst') and IsTaskSelected('TaskSecond') then
Result := '{#PathBoth}'
else
// both tasks are not selected, so let's check if the first one is; if yes, then
// assign the PathFirst preprocessor variable to the Result
if IsTaskSelected('TaskFirst') then
Result := '{#PathFirst}'
else
// first task nor both are selected, so let's check if the second one is; if so,
// assign the PathSecond preprocessor variable to the Result
if IsTaskSelected('TaskSecond') then
Result := '{#PathSecond}'
else
// no task is selected (this is the last possible situation), let's assign the
// PathNone preprocessor variable to the Result
Result := '{#PathNone}';
// finally prepend to the Result the {app} constant and expand all the constants
Result := ExpandConstant('{app}\' + Result);
end;
Similar you can do with many section parameters, but not all (this is quite a broad topic). Beware also, that some of the parameters are evaluated eariler (when the tasks were not yet seen by the user), some of them later. Also some of the parameters are evaluated more than once (assigned getter functions may execute more than once).
So it depends which parameters are you going to specify that way. For your mentioned [Files] section DestDir parameter and [Icons] section Parameters parameter you are fine with this approach.
Related
So I use the DKLang Localization Package for multi-language support.
TDKLanguageController has a property called StoreList (of type TStrings), where you can enter a TMask-compatible wildcard strings to force a property to be processed by DKLang, for example, "*.SomeStringProp" will force any component properties called SomeStringProp to be processed by DKLang.
The problem I'm having now is to force DKlang to handle Virtual treeview's header captions, ie. TVirtualStringTree.Header.Columns[i].Text.
I've tried "*.Text", "Header.Columns.Text" and "Header.Columns*.Text", none of them worked.
Thanks.
You can declare constant for each column text:
myVirtualStringTree.Header.Columns[0].Text := myConstant1;
myVirtualStringTree.Header.Columns[1].Text := myConstant2;
myVirtualStringTree.Header.Columns[2].Text := myConstant3;
and manage those constants with the DKLang editor by the "user defined constants".
Delphi XE6 - I have a set. I would like a simple way to turn ALL elements off. i.e. instead of Exclude, something like ExcludeALL. I have tried to loop through all elements, but I get an error.
Code
type
TSearchParametersType =
(smDUNSAvailable = 1,
smDUNSHit,
smDUNSMiss,
smDUNSAbsent,
smRegistryAvailable,
smRegistryHit,
smRegistryAbsent,
smRegistryMiss,
smNameAvailable,
smNameHitExact,
smNameHitWords,
smNameMiss
);
// Now create a set type, where we can have a variable that has all the values of TSearchParametersType
type
TSearchParametersSet = set of TSearchParametersType;
...
var
i : Integer;
sSearchStatus: TSearchParametersSet;
begin
for i := smDUNSAvailable to smNameMiss do
Exclude(sSearchStatus, i);
The error I get is "Incompatible Type: 'Integer' and TSearchParametersType. "
Is there a simple way to Exclude ALL, other than MANUALLY going through every element?
Thanks
From the documentation:
Every set type can hold the empty set, denoted by [].
So you can assign the empty set to your variable like this:
sSearchStatus := [];
FWIW, your code fails because smDUNSAvailable and smNameMiss are of type TSearchParametersType and so not compatible with the variable i which is of type Integer. In order to make your code work you would need to change the loop variable to be of type TSearchParametersType.
Let me start by saying that David's answer is the correct one.
I'll just post another one to show how you could do it manually. This code might come in handy some other time:
var
sSearchStatus: TSearchParametersSet;
SearchParametersType : TSearchParametersType;
begin
sSearchStatus := [smDUNSHit, smDUNSMiss, smDUNSAbsent, smRegistryAvailable, smRegistryHit];
for SearchParametersType := low(TSearchParametersType) to high(TSearchParametersType) do
Exclude(sSearchStatus, SearchParametersType);
end;
I just used the SecondaryShortCuts-Feature of Delphi's TAction. But Shortcuts are defined by Strings, like "F5" or "Shift+F5". Now the problem: On my German Windows the action doesn't fire, because the key "Shift" is called "Umsch" in German!
Does it mean the SecondaryShortCuts-Property is completely useless at design time, because nobody can create applications which work internationally with that?
I could set the key at runtime by translating the VK_SHIFT into the correct name. I tried it via GetKeyNameText but this didn't worked because it gave the long form "Umschalt" not "Umsch". Anybody know the function to get the short version of the key name?
You could try this: Generate the shortcut text from a shortcut:
var
s: TShortCut;
begin
s := ShortCut(Ord('A'), [ssShift]);
Action1.SecondaryShortCuts.Add(ShortCutToText(s));
By the way, these values are determined by the following constants. Have you translated those? And if so, do you need to?:
SmkcShift = 'Shift+';
SmkcCtrl = 'Ctrl+';
SmkcAlt = 'Alt+';
How to add parameters in TSmartQuery?
I mean on the Parameter tab which can be seen when I click on params properties.
I found two ways:
-editing the dfm file
-parameters are filled out automatically based on used :vars in Sql tab.
I did not managed to add them manually using a user interface.
TSmartQuery is component from ODAC library.
TSmartQuery is similar to other TQuery family you can use parameters in different ways depend on your needs:
If you already used Sql with parameters such as: Qry1.Sql.Text := 'Select * from Table where Id = :id';
then you can the defined parameter values as : Qry1.ParamByName('Id').asInteger := 10;
If you have an instance from TParam you can add to the qry like : Qry1.Params.AddParam(myParam).
You can create Parameter and assigned directly to the ParamList with : Qry1.Params.CreateParam();
which defined as:
function CreateParam(FldType: TFieldType; const ParamName: _string;
ParamType: TParamType): TDAParam;
2 & 3 mostly used with Stored Procedures because you need to define if the parameter will be input or output param.
Update:
I didn't notice that you are using Intraweb when I post my answer, but it should be the same way as you do with normal Delphi applications.
I have a set of data that I need to store at design-time to construct the contents of a group of components at run-time.
Something like this:
type
TVulnerabilityData = record
Vulnerability: TVulnerability;
Name: string;
Description: string;
ErrorMessage: string;
end;
What's the best way of storing this data at design-time for later retrieval at run-time? I'll have about 20 records for which I know all the contents of each "record" but I'm stuck on what's the best way of storing the data.
The only semi-elegant idea I've come up with is "construct" each record on the unit's initialization like this:
var
VulnerabilityData: array[Low(TVulnerability)..High(TVulnerability)] of TVulnerabilityData;
....
initialization
VulnerabilityData[0].Vulnerability := vVulnerability1;
VulnerabilityData[0].Name := 'Name of Vulnerability1';
VulnerabilityData[0].Description := 'Description of Vulnerability1';
VulnerabilityData[0].ErrorMessage := 'Error Message of Vulnerability1';
VulnerabilityData[1]......
.....
VulnerabilityData[20]......
Is there a better and/or more elegant solution than this?
Thanks for reading and for any insights you might provide.
You can also declare your array as consts and initialize it...
const
VulnerabilityData: array[Low(TVulnerability)..High(TVulnerability)] of TVulnerabilityData =
(
(Vulnerability : vVulnerability1; Name : Name1; Description : Description1; ErrorMessage : ErrorMessage1),
(Vulnerability : vVulnerability2; Name : Name2; Description : Description2; ErrorMessage : ErrorMessage2),
[...]
(Vulnerability : vVulnerabilityX; Name : NameX; Description : DescriptionX; ErrorMessage : ErrorMessageX)
)
);
I don't have an IDE on this computer to double check the syntax... might be a comma or two missing. But this is how you should do it I think.
not an answer but may be a clue: design-time controls can have images and other binary data associated with it, why not write your data to a resource file and read from there? iterating of course, to make it simpler, extensible and more elegant
The typical way would be a file, either properties style (a=b\n on each line) cdf, xml, yaml (preferred if you have a parser for it) or a database.
If you must specify it in code as in your example, you should start by putting it in something you can parse into a simple format then iterate over it. For instance, in Java I'd instantiate an array:
String[] vals=new String[]{
"Name of Vulnerability1", "Description of Vulnerability1", "Error Message of Vulnerability1",
"Name of Vulnerability2", ...
}
This puts all your data into one place and the loop that reads it can easily be changed to read it from a file.
I use this pattern all the time to create menus and for other string-intensive initialization.
Don't forget that you can throw some logic in there too! For instance, with menus I will sometimes create them using data like this:
"^File", "Open", "Close", "^Edit", "Copy", "Paste"
As I'm reading this in I scan for the ^ which tells the code to make this entry a top level item. I also use "+Item" to create a sub-group and "-Item" to go back up to the previous group.
Since you are completely specifying the format you can add power later. For instance, if you coded menus using the above system, you might decide at first that you could use the first letter of each item as an accelerator key. Later you find out that File/Close conflicts with another "C" item, you can just change the protocol to allow "Close*e" to specify that E should be the accelerator. You could even include ctrl-x with a different character. (If you do shorthand data entry tricks like this, document it with comments!)
Don't be afraid to write little tools like this, in the long run they will help you immensely, and I can turn out a parser like this and copy/paste the values into my code faster than you can mold a text file to fit your example.