-- File: SearchPathToolImpl.mesa - last edit:
-- Breisacher.es	28-Jan-86 17:28:35
-- MEW			10-Apr-86 12:38:49

-- Copyright (C) 1985, 1986 by Xerox Corporation. All rights reserved.

DIRECTORY 
  Attention,
  BWSFileTypes,
  Catalog,
  Event,
  FormWindow,
  Heap USING [Create],
  MenuData USING [CreateItem, MenuProc],
  MFileOnNSFile,
  NSFile,
  NSString,
  OptionFile,
  PropertySheet,
  Selection, 
  StarDesktop,
  Window,
  XString;
  
SearchPathToolImpl: MONITOR
  IMPORTS
    Attention, Catalog, Event, FormWindow, Heap, MenuData, MFileOnNSFile, NSFile, NSString, OptionFile, PropertySheet, Selection, StarDesktop, XString = BEGIN
  
  -- TYPEs
  
  FormItems: TYPE = {add, path, name, remove};
  
  firstElementItem: CARDINAL = FormItems.path.ORD;
  itemsPerElement: CARDINAL = FormItems.remove.ORD - FormItems.path.ORD + 1;
  
  << grunge - no way to associate clientData with a choice item, so I have to keep this data structure with the FormWindow - what a pain. >>
  RefsHandle: TYPE = LONG POINTER TO Refs;
  Refs: TYPE = LONG POINTER TO RefSeq;
  RefSeq: TYPE = RECORD [SEQUENCE l: CARDINAL OF NSFile.Reference];

  -- Data

  zone: UNCOUNTED ZONE = Heap.Create [initial:1];
  
  toolW: Window.Handle ← NIL;
  
  -- Procedures

  Init: PROC = {
    rb: XString.ReaderBody ← XString.FromSTRING["SearchPathTool"L];
    Attention.AddMenuItem[MenuData.CreateItem[
      zone: NIL, name: @rb, proc: MenuProc]];
    IF UserIsLoggedOn[] THEN [] ← ReadOptionFile[StarDesktop.logon, NIL, NIL];
    SetUpWaitForLogonEvent[];
    };
  
  MenuProc: MenuData.MenuProc = {
    rb: XString.ReaderBody ← XString.FromSTRING["SearchPathTool"L];
    rb2: XString.ReaderBody ← XString.FromSTRING["SearchPathTool already open!"L];
    IF toolW = NIL THEN toolW ← PropertySheet.Create [
      formWindowItems: MakeItems,
      formWindowItemsLayout: DoLayout,
      menuItemProc: DoIt,
      size: [0,0],
      placeToDisplay: [300,300],
      title: @rb,
      display: TRUE,
      clientData: zone.NEW [Refs ← NIL]]
    ELSE Attention.Post[@rb2];
    };
    
  sysRef: NSFile.Reference ← GetSysRef[];

  MakeItems: FormWindow.MakeItemsProc = {
    ith: CARDINAL ← 0;
    elements: CARDINAL ← 0;
    refs: RefsHandle ← clientData;

    Countem: MFileOnNSFile.EachElementProc = {elements ← elements + 1};
    
    EachElement: MFileOnNSFile.EachElementProc = {
      MakeElementItems [ith, element, refs↑, window];
      ith ← ith + 1;
      };

    BEGIN
    rb: XString.ReaderBody ← XString.FromSTRING["Add An Element"L];
    FormWindow.MakeCommandItem [
      window: window,
      myKey: FormItems.add.ORD,
      commandName: @rb,
      commandProc: AddElement];
    END;
    MFileOnNSFile.EnumeratePath [Countem];
    refs↑ ← zone.NEW [RefSeq [elements]];
    MFileOnNSFile.EnumeratePath [EachElement];
    };
    
  MakeElementItems: PROCEDURE [ith: CARDINAL, ref: NSFile.Reference, 
    refs: Refs, window: Window.Handle] = {
    choices: ARRAY [0..3) OF FormWindow.ChoiceItem ← [
      [string[0, XString.FromSTRING["Desktop"L]]],
      [string[1, XString.FromSTRING["SystemFolder"L]]],
      [string[2, XString.FromSTRING["Other Folder"L]]]];
    desktop: NSFile.Reference ← StarDesktop.GetCurrentDesktopFile [];
    initChoice: CARDINAL = SELECT ref FROM 
      desktop => 0,
      sysRef => 1,
      ENDCASE => 2;
    refs[ith] ← ref;
    FormWindow.MakeChoiceItem [
      window: window,
      myKey: FormItems.path.ORD + (ith * itemsPerElement),
      values: DESCRIPTOR[choices],
      changeProc: ElementChanged,
      initChoice: initChoice];
    BEGIN
    nameVisibility: FormWindow.Visibility = IF initChoice = 2 THEN visible ELSE invisible;
    fh: NSFile.Handle;
    initName: XString.Reader ← NIL;
    namerb: XString.ReaderBody;
    attrRec: NSFile.AttributesRecord;
    attrs: NSFile.Attributes ← NIL;
    IF nameVisibility = visible THEN {
      fh ← NSFile.OpenByReference [ref];
      attrs ← @attrRec;
      NSFile.GetAttributes [fh, [[pathname: TRUE]], attrs];
      namerb ← XString.FromNSString [attrs.pathname];
      initName ← @namerb;
      NSFile.Close [fh];
      };
    FormWindow.MakeTextItem [
      window: window,
      myKey: FormItems.name.ORD + (ith * itemsPerElement),
      width: 400,
      initString: initName,
      visibility: nameVisibility];
    IF attrs # NIL THEN NSFile.ClearAttributes [attrs];
    END;
    BEGIN
    rb: XString.ReaderBody ← XString.FromSTRING["Remove"L];
    FormWindow.MakeCommandItem [
      window: window,
      myKey: FormItems.remove.ORD + (ith * itemsPerElement),
      commandName: @rb,
      commandProc: RemoveElement];
    END;
    };
    
  LayoutElementItems: PROCEDURE [ith: CARDINAL, window: Window.Handle] = {
    spaceAboveLine: CARDINAL = 10;
    m: CARDINAL = 5;
    line: FormWindow.Line;
    line ← FormWindow.AppendLine [window, spaceAboveLine];
    FormWindow.AppendItem [window, FormItems.path.ORD + (ith * itemsPerElement), line, m,,FALSE];
    FormWindow.AppendItem [window, FormItems.remove.ORD + (ith * itemsPerElement), line, m,,FALSE];
    line ← FormWindow.AppendLine [window, spaceAboveLine];
    FormWindow.AppendItem [window, FormItems.name.ORD + (ith * itemsPerElement), line, m,,FALSE];
    };
    
  AddElement: FormWindow.CommandProc = {
    refs: RefsHandle ← FormWindow.GetClientData [window];
    count: CARDINAL = (IF refs↑ = NIL THEN 0 ELSE refs.l);
    temp: Refs ← zone.NEW [RefSeq [count+1]];
    FOR i: CARDINAL IN [0..count) DO
      temp[i] ← refs[i];
      ENDLOOP;
    zone.FREE [refs]; -- not @refs!
    refs↑ ← temp;
    MakeElementItems [ith: count, ref: sysRef, refs: refs↑,
      window: window];
    LayoutElementItems [count, window];
    FormWindow.Repaint [window];
    };
    
  RemoveElement: FormWindow.CommandProc = {
    refs: RefsHandle ← FormWindow.GetClientData [window];
    count: CARDINAL = refs.l;
    temp: Refs ← zone.NEW [RefSeq [count-1]];
    rb: XString.ReaderBody;
    elementToRemove: CARDINAL = (item - firstElementItem)/itemsPerElement;
    FOR i: CARDINAL IN [0..elementToRemove) DO
      temp[i] ← refs[i];
      ENDLOOP;
    FOR i: CARDINAL IN [elementToRemove+1..count-1) DO
      temp[i-1] ← refs[i];
      ENDLOOP;
    zone.FREE [refs]; -- not @refs!
    refs↑ ← temp;
    FormWindow.SetChoiceItemValue [window: window, item: item-2, 
      newValue: FormWindow.GetChoiceItemValue [window, 
        FormItems.path.ORD + ((count-1) * itemsPerElement)],
      repaint: FALSE];
    rb ← FormWindow.LookAtTextItemValue [window, 
      FormItems.name.ORD + ((count-1) * itemsPerElement)];
    FormWindow.SetTextItemValue [window: window, item: item-1, 
      newValue: @rb, repaint: FALSE];
    FormWindow.DoneLookingAtTextItemValue [window, FormItems.name.ORD + ((count-1) * itemsPerElement)];
    FormWindow.DestroyItem [window, FormItems.path.ORD + ((count-1) * itemsPerElement), FALSE]; -- path
    FormWindow.DestroyItem [window, FormItems.name.ORD + ((count-1) * itemsPerElement), FALSE]; -- name
    FormWindow.DestroyItem [window, FormItems.remove.ORD + ((count-1) * itemsPerElement), FALSE]; -- me
    FormWindow.Repaint [window];
    };
    
  ElementChanged: FormWindow.ChoiceChangeProc = {
    refs: RefsHandle ← FormWindow.GetClientData [window];
    ith: CARDINAL = (item - firstElementItem)/itemsPerElement;
    IF calledBecauseOf = client THEN RETURN;
    SELECT newValue FROM 
      0 => {
        refs[ith] ← StarDesktop.GetCurrentDesktopFile [];
	IF oldValue = 2 THEN 
	  FormWindow.SetVisibility [window, item+1, invisible]};
      1 => {
        refs[ith] ← sysRef;
	IF oldValue = 2 THEN 
	  FormWindow.SetVisibility [window, item+1, invisible]};
      ENDCASE => {
        v: Selection.Value;
	ref: LONG POINTER TO NSFile.Reference;
	namerb: XString.ReaderBody;
	attrRec: NSFile.AttributesRecord;
	fh: NSFile.Handle;
	Complain: PROCEDURE = {
          rb: XString.ReaderBody ← XString.FromSTRING ["Please select a folder before bugging ""Other Folder"""L];
	  Attention.Post [@rb];
	  FormWindow.SetChoiceItemValue [window, item, oldValue];
	  };
	IF ~Selection.CanYouConvert [file] THEN {Complain[]; RETURN};
	v ← Selection.Convert [file];
	IF v.value = NIL THEN {Complain[]; Selection.Free [@v]; RETURN};
	ref ← v.value;
	refs[ith] ← ref↑;
        fh ← NSFile.OpenByReference [ref↑];
        NSFile.GetAttributes [fh, [[pathname: TRUE]], @attrRec];
	namerb ← XString.FromNSString [attrRec.pathname];
        NSFile.ClearAttributes [@attrRec];
	NSFile.Close [fh];
	FormWindow.SetTextItemValue [window, item+1, @namerb, FALSE];
	FormWindow.SetVisibility [window, item+1, visible];
	Selection.Free [@v];
        };
    };
    
  DoLayout: FormWindow.LayoutProc = {
    ith: CARDINAL ← 0;
    tabChoice: fixed FormWindow.TabStops = [fixed[50]];
    spaceAboveLine: CARDINAL = 10;
    m: CARDINAL = 5;
    line: FormWindow.Line;
    
    EachElement: MFileOnNSFile.EachElementProc = {
      LayoutElementItems [ith, window];
      ith ← ith + 1;
      };

    FormWindow.SetTabStops[window: window, tabStops: tabChoice]; 
    line ← FormWindow.AppendLine [window, spaceAboveLine];
    FormWindow.AppendItem [window, FormItems.add.ORD, line, m];
    MFileOnNSFile.EnumeratePath [EachElement];
    };

  DoIt: PropertySheet.MenuItemProc = {
    refs: RefsHandle ← clientData;
    toolW ← NIL;
    IF menuItem = done THEN
      MFileOnNSFile.SetPath [DESCRIPTOR[refs] !
        MFileOnNSFile.BadPath => GOTO NoGood];
    zone.FREE [refs];
    zone.FREE [@refs];
    RETURN[ok: TRUE];
    EXITS NoGood => {
      rb: XString.ReaderBody ← XString.FromSTRING ["Bad path"L];
      Attention.Post [@rb];
      RETURN [ok: FALSE]};
    };
    
  GetSysRef: PROCEDURE RETURNS [ref: NSFile.Reference] = {
    fh: NSFile.Handle ← Catalog.Open [BWSFileTypes.systemFileCatalog];
    ref ← NSFile.GetReference [fh];
    NSFile.Close [fh];
    };
    
  UserIsLoggedOn: PROCEDURE RETURNS [BOOLEAN] = {
    RETURN[StarDesktop.GetCurrentDesktopFile[] # NSFile.nullReference]};
    
  SetUpWaitForLogonEvent: PROCEDURE = {
    [] ← Event.AddDependency [ReadOptionFile, NIL, StarDesktop.logon]};
    
  ReadOptionFile: Event.AgentProcedure = {
    section: XString.ReaderBody ← XString.FromSTRING ["SearchPath"L];
    entry: XString.ReaderBody ← XString.FromSTRING ["Element"L];
    elements: CARDINAL ← 0;
    LocalSeq: TYPE = RECORD [SEQUENCE COMPUTED CARDINAL OF NSFile.Reference];
    refs: LONG POINTER TO LocalSeq ← NIL;
    fh: NSFile.Handle;
    nssElement: NSString.String;
    
    Countem: PROCEDURE [element: XString.Reader] = {
      elements ← elements + 1};
    
    EachElement: PROCEDURE [element: XString.Reader] = {
      nssElement ← XString.NSStringFromReader [element, zone];
      fh ← NSFile.OpenByName [NSFile.nullHandle, nssElement !
        NSFile.Error => GOTO BadElement];
      refs[elements] ← NSFile.GetReference [fh];
      NSFile.Close [fh];
      NSString.FreeString [zone, nssElement];
      elements ← elements + 1;
      EXITS BadElement => {
        rb: XString.ReaderBody ← XString.FromSTRING ["Bad element in UserProfile: "L];
        Attention.Post[@rb];
	Attention.Post[element, FALSE];
	NSString.FreeString [zone, nssElement]};
      };
     
    -- Sorta grungy, but easy. Go through elements once just to count them.
    DO
      OptionFile.GetStringValue [@section, @entry, Countem, elements !
        OptionFile.Error => EXIT];
      ENDLOOP;
    IF elements = 0 THEN {
      refs ← zone.NEW[LocalSeq[1]];
      refs[0] ← StarDesktop.GetCurrentDesktopFile[];
      elements ← 1 }
    ELSE {
      refs ← zone.NEW[LocalSeq[elements]];
      -- Now again to get the values.
      elements ← 0;
      DO
	OptionFile.GetStringValue [@section, @entry, EachElement, elements !
	  OptionFile.Error => EXIT];
	ENDLOOP;
      };
    MFileOnNSFile.SetPath [DESCRIPTOR[refs, elements] !
      MFileOnNSFile.BadPath => {
        rb: XString.ReaderBody ← XString.FromSTRING ["Bad path"L];
        Attention.Post [@rb];
	CONTINUE}];
    zone.FREE [@refs];
    };

  -- Main line code
    
  Init[];
  
  END.