i21sys/Mod/Windows.odc

MODULE i21sysWindows; (**  **)
(** Для Информатики-21: обслуживание окон документов в Блэкбоксе. **)
(* 2009-10-09 Ф.В.Ткачев, полностью переделано. *)
(* 2009-03-17 Ф.В.Ткачев 
-- все ссылки на Win API переведены на псевдо модуль WinApi
-- убраны IsOpen, Close, LogClose
-- уточнена процедура SetPos
-- экспортирована процедура ThisWindows
*)
(* 2008-02-16 Ф.В.Ткачев, переименования и мелкая редакция. *)
(** Finding, positioning, setting size of windows. **)
(** 2001-08-03 Modifications for BlackBox 1.4: winTitle for Log window now is "#Dev:Log" (formerly "Log") **)

	IMPORT  Log := StdLog,  Views,  Windows,  HostWindows,  WinApi,  Ports,  Stores;
	
	CONST
		minimized = 0;  maximized = 1;  (* Window states *)
	
	TYPE
		State* = RECORD (* позиция окна (с рамками и т.п.) на экране в пикселях *)
			l, t, r, b: INTEGER;
			state: SET
		END;
	
	PROCEDURE GetState* ( v: Views.View;  OUT s: State );
		VAR  win: Windows.Window;   
			wh: WinApi.HANDLE;  ret: WinApi.BOOL;  wp: WinApi.WINDOWPLACEMENT;  rect: WinApi.RECT;
	BEGIN
		win := Windows.dir.First();
		WHILE ( win # NIL ) & ( win.doc.ThisView() # v ) DO
			win := Windows.dir.Next( win );
		END;
		ASSERT( win # NIL );
		wh := win( HostWindows.Window ).wnd;
		ret := WinApi.GetWindowPlacement( wh, wp ); (* relative position in app window *)
		s.l := wp.rcNormalPosition.left;
		s.t := wp.rcNormalPosition.top;
		s.r := wp.rcNormalPosition.right;
		s.b := wp.rcNormalPosition.bottom;
		s.state := {};
		IF  WinApi.IsIconic	( wh ) = 1  THEN  INCL( s.state, minimized  )  END;
		IF  WinApi.IsZoomed ( wh ) = 1  THEN  INCL( s.state, maximized )  END;
	END GetState;

	PROCEDURE SetState* ( v: Views.View;  s: State );
		VAR  win: Windows.Window;
			wh: WinApi.HANDLE;  ret: WinApi.BOOL;  x, y, w, h: INTEGER;
	BEGIN
		win := Windows.dir.First();
		WHILE ( win # NIL ) & ( win.doc.ThisView() # v ) DO
			win := Windows.dir.Next( win );
		END;
		IF  win # NIL  THEN
			wh := win( HostWindows.Window ).wnd;
			x := s.l;  w := s.r - s.l;  
			y := s.t;  h := s.b - s.t;
			ret := WinApi.MoveWindow( wh, x, y, w, h, 1 );
		END;
	END SetState;
	
	PROCEDURE ExternalizeState* ( wr: Stores.Writer;  s: State );
	BEGIN
		wr.WriteInt( s.l );
		wr.WriteInt( s.t );
		wr.WriteInt( s.r );
		wr.WriteInt( s.b );
		wr.WriteSet( s.state );
	END ExternalizeState;
	
	PROCEDURE InternalizeState* ( rd: Stores.Reader;  OUT s: State );
	BEGIN
		rd.ReadInt( s.l );
		rd.ReadInt( s.t );
		rd.ReadInt( s.r );
		rd.ReadInt( s.b );
		rd.ReadSet( s.state );
	END InternalizeState;
	
	(** все экспортированные процедуры работают с передним окном **)
	
	PROCEDURE SetSize* ( w, h: INTEGER (*пиксели; внутр. размеры*) );
		VAR  win: Windows.Window;
	BEGIN
		win := Windows.dir.First();
		IF  win # NIL  THEN  win.SetSize( w, h )  END
	END SetSize;  (*"i21sysWindows.SetSize(300,200)" *)

	PROCEDURE WinSetPos ( win: Windows.Window;  x, y: INTEGER );
		VAR  w, h: INTEGER;
			wh: WinApi.HANDLE;  wp: WinApi.WINDOWPLACEMENT;  rect: WinApi.RECT;  ret: WinApi.BOOL;
	BEGIN
		wh := win( HostWindows.Window ).wnd;
		ret := WinApi.GetWindowRect( wh, rect ); (* rect := window's bounds, in pixels *)
		(* абсолютный внешний размер в пикселях: *) 
		w := rect.right - rect.left ;  h := rect.bottom - rect.top ;
		ret := WinApi.MoveWindow( wh, x, y, w, h, 1 ); (* позиция в реально видимой части экрана *)
	END WinSetPos;
	
	PROCEDURE SetPos* ( x, y: INTEGER ); (* позиция в реально видимой части экрана *)
		VAR  win: Windows.Window;
	BEGIN
		win := Windows.dir.First();
		IF  win # NIL  THEN  WinSetPos( win, x, y )  END;
	END SetPos;  (*"i21sysWindows.SetPos(0,0)" *)
	
	PROCEDURE AlignTopLeft*; (* вызывается из меню; передвигает переднее окно *)
		VAR  win: Windows.Window;
	BEGIN
		win := Windows.dir.First();
		IF  win # NIL  THEN  WinSetPos( win, 0, 0 )  END;
	END AlignTopLeft;   (*i21sysWindows.AlignTopLeft *)
	
	PROCEDURE WinAlignTopRight ( win: Windows.Window );
		VAR  r, b: INTEGER;  x, y, w, h: INTEGER;
			wh: WinApi.HANDLE;  wp: WinApi.WINDOWPLACEMENT;  rect: WinApi.RECT;  ret: WinApi.BOOL;
	BEGIN
		wh := win( HostWindows.Window ).wnd;
		ret := WinApi.GetWindowPlacement( wh, wp ); (* relative position in app window *)
		rect := wp.rcNormalPosition;
		w := rect.right - rect.left ;  h := rect.bottom - rect.top ;
		x := HostWindows.mainW - w;  y := 0;
		ret := WinApi.MoveWindow( wh, x, y, w, h, 1 );
	END WinAlignTopRight;

	PROCEDURE AlignTopRight*; (* вызывается из меню; передвигает переднее окно *)
		VAR  win: Windows.Window;
	BEGIN
		win := Windows.dir.First();
		IF  win # NIL  THEN  WinAlignTopRight( win )  END;
	END AlignTopRight;  (* i21sysWindows.AlignTopRight *)
		
	PROCEDURE ThisWindow* ( winTitle: Views.Title ): Windows.Window;
		VAR  win: Windows.Window;  t: Views.Title;
	BEGIN
		t := '';
		win := Windows.dir.First();
		LOOP (* схема линейного поиска *)
			IF  win = NIL  THEN  EXIT  END;
			win.GetTitle( t );
			IF  t = winTitle  THEN  EXIT  END;
			win := Windows.dir.Next( win )
		END;
		ASSERT( ( win = NIL ) OR ( t = winTitle ), 60 );
		RETURN  win
	END ThisWindow;

END i21sysWindows.
	
	
	PROCEDURE AlignTopRightWin ( win: Windows.Window );  (* старая версия *)
		VAR  l, t, r, b: INTEGER;  x, y, w, h: INTEGER;
			wh: WinApi.HANDLE;  wp: WinApi.WINDOWPLACEMENT;  rect: WinApi.RECT;  ret: WinApi.BOOL;
	BEGIN
		wh := win( HostWindows.Window ).wnd;
		(* внутренние границы окна Блэкбокса: *);
		HostWindows.GetMainBorder( l, t, r, b );
		ret := WinApi.GetWindowPlacement( wh, wp ); (* relative position in app window *)
		rect := wp.rcNormalPosition;
		w := rect.right - rect.left ;  h := rect.bottom - rect.top ;
		x := r - w;  y := t;
		ret := WinApi.MoveWindow( wh, x, y, w, h, 1 );
	END AlignTopRightWin;
	
	PROCEDURE SetPos* ( winTitle: Views.Title;  x, y: INTEGER (*pixels*) );
		VAR  win: Windows.Window;
	BEGIN
		win := ThisWindow( winTitle );
		IF  win # NIL  THEN  SetPosWin( win, x, y )  END;
	END SetPos;
	
	PROCEDURE SetSize* ( winTitle: Views.Title;  w, h: INTEGER (*пиксели; внутр. размеры*) );
		VAR  win: Windows.Window;
	BEGIN
		win := ThisWindow( winTitle );
		IF  win # NIL  THEN  win.SetSize( w, h )  END
	END SetSize;

	PROCEDURE LogTopLeft*;
	BEGIN  SetPos( "#Dev:Log", 0, 0 );
	END LogTopLeft; (*i21sysWindows.LogTopLeft*)

	PROCEDURE LogTopRight*;
	BEGIN
		AlignTopRight( '#Dev:Log' );
	END LogTopRight; (*i21sysWindows.LogTopRight*)
	(*"i21sysWindows.LogTopLeft;i21sysWindows.LogTopRight" --проверка, что размер не меняется*)

(*
	PROCEDURE CorrectMinimized; (* это делает HostMenus.ArrangeIcons *)
	(* решается ОС-ная проблема: при разворачивании на (или из) два экрана размер внутреннего поля Блэкбокса меняется -- положения минимизированных окон становятся некорректными. Поправление: каждое минимизированное окно сначала восстанавливается, потом снова минимизируется. *)
		VAR  win: Windows.Window;  wh: WinApi.HANDLE;  ret: INTEGER;
			aw: POINTER TO ARRAY OF Windows.Window;  n: INTEGER;
	BEGIN
		n := 0;
		win := Windows.dir.First();
		WHILE  win # NIL  DO  INC( n );  win := Windows.dir.Next( win )  END;
		NEW( aw, n );
		
		n := 0;
		win := Windows.dir.First();
		WHILE  win # NIL  DO  aw[ n ] := win;  INC( n );  win := Windows.dir.Next( win )  END;
		
		FOR  n := 0  TO  LEN( aw ) - 1  DO (* while как выше >> бесконечный цикл *)
			wh := aw[ n ]( HostWindows.Window ).wnd;
			IF  WinApi.IsIconic( wh ) = 1  THEN
				ret := WinApi.ShowWindow( wh, 1 ); (* восстановить окно *)
				ret := WinApi.ShowWindow( wh, 6 ); (* снова минимизировать окно *)
			END;
		END;
	END CorrectMinimized; (* i21sysWindowsTests.CorrectMinimized *)
 CorrectMinimized *)