1 {
2 Descripción
3 ===========
4 Utilidades para la creación de editores con el resaltador SynFacilSyn.
5 
6 Trabaja con SynFacilCompletion 1.0 o superior
7 }
8 unit SynFacilUtils; {$mode objfpc}{$H+}
9 interface
10 uses  Classes, SysUtils, Clipbrd, SynEdit, SynEditMarkupHighAll,
11       lconvencoding, Graphics, FileUtil, Dialogs, Controls, Forms, LCLType, ComCtrls,
12       SynEditKeyCmds, SynEditTypes, Menus, strUtils, LazUTF8, MisUtils, FormSelFuente,
13       SynFacilCompletion;  //necesario para rutinas de manejo de sintaxis
14 
15 type
16   //Tipos de delimitador de línea de archivo.
17   TLineEnd = (TAR_DESC,    //Tipo desconocido
18              TAR_DOS,     //Tipo Windows/DOS
19              TAR_UNIX,    //Tipo Unix/Linux
20              TAR_MAC      //Tipo Mac OS
21              );
22 
23   { TSynFacilEditor }
24   TEventoArchivo = procedure of object;
25 
26   //Define las propiedades que debe tener un texto que se está editando
27   TSynFacilEditor = class
28     procedure edKeyPress(Sender: TObject; var Key: char);
29     procedure edUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
30   private
31     procedure DoSelectLanguage(Sender: TObject);
32     procedure CheckLanguageMenu(XMLfile: string);
33     procedure ReplaceDialog1Find(Sender: TObject);
34     procedure ReplaceDialog1Replace(Sender: TObject);
35   protected
36     ed          : TSynEdit;    //referencia al editor
37     fPanLangName: TStatusPanel;
38     mnRecents   : TMenuItem;  //Menú de archivos recientes
39     mnLanguages : TMenuItem;  //Menú de lenguajes
40     mnLineEnding: TMenuItem;  //Menú de Fin de línea
41     mnEncoding  : TMenuItem;  //Menú de Codificación de texto
42     LangPath    : string;     //ruta donde están los lengaujes
43     MaxRecents  : integer;    //Máxima cantidad de archivos recientes
44     //paneles con información del estado del editor
45     fPanFileSaved : TStatusPanel;  //Panel para mensaje "Guardado"
46     fPanCursorPos : TStatusPanel;  //Panel para mostrar posición del cursor
47     //paneles para información del archivo
48     fPanFileName  : TStatusPanel;  //Panel para mostrar el nombre de archivo
49     fPanForEndLin : TStatusPanel;  //Panel para mostrar el tipo de delimitador de línea
50     fPanCodifFile : TStatusPanel;  //Panel para mostrar la codificaión de archivo
51     procedure edChange(Sender: TObject);
52     procedure edStatusChange(Sender: TObject; Changes: TSynStatusChanges);
53     procedure edMouseDown(Sender: TObject; Button: TMouseButton;
54       Shift: TShiftState; X, Y: Integer);
55     procedure edKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
56     procedure edKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
57     procedure edCommandProcessed(Sender: TObject;
58       var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer);
59 
60     //Estado de modificación
61     procedure SetModified(valor: boolean);
GetModifiednull62     function GetModified: boolean;
63     procedure SetPanCodifFile(AValue: TStatusPanel);
64     procedure SetPanCursorPos(AValue: TStatusPanel);
65     procedure SetPanFileName(AValue: TStatusPanel);
66     procedure SetPanFileSaved(AValue: TStatusPanel);
67     procedure SetPanForEndLin(AValue: TStatusPanel);
68     procedure SetPanLangName(AValue: TStatusPanel);
GetTextnull69     function GetText: string;
70     procedure SetText(AValue: string);
71   public
72     FileName: string;    //Nombre del archivo
73     DelArc  : TLineEnd;  //Tipo de delimitador de fin de línea
74     CodArc  : string;    //codificación de archivo
75     linErr  : integer;   //línea de error. SOlo usada para marcar un error
76     Error   : string;    //mensaje de error en alguna operación
77     extDef  : string;    //extensión por defecto para los archivos (txt, xml, ...)
78     nomDef  : string;    //nombre por defecto pàra nuevos archivos
79     RecentFiles: TStringList;  //Lista de archivos recientes
80     hl      : TSynFacilComplet; //Resaltador.
81     //eventos
82     OnChangeEditorState:TEventoArchivo;  {Cuando cambia el estado de modificado, con opción
83                           "Undo", con "Redo", con opción "Copiar", "Cortar", "Pegar"}
84     OnChangeFileInform: TEventoArchivo;  {Cuando cambia información de nombre de archivo, tipo
85                            de delimitador de línea o tipo de codificación}
86     OnSelectionChange : TEventoArchivo; //Cuando cambia el área seleccionada
87     OnFileOpened : TEventoArchivo; //Cuando se ha cargado un nuevo archivo
88     //Reflejo de los eventos de TSynEdit:
89     OnEditChange : TNotifyEvent;
90     OnMouseDown  : TMouseEvent;
91     OnKeyUp      : TKeyEvent;
92     OnKeyDown    : TKeyEvent;
93     OnKeyPress   : TKeyPressEvent;
94     OnUTF8KeyPress: TUTF8KeyPressEvent;
95     //funciones comunes de un editor
96     procedure NewFile(QuerySave: boolean=true); virtual;
97     procedure LoadFile(arc8: string); virtual;
98     procedure SaveFile; virtual;
OpenDialognull99     function OpenDialog(OpenDialog1: TOpenDialog): boolean; virtual;
SaveAsDialognull100     function SaveAsDialog(SaveDialog1: TSaveDialog): boolean; virtual;
SaveQuerynull101     function SaveQuery: boolean; virtual;
102     //búsqueda/reemplazo
103     procedure FindDialog;
104     procedure FindNextWord(Sender: TObject);  //el método FindNext() ya existe
105     procedure ReplaceDialog;
106     //herramientas
107     procedure TabToSpaces(fSelFuente: TfrmSelFuente);
108     procedure TrimLines(fSelFuente: TfrmSelFuente);
FiltLinesnull109     function FiltLines(fSelFuente: TfrmSelFuente; sal: TStringList): boolean;
110     //Funciones para cambio de Fin de Línea y Codificación
111     procedure ChangeEndLineDelim(nueFor: TLineEnd); //cambia Fin de línea
112     procedure InitMenuLineEnding(mnLineEnding0: TMenuItem);  //configura menú
113     procedure LineEndingClick(Sender: TObject);     //evento click
114     procedure ChangeEncoding(nueCod: string);       //cambia codificación
115     procedure InitMenuEncoding(mnEncoding0: TMenuItem);  //configura menú
116     procedure EncodingClick(Sender: TObject);       //evento click
117     procedure ChangeFileInform;
118     //funciones para completado de código
119     procedure CloseCompletionWindow;
120   public  //Espejo de funciones comunes del editor
121     procedure Cut;
122     procedure Copy;
123     procedure Paste;
124     procedure Undo;
125     procedure Redo;
126     procedure SelectAll;
127     //Lee estado
CanUndonull128     function CanUndo: boolean;
CanRedonull129     function CanRedo: boolean;
CanCopynull130     function CanCopy: boolean;
CanPastenull131     function CanPaste: boolean;
132 
133     property Modified: boolean read GetModified write SetModified;
134   public  //Paneles informativos
135     property PanFileSaved: TStatusPanel read fPanFileSaved write SetPanFileSaved;
136     property PanCursorPos: TStatusPanel read fPanCursorPos write SetPanCursorPos;
137 
138     property PanFileName : TStatusPanel read fPanFileName  write SetPanFileName;
139     property PanForEndLin: TStatusPanel read fPanForEndLin write SetPanForEndLin;
140     property PanCodifFile: TStatusPanel read fPanCodifFile write SetPanCodifFile;
141     property PanLangName : TStatusPanel read fPanLangName write SetPanLangName;
142 
143     property Text: string read GetText write SetText;  //devuelve el contenido real del editor
144     procedure RefreshPanCursor;  //Refresca panel de la posición del cursor
145   public  //Rutinas de inicio
146     procedure InitMenuRecents(menRecents0: TMenuItem; RecentList: TStringList;
147       MaxRecents0: integer=5);
148     procedure RecentClick(Sender: TObject);
149     procedure ActualMenusReciente(Sender: TObject);
150     procedure AgregArcReciente(arch: string);
151 
152     procedure InitMenuLanguages(menLanguage0: TMenuItem; LangPath0: string);
153     procedure LoadSyntaxFromFile(XMLfile: string);  //carga un archivo de sintaxis
154     procedure LoadSyntaxFromPath(arc: string='');  //carga sintaxis viendo extensión de archivo
155     procedure SetLanguage(lang: string);
156     constructor Create(ed0: TsynEdit; nomDef0, extDef0: string); virtual;
157     destructor Destroy; override;
158   end;
159 
160 procedure InicEditorC1(ed: TSynEdit);
161 procedure StringToFile(const s: string; const FileName: string);
StringFromFilenull162 function StringFromFile(const FileName: string): string;
163 procedure VerTipoArchivo(archivo: string; var Formato: TLineEnd; var Codificacion: string);
CargarArchivoLinnull164 function CargarArchivoLin(arc8: string; Lineas: TStrings;
165                            var TipArc: TLineEnd; var CodArc: string): string;
GuardarArchivoLinnull166 function GuardarArchivoLin(arc0: string; Lineas: TStrings;
167                            var TipArc: TLineEnd; var CodArc: string): string;
168 implementation
169 const
170   szChar = SizeOf(Char);
171 
172 var
173   //Diálogos para búsqueda/reemplazo
174   FindDialog1: TFindDialog;
175   ReplaceDialog1: TReplaceDialog;
176 
177 procedure msgErr(msje: string);  //Rutina útil
178 //Mensaje de error
179 begin
180   Application.MessageBox(PChar(msje), '', MB_ICONERROR);
181 end;
182 procedure InicEditorC1(ed: TSynEdit);
183 //Inicia un editor con una configuración especial para empezar a trabajar con el.
184 var
185   SynMarkup: TSynEditMarkupHighlightAllCaret;  //para resaltar palabras iguales
186 begin
187    //Inicia resaltado de palabras iguales
188   SynMarkup := TSynEditMarkupHighlightAllCaret(ed.MarkupByClass[TSynEditMarkupHighlightAllCaret]);
189   SynMarkup.MarkupInfo.FrameColor := clSilver;
190   SynMarkup.MarkupInfo.Background := TColor($FFF0B0);
191 
192   SynMarkup.WaitTime := 250; // millisec
193   SynMarkup.Trim := True;     // no spaces, if using selection
194   SynMarkup.FullWord := True; // only full words If "Foo" is under caret, do not mark it in "FooBar"
195   SynMarkup.IgnoreKeywords := true;
196 
197   //  ed.Font.Name:='Courier New';
198   //  ed.Font.Size:=10;
199   ed.Options:=[eoHideRightMargin,eoBracketHighlight];  //quita la línea vertical
200   ed.Options := ed.Options + [eoKeepCaretX];  //Limita posición X del cursor para que no escape de la línea
201   ed.Options := ed.Options + [eoTabIndent];  //permite indentar con <Tab>
202   ed.Options2 := ed.Options2 + [eoCaretSkipTab];  //trata a las tabulaciones como un caracter
203 end;
204 procedure StringToFile(const s: string; const FileName: string);
205 ///   saves a string to a file
206 var
207   FileStream: TFileStream;
208 begin
209   FileStream := TFileStream.Create(FileName, fmCreate);
210   try
211     FileStream.WriteBuffer(Pointer(s)^, (Length(s) * szChar));
212   finally
213     FreeAndNil(FileStream);
214   end; // try
215 end;
StringFromFilenull216 function StringFromFile(const FileName: string): string;
217 ///   returns the content of the file as a string
218 var
219   FileStream: TFileStream;
220 begin
221   FileStream := TFileStream.Create(FileName, fmOpenRead);
222   try
223     SetLength(Result, (FileStream.Size div szChar));
224     FileStream.ReadBuffer(Pointer(Result)^, FileStream.Size);
225   finally
226     FreeAndNil(FileStream);
227   end; // try
228 end;
229 procedure VerTipoArchivo(archivo: string; var Formato: TLineEnd; var Codificacion: string);
230 (*Obtiene el tipo de delimitador de línea (Line Ending) de un archivo de texto, explorando
231  los primeros bytes de archivo. Solo explora los primeros 8K del archivo.
232  Si no encuentra un salto de línea en ese tamaño, no podrá deetrminar de que tipo de
233  archivo se trata. También explora el posible tipo de codificación usado.
234  *)
235 const TAM_BOL = 8192;
236 var ar: file;
237     bolsa : Array[0..TAM_BOL] of char;  //deja un byte más para el delimitador
238     Pbolsa: PChar;  //variable Pchar para "bolsa"
239     Pos13: Word;    //posición de caracter #13
240     Pos10: Word;    //posición de caracter #10
241     Leidos: Word;   //bytes leidos
242 begin
243    //Lee bloque de datos
244    AssignFile(ar,archivo);
245    reset(ar,1);  { TODO : Dio error al abrir un archivo de solo lectura }
246    BlockRead(ar, bolsa{%H-}, TAM_BOL, Leidos{%H-});  //Lectura masiva
247    CloseFile(ar);
248    bolsa[Leidos] := #0; //agrega delimitador
249    Pbolsa := @bolsa;    //cadena PChar
250    //Explora en busca de delimitadores de línea
251    Pos13 := Pos(#13, Pbolsa);
252    Pos10 := Pos(#10, Pbolsa);
253    if Pos13 = 0 then
254       //solo hay separador #10 o ninguno
255       if Pos10<>0 then
256          Formato := TAR_UNIX     //solo hay #10
257       else
258          Formato := TAR_DESC  //no se encontró separador
259    else if Pos10 = 0 then
260       //solo hay separador #13 o ninguno
261       if Pos13 <> 0 then
262          Formato := TAR_MAC     //solo hay #13
263       else
264          Formato := TAR_DESC  //no se encontró separador
265    else if Pos10 = Pos13 + 1 then
266       Formato := TAR_DOS    //no se encontró #13#10
267    else
268       Formato := TAR_DESC;  //no se reconoce delimitadores
269    //Analiza codifiación
270    Codificacion := GuessEncoding(Pbolsa);  //analiza los primeros bytes
271 { TODO : Ver por qué no detectó correctaente la carga de un archivo UTF-8 sin BOM }
272 end;
LineEnd_To_Strnull273 function LineEnd_To_Str(delim: TLineEnd): string;
274 //proporciona una descripción al tipo de delimitador
275 begin
276   Result := 'Unknown'; //'Desconoc.';
277   case delim of
278     TAR_DOS : Result := 'DOS/Win';  //DOS/Windows
279     TAR_UNIX: Result := 'UNIX/Linux';
280     TAR_MAC : Result := 'MAC OS';
281     TAR_DESC: Result := '<Unknown>'; //'Desconoc.';
282   end;
283 end;
Str_To_LineEndnull284 function Str_To_LineEnd(str: string): TLineEnd;
285 //proporciona una descripción al tipo de delimitador
286 begin
287   Result := TAR_DESC;   //desconocido
288   case str of
289     'DOS/Win'   : Result := TAR_DOS;
290     'UNIX/Linux': Result := TAR_UNIX;
291     'MAC OS'    : Result := TAR_MAC;
292     'Unknown'   : Result := TAR_DESC;
293   end;
294 end;
CargarArchivoLinnull295 function CargarArchivoLin(arc8: string; Lineas: TStrings;
296                            var TipArc: TLineEnd; var CodArc: string): string;
297 {Carga el contenido de un archivo en un "TStrings". Si la codificación es diferente de
298  UTF-8, hace la conversión. Esta pensado para usarse en un SynEdit.
299  Además actualiza el Tipo de Delimitador de línea y la Codificación.
300  Devuelve una cadena que indica si hubo conversión }
301 var
302   arc0: String;
303 begin
304   CodArc := '';
305   arc0 := UTF8ToSys(arc8);   //pone en modo ANSI
306   VerTipoArchivo(arc0, TipArc, CodArc);  //actualiza tipo de archivo de trabajo
307   //Carga archivo solicitado
308   Lineas.LoadFromFile(arc8);
309   //realiza las conversiones necesarias, ya que "ed", solo maneja UTF-8
310   if CodArc = 'cp1252' then begin
311     Lineas.Text := CP1252ToUTF8(Lineas.Text);
312     Result := 'Convertido a UTF-8';
313   end else if CodArc = 'utf8bom' then begin
314     Lineas.Text := UTF8BOMToUTF8(Lineas.Text);
315     Result := 'Convertido a UTF-8';
316   end else if CodArc = 'ISO-8859-1' then begin
317     Lineas.Text := ISO_8859_1ToUTF8(Lineas.Text);
318     Result := 'Convertido a UTF-8';
319   end else if CodArc = 'utf8' then begin
320     Result := 'Sin convertir';  //no se cambia
321   end else begin  //cualquier otra codificación se asume UTF-8 y no se cambia
322     //En windows tal vez debería cargarse cp1252 por defecto.
323     Result := 'utf8';
324   end;
325 end;
GuardarArchivoLinnull326 function GuardarArchivoLin(arc0: string; Lineas: TStrings;
327                            var TipArc: TLineEnd; var CodArc: string): string;
328 {Guarda el contenido de un "TStrings" en un archivo. Si la codificación es diferente de
329  UTF-8, hace la conversión. Esta pensado para usarse en un SynEdit.
330  Además usa el Tipo de Delimitador de línea para guardar el archivo.
331  Devuelve una cadena con un mensaje de error, si es que lo hubiera. }
332 begin
333   Result := '';  //sin error por defecto
334   //configura tipo de separador
335 //  case TipArc of
336 //  TAR_DOS: TSynEditLines(ed.Lines).FileWriteLineEndType := sfleCrLf;
337 //  TAR_UNIX: TSynEditLines(ed.Lines).FileWriteLineEndType := sfleLf;
338 //  TAR_MAC: TSynEditLines(ed.Lines).FileWriteLineEndType := sfleCr;
339 //  TAR_DESCON: TSynEditLines(ed.Lines).FileWriteLineEndType := sfleCrLf;
340 //  end;
341   case TipArc of
342   TAR_DOS:  Lineas.TextLineBreakStyle := tlbsCRLF;
343   TAR_UNIX: Lineas.TextLineBreakStyle := tlbsLF;
344   TAR_MAC:  Lineas.TextLineBreakStyle := tlbsCR;
345   TAR_DESC: Lineas.TextLineBreakStyle := tlbsCRLF;
346   end;
347 
348   if CodArc = 'utf8' then begin
349     //opción sin conversión
350     StringToFile(Lineas.Text,arc0);
351   end else if CodArc = 'cp1252' then  begin
352     StringToFile(UTF8ToCP1252(Lineas.Text),arc0);
353   end else if CodArc = 'utf8bom' then begin
354     StringToFile(UTF8ToUTF8BOM(Lineas.Text),arc0);
355   end else if CodArc = 'ISO-8859-1' then begin
356     StringToFile(UTF8ToISO_8859_1(Lineas.Text),arc0);
357   end else begin //si es otra codificación, se guarda como UTF-8
358     MsgExc('¡Codificación de archivo desconocida!');
359     StringToFile(Lineas.Text,arc0);
360   end;
361 end;
362 { TSynFacilEditor }
363 //respuesta a eventos del editor
364 procedure TSynFacilEditor.edMouseDown(Sender: TObject; Button: TMouseButton;
365   Shift: TShiftState; X, Y: Integer);
366 begin
367   RefreshPanCursor;
368   linErr := 0;  //para que quite la marca de fondo del error.
369                 //Solo se notará cuando se refresque la línea en el editor.
370   //pasa el evento
371   if OnMouseDown <> nil then OnMouseDown(Sender, Button, Shift, X, Y);
372 end;
373 procedure TSynFacilEditor.edStatusChange(Sender: TObject; Changes: TSynStatusChanges);
374 //Cambia el estado del editor
375 begin
376   if scSelection in changes then begin   //cambios en la selección
377     if OnSelectionChange<>nil then OnSelectionChange;  //dispara eventos
378     if OnChangeEditorState<>nil then OnChangeEditorState;  //para iniciar controles
379   end;
380 end;
381 procedure TSynFacilEditor.edChange(Sender: TObject);
382 begin
383   if fPanFileSaved <> nil then begin
384     if GetModified then fPanFileSaved.Text:=dic('Sin Guardar') else fPanFileSaved.Text:=dic('Guardado');
385   end;
386   //Ha habido cambio de contenido
387   if OnChangeEditorState<>nil then OnChangeEditorState;  //para iniciar controles
388   //Pasa el evento
389   if OnEditChange <> nil then OnEditChange(Sender);
390 end;
391 procedure TSynFacilEditor.edCommandProcessed(Sender: TObject;
392   var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer);
393 begin
394   RefreshPanCursor;
395   linErr := 0;  //para que quite la marca de fondo del error.
396                 //Solo se notará cuando se refresque la línea en el editor.
397 end;
398 procedure TSynFacilEditor.edKeyDown(Sender: TObject; var Key: Word;
399   Shift: TShiftState);
400 begin
401   //Pasa el evento
402   if OnKeyDown <> nil then OnKeyDown(Sender, Key, Shift);
403 end;
404 procedure TSynFacilEditor.edKeyUp(Sender: TObject; var Key: Word;
405   Shift: TShiftState);
406 begin
407   //pasa el evento al resaltador por si necesita abrir el menú de completado
408   hl.KeyUp(Sender, Key, Shift);
409   //Pasa el evento
410   if OnKeyUp <> nil then OnKeyUp(Sender, Key, Shift);
411 end;
412 
413 procedure TSynFacilEditor.edKeyPress(Sender: TObject; var Key: char);
414 begin
415   //Pasa evento
416   if OnKeyPress <> nil then OnKeyPress(Sender, Key);
417 end;
418 
419 procedure TSynFacilEditor.edUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char
420   );
421 begin
422   //pasa el evento al resaltador por si necesita abrir el menú de completado
423   hl.UTF8KeyPress(Sender, UTF8Key);
424   //Pasa el evento
425   if OnUTF8KeyPress <> nil then OnUTF8KeyPress(Sender, UTF8Key);
426 end;
427 
428 //Manejo de archivos recientes
429 procedure TSynFacilEditor.InitMenuRecents(menRecents0: TMenuItem; RecentList: TStringList;
430                                      MaxRecents0: integer=5);
431 //Configura un menú, con el historial de los archivos abiertos recientemente
432 //"nRecents", es el número de archivos recientes que se guardará
433 var
434   i: Integer;
435 begin
436   mnRecents := menRecents0;
437   RecentFiles := RecentList;  //gaurda referencia a lista
438   MaxRecents := MaxRecents0;
439   //configura menú
440   mnRecents.Caption:= dic('&Recientes');
441   mnRecents.OnClick:=@ActualMenusReciente;
442   for i:= 1 to MaxRecents do begin
443     AddItemToMenu(mnRecents, '&'+IntToStr(i), @RecentClick);
444   end;
445 end;
446 procedure TSynFacilEditor.RecentClick(Sender: TObject);
447 //Se selecciona un archivo de la lista de recientes
448 begin
449   if SaveQuery then Exit;   //Verifica cambios
450   LoadFile(MidStr(TMenuItem(Sender).Caption,4,150));
451 end;
452 procedure TSynFacilEditor.ActualMenusReciente(Sender: TObject);
453 {Actualiza el menú de archivos recientes con la lista de los archivos abiertos
454 recientemente. }
455 var
456   i: Integer;
457 begin
458   if mnRecents = nil then exit;
459   if RecentFiles = nil then exit;
460   //proteciión
461   if RecentFiles.Count = 0 then begin
462     mnRecents[0].Caption:=dic('No hay archivos');
463     mnRecents[0].Enabled:=false;
464     for i:= 1 to mnRecents.Count-1 do begin
465       mnRecents[i].Visible:=false;
466     end;
467     exit;
468   end;
469   //hace visible los ítems
470   mnRecents[0].Enabled:=true;
471   for i:= 0 to mnRecents.Count-1 do begin
472     if i<RecentFiles.Count then
473       mnRecents[i].Visible:=true
474     else
475       mnRecents[i].Visible:=false;
476   end;
477   //pone etiquetas a los menús, incluyendo un atajo numérico
478   for i:=0 to RecentFiles.Count-1 do begin
479     mnRecents[i].Caption := '&'+IntToStr(i+1)+' '+RecentFiles[i];
480   end;
481 end;
482 procedure TSynFacilEditor.AgregArcReciente(arch: string);
483 //Agrega el nombre de un archivo reciente
484 var hay: integer; //bandera-índice
485     i: integer;
486 begin
487   if RecentFiles = nil then exit;
488   //verifica si ya existe
489   hay := -1;   //valor inicial
490   for i:= 0 to RecentFiles.Count-1 do
491     if RecentFiles[i] = arch then hay := i;
492   if hay = -1 then  //no existe
493     RecentFiles.Insert(0,arch)  //agrega al inicio
494   else begin //ya existe
495     RecentFiles.Delete(hay);     //lo elimina
496     RecentFiles.Insert(0,arch);  //lo agrega al inicio
497   end;
498   while RecentFiles.Count>MaxRecents do  //mantiene tamaño máximo
499     RecentFiles.Delete(MaxRecents);
500 end;
501 
502 procedure TSynFacilEditor.ReplaceDialog1Find(Sender: TObject);
503 var
504   encon  : integer;
505   buscado : string;
506   opciones: TSynSearchOptions;
507 begin
508   buscado := ReplaceDialog1.FindText;
509   opciones := [];
510   if not(frDown in ReplaceDialog1.Options) then opciones += [ssoBackwards];
511   if frMatchCase in ReplaceDialog1.Options then opciones += [ssoMatchCase];
512   if frWholeWord in ReplaceDialog1.Options then opciones += [ssoWholeWord];
513   if frEntireScope in ReplaceDialog1.Options then opciones += [ssoEntireScope];
514 
515   encon := ed.SearchReplace(buscado,'',opciones);
516   if encon = 0 then
517     MsgBox('No se encuentra: %s', [buscado]);
518 end;
519 procedure TSynFacilEditor.ReplaceDialog1Replace(Sender: TObject);
520 var
521   encon, r : integer;
522   buscado : string;
523   opciones: TSynSearchOptions;
524 begin
525   buscado := ReplaceDialog1.FindText;
526   opciones := [ssoFindContinue];
527 //  opciones := [];
528   if not(frDown in ReplaceDialog1.Options) then opciones += [ssoBackwards];
529   if frMatchCase in ReplaceDialog1.Options then opciones += [ssoMatchCase];
530   if frWholeWord in ReplaceDialog1.Options then opciones += [ssoWholeWord];
531   if frEntireScope in ReplaceDialog1.Options then opciones += [ssoEntireScope];
532   if frReplaceAll in ReplaceDialog1.Options then begin
533     //se ha pedido reemplazar todo
534     encon := ed.SearchReplace(buscado,ReplaceDialog1.ReplaceText,
535                               opciones+[ssoReplaceAll]);  //reemplaza
536     msgbox('Se reemplazaron %d ocurrencias.',[encon]);
537     exit;
538   end;
539   //reemplazo con confirmación
540   ReplaceDialog1.CloseDialog;
541   encon := ed.SearchReplace(buscado,'',opciones);  //búsqueda
542   while encon <> 0 do begin
543       //pregunta
544       r := Application.MessageBox(Pchar(dic('¿Reemplazar esta ocurrencia?')),
545                 Pchar(dic('Reemplazo')), MB_YESNOCANCEL);
546       if r = IDCANCEL then exit;
547       if r = IDYES then begin
548         ed.TextBetweenPoints[ed.BlockBegin,ed.BlockEnd] := ReplaceDialog1.ReplaceText;
549       end;
550       //busca siguiente
551       encon := ed.SearchReplace(buscado,'',opciones);  //búsca siguiente
552   end;
553   MsgBox('No se encuentra: %s', [buscado]);
554 end;
555 procedure TSynFacilEditor.SetModified(valor: boolean);
556 //Cambia el valor del campo "Modified", del editor
557 begin
558   if ed.Modified<> valor then begin
559     //se ha cambiado el estado de "Modificado"
560     ed.Modified := valor;    //Fija valor
561     //dispara evento
562     if fPanFileSaved <> nil then begin
563       if GetModified then fPanFileSaved.Text:=dic('Sin Guardar') else fPanFileSaved.Text:=dic('Guardado');
564     end;
565     if OnChangeEditorState<>nil then OnChangeEditorState;
566   end;
567 end;
TSynFacilEditor.GetModifiednull568 function TSynFacilEditor.GetModified: boolean;
569 //Lee el valor del campo "Modified", del editor.
570 begin
571   Result := ed.Modified;
572 end;
573 //"Setters" de los paneles
574 procedure TSynFacilEditor.SetPanCursorPos(AValue: TStatusPanel);
575 begin
576   if FPanCursorPos=AValue then Exit;
577   fPanCursorPos:=AValue;
578   RefreshPanCursor;
579 end;
580 procedure TSynFacilEditor.SetPanFileSaved(AValue: TStatusPanel);
581 begin
582   if fPanFileSaved=AValue then Exit;
583   fPanFileSaved:=AValue;
584   if fPanFileSaved <> nil then begin
585     if GetModified then fPanFileSaved.Text:=dic('Sin Guardar') else fPanFileSaved.Text:=dic('Guardado');
586   end;
587 end;
588 
589 procedure TSynFacilEditor.SetPanFileName(AValue: TStatusPanel);
590 begin
591   if fPanFileName=AValue then Exit;
592   fPanFileName:=AValue;
593   if fPanFileName <> nil then begin
594     fPanFileName.Text := SysToUTF8(FileName);
595   end;
596 end;
597 procedure TSynFacilEditor.SetPanForEndLin(AValue: TStatusPanel);
598 begin
599   if fPanForEndLin=AValue then Exit;
600   fPanForEndLin:=AValue;
601   if fPanForEndLin <> nil then begin
602     fPanForEndLin.Text:=LineEnd_To_Str(DelArc);
603   end;
604 end;
605 procedure TSynFacilEditor.SetPanCodifFile(AValue: TStatusPanel);
606 begin
607   if fPanCodifFile=AValue then Exit;
608   fPanCodifFile:=AValue;
609   if fPanCodifFile <> nil then begin
610     fPanCodifFile.Text:=CodArc;
611   end;
612 end;
613 procedure TSynFacilEditor.SetPanLangName(AValue: TStatusPanel);
614 begin
615   if fPanLangName=AValue then Exit;
616   fPanLangName:=AValue;
617   if fPanLangName<> nil then begin
618     fPanLangName.Text:= hl.LangName;
619   end;
620 end;
GetTextnull621 function TSynFacilEditor.GetText: string;
622 //Devuelve el contenido del editor, quitando el salto de línea final
623 begin
624   Result := ed.Text;
625   if AnsiEndsStr(LineEnding, Result) then begin
626      Setlength(Result, length(Result)-length(LineEnding));
627   end;
628 end;
629 procedure TSynFacilEditor.SetText(AValue: string);
630 //Fija el contenido del editor
631 begin
632   ed.Text:=AValue;
633 end;
634 
635 procedure TSynFacilEditor.NewFile(QuerySave: boolean=true);
636 //Inicia al editor con un nuevo nombre de archivo
637 //"QuerySave" indica si se debe o no preguntar por archivo modificado
638 begin
639   if QuerySave then begin
640     if SaveQuery then Exit;   //Verifica cambios
641     if Error<>'' then exit;  //hubo error
642   end;
643   Error := '';    //limpia bandera de error
644   if extDef<> '' then //genera nombre por defecto
645     FileName := nomDef + '.' + extDef
646   else FileName := nomDef;
647   //verifica existencia
648 //  if FileExists(Arc) then   //ya existe
649 //     AbrirArchivo(Arc)  //lo abre
650 //  else begin   //no existe
651 //    mnArGuarClick(nil);  //Lo crea
652   DelArc := TAR_DOS;  //inicia con Windows por defecto
653   CodArc := 'cp1252'; //inicia en formato Windows
654   ed.ClearAll;        //limpia editor
655   ed.ClearUndo;       //limpia acciones "deshacer"
656   SetModified(false);
657   ChangeFileInform;   //actualiza
658   if OnChangeEditorState<>nil then OnChangeEditorState;  //para iniciar controles
659 end;
660 procedure TSynFacilEditor.LoadFile(arc8: string);
661 //Carga el contenido de un archivo en el editor, analizando la codificación.
662 //Si ocurre algún error, muestra el mensaje en pantalla y actualiza "Error".
663 var
664   arc0: String;
665 begin
666   Error := '';    //limpia bandera de error
667   arc0 := UTF8ToSys(arc8);   //pone en modo ANSI
668   //verifica existencia de archivo
669   if not FileExists(arc0) then begin
670     Error := dic('No se encuentra el archivo: ') + arc0;
671     msgErr(Error);
672     Exit;                    //sale
673   end;
674   //carga y lee formato
675   CargarArchivoLin(arc8, ed.Lines, DelArc, CodArc);
676 //  StatusBar1.Panels[4].Text := CodArc;  //actualiza codificación
677   FileName := arc0;         //fija nombre de archivo de trabajo
678   SetModified(false);  //Inicia estado
679   linErr := 0;            //limpia línea marcada por si acaso
680   ChangeFileInform;   //actualiza
681   if OnFileOpened<>nil then OnFileOpened;  //dispara evento
682   AgregArcReciente(arc8);  //agrega a lista de recientes
683 end;
684 procedure TSynFacilEditor.SaveFile;
685 //Guarda el contenido del editor en su archivo correspondiente
686 //Si ocurre algún error, muestra el mensaje en pantalla y actualiza "Error".
687 begin
688   Error := '';    //limpia bandera de error
689   try
690     GuardarArchivoLin(FileName, ed.Lines, DelArc, CodArc);  //guarda en formato original
691     SetModified(false);
692     edChange(self);  //para que actualice el panel fPanFileSaved
693     //se actualiza por si acaso, se haya guardado con otro nombre
694     ChangeFileInform;   //actualiza
695   except
696     Error := dic('Error guardando archivo: ') + FileName;
697     msgErr(Error);
698   end;
699 end;
TSynFacilEditor.OpenDialognull700 function TSynFacilEditor.OpenDialog(OpenDialog1: TOpenDialog): boolean;
701 //Muestra el cuadro de diálogo para abrir un archivo, teniendo cuidado de
702 //pedir confirmación para grabar el contenido actual. Si hay error devuelve FALSE.
703 var arc0: string;
704 begin
705   Error := '';
706   if SaveQuery then exit(true);   //Verifica cambios
707   if Error<>'' then exit(false);  //hubo error
708   if not OpenDialog1.Execute then exit(true);    //se canceló
709   arc0 := OpenDialog1.FileName;
710   LoadFile(arc0);  //legalmente debería darle en UTF-8
711   Result := true;   //sale sin incidencias
712 end;
SaveAsDialognull713 function TSynFacilEditor.SaveAsDialog(SaveDialog1: TSaveDialog): boolean;
714 //Guarda el contenido del editor, permitiendo cambiar el nombre con un diálogo.
715 //Si se ignora la acción, devuelve "true".
716 //Si ocurre algún error, muestra el mensaje en pantalla y actualiza "Error".
717 var
718   arc0: String;
719   resp: TModalResult;
720 begin
721   Result := false;
722   if not SaveDialog1.Execute then begin  //se canceló
723     Result := true;   //Sale con "true"
724     exit;    //se canceló
725   end;
726   arc0 := SaveDialog1.FileName;
727   if FileExists(arc0) then begin
728     resp := MessageDlg('', dic('El archivo %s ya existe.' + LineEnding +
729                   '¿Deseas sobreescribirlo?',[arc0]),
730                        mtConfirmation, [mbYes, mbNo, mbCancel],0);
731     if (resp = mrCancel) or (resp = mrNo) then Exit;
732   end;
733   FileName := UTF8ToSys(arc0);   //asigna nuevo nombre
734   if ExtractFileExt(FileName) = '' then FileName += '.'+extDef;  //completa extensión
735   SaveFile;   //lo guarda
736 end;
SaveQuerynull737 function TSynFacilEditor.SaveQuery: boolean;
738 //Verifica si es necesario guardar el archivo antes de ejecutar alguna oepración con el editor.
739 //Si se ignora la acción, devuelve "true".
740 //Si ocurre algún error, muestra el mensaje en pantalla y actualiza "Error".
741 var resp: integer;
742 begin
743   Result := false;
744   if ed = nil then begin
745     Error := dic('Error Interno: Editor no inicializado.');
746     msgErr(Error);
747     exit;
748   end;
749   if ed.Modified then begin
750     resp := MessageDlg('', dic('El archivo %s ha sido modificado.' +  LineEnding +
751                      '¿Deseas guardar los cambios?',[ExtractFileName(FileName)]),
752                        mtConfirmation, [mbYes, mbNo, mbCancel],0);
753     if resp = mrCancel then begin
754       Result := true;   //Sale con "true"
755       Exit;
756     end;
757     if resp = mrYes then begin  //guardar
758       SaveFile;  //Actualizar "Error"
759     end;
760   end;
761 end;
762 //Búsqueda y reemplazo
763 procedure TSynFacilEditor.FindDialog;
764 //Realiza una búsqueda en el texto del editor, usando el ´diálogo de búsqeudas.
765 begin
766   FindDialog1.OnFind:=@FindNextWord;
767   if ed.SelAvail then FindDialog1.FindText:=ed.SelText;
768   FindDialog1.Execute;
769 end;
770 procedure TSynFacilEditor.FindNextWord(Sender: TObject);
771 //Se ejecuta para encontrar el siguiente elemento. Es llamado por el evento FindText()
772 //de "FindDialog1", pero puede ser llamado también de forma directa.
773 var
774   encon  : integer;
775   buscado : string;
776   opciones: TSynSearchOptions;
777 begin
778   buscado := FindDialog1.FindText;
779   opciones := [];
780   if not(frDown in FindDialog1.Options) then opciones += [ssoBackwards];
781   if frMatchCase in FindDialog1.Options then opciones += [ssoMatchCase];
782   if frWholeWord in FindDialog1.Options then opciones += [ssoWholeWord];
783   if frEntireScope in FindDialog1.Options then opciones += [ssoEntireScope];
784 
785   encon := ed.SearchReplace(buscado,'',opciones);
786   if encon = 0 then
787     MsgBox('No se encuentra: %s', [buscado]);
788 end;
789 procedure TSynFacilEditor.ReplaceDialog;
790 //Realiza una búsqueda en el texto del editor, usando el ´diálogo de búsqeudas.
791 begin
792   ReplaceDialog1.OnFind:=@ReplaceDialog1Find;
793   ReplaceDialog1.OnReplace:=@ReplaceDialog1Replace;
794   if ed.SelAvail then ReplaceDialog1.FindText:=ed.SelText;
795   ReplaceDialog1.Execute;
796 end;
797 //herramientas
798 procedure TSynFacilEditor.TabToSpaces(fSelFuente: TfrmSelFuente);
799 //Convierte tabulaciones a espacios.
800 //Requiere un formaulario de tipo TfrmSelFuente para mostrar el dialogo.
801 var
802   i: integer;
803 begin
804   if ed.SelAvail then begin
805     fSelFuente.optLin.Enabled:=false;
806     fSelFuente.optSel.Enabled:=true;
807     fSelFuente.optSel.Checked := true;
808   end else begin
809     fSelFuente.optSel.Enabled:=false;
810     fSelFuente.optLin.Enabled := true;
811     fSelFuente.optLin.Checked := true;
812   end;
813   fSelFuente.ShowModal;
814   If fSelFuente.cancelado Then begin  //se canceló
815     //no hace nada
816   end else If fSelFuente.optLin.Checked Then begin  //línea
817     ed.LineText:=StringReplace(ed.LineText,#9, stringOfChar(' ',ed.TabWidth), [rfReplaceAll]);
818   end else If fSelFuente.optSel.Checked Then begin  //seleción
819     ed.SelText:=StringReplace(ed.seltext,#9, stringOfChar(' ',ed.TabWidth), [rfReplaceAll]);;
820   end else begin                           //todo
821     for i := 0 to ed.Lines.Count-1 do
822       ed.Lines[i] := StringReplace(ed.Lines[i],#9, stringOfChar(' ',ed.TabWidth), [rfReplaceAll]);
823   End;
824   ed.ClearUndo;   //porque no se puede deshacer
825   if OnChangeEditorState<>nil then OnChangeEditorState;  //actualiza
826 end;
827 procedure TSynFacilEditor.TrimLines(fSelFuente: TfrmSelFuente);
828 //Quita espacios laterales
829 //Requiere un formaulario de tipo TfrmSelFuente para mostrar el dialogo.
830 var
831   i: integer;
832   f1: integer;
833   f2: integer;
834 begin
835   if ed.SelAvail then begin
836     fSelFuente.optLin.Enabled:=false;
837     fSelFuente.optSel.Enabled:=true;
838     fSelFuente.optSel.Checked := true;
839   end else begin
840     fSelFuente.optSel.Enabled:=false;
841     fSelFuente.optLin.Enabled := true;
842     fSelFuente.optLin.Checked := true;
843   end;
844   fSelFuente.ShowModal;
845   If fSelFuente.cancelado Then begin  //se canceló
846     //no hace nada
847   end else If fSelFuente.optLin.Checked Then begin  //línea
848     ed.LineText:=trim(ed.LineText);
849   end else If fSelFuente.optSel.Checked Then begin  //seleción
850     f1 := ed.BlockBegin.y;
851     f2 := ed.BlockEnd.y;
852     for i := f1 to f2-1 do
853       ed.Lines[i-1] := trim(ed.Lines[i]);
854   end else begin                           //todo
855     for i := 0 to ed.Lines.Count-1 do
856       ed.Lines[i] := trim(ed.Lines[i]);
857   End;
858   ed.ClearUndo;   //porque no se puede deshacer
859   if OnChangeEditorState<>nil then OnChangeEditorState;  //actualiza
860 end;
FiltLinesnull861 function TSynFacilEditor.FiltLines(fSelFuente: TfrmSelFuente; sal: TStringList): boolean;
862 //Filtra líneas del contenido. Si se cancela la operación devuelve false.
863 var
864   i: integer;
865   s: string;
866 begin
867   Result := true;  //por defecto
868   sal := TStringList.Create;  //salida
869   fSelFuente.optLin.Enabled:=false;
870   if ed.SelAvail and (ed.BlockEnd.y - ed.BlockBegin.y >0) then begin
871      //hay selección de más de una línea
872      fSelFuente.optSel.Enabled:=true;
873      fSelFuente.optSel.Checked := true;
874      fSelFuente.ShowModal;  //solo muestra aquí
875      If fSelFuente.cancelado Then exit;  //se canceló
876   end else begin  //no hay selección
877      fSelFuente.optTod.Checked := true;
878 //      fSelFuente.ShowModal;
879   end;
880   If fSelFuente.optSel.Checked Then begin  //seleción
881     s := InputBox('Filtrar líneas:','Ingrese texto: ','');
882     if s = '' then exit;
883     for i:= ed.BlockBegin.y to ed.BlockEnd.y do
884       if AnsiContainsText(ed.Lines[i],s) then sal.Add(ed.Lines[i]);
885   end Else begin                           //todo
886     if ed.BlockEnd.y = ed.BlockBegin.y then  //hay texto seleccionado, suguiere
887       s := InputBox('Filtrar líneas:','Ingrese texto: ',ed.SelText)
888     else
889       s := InputBox('Filtrar líneas:','Ingrese texto: ','');
890     if s = '' then exit;
891     for i:= 0 to ed.Lines.Count-1 do
892       if AnsiContainsText(ed.Lines[i],s) then sal.Add(ed.Lines[i]);
893   End;
894 end;
895 //Funciones para cambio de Fin de Línea y Codificación
896 procedure TSynFacilEditor.ChangeEndLineDelim(nueFor: TLineEnd);
897 //Cambia el formato de salto de línea del contenido
898 begin
899   if DelArc <> nueFor then begin  //verifica si hay cambio
900     DelArc := nueFor;
901     SetModified(true); //para indicar que algo ha cambiado
902     ChangeFileInform;   //actualiza
903   end;
904 end;
905 procedure TSynFacilEditor.InitMenuLineEnding(mnLineEnding0: TMenuItem);
906 //Inicia un menú con los tipos de delimitador de línea que maneja la unidad, y les
907 //les asigna un evento para implementar el cambio.
908 begin
909   if mnLineEnding0 = nil then exit;
910   mnLineEnding := mnLineEnding0;  //guarda referencia a menú
911   //configura menú
912   mnLineEnding.Caption:= dic('Fin de Línea');
913   mnLineEnding.Clear;
914   //llena opciones
915   AddItemToMenu(mnLineEnding, LineEnd_To_Str(TAR_UNIX), @LineEndingClick);
916   AddItemToMenu(mnLineEnding, LineEnd_To_Str(TAR_DOS), @LineEndingClick);
917   AddItemToMenu(mnLineEnding, LineEnd_To_Str(TAR_MAC), @LineEndingClick);
918   AddItemToMenu(mnLineEnding, LineEnd_To_Str(TAR_DESC), @LineEndingClick).Enabled:=false;
919   CheckOnlyOneItem(mnLineEnding, LineEnd_To_Str(delArc));   //actualiza
920 end;
921 procedure TSynFacilEditor.LineEndingClick(Sender: TObject);
922 var
923   it: TMenuItem;
924   delim: TLineEnd;
925 begin
926   it := TMenuItem(Sender);
927   delim := Str_To_LineEnd(it.Caption);
928   ChangeEndLineDelim(delim);
929   CheckOnlyOneItem(it); //marca menú
930 end;
931 procedure TSynFacilEditor.ChangeEncoding(nueCod: string);
932 //Cambia la codificación del archivo
933 begin
934   if CodArc <> nueCod then begin
935     CodArc := nueCod;
936     SetModified(true); //para indicar que algo ha cambiado
937     ChangeFileInform;   //actualiza
938   end;
939 end;
940 procedure TSynFacilEditor.InitMenuEncoding(mnEncoding0: TMenuItem);
941 //Inicia un menú con los tipos de delimitador de línea que maneja la unidad, y les
942 //les asigna un evento para implementar el cambio.
943 begin
944   if mnEncoding0 = nil then exit;
945   mnEncoding := mnEncoding0;  //guarda referencia a menú
946   //configura menú
947   mnEncoding.Caption:= dic('Codificación');
948   mnEncoding.Clear;
949   //llena opciones
950   AddItemToMenu(mnEncoding, 'utf8', @EncodingClick);
951   AddItemToMenu(mnEncoding, 'utf8bom', @EncodingClick);
952   AddItemToMenu(mnEncoding, 'cp1252', @EncodingClick);
953   AddItemToMenu(mnEncoding, 'ISO-8859-1', @EncodingClick);
954   AddItemToMenu(mnEncoding, '<Unknown>', @EncodingClick).Enabled:=false;
955   CheckOnlyOneItem(mnEncoding, codArc);   //actualiza
956 end;
957 procedure TSynFacilEditor.EncodingClick(Sender: TObject);
958 var
959   it: TMenuItem;
960 begin
961   it := TMenuItem(Sender);
962   ChangeEncoding(it.Caption);
963   CheckOnlyOneItem(it); //marca menú
964 end;
965 procedure TSynFacilEditor.ChangeFileInform;
966 //Se debe llamar siempre que puede cambiar la información de nombre de archivo, tipo de
967 //delimitador de línea o tipo de codificación del archivo.
968 begin
969   //actualiza información en los paneles
970   if fPanFileName <> nil then begin
971     fPanFileName.Text := SysToUTF8(FileName);
972   end;
973   if fPanForEndLin <> nil then begin
974     fPanForEndLin.Text:=LineEnd_To_Str(DelArc);
975   end;
976   if fPanCodifFile <> nil then begin
977     fPanCodifFile.Text:=CodArc;
978   end;
979   //actualiza menús
980   CheckOnlyOneItem(mnLineEnding, LineEnd_To_Str(delArc));
981   CheckOnlyOneItem(mnEncoding, codArc);
982   //dispara evento
983   if OnChangeFileInform<>nil then OnChangeFileInform;
984 end;
985 procedure TSynFacilEditor.CloseCompletionWindow;
986 //Cierra la ventana de completado
987 begin
988   hl.CloseCompletionWindow;
989 end;
990 //Espejo de funciones comunes del editor
991 procedure TSynFacilEditor.Cut;
992 begin
993   ed.CutToClipboard;
994 end;
995 procedure TSynFacilEditor.Copy;
996 begin
997   ed.CopyToClipboard;
998 end;
999 procedure TSynFacilEditor.Paste;
1000 begin
1001   ed.PasteFromClipboard;
1002 end;
1003 procedure TSynFacilEditor.Undo;
1004 //Deshace una acción en el editor
1005 begin
1006   ed.Undo;
1007 end;
1008 procedure TSynFacilEditor.Redo;
1009 //Rehace una acción en el editor
1010 begin
1011   ed.Redo;
1012 end;
1013 procedure TSynFacilEditor.SelectAll;
1014 begin
1015   ed.SelectAll;
1016 end;
1017 //Lee estado
CanUndonull1018 function TSynFacilEditor.CanUndo: boolean;
1019 //Indica si Hay Algo por deshacer
1020 begin
1021   Result := ed.CanUndo;
1022 end;
CanRedonull1023 function TSynFacilEditor.CanRedo: boolean;
1024 //Indica si Hay Algo por rehacer
1025 begin
1026   Result := ed.CanRedo;
1027 end;
CanCopynull1028 function TSynFacilEditor.CanCopy: boolean;
1029 //Indica si hay algo por copiar
1030 begin
1031   Result := ed.SelAvail;
1032 end;
TSynFacilEditor.CanPastenull1033 function TSynFacilEditor.CanPaste: boolean;
1034 //Indica si Hay Algo por pegar
1035 begin
1036   Result := ed.CanPaste;
1037 end;
1038 procedure TSynFacilEditor.RefreshPanCursor;
1039 begin
1040   if fPanCursorPos <> nil then
1041     fPanCursorPos.Text:= dic('fil=%d, col=%d',[ed.CaretY, ed.CaretX]);
1042 end;
1043 procedure TSynFacilEditor.InitMenuLanguages(menLanguage0: TMenuItem; LangPath0: string);
1044 //Inicia un menú con la lista de archivos XML (que representan a lenguajes) que hay
1045 //en una carpeta en particular y les asigna un evento.
1046 var
1047   Hay: Boolean;
1048   SR : TSearchRec;
1049 begin
1050   if menLanguage0 = nil then exit;
1051   mnLanguages := menLanguage0;  //guarda referencia a menú
1052   LangPath := LangPath0;        //guarda ruta
1053   if (LangPath<>'') and (LangPath[length(LangPath)] <> DirectorySeparator) then
1054      LangPath+=DirectorySeparator;
1055   //configura menú
1056   mnLanguages.Caption:= dic('&Lenguajes');
1057   //explora archivos
1058   Hay := FindFirst(LangPath + '*.xml', faAnyFile - faDirectory, SR) = 0;
1059   while Hay do begin
1060      //encontró archivo
1061     AddItemToMenu(mnLanguages, '&'+ChangeFileExt(SR.name,''),@DoSelectLanguage);
1062     Hay := FindNext(SR) = 0;
1063   end;
1064 end;
1065 procedure TSynFacilEditor.DoSelectLanguage(Sender: TObject);
1066 //Se ha seleccionado un lenguaje desde el menú.
1067 var
1068   arcXML: String;
1069   it: TMenuItem;
1070 begin
1071   it := TMenuItem(Sender);
1072   arcXML := LangPath + RightStr(it.Caption,length(it.Caption)-1 ) + '.xml';
1073   hl.LoadFromFile(arcXML);  //carga la sintaxis indicada
1074   if fPanLangName<> nil then begin
1075     fPanLangName.Text:= hl.LangName;
1076   end;
1077   CheckOnlyOneItem(it); //marca menú
1078 end;
1079 procedure TSynFacilEditor.CheckLanguageMenu(XMLfile: string);
1080 //Marca el ítem del menú de lenguaje que corresponde al nombre de archivo indicado.
1081 var
1082   XML: String;
1083 begin
1084   if mnLanguages = nil then exit;
1085   XML := ExtractFileName(XMLfile);  //por si tenía ruta
1086   XML := ChangeFileExt(XML,'');
1087   CheckOnlyOneItem(mnLanguages, XML);
1088 end;
1089 procedure TSynFacilEditor.LoadSyntaxFromFile(XMLfile: string);
1090 //Carga un archivo de sintaxis en el editor.
1091 begin
1092   hl.LoadFromFile(XMLfile);  //carga sintaxis
1093   if fPanLangName<> nil then begin
1094     fPanLangName.Text:= hl.LangName;
1095   end;
1096   //verifica si se puede marcar en el menú
1097   if mnLanguages = nil then exit;   //no se ha confogurado menú
1098   if LangPath = ExtractFilePath(XMLfile) then begin
1099     //La ruta correponde a la definida para el menú.
1100     CheckLanguageMenu(XMLfile);  //actualiza menú y panel
1101   end else begin
1102     //es una ruta distinta
1103   end;
1104 end;
1105 procedure TSynFacilEditor.LoadSyntaxFromPath(arc: string = '');
1106 //Carga la sintaxis de un archivo, buscando el archivo XML, apropiado en la ruta
1107 //de lengaujes definida con InitMenuLanguages().
1108 //Si no se indica el nombre del archivo, se usará el archivo actual
1109 var
1110   XML: String;
1111 begin
1112   if arc='' then begin
1113     arc := FileName;
1114   end;
1115   XML := hl.LoadSyntaxFromPath(arc,LangPath);
1116   //marca menú
1117   if XML<>'' then begin  //encontró
1118     if fPanLangName<> nil then begin
1119       fPanLangName.Text:= hl.LangName;
1120     end;
1121     CheckLanguageMenu(XML);  //actualiza menú
1122     exit;
1123   end;
1124   //No encontró archivo XML apropiado
1125   //Carga una sintaxis básica para limpiar la que pudiera haber
1126   hl.ClearMethodTables;           //limpìa tabla de métodos
1127   hl.ClearSpecials;               //para empezar a definir tokens
1128   //crea tokens por contenido
1129   hl.DefTokIdentif('[$A-Za-z_]', '[A-Za-z0-9_]*');
1130   hl.DefTokContent('[0-9]', '[0-9.]*', hl.tnNumber);
1131   hl.DefTokDelim('"','"', hl.tnString);
1132   hl.Rebuild;  //reconstruye
1133   CheckLanguageMenu('');  //actualiza menú
1134   if fPanLangName<> nil then begin
1135     fPanLangName.Text:= dic('Sin lenguaje');
1136   end;
1137 end;
1138 
1139 constructor TSynFacilEditor.Create(ed0: TsynEdit; nomDef0, extDef0: string);
1140 begin
1141   ed := ed0;
1142   hl := TSynFacilComplet.Create(ed.Owner);  //crea resaltador
1143   hl.SelectEditor(ed);  //inicia
1144   //intercepta eventos
1145   ed.OnChange:=@edChange;   //necesita interceptar los cambios
1146   ed.OnStatusChange:=@edStatusChange;
1147   ed.OnMouseDown:=@edMouseDown;
1148   ed.OnKeyUp:=@edKeyUp;     //para funcionamiento del completado
1149   ed.OnKeyDown:=@edKeyDown;
1150   ed.OnKeyPress:=@edKeyPress;
1151   ed.OnUTF8KeyPress:=@edUTF8KeyPress;
1152   ed.OnCommandProcessed:=@edCommandProcessed;  //necesita para actualizar el cursor
1153 //  RecentFiles := TStringList.Create;
1154   MaxRecents := 1;   //Inicia con 1
1155   //guarda parámetros
1156   nomDef := nomDef0;
1157   extDef := extDef0;
1158   NewFile;   //Inicia editor con archivo vacío
1159 end;
1160 destructor TSynFacilEditor.Destroy;
1161 begin
1162   hl.UnSelectEditor;
1163   hl.Free;
1164 //  RecentFiles.Free;
1165   inherited Destroy;
1166 end;
1167 procedure TSynFacilEditor.SetLanguage(lang: string);
1168 //Rutina de traducción
1169 begin
1170   case lowerCase(lang) of
1171   'es': begin
1172       dicClear;  //esttán en este idioma
1173     end;
1174   'en': begin
1175       //configura mensajes
1176       dicSet('fil=%d, col=%d','row=%d col=%d');
1177       dicSet('Guardado','Saved');
1178       dicSet('Sin Guardar','Modified');
1179       dicSet('El archivo %s ha sido modificado.' +  LineEnding + '¿Deseas guardar los cambios?',
1180              'File %s, has been modified.' + LineEnding + 'Save?');
1181       dicSet('No se encuentra el archivo: ', 'File not found: ');
1182       dicSet('Error guardando archivo: ', 'Error saving file: ');
1183       dicSet('El archivo %s ya existe.' + LineEnding + '¿Deseas sobreescribirlo?',
1184              'File %s already exists.' + LineEnding + 'Overwrite?');
1185       dicSet('&Recientes', '&Recents');
1186       dicSet('&Lenguajes', '&Languages');
1187       dicSet('No hay archivos', 'No files');
1188       dicSet('Error Interno: Editor no inicializado.', 'Internal: Not initialized Editor.');
1189       dicSet('Sin lenguaje', 'No language');
1190       dicSet('¡Codificación de archivo desconocida!','Unknown file encoding');
1191       dicSet('Fin de Línea','Line Ending');
1192       dicSet('Codificación','Encoding');
1193       dicSet('No se encuentra: %s','No found: %s');
1194       dicSet('Se reemplazaron %d ocurrencias.','%d occurrences were replaced.');
1195       dicSet('¿Reemplazar esta ocurrencia?','Replace this?');
1196       dicSet('Reemplazo','Replace');
1197     end;
1198   end;
1199 end;
1200 
1201 initialization
1202   //crea diálogos
1203   FindDialog1:= TFindDialog.Create(nil);
1204   ReplaceDialog1:= TReplaceDialog.Create(nil);
1205 finalization;
1206   ReplaceDialog1.Destroy;
1207   FindDialog1.Destroy;
1208 end.
1209 
1210