i21sys/Mod/UnicodeTools.odc

MODULE i21sysUnicodeTools;
(** инструменты в помощь преобразованию [частей] документов в Юникод и обратно **)
(** "StdCmds.OpenToolDialog('i21sys/Rsrc/UnicodeTools','EASCII <> Юникод')" **)

	IMPORT  Log := StdLog, In := i21sysIn, i21sysCyrillic,
		Stores,  Models,  Dialog, Strings, Views, TextModels, TextViews, TextControllers;
	
	TYPE String = ARRAY 1000 OF CHAR;
	
	PROCEDURE ExamineSelection*;
		VAR  ch: CHAR;  s, p: ARRAY 100 OF CHAR;
	BEGIN
		In.OpenSelection;
		In.Char(ch);
		WHILE In.done DO
			Strings.IntToStringForm( ORD(ch), Strings.hexadecimal, 6, Strings.digitspace, Strings.showBase, s );
			IF  ch < 80X THEN  p := '(pure ASCII)'
			ELSIF ch < 100X THEN  p := '(Extended ASCII)'
			ELSIF (400X <= ch) & (ch <= 527X) THEN  p := '(Unicode Cyrillic)'
			ELSE  p := '(Unicode, non-Cyrillic)'
			END;
			Log.Char(ch);  Log.Tab;  Log.String(s);  Log.Tab;  Log.String(p);  Log.Ln;
			In.Char(ch);
		END;
	END ExamineSelection; (* i21sysUnicodeTools.ExamineSelection *)

	PROCEDURE ToEASCII (t: TextModels.Model;  beg, end: INTEGER); (* преобразуем текст *)
		VAR  rd: TextModels.Reader;  wr: TextModels.Writer;  ch: CHAR;  pos: INTEGER;
			script: Stores.Operation;
	BEGIN
		end := MIN(end, t.Length());
		rd := t.NewReader(NIL);  
		rd.SetPos(beg);
		wr := t.NewWriter(NIL);
		
		Models.BeginScript (t, "Convert to EASCII", script); (* все действия до EndScript будут отменяться как целое при нажатии Ctrl+Z *)
		rd.Read;
		WHILE ~rd.eot & (rd.Pos() <= end) DO
			IF rd.view = NIL THEN
				ch := i21sysCyrillic.ExtendedAsciiOf( rd.char );
				IF ch # rd.char THEN (* заменить литеру *)
					pos := rd.Pos();
					wr.SetPos( pos );
					wr.WriteChar( ch );
					t.Delete( pos-1, pos );
					rd.SetPos( pos );
				END;
			END;
			rd.Read;
		END;
		Models.EndScript (t, script);
	END ToEASCII;

	PROCEDURE ToUnicode (t: TextModels.Model;  beg, end: INTEGER); (* преобразуем текст *)
		VAR  rd: TextModels.Reader;  wr: TextModels.Writer;  ch: CHAR;  pos: INTEGER;
			script: Stores.Operation;
	BEGIN
		end := MIN(end, t.Length());
		rd := t.NewReader(NIL);  
		rd.SetPos(beg);
		wr := t.NewWriter(NIL);
		
		Models.BeginScript (t, "Convert to Unicode", script); (* все действия до EndScript будут отменяться как целое при нажатии Ctrl+Z *)
		rd.Read;
		WHILE ~rd.eot & (rd.Pos() <= end) DO
			IF rd.view = NIL THEN
				ch := i21sysCyrillic.UnicodeOf( rd.char );
				IF ch # rd.char THEN (* заменить литеру *)
					pos := rd.Pos();
					wr.SetPos( pos );
					wr.WriteChar( ch );
					t.Delete( pos-1, pos );
					rd.SetPos( pos );
				END;
			END;
			rd.Read;
		END;
		Models.EndScript (t, script);
	END ToUnicode;


	PROCEDURE SelToEASCII*;
		VAR  c: TextControllers.Controller;  t: TextModels.Model;  v: Views.View;  beg, end: INTEGER;
	BEGIN
		(* взять текст в фокусе *)
		c := TextControllers.Focus();
		IF  c # NIL  THEN
			ASSERT(c.text # NIL); (* c.text = нужный текст *)
			IF c.HasSelection() THEN
				c.GetSelection(beg, end); 
				ToEASCII( c.text, beg, end ); 
				c.SetSelection(beg, end); 
			ELSE Dialog.ShowMsg("#Dev:NoSelectionFound")
			END;
		ELSE Dialog.ShowMsg("#Dev:NoTextViewFound")
		END;
	END SelToEASCII;
	
	
	PROCEDURE SelToUnicode*;
		VAR  c: TextControllers.Controller;  t: TextModels.Model;  v: Views.View;  beg, end: INTEGER;
	BEGIN
		(* взять текст в фокусе *)
		c := TextControllers.Focus();
		IF  c # NIL  THEN
			ASSERT(c.text # NIL); (* c.text = нужный текст *)
			IF c.HasSelection() THEN
				c.GetSelection(beg, end); 
				ToUnicode( c.text, beg, end ); 
				c.SetSelection(beg, end); 
			ELSE Dialog.ShowMsg("#Dev:NoSelectionFound")
			END;
		ELSE Dialog.ShowMsg("#Dev:NoTextViewFound")
		END;
	END SelToUnicode;
	

	PROCEDURE IsEASCII (rd: TextModels.Reader): BOOLEAN; (*element last read is EASCII char*)
		VAR  ch: CHAR;  ord: INTEGER;
	BEGIN
		IF rd.view = NIL THEN  ch := rd.char;  ord := ORD(ch) END;
		RETURN (rd.view = NIL) & (128 <= ord) & (ord < 256) & (ord # 0A0H (*non-break*))
	END IsEASCII;
	
	PROCEDURE IsEASCII2 (rd: TextModels.Reader): BOOLEAN; (*element last read is EASCII char*)
		VAR  ch: CHAR;  ord: INTEGER;
	BEGIN
		IF rd.view = NIL THEN  ch := rd.char;  ord := ORD(ch)  END;
		RETURN (rd.view = NIL) 
					& ( (0 <= ord) & (ord < 256) & ~(("A"<=ch) & (ch <="Z") OR ("a"<=ch) & (ch <="z")))
	END IsEASCII2;
	
	PROCEDURE NextEASCII*; 
	(* Find next stretch of EASCII characters after current selection|cursor.
		The stretch may include digits and special characters from the pure ASCII range -- but not Latin characters.
		в том числе для поиска в английском тексте немецких букв с умляутом и т.п.
	*)
		VAR  c: TextControllers.Controller;  t: TextModels.Model;  beg, end, pos: INTEGER;
			rd: TextModels.Reader;
	BEGIN
		(* взять текст в фокусе *)
		c := TextControllers.Focus();
		IF  c = NIL  THEN
			Dialog.ShowMsg("#Dev:NoTextViewFound")
		ELSE
			ASSERT(c.text # NIL); (* c.text = нужный текст *)
			rd := c.text.NewReader(NIL);
			IF c.HasSelection() THEN
				c.GetSelection(beg, pos); 
			ELSE 
				pos := c.CaretPos();
			END;
			rd.SetPos(pos);
			(* rd is set at the correct start of search *)
			
			(* searching next EASCII char *)
			rd.Read;
			WHILE  ~rd.eot & ~IsEASCII(rd)  DO  rd.Read  END;
			
			IF ~rd.eot THEN (* found *)
				beg := rd.Pos() - 1; (* -1 because rd has jumped over EASCII char *)
				(* теперь найти конец *)
				
				(* searching for view or non-EASCII char *)
				rd.Read;
				WHILE ~rd.eot & IsEASCII2(rd) DO
					rd.Read
				END;
				
				IF ~rd.eot THEN
					end := rd.Pos() - 1
				ELSE
					end := c.text.Length()
				END;
				c.SetSelection(beg, end)
			
			ELSE (* not found *)
				c.SetCaret(c.text.Length());
				Log.String("reached end of text");  Log.Ln;
			END;
		END;
	END NextEASCII;
	
	
	PROCEDURE IsUnicode (rd: TextModels.Reader): BOOLEAN; (*element last read is Unicode char*)
		VAR  ord: INTEGER;
	BEGIN
		ord := 0;
		IF rd.view = NIL THEN  ord := ORD(rd.char) END;
		RETURN (rd.view = NIL) & (256 <= ord)
	END IsUnicode;
	
	PROCEDURE IsUnicode2 (rd: TextModels.Reader): BOOLEAN; (*element last read is Unicode char*)
		VAR  ch: CHAR;  ord: INTEGER;
	BEGIN
		IF rd.view = NIL THEN  ch := rd.char;  ord := ORD(ch)  END;
		RETURN (rd.view = NIL) 
					& ( 
							(256 <= ord)
						OR
							(0 <= ord) & (ord < 128) & ~(("A"<=ch) & (ch <="Z") OR ("a"<=ch) & (ch <="z"))
						)
	END IsUnicode2;
	
	PROCEDURE NextUnicode*; 
	(* Find next stretch of Unicode characters after current selection|cursor.
		The stretch may include digits and special characters from the pure ASCII range -- but not Latin characters.
		в том числе для поиска в английском тексте немецких букв с умляутом и т.п.
	*)
		VAR  c: TextControllers.Controller;  t: TextModels.Model;  beg, end, pos: INTEGER;
			rd: TextModels.Reader;
	BEGIN
		(* взять текст в фокусе *)
		c := TextControllers.Focus();
		IF  c = NIL  THEN
			Dialog.ShowMsg("#Dev:NoTextViewFound")
		ELSE
			ASSERT(c.text # NIL); (* c.text = нужный текст *)
			rd := c.text.NewReader(NIL);
			IF c.HasSelection() THEN
				c.GetSelection(beg, pos); 
			ELSE 
				pos := c.CaretPos();
			END;
			rd.SetPos(pos);
			(* rd is set at the correct start of search *)
			
			(* searching next Unicode char *)
			rd.Read;
			WHILE  ~rd.eot & ~IsUnicode(rd)  DO  rd.Read  END;
			
			IF ~rd.eot THEN (* found *)
				beg := rd.Pos() - 1; (* -1 because rd has jumped over Unicode char *)
				(* теперь найти конец *)
				
				(* searching for view or non-Unicode char *)
				rd.Read;
				WHILE ~rd.eot & IsUnicode2(rd) DO
					rd.Read
				END;
				
				IF ~rd.eot THEN
					end := rd.Pos() - 1
				ELSE
					end := c.text.Length()
				END;
				c.SetSelection(beg, end)
			
			ELSE (* not found *)
				c.SetCaret(c.text.Length());
				Log.String("reached end of text");  Log.Ln;
			END;
		END;
	END NextUnicode;
	
END i21sysUnicodeTools.

Чтобы понять, что за буквы стоят в тексте (Юникод или Extended ASCII), выделить фрагмент и вызывать процедуру ExamineSelection.
В Рабочем журнале (Log) будет появляться информация по каждой литере.

i21sysUnicodeTools.ExamineIn

перенабито в ББ 	"об этой конфигурации Блэкбокса/di21_readme.odc"
из Total Commander простой Paste 	"   \di21_readme.odc"
из Total Commander Paste Special, Unicode 	"   \di21_readme.odc"
из Total Commander простой Paste Special, Plain Text 	"?? ???? ???????????? ?????????\di21_readme.odc"



i21sysUnicodeTools.NextEASCII
i21sysUnicodeTools.NextUnicode

i21sysUnicodeTools.SelToEASCII
i21sysUnicodeTools.SelToUnicode



выделение может содержать, наряду с EASCII, ещё цифры 00 и любые знаки препинания.