Search code examples
inno-setuppascalscript

Identify, what Inno Setup tasks correspond to the items of TaskList (to show text on mouse over for certain tasks only)


So, I have these comtools tasks, which, as you can see, can have a different position in the tasks list, depending on which components were previously chosen. Moreover, they may not exist at all, if the user doesn't wish to install those components. What I need is static text shown, but only when the cursor hovers over the comtools tasks.

[Tasks]
Name: "acorig"; Description: "ac original"; GroupDescription: "Choose which version of ac to install:"; Flags: exclusive; Components: ac
Name: "acenh"; Description: "ac enhanced"; GroupDescription: "Choose which version of ac to install:"; Flags: exclusive unchecked; Components: ac
Name: "ac2comtools"; Description: "ac2"; GroupDescription: "Also install community-made tools (unsupported) for:"; Flags: unchecked; Components: ac2
Name: "bccomtools"; Description: "bc"; GroupDescription: "Also install community-made tools (unsupported) for:"; Flags: unchecked; Components: bc
Name: "bc2comtools"; Description: "bc2"; GroupDescription: "Also install community-made tools (unsupported) for:"; Flags: unchecked; Components: bc
Name: "bc3comtools"; Description: "bc3"; GroupDescription: "Also install community-made tools (unsupported) for:"; Flags: unchecked; Components: bc3
Name: "bc4comtools"; Description: "bc4"; GroupDescription: "Also install community-made tools (unsupported) for:"; Flags: unchecked; Components: bc4

Yes, I did see https://stackoverflow.com/a/37796528/3993104, but that ties the description to index, which is not practical here. Also, that code shows description for all items in TasksList.


Edit: So, the only change I made to the code after Martin's answer (aside from moving everything over to the tasks page) was adding Martin's function, and editing HoverComponentChanged like this:

procedure HoverComponentChanged(Index: Integer);
var 
  Description: string;
begin
  case Index of
    -1: Description := '';
    LookupTask('ac2comtools'): Description := 'This is the description of AC2';
    LookupTask('bccomtools'): Description := 'This is the description of BC';
    LookupTask('bc2comtools'): Description := 'This is the description of BC2';
    LookupTask('bc3comtools'): Description := 'This is the description of BC3';
  else
    Description := '';
  end;
  TaskLabel.Caption := Description;
end;

-1 is a failsafe, since, when one of the components in deselected, then the index for the corresponding task is -1, which means you will see the description for the first deselected component in this list, when your cursor is outside of TasksList.


Solution

  • There's really no straightforward way to identify, what tasks correspond to the items of TaskList.


    Quick and dirty way is to use item description.

    In this case, it's better to define the description using a custom message, so that you can refer to it in the code. And it's a must anyway, if your installer is localized.

    [CustomMessages]
    AC2ComTools=ac2
    
    [Tasks]
    Name: "ac2comtools"; Description: {cm:AC2ComTools}
    
    [Code]
    
    function LookupTask(TaskCustomMessage: string): Integer;
    var
      Description: string;
    begin
      Description := CustomMessage(TaskCustomMessage);
      Result := WizardForm.TasksList.Items.IndexOf(Description);
      Log(Format('Index of "%s" task is %d', [Description, Result]));
    end;
    

    Use it like:

    AC2ComToolsIndex := LookupTask('ac2comtools');
    

    Another way is to reproduce the logic of Inno Setup for deciding, what tasks to show.

    Use WizardIsComponentSelected function (IsComponentSelected before Inno Setup 6.0.2).

    // Before Inno Setup 6.0.2, use IsComponentSelected.
    if WizardIsComponentSelected('ac2') then
    begin
      if WizardIsComponentSelected('ac') then AC2ComToolsIndex := 4
        else AC2ComToolsIndex := 1;
    end
      else AC2ComToolsIndex := -1;
    

    If you want to create a full mapping from task name to TaskList item index automatically, you can do something like this each time the task list changes, i.e. on call to CurPageChanged(wpSelectTasks):

    • Remember current task state
    • Check all task checkboxes
    • Read WizardSelectedTasks(False) to get names of all tasks
    • Generate the mapping
    • Restore previous task state.

    This is relatively easy, when there are only checkboxes, not radio buttons (i.e. no task with exclusive flag).

    var
      Tasks: TStringList;
    
    procedure CurPageChanged(CurPageID: Integer);
    var
      TasksChecked: array of Boolean;
      I: Integer;
    begin
      if CurPageID = wpSelectTasks then
      begin
        SetArrayLength(TasksChecked, WizardForm.TasksList.Items.Count);
        // Remember current task state + Check all task checkboxes
        for I := 0 to WizardForm.TasksList.Items.Count - 1 do
        begin
          TasksChecked[I] := WizardForm.TasksList.Checked[I];
          WizardForm.TasksList.Checked[I] := True;
        end;
        Tasks := TStringList.Create;
        Tasks.CommaText := WizardSelectedTasks(False);  
        for I := 0 to WizardForm.TasksList.Items.Count - 1 do
        begin
          // Insert blank items for "group descriptions"
          if WizardForm.TasksList.ItemObject[I] = nil then
            Tasks.Insert(I, '');
          // Restore previous task state
          WizardForm.TasksList.Checked[I] := TasksChecked[I];
        end;
      end;
    end;
    

    Now you can use the Tasks to lookup corresponding task index:

    AC2ComToolsIndex := Tasks.IndexOf('ac2comtools');
    

    Though as you have exclusive tasks, you would need much more complicated code.