############################################################################
#
#       File:     file_dlg.icn
#
#       Subject:
#
#       Author:   Robert Parlett
#
#       Date:     October, 1998
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#   File dialog box
#
#
############################################################################
#
#  Links:
#
############################################################################

$ifdef _MS_WINDOWS_NT
$define PATHCHAR "\\"
$else
$define PATHCHAR "/"
$endif

############################################################################
#
# File dialog class
#
class FileDialog : _Dialog(
   init_dir,                # Initial directory name
   init_file,               # Initial file name
   res,                     # Resulting file path
   dir,                     # TextField directory
   file,                    # TextField filename
   dlist,                   # TextList of directories
   flist,                   # TextList of files
   okay,                    #
   cancel,                  #
   extra_attribs            # Custom attributes
   )

   #
   # Extra attributes set by caller.
   #
   method set_extra_attribs(l)
      return self.extra_attribs := l
   end

   #
   # Get the directory part of the result
   #
   method get_directory()
      return directory_name(\self.res)
   end

   #
   # Get the result, (will fail if cancel was pressed).
   #
   method get_result()
      return \self.res
   end

   #
   # Set the initial directory.
   #
   method set_directory(s)
      return self.init_dir := s
   end

   #
   # Set the initial file
   #
   method set_file(s)
      return self.init_file := s
   end

   #
   # Set the initial file/directory from a whole path.
   #
   method set_path(s)
      self.init_dir := directory_name(s)
      self.init_file := file_name(s)
      return s
   end

   #
   # Set the result
   #
   method set_result()
      self.res := self$get_std_dir() || file$get_contents()
   end

   #
   # Get the directory TextField contents standardized with a trailing /
   #
   method get_std_dir()
      s := dir$get_contents()
      if (s[-1] ~== PATHCHAR) then
         s ||:= PATHCHAR

      return s
   end

   method dialog_event(ev)
      case ev$get_component() of {
         cancel : {
            if ev$get_code() > 0 then
               self$dispose()
         }

         okay :  {
            if ev$get_code() > 0 then {
               self$set_result()
               self$dispose()
            }
         }

         file :  {
            #
            # If return pressed in file TextField, same as okay
            #
            if ev$get_code() = 0 then {
               self$set_result()
               self$dispose()
            }
         }

         dlist : {
            if ev$get_code() > 0 then {
               #
               # Clicked in the directory list; get the item clicked on.
               #
               value := dlist$get_contents()[dlist$get_selections()[1]]
               s := self$get_std_dir()
               #
               # Go to parent directory (unless at root directory)
               #
               if value == (".." || PATHCHAR) then {
                  if s ~== PATHCHAR then {
                     s[-1] := ""
                     while s[-1] ~== PATHCHAR do s[-1] := ""
                  }
                  dir$set_contents(s)
               } else
                  dir$set_contents(s ||:= value)
               #
               # Update directory and file lists.
               #
               l1 := []
               l2 := []
               get_directory_list(s, l1, l2)
               dlist$set_no_updates()
               dlist$set_contents(l1)
               dlist$goto_pos(1, 0)
               dlist$clear_selections()
               dlist$clear_no_updates()
               flist$set_no_updates()
               flist$set_contents(l2)
               flist$goto_pos(1, 0)
               flist$clear_selections()
               flist$clear_no_updates()
               file$set_contents("")
            }
         }

         flist : {
            if ev$get_code() > 0 then {
               #
               # Clicked in file list; set TextField
               #
               file$set_contents(flist$get_contents()[flist$get_selections()[1]])
            }
         }

         dir : {
            if ev$get_code() = 0 then {
               #
               # Return pressed in directory TextField; update lists.
               #
               l1 := []
               l2 := []
               dir$set_contents(s := self$get_std_dir())
               get_directory_list(s, l1, l2)
               dlist$set_no_updates()
               dlist$set_contents(l1)
               dlist$goto_pos(1, 0)
               dlist$clear_selections()
               dlist$clear_no_updates()
               flist$set_no_updates()
               flist$set_contents(l2)
               flist$goto_pos(1, 0)
               flist$clear_selections()
               flist$clear_no_updates()
               file$set_contents("")
            }
         }
      }
   end

   method init_dialog()
      self$set_focus(file)
   end

   method component_setup()
      #
      # Defaults if none set by caller.
      #
      if /init_dir | (init_dir == "") then {
         init_dir := chdir()
#ifdef _UNIX
         if /init_dir & pd := open("pwd", "pr") then {
            init_dir := read(pd)
            close(pd)
            }
#endif
      }
      /init_dir := PATHCHAR
      /init_file := ""

      if (init_dir[-1] ~== PATHCHAR) then init_dir ||:= PATHCHAR

      self$set_attribs_list(["size=500,450", "resize=on"] ||| self.extra_attribs)

      l := Label()
      l$set_label("Directory")
      l$set_pos(50, 50)
      l$set_align("l", "c")
      self$add(l)

      dir := TextField()
      dir$set_pos(150, 50)
      dir$set_size("100%-200")
      dir$set_align("l", "c")
      dir$set_contents(init_dir)
      self$add(dir)

      l := Label()
      l$set_label("File")
      l$set_pos(50, 100)
      l$set_align("l", "c")
      self$add(l)

      file := TextField()
      file$set_pos(150, 100)
      file$set_size("100%-200")
      file$set_align("l", "c")
      file$set_contents(init_file)
      self$add(file)

      l1 := []
      l2 := []
      get_directory_list(init_dir, l1, l2)

      dlist := TextList()
      dlist$set_select_one()
      dlist$set_pos(50, 150)
      dlist$set_size("50%-75", "100%-250")
      dlist$set_contents(l1)
      self$add(dlist)

      flist := TextList()
      flist$set_select_one()
      flist$set_pos("50%+25", 150)
      flist$set_size("50%-75", "100%-250")
      flist$set_contents(l2)
      self$add(flist)

      okay := TextButton()
      okay$set_label("Okay")
      okay$set_pos("33%", "100%-50")
      okay$set_align("c", "c")
      self$add(okay)

      cancel := TextButton()
      cancel$set_label("Cancel")
      cancel$set_pos("66%", "100%-50")
      cancel$set_align("c", "c")
      self$add(cancel)
   end

   initially
      self$_Dialog.initially()
      self.extra_attribs := []
end

#
# Read a directory.
#
procedure get_directory_list(s, dir_list, file_list)
   /fatal_error := stop
$ifdef _MS_WINDOWS_NT
   s := map(s,"/","\\")
   if (*s > 1) & (s[-1] == PATHCHAR) & (map(s,&letters,repl("a",52)) ~== "a:\\") then
      s[-1] := ""
$else
   if (*s > 1) & (s[-1] == PATHCHAR) then s[-1] := ""
$endif

   p := open(s) | {
       write(&errout, "get_directory_list: can't open ", image(s))
       fail
   }
   if not (s[-1] == PATHCHAR) then s ||:= PATHCHAR
   while s2 := read(p) do {
      sr := stat(s||s2) | {
          write(&errout, "get_directory_list: can't stat ", image(s2))
          fail
      }
      if sr.mode[1] == "d" then
         put(dir_list, s2 || PATHCHAR)
      else
         put(file_list, s2)
      }
   close(p)
   return
end

#
# Return the directory name of the file name s, including the trailing /
#
procedure directory_name(s)
   local i
   every i := find(PATHCHAR, s)
   return s[1:\i + 1] | ""
end

#
# Return the file name of s
#
procedure file_name(s)
   local i
   every i := find(PATHCHAR, s)
   return s[\i + 1:0] | s
end

