subroutine gtl_zoom(line,error)
  use gtv_buffers
  use gtv_graphic
  use gtv_dependencies_interfaces
  use gtv_interfaces, except_this=>gtl_zoom
  use gtv_protocol
  use gtv_tree
  use gtv_types
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  !   Support for command:
  ! GTVL\ZOOM [Off|Ax Bx Ay By]
  !
  !   Map mouse button events (^, &, and * are returned by x_curs()) to
  ! keyboard events (" ", "Z", and "E", respectively).
  ! ---
  !  Thread status: safe for reading from main thread
  !---------------------------------------------------------------------
  character(len=*), intent(in)  :: line   !
  logical,          intent(out) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ZOOM'
  real :: ax,ay,bx,by
  integer*4 :: nc,n,nd,sever
  integer, parameter :: nwhat=2
  character(len=*), parameter :: what(nwhat) = (/ 'OFF    ','REFRESH' /)
  character(len=12) :: keyw,argum
  character(len=80) :: gpname
  type(gt_display), pointer :: main_output
  type(gt_directory), pointer :: dir_zoom
  !
  ! Non-sense if protocol is other than X
  if (cw_device%protocol.ne.p_x) then
    call gtv_message(seve%e,rname,'Available only with X device')
    error = .true.
    return
  endif
  !
  error = gterrtst()
  error = .false.
  !
  call gtv_open_segments_for_reading_from_main()
  !
  if (cw_directory%x%nbwin.eq.0) then
    ! No window here, zoom in first window of grand-pere
    dir_zoom => cw_directory%ancestor
    call cree_chemin_dir(dir_zoom,gpname,nd)
    call gtv_message(seve%i,rname,'Zooming in directory '//gpname)
    !
    if (dir_zoom%x%nbwin.eq.0) then
      call gtv_message(seve%e,rname,'No window found')
      error = .true.
      goto 100
    endif
  else
    ! Zoom in first window of current working directory
    dir_zoom => cw_directory
  endif
  !
  ! Get the instance of the window we zoom in
  call get_slot_output_by_num(dir_zoom,dir_zoom%x%curwin,main_output,error)
  ! Do not return here
  !
100 continue
  !
  call gtv_close_segments_for_reading_from_main()
  !
  if (error)  return
  !
  if (sic_narg(0).eq.4) then
    ! ZOOM X1 X2 Y1 Y2  Defines the viewing window by its corners
    call sic_r4 (line,0,1,ax,.false.,error)
    if (error) return
    call sic_r4 (line,0,2,bx,.false.,error)
    if (error) return
    call sic_r4 (line,0,3,ay,.false.,error)
    if (error) return
    call sic_r4 (line,0,4,by,.false.,error)
    if (error) return
    !
    call new_zoom_window(dir_zoom,main_output,0,0,ax,bx,ay,by,error)
    if (error)  return
    return
    !
  elseif (sic_present(0,1)) then
    ! ZOOM OFF
    call sic_ke (line,0,1,argum,nc,.false.,error)
    if (error) return
    call sic_ambigs('ZOOM',argum,keyw,n,what,nwhat,error)
    if (error) return
    !
    if (keyw.eq.'OFF') then
      ! ZOOM OFF
      call clear_zoom_win(dir_zoom,error)
      !
    elseif (keyw.eq.'REFRESH') then
      ! ZOOM REFRESH
      if (strict2011) then
        sever = seve%e
      else
        sever = seve%w
      endif
      call gtv_message(sever,rname,'ZOOM REFRESH is obsolete, use REFRESH instead')
      call gtv_message(sever,rname,'Windows usually know by themselves when refresh is needed')
      if (strict2011) then
        error = .true.
        return
      endif
    endif
    return
    !
  else
    ! Interactive zoom with cursor
    call gti_zoom_interactive(dir_zoom,main_output,error)
    if (error)  return
    !
  endif
  !
end subroutine gtl_zoom
!
subroutine gti_zoom_interactive(dir,main_output,error)
  use gildas_def
  use gbl_message
  use gtv_dependencies_interfaces
  use gtv_interfaces, except_this=>gti_zoom_interactive
  use gtv_protocol
  use gtv_types
  !---------------------------------------------------------------------
  ! @ private
  ! Create the ZOOM window with the cursor
  !---------------------------------------------------------------------
  type(gt_directory)             :: dir          ! Directory where we perform the zoom
  type(gt_display), intent(in)   :: main_output  ! Window where we perform the zoom
  logical,         intent(inout) :: error        ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ZOOM'
  real*4 :: ax,ay,bx,by,sx,sy,xcurse,ycurse,zoom
  character(len=1) :: c
  real, parameter :: f=1.414
  real*4 :: rect_xw,rect_yw
  character(len=commandline_length) :: line
  character(len=1), parameter :: backslash=char(92)
  !
  save xcurse,ycurse
  data xcurse/0.0/, ycurse/0.0/
  !
  if (.not.gtg_curs_sub(main_output%dev)) then
    call gtv_message(seve%e,rname,'No cursor available')
    error = .true.
    return
  endif
  !
  if (main_output%x%is_zoom) then
    call gtv_message(seve%e,rname,'Can not zoom in a zoom window')
    error = .true.
    return
  endif
  !
  ! Loop over commands
  call gtg_screen_sub(main_output,ax,bx,ay,by)
  error = gterrtst()
  if (error) return
  !
  zoom = 1.
  sx = (bx-ax)*0.5
  sy = (by-ay)*0.5
  !
  do
    rect_xw = sx*zoom  ! Rectangle X half-width (world units)
    rect_yw = sy*zoom  ! Rectangle Y half-width (world units)
    call gicurs_sub(main_output,xcurse,ycurse,rect_xw,rect_yw,c)
    error = gterrtst()
    if (error) return
    !
    call sic_upper(c)
    if (c.eq.'0') then
      ! Restore original plot
      call gtwindow(main_output,0.,dir%phys_size(1),0.,dir%phys_size(2))
      call gtg_screen_sub(main_output,ax,bx,ay,by)
      sx = (bx-ax)*0.5
      sy = (by-ay)*0.5
      zoom = 1.0
      !
    elseif (c.eq.'Z' .or. c.eq.'&') then
      ! Zooming
      zoom = zoom/f
      !
    elseif (c.eq.'-') then
      ! Unzoom
      zoom = zoom*f
      !
    elseif (c.eq.' ' .or. c.eq.'^') then
      ! Execute buffered commands
      ax = xcurse - sx*zoom
      bx = xcurse + sx*zoom
      ay = ycurse - sy*zoom
      by = ycurse + sy*zoom
      !
      ! Define clipping area and loop on next command
      call new_zoom_window(dir,main_output,0,0,ax,bx,ay,by,error)
      if (error)  return
      !
    elseif (c.eq.'H') then
      ! HELP ZOOM
      line = 'SIC'//backslash//'HELP ZOOM'
      call exec_command(line,error)
      if (error)  return
      !
    elseif (c.eq.'E' .or. c.eq.'*') then
      exit
      !
    endif
    !
  enddo
  !
end subroutine gti_zoom_interactive
!
subroutine new_zoom_window(dir,main_output,width,height,px1,px2,py1,py2,error)
  use gtv_buffers
  use gtv_interfaces, except_this=>new_zoom_window
  use gtv_protocol
  use gtv_types
  use gtv_tree
  use gtv_types
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  !  Open or reuse a zoom window attached to the input directory.
  ! Default size is 2/3 of the window we zoom in.
  ! ---
  !  Thread status: safe to call from main thread.
  !---------------------------------------------------------------------
  type(gt_directory)              :: dir          ! Directory where we attach the zoom window
  type(gt_display), intent(in)    :: main_output  ! Number of the window we zoom in
  integer(kind=4),  intent(in)    :: width        ! Zoom window width (0 if default)
  integer(kind=4),  intent(in)    :: height       ! Zoom window height (0 if default)
  real(kind=4),     intent(in)    :: px1,px2      ! X range for zoom window (world unit)
  real(kind=4),     intent(in)    :: py1,py2      ! Y range for zoom window (world unit)
  logical,          intent(inout) :: error        ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ZOOM'
  logical :: reuse,new,found
  integer :: larg,haut,zoom_win_num
  type(gt_display), pointer :: zoom_output
  !
  found = .false. !
  reuse = .true.  ! Currently it is not possible to define more than 1 zoom
                  ! window per directory. May be customized some day.
  new = .true.    ! By default zoom window will be another one added at end
                  ! of list
  !
  if (reuse) then
    call gtv_open_segments_for_writing_from_main()
    !
    call get_zoom_win(dir,zoom_win_num,found)
    if (found) then
      ! Reuse the previous zoom window
      call get_slot_output_by_num(dir,zoom_win_num,zoom_output,error)
      if (.not.error)  new = .false.
    endif
    !
    call gtv_close_segments_for_writing_from_main()
  endif
  !
  if (new) then
    ! No previous window reused, create a new one.
    !
    ! Size?
    if (height.eq.0 .or. width.eq.0) then
      call x_size(main_output%x%graph_env,larg,haut)
      larg = (2*larg)/3
      haut = (2*haut)/3
    else
      larg = width
      haut = height
    endif
    !
    ! Create. The zoom window will be attached to the output instances list:
    call get_free_slot_output(zoom_output,error)
    if (error)  return
    !
    zoom_output%dev => main_output%dev  ! Point on the shared X-protocol instance
    zoom_output%color = .true.
    zoom_output%background = main_output%background  ! Use the main window color
    call x_display_reset(zoom_output%x)  ! Default values
    zoom_output%x%geometry%x = larg
    zoom_output%x%geometry%unitx = 'p'
    zoom_output%x%geometry%y = haut
    zoom_output%x%geometry%unity = 'p'
    zoom_output%x%name = 'ZOOM'
    call create_window(zoom_output,.false.,dir,.false.,.true.,error)
    zoom_output%x%is_zoom = .true.
    !
  endif
  !
  if (error) then
    call gtv_message(seve%e,rname,'Cannot create or reuse zoom window')
    return
  endif
  !
  call gtwindow(zoom_output,px1,px2,py1,py2)  ! Set clipping area and redraw
  !
end subroutine new_zoom_window
!
subroutine get_zoom_win(dir,win_num,zoomfound)
  use gildas_def
  use gtv_buffers
  use gtv_interfaces, except_this=>get_zoom_win
  use gtv_protocol
  use gtv_types
  !---------------------------------------------------------------------
  ! @ private
  !  Get the last zoom window number of input directory.
  ! ---
  !  Thread status: not thread safe. Calling routine must be thread
  ! safe for reading.
  !---------------------------------------------------------------------
  type(gt_directory)           :: dir        ! Input directory
  integer(kind=4), intent(out) :: win_num    ! Zoom window number
  logical,         intent(out) :: zoomfound  ! Found one zoom window?
  ! Local
  integer(kind=address_length) :: graph_env
  integer :: iwin
  logical :: error,found
  type(gt_display), pointer :: output
  !
  error = .false.
  zoomfound = .false.
  !
  do iwin=dir%x%nbwin-1,0,-1
    graph_env = c_get_win_genv(dir%x%genv_array,iwin)
    call get_slot_output_by_genv(graph_env,output,.true.,found,error)
    if (error)  exit  ! iwin loop
    if (output%x%is_zoom) then
      win_num = iwin
      zoomfound = .true.
      exit  ! iwin loop
    endif
  enddo
  !
end subroutine get_zoom_win
!
subroutine clear_zoom_win(dir,error)
  use gtv_interfaces, except_this=>clear_zoom_win
  use gtv_types
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  !  Clear the last zoom window of input directory. Nothing done if
  ! no zoom window is attached.
  !---------------------------------------------------------------------
  type(gt_directory)                :: dir    ! Input directory
  logical,            intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ZOOM'
  integer*4 :: win_num
  logical :: found
  !
  call gtv_open_segments_for_writing_from_main()
  !
  call get_zoom_win(dir,win_num,found)
  !
  if (found) then
    call win_destroy_one(dir,win_num,error)
  else
    call gtv_message(seve%w,rname,'No zoom window found, nothing done')
  endif
  !
  call gtv_close_segments_for_writing_from_main()
  !
end subroutine clear_zoom_win
