1 {Frame que contiene a una vista para mostrar lso editores.}
2 unit FrameEditView;
3 {$mode objfpc}{$H+}
4 interface
5 uses
6   Classes, SysUtils, FileUtil, LazUTF8, LazFileUtils, Forms, Controls, Dialogs,
7   ComCtrls, ExtCtrls, Graphics, LCLProc, Menus, LCLType, StdCtrls, strutils,
8   fgl, Types, SynEdit, SynEditMiscClasses, SynEditKeyCmds, SynPluginMultiCaret,
9   SynEditMarkupHighAll, SynEditTypes, SynPluginSyncroEdit, Globales, SynFacilUtils,
10   SynFacilBasic, SynFacilCompletion, SynFacilHighlighter, MisUtils, XpresBas;
11 type
12   { TMarkup }
13   {Marcador para resltar errores de sintaxis en SynEdit}
14   TMarkup = class(TSynEditMarkupHighlightMatches)
15     public
16       procedure SetMark(p1, p2: TPoint);
17   end;
18 
19   { TSynFacilComplet2 }
20   {Versión personalizada de  TSynFacilComplet, que define palabras claves y
21    hace público el campo SpecIdentifiers}
22   TSynFacilComplet2 = class(TSynFacilComplet)
IsKeywordnull23     function IsKeyword(const AKeyword: string): boolean; override;
24   public
25     property SpecIdentif: TArrayTokSpec read SpecIdentifiers;
26 //    SpecIdentifiers: TArrayTokSpec;
27   end;
28 
29   { TSynEditor }
30   {Versión personalizada de TSynFacilEditor, que usa TSynFacilComplet2, como resaltador}
31   TSynEditor = class(TSynFacilEditor)
32   private  //Manejo de edición síncrona
33     cursorPos: array of TPOINT;  //guarda posiciones de cursor
34     procedure AddCursorPos(x,y: integer);
35     procedure SetCursors;
36   private
37     FCaption : string;
38     procedure edSpecialLineMarkup(Sender: TObject; Line: integer;
39       var Special: boolean; Markup: TSynSelectedColor);
40     procedure SetCaption(AValue: string);
41   protected
42     const MAX_NMARK = 4;
43   protected
44     MarkErr: array[0..MAX_NMARK] of TMarkup;   //lista de marcadores
45     MarkFree: integer;  //Índice al marcador libre
GetFreeMarknull46     function GetFreeMark: TMarkup;
47     procedure edKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
48   public  //Inicialización
49     SynEdit: TSynEdit;  //Editor SynEdit
50     x1      : integer;  //Coordenada inicial de dibujo
51     tabWidth: integer;  //ancho de lengueta
52     panTabs : TPanel;   //referencia al Panel de las lenguetas.
53     property Caption: string read FCaption write SetCaption;  //etiqueta de la pestaña
SaveAsDialognull54     function SaveAsDialog(SaveDialog1: TSaveDialog): boolean; override;
SaveQuerynull55     function SaveQuery(SaveDialog1: TSaveDialog): boolean; reintroduce;
56     procedure ClearMarkErr;
57     procedure MarkError(p1: TPoint);
58     constructor Create(AOwner: TComponent; nomDef0, extDef0: string; panTabs0: TPanel); reintroduce;
59     destructor Destroy; override;
60   end;
61 
62   TEditorList = specialize TFPGObjectList<TSynEditor>;
63   TSynEditorEvent = procedure(ed: TSynEditor) of object;
64   { TfraEditView }
65   TfraEditView = class(TFrame)
66     imgBookMarks: TImageList;
67     ImgCompletion: TImageList;
68     lblBackground: TLabel;
69     mnCloseOthers: TMenuItem;
70     mnCloseAll: TMenuItem;
71     mnNewTab: TMenuItem;
72     mnCloseTab: TMenuItem;
73     mnNewTab1: TMenuItem;
74     OpenDialog1: TOpenDialog;
75     Panel1: TPanel;
76     Panel2: TPanel;
77     PopUpTabs: TPopupMenu;
78     SaveDialog1: TSaveDialog;
79     SynPluginSyncroEdit1: TSynPluginSyncroEdit;
80     UpDown1: TUpDown;
81     procedure FrameResize(Sender: TObject);
82     procedure mnCloseOthersClick(Sender: TObject);
83     procedure mnCloseAllClick(Sender: TObject);
84     procedure mnCloseTabClick(Sender: TObject);
85     procedure mnNewTabClick(Sender: TObject);
86     procedure UpDown1Click(Sender: TObject; Button: TUDBtnType);
87   private  //Métodos para dibujo de las lenguetas
88     xIniTabs : integer;  //Coordenada inicial desde donde se dibujan las lenguetas
89     tabDrag  : integer;
90     tabSelec : integer;
91     procedure ConfigureSyntax(ed: TSynEditor; Complete: boolean = true);
92     procedure MakeActiveTabVisible;
93     procedure Panel1DragDrop(Sender, Source: TObject; X, Y: Integer);
94     procedure Panel1DragOver(Sender, Source: TObject; X, Y: Integer;
95       State: TDragState; var Accept: Boolean);
96     procedure Panel1EndDrag(Sender, Target: TObject; X, Y: Integer);
97     procedure RefreshTabs;
98     procedure SetTabIndex(AValue: integer);
99     procedure DibLeng(edi: TSynEditor; coltex: TColor; Activo: boolean; txt: string
100       );   //dibuja una lengueta
101     procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton;
102       Shift: TShiftState; X, Y: Integer);
103     procedure Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer
104       );
105     procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton;
106       Shift: TShiftState; X, Y: Integer);
107     procedure UpdateX1CoordTabs;
108     procedure Panel1Paint(Sender: TObject);
109     procedure InitTabs;
110   private
111     FTabIndex  : integer;
112     FTabViewMode: integer;
113     fMultiCaret: TSynPluginMultiCaret;
114     fSynchro   : TSynPluginSyncroEdit;
115     procedure ChangeEditorState;
116     procedure editChangeFileInform;
GetCanRedonull117     function GetCanRedo: boolean;
GetCanUndonull118     function GetCanUndo: boolean;
GetModifiednull119     function GetModified: boolean;
LastIndexnull120     function LastIndex: integer;
NewNamenull121     function NewName(ext: string): string;
AddEditnull122     function AddEdit(ext: string): TSynEditor;
123     procedure DeleteEdit;
124     procedure SetTabViewMode(AValue: integer);
125   public  //Manejo de pestañas
126     editors    : TEditorList;
127     property TabViewMode: integer read FTabViewMode write SetTabViewMode;  //Modo de visualización
128     property TabIndex: integer read FTabIndex write SetTabIndex;   //panel actualmente activo
Countnull129     function Count: integer;
ActiveEditornull130     function ActiveEditor: TSynEditor;
SearchEditorIdxnull131     function SearchEditorIdx(filname: string): integer;
SearchEditorIdxByTabnull132     function SearchEditorIdxByTab(tabName: string): integer;
SelectEditornull133     function SelectEditor(filname: string): boolean;
134     procedure SelectNextEditor;
135     procedure SelectPrevEditor;
HasFocusnull136     function HasFocus: boolean;
137     procedure SetFocus; override;
138   public  //Eventos
139     OnChangeEditorState: TSynEditorEvent;
140     OnChangeFileInform: procedure of object;
141     OnSelectEditor: procedure of object;  //Cuando cambia la selección de editor
142     OnRequireSynEditConfig: procedure(ed: TsynEdit) of object;
143     OnRequireFieldsComplet: procedure(ident: string; opEve: TFaOpenEvent;
144                                       tokPos: TSrcPos) of object;
145     OnRequireSetCompletion: procedure(ed: TSynEditor) of object;
146   public  //Administración de archivos
147     tmpPath: string;  //ruta usada para crear archivos temporales para los editores
148     property Modified: boolean read GetModified;
149     property CanUndo: boolean read GetCanUndo;
150     property CanRedo: boolean read GetCanRedo;
151     procedure Undo;
152     procedure Redo;
153     procedure SelectAll;
154     procedure NewPasFile;
155     procedure NewLstFile;
LoadFilenull156     function LoadFile(fileName: string): boolean;
SelectOrLoadnull157     function SelectOrLoad(fileName: string): boolean;
SelectOrLoadnull158     function SelectOrLoad(const srcPos: TSrcPos; highlightLine: boolean): boolean;
159     procedure SaveFile;
160     procedure SaveAll;
OpenDialognull161     function OpenDialog: boolean;
SaveAsDialognull162     function SaveAsDialog: boolean;
CloseEditornull163     function CloseEditor: boolean;
CloseAllnull164     function CloseAll(out lstClosedFiles: string): boolean;
165     procedure LoadLastFileEdited;
166     procedure LoadListFiles(lst: string);
167   private  //Manejo de menús recientes
168     mnRecents   : TMenuItem;  //Menú de archivos recientes
169     RecentFiles : TStringList;  //Lista de archivos recientes
170     MaxRecents  : integer;    //Máxima cantidad de archivos recientes
171     procedure RecentClick(Sender: TObject);
172     procedure ActualMenusReciente(Sender: TObject);
173     procedure AgregArcReciente(arch: string);
174   public   //Inicialización
175     procedure UpdateSynEditConfig;
176     procedure UpdateSynEditCompletion;
177     procedure InitMenuRecents(menRecents0: TMenuItem; RecentList: TStringList;
178       MaxRecents0: integer = 5);
179     constructor Create(AOwner: TComponent) ; override;
180     destructor Destroy; override;
181     procedure SetLanguage;
182   end;
183 
184 implementation
185 {$R *.lfm}
186 const
187   MIN_WIDTH_TAB = 50;  //Ancho por defecto de la lengueta
188   FONT_TAB_SIZE = 9;
189   SEPAR_TABS = 2;  //Separación adicional, entre pestañas
190 
191 { TSynFacilComplet2 }
192 var
193   MSG_NOFILES: string;
194   MSG_MODIFSAV: string;
195   MSG_PASFILES: string;
196   MSG_ALLFILES: string;
197   MSG_NOSYNFIL: string;
198 
199 { TMarkup }
200 procedure TMarkup.SetMark(p1, p2: TPoint);
201 begin
202   Matches.StartPoint[0] := p1;
203   Matches.EndPoint[0]   := p2;
204   InvalidateSynLines(p1.y, p2.y);
205 end;
206 
TSynFacilComplet2.IsKeywordnull207 function TSynFacilComplet2.IsKeyword(const AKeyword: string): boolean;
208 {Esta rutina es llamada por el Markup, que resalta palabras iguales. Se implementa
209 para evitar que se resalten palabras muy conocidas}
210 begin
211   //Para el lenguaje Pascal, esta rutina funciona bien
212   case UpCase(AKeyword) of
213   'CONS','VAR','TYPE','BEGIN','END','IF','THEN','ELSE','WHILE',
214   'DO','REPEAT','UNTIL','FOR','TO','AND','OR','XOR','NOT','DIV','MOD','IN':
215     exit(true)
216   else
217     exit(false);
218   end;
219 end;
220 { TSynEditor }
221 procedure TSynEditor.AddCursorPos(x, y: integer);
222 {Agrega una posición de cursor al areglo CursorPos[]}
223 var
224   n: Integer;
225 begin
226   n := high(CursorPos) + 1;
227   setlength(CursorPos, n + 1);
228   CursorPos[n].x := x;
229   CursorPos[n].y := y;
230 end;
231 procedure TSynEditor.SetCursors;
232 var
233   i: Integer;
234 begin
235   if high(cursorPos)<0 then exit;
236 //  ed.CommandProcessor(ecPluginMultiCaretClearAll, '', nil);
237   for i:= high(cursorPos) downto 0 do begin
238     //Explora la revés para dejar el último cursor al inicio del texto
239     if i = 0 then begin
240       //El último
241       ed.CaretY := cursorPos[i].y;   //primero la fila
242       ed.CaretX := cursorPos[i].x;
243       ed.ExecuteCommand(ecPluginMultiCaretSetCaret, '', nil);
244 //    ed.CommandProcessor(ecPluginMultiCaretSetCaret, '', nil);
245     end else begin
246       ed.CaretY := cursorPos[i].y;   //primero la fila
247       ed.CaretX := cursorPos[i].x;
248 //    ed.ExecuteCommand(ecPluginMultiCaretSetCaret, '', nil);
249       ed.CommandProcessor(ecPluginMultiCaretSetCaret, '', nil);
250     end;
251   end;
252 end;
253 procedure TSynEditor.edSpecialLineMarkup(Sender: TObject; Line: integer;
254   var Special: boolean; Markup: TSynSelectedColor);
255 begin
256   if Line = self.linErr then begin
257       Special := True ;  //marca como línea especial
258       Markup.Background := TColor($3030A0); //color de fondo
259   end;
260 end;
261 procedure TSynEditor.SetCaption(AValue: string);
262 {Cambiar el título, cambia el ancho de la lengueta}
263 var
264   w: Integer;
265 begin
266   if FCaption = AValue then Exit;
267   FCaption := AValue;
268   panTabs.Canvas.Font.Size := FONT_TAB_SIZE;  {Fija atrubutos de texto, para que el
269                                         cálculo con "TextWidth", de ancho sea correcto}
270   w := panTabs.Canvas.TextWidth(AValue) + 30;
271   if w < MIN_WIDTH_TAB then w := MIN_WIDTH_TAB;
272   tabWidth := w;
273   panTabs.Invalidate;   //Para refrescar el dibujo
274 end;
275 procedure TSynEditor.edKeyDown(Sender: TObject; var Key: Word;
276   Shift: TShiftState);
277 //var
278 //  lexState: TFaLexerState;
279 begin
280 //  if (Shift = [ssCtrl]) and (Key = VK_J) then begin
281 //    //Exploramos el texto usando el resaltador
282 //    //Utilizaremos el mismo resaltador
283 
284   //    SetCursors;           //Coloca los cursores
285 ////    ed.CommandProcessor(ecSelWordRight, '', nil);
286 //  end;
287   if Key = VK_ESCAPE then begin
288     //Cancela una posible edición de múltiples cursores
289     ed.CommandProcessor(ecPluginMultiCaretClearAll, '', nil);
290   end;
291   //Pasa el evento
292   if OnKeyDown <> nil then OnKeyDown(Sender, Key, Shift);
293 end;
SaveAsDialognull294 function TSynEditor.SaveAsDialog(SaveDialog1: TSaveDialog): boolean;
295 begin
296   SaveDialog1.Filter := MSG_PASFILES + '|*.pas|' + MSG_ALLFILES + '|*.*';
297   SaveDialog1.DefaultExt := '.pas';
298   Result := inherited SaveAsDialog(SaveDialog1);
299   if Result then exit;
300   //Se ha cambiado el nombre del archivo. Actualiza.
301   Caption := ExtractFileName(FileName);
302 end;
TSynEditor.SaveQuerynull303 function TSynEditor.SaveQuery(SaveDialog1: TSaveDialog): boolean;
304 {Versión de SaveQuery(), que verifica si el editor tiene nombre.}
305 //Verifica si es necesario guardar el archivo antes de ejecutar alguna operación.
306 //Si se ignora la acción, devuelve "true".
307 //Si ocurre algún error, muestra el mensaje en pantalla y actualiza "Error".
308 var
309   resp: integer;
310 begin
311   Result := false;
312   if SynEdit.Modified then begin
313     resp := MessageDlg('', Format(MSG_MODIFSAV, [ExtractFileName(FileName)]),
314                        mtConfirmation, [mbYes, mbNo, mbCancel],0);
315     if resp = mrCancel then begin
316       Result := true;   //Sale con "true"
317       Exit;
318     end;
319     if resp = mrYes then begin  //guardar
320       if FileName='' then begin
321         //Es un archivo nuevo
322         SaveAsDialog(SaveDialog1);
323       end else begin
324         SaveFile;  //ACtualiz "Error"
325       end;
326     end;
327   end;
328 end;
GetFreeMarknull329 function TSynEditor.GetFreeMark: TMarkup;
330 //Devuelve referencia a un marcador no usado. Si no encuentra alguno, devuelve NIL.
331 begin
332   if MarkFree <= MAX_NMARK then begin
333     Result := MarkErr[MarkFree];
334     MarkFree := MarkFree + 1;
335   end else begin
336     Result := nil;
337   end;
338 end;
339 procedure TSynEditor.ClearMarkErr;
340 {Limpia marcadores de error.}
341 var
342   i: Integer;
343 begin
344   for i:=0 to MAX_NMARK do begin
345     MarkErr[i].Enabled := false;
346   end;
347   MarkFree := 0;
348   SynEdit.Invalidate;
349 end;
350 procedure TSynEditor.MarkError(p1: TPoint);
351 {Marca el token que se encuentra en la coordenada indicada. Para ubicar al token afectado,
352 usa información del lexer/resaltador del editor, que debe ser consistente con el lexer del
353 compilador, para obtener resultados corectos.
354 En caso de errores dentro de bloques ASM o Directivas, usa un lexer interno simple,
355 porque no se tiene acceso a los lexer que procesan los bloques ASM y Directivas.}
LocEndOfWordnull356   function LocEndOfWord(const lin: string; col1: integer): integer;
357   {Devuelve el final de la palabra actual, que empieza en "col1".}
358   var
359     i: Integer;
360   begin
361     i := col1;  //empìeza por aquí
362     if i>length(lin) then exit(length(lin));
363     if lin[i] in ['A'..'Z','a'..'z','_'] then begin
364       //Es identificador. Ubica los límites del identificador.
365       while (i<=length(lin)) and (lin[i] in ['A'..'Z','a'..'z','0'..'9','_']) do begin
366         inc(i);
367       end;
368     end else begin
369       //Es otro token. Ubica espacio o fin de línea
370       while (i<=length(lin)) and (lin[i]<>' ') do begin
371         inc(i);
372       end;
373     end;
374     Result := i;
375   end;
376 var
377   toks: TATokInfo;
378   tokIdx, col1, col2: integer;
379   curTok: TFaTokInfo;
380   lin: String;
381   MarkErr1: TMarkup;
382 begin
383   hl.ExploreLine(p1, toks, tokIdx);  //Explora la línea aludida
384   if tokIdx = -1 then exit;
385   MarkErr1 := GetFreeMark;
386   if MarkErr1 = nil then exit;
387   curTok := toks[tokIdx];  //token actual
388   //Obtiene línea actual
389   if hl.CurrentLines = nil then begin
390     exit;
391   end else begin
392     lin := hl.CurrentLines[p1.y-1];
393   end;
394   MarkErr1.Enabled := true;
395   //Obtiene en los límites del token actual
396   col1 := curTok.posIni+1;
397   col2 := curTok.posIni+1+curTok.length;
398   if curTok.TokTyp = hl.tnEol then begin
399     //Es la marca de final de línea. Extiende para que sea visible
400     MarkErr1.SetMark(Point(col1, p1.y),
401                     Point(col2 + 1, p1.y));
402   end else if curTok.TokTyp = hl.GetAttribIDByName('Asm') then begin
403     //Es bloque ensamblador.
404     col2 := LocEndOfWord(lin, p1.x);  //ubica a la palabra actual
405     MarkErr1.SetMark(Point(p1.x, p1.y),
406                     Point(col2, p1.y));
407   end else if curTok.TokTyp = hl.GetAttribIDByName('Directive') then begin
408     //Es directiva
409     col2 := LocEndOfWord(lin, p1.x);  //ubica a la palabra actual
410     MarkErr1.SetMark(Point(p1.x, p1.y),
411                     Point(col2, p1.y));
412   end else begin
413     //Es un token normal
414     MarkErr1.SetMark(Point(col1, p1.y),
415                     Point(col2, p1.y));
416   end;
417 end;
418 constructor TSynEditor.Create(AOwner: TComponent; nomDef0, extDef0: string;
419   panTabs0: TPanel);
420 var
421   i: Integer;
422   mark: TMarkup;
423 begin
424   SynEdit:= TSynEdit.Create(AOwner);// Crea un editor
425 //  inherited Create(SynEdit, nomDef0, extDef0);
426   ////////////// Código modificado del constructor /////////////
427   ed := SynEdit;
428   hl := TSynFacilComplet2.Create(ed.Owner);  //crea resaltador
429   hl.SelectEditor(ed);  //inicia
430   //Intercepta eventos
431   ed.OnChange:=@edChange;   //necesita interceptar los cambios
432   ed.OnStatusChange:=@edStatusChange;
433   ed.OnMouseDown:=@edMouseDown;
434   ed.OnKeyUp:=@edKeyUp;     //para funcionamiento del completado
435   ed.OnKeyDown:=@edKeyDown;
436   ed.OnKeyPress:=@edKeyPress;
437   ed.OnUTF8KeyPress:=@edUTF8KeyPress;
438   ed.OnCommandProcessed:=@edCommandProcessed;  //necesita para actualizar el cursor
439 //  RecentFiles := TStringList.Create;
440   MaxRecents := 1;   //Inicia con 1
441   NewFile;   //Inicia editor con archivo vacío
442   ///////////////////////////////////////////////////////////////
443   tabWidth := 30;  //valor por defecto
444   panTabs := panTabs0;
445 
446   //configuración del editor
447   SynEdit.Options:=[eoBracketHighlight];  //quita la línea vertical
448   SynEdit.Options := SynEdit.Options - [eoSmartTabs];
449   SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces];
450   SynEdit.Options := SynEdit.Options + [eoKeepCaretX];
451   SynEdit.Options := SynEdit.Options + [eoTabIndent];  //permite indentar con <Tab>
452   SynEdit.Options2:= SynEdit.Options2 + [eoCaretSkipTab];
453   SynEdit.TabWidth:= 2;
454   SynEdit.OnSpecialLineMarkup:=@edSpecialLineMarkup;
455   InicEditorC1(SynEdit);
456   SynEdit.Options := SynEdit.Options + [eoTabsToSpaces];  //permite indentar con <Tab>
457 
458   //Crea marcadores para los errores de sinatxis
459   for i:=0 to MAX_NMARK do begin
460     mark := TMarkup.Create(SynEdit);
461     MarkErr[i] := mark;   //asigna referencia
462     mark.MarkupInfo.Background := clNone;
463     mark.MarkupInfo.Foreground := clNone;
464     mark.MarkupInfo.FrameColor := clRed;
465     mark.MarkupInfo.FrameEdges := sfeBottom;
466     mark.MarkupInfo.FrameStyle := slsWaved;
467     SynEdit.MarkupManager.AddMarkUp(mark);   //agrega marcador
468   end;
469 
470   NewFile;        //para actualizar estado
471 end;
472 destructor TSynEditor.Destroy;
473 begin
474   inherited Destroy;
475   FreeAndNil(SynEdit);  //El "Owner", intentará destruirlo, por eso lo ponemos en NIL
476 end;
477 { TfraEditView }
478 procedure TfraEditView.SetLanguage;
479 begin
480   {$I ..\language\tra_FrameEditView.pas}
481 end;
482 procedure TfraEditView.RefreshTabs;
483 begin
484   if FTabViewMode = 0 then begin
485     //Se muestran siempre
486     Panel1.Visible := true;
487   end else if FTabViewMode = 1 then begin
488     //Se oculta, cuando hay una sola pestaña
489     if editors.Count = 1 then begin
490       //No vale la pena
491       Panel1.Visible := false;
492     end else begin
493       Panel1.Visible := true;
494     end;
495   end else begin
496     //Se oculta siempre
497     Panel1.Visible := false;
498   end;
499   Panel1.Invalidate;   //para refrescar
500 end;
501 procedure TfraEditView.SetTabIndex(AValue: integer);
502 begin
503   if AValue>editors.Count-1 then AValue := editors.Count-1;
504   if FTabIndex = AValue then Exit;
505   if FTabIndex<>-1 then begin  //que no sea la primera vez
506     editors[FTabIndex].SynEdit.Visible := false;  //oculta el anterior
507   end;
508   FTabIndex := AValue;   //cambia valor
509   editors[FTabIndex].SynEdit.Visible := true;  //muestra el nuevo
510   if OnSelectEditor<>nil then OnSelectEditor;  //dispara evento
511   RefreshTabs;
512 end;
513 //Métodos pàra el dibujo de lenguetas
514 procedure TfraEditView.DibLeng(edi: TSynEditor; coltex: TColor; Activo: boolean;
515   txt: string);
516 var
517   x1, x2: integer;
518 
519   procedure GetX1X2(const xrmin: integer; y: integer; out xr1, xr2: integer);
520   {devuelve las coordenadas x1 y x2 de la línea "y" de la lengueta}
521   begin
522     case y of
523     0: begin  //priemra fila
524         xr1 := x1+4;
525         xr2 := xrmin -4;
526       end;
527     1: begin
528         xr1 := x1+2;
529         xr2 := xrmin -2;
530       end;
531     2: begin
532         xr1 := x1+1;
533         xr2 := xrmin ;
534       end;
535     3: begin
536         xr1 := x1+1;
537         xr2 := xrmin + 1;
538       end;
539     else  //otras filas
540       xr1 := x1;
541       xr2 := xrmin + (y div 2);
542     end;
543   end;
544 var
545   cv: TCanvas;
546   y1, y2, alto, xr1, xr2, xrmin, xrmin2, i: Integer;
547   r: TRect;
548   colBorde: TColor;
549 begin
550   //Lee coordenadas horizontales
551   x1 := edi.x1;
552   x2 := edi.x1 + edi.tabWidth;
553   alto := panel1.Height;
554   y1 := 0;
555   y2 := y1 + alto;
556   //Inicia dibujo
557   cv := Panel1.canvas;
558   cv.Font.Size:= FONT_TAB_SIZE;
559   cv.Font.Color := clBlack;
560   cv.Font.Color := coltex;   //Color de texto
561   //Fija Línea y color de fondo
562   cv.Pen.Style := psSolid;
563   cv.Pen.Width := 1;
564   if Activo then cv.Pen.Color := clWhite else cv.Pen.Color := clMenu;
565   //Dibuja fondo de lengueta. El dibujo es línea por línea
566   xrmin := x2 - (alto div 4);  //corrige inicio, para que el punto medio de la pendiente,  caiga en x2
567   xrmin2 := x2 + (alto div 4)+1;  //corrige inicio, para que el punto medio de la pendiente,  caiga en x2
568   for i:=0 to alto-1 do begin
569     GetX1X2(xrmin, i, xr1, xr2);
570     cv.Line(xr1, i, xr2, i);
571   end;
572   //Dibuja borde de lengueta
573   colBorde := clGray;
574   cv.Pen.Color := colBorde;
575   cv.Line(x1,y1+4,x1,y2);  //lateral izquierdo
576   cv.Line(x1+4,y1, xrmin-4, y1);  //superior
577   cv.Line(xrmin+2, y1+4, xrmin2, y2);  //lateral derecho
578   //Bordes
579   GetX1X2(xrmin, 0, xr1, xr2);
580   cv.Pixels[xr1,0] := colBorde;
581   cv.Pixels[xr2,0] := colBorde;
582   GetX1X2(xrmin, 1, xr1, xr2);
583   cv.Pixels[xr1,1] := colBorde;
584   cv.Pixels[xr1+1,1] := colBorde;
585   cv.Pixels[xr2,1] := colBorde;
586   cv.Pixels[xr2-1,1] := colBorde;
587   GetX1X2(xrmin, 2, xr1, xr2);
588   cv.Pixels[xr1,2] := colBorde;
589   cv.Pixels[xr2,2] := colBorde;
590   cv.Pixels[xr2-1,2] := colBorde;
591   GetX1X2(xrmin, 3, xr1, xr2);
592   cv.Pixels[xr1,3] := colBorde;
593   cv.Pixels[xr2,3] := colBorde;
594   //Dibuja ícono
595   ImgCompletion.Draw(cv, x1+4, 4, 7);
596   //Elimina objetos y pone texto
597   r.Top := y1;
598   r.Bottom := y2;
599   r.Left := x1+20;  //Deja espacio para el ícono
600   r.Right := x2-7;  //deja espacio para el botón de cierre
601   cv.TextRect(r, x1+22, 4 ,txt);
602 end;
603 procedure TfraEditView.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
604   Shift: TShiftState; X, Y: Integer);
605 var
606   x2, i: Integer;
607   edi: TSynEditor;
608 begin
609   {Se asuma que las lenguetas ya tienen su coordenada x1, actualizada, porque ya
610   han sido dibujadas, así que no llamaremos a UpdateX1CoordTabs.}
611   for i := 0 to editors.Count-1 do begin
612     edi := editors[i];
613     x2 := edi.x1 + edi.tabWidth;
614     if (X>edi.x1) and (X<x2) then begin
615       TabIndex := i;  //Selecciona
616       if Shift = [ssRight] then begin
617         PopUpTabs.PopUp;
618       end else if Shift = [ssMiddle] then begin
619         //Cerrar el archivo
620         CloseEditor;
621       end else if Shift = [ssLeft] then begin
622         //Solo selección
623         MakeActiveTabVisible;
624         //Inicia el arrastre
625         Panel1.BeginDrag(false, 10);
626         tabDrag := i;  //gaurda el índice del arrastrado
627       end;
628       exit;
629     end;
630   end;
631 end;
632 procedure TfraEditView.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
633   Y: Integer);
634 begin
635 //  debugln('Move');
636 end;
637 procedure TfraEditView.Panel1DragOver(Sender, Source: TObject; X, Y: Integer;
638   State: TDragState; var Accept: Boolean);
639 var
640   edi: TSynEditor;
641   x2, i, x2Mid: Integer;
642 begin
643   Accept := true;
644   //Ve a cual lengüeta selecciona
645   tabSelec := -1;
646   for i := 0 to editors.Count-1 do begin
647     edi := editors[i];
648     x2Mid := edi.x1 + edi.tabWidth div 2;
649     x2 := edi.x1 + edi.tabWidth;
650     if (X>edi.x1) and (X<x2) then begin
651       if X<x2Mid then begin
652         //Está en la primera mitad.
653         tabSelec := i;  //Selecciona
654       end else begin
655         //En la mitad final, selecciona el siguiente
656         tabSelec := i+1;  //Selecciona
657       end;
658     end;
659   end;
660   //Genera marca en la lengüeta
661   if tabSelec<>-1 then begin
662 //    debugln('leng selec: %d', [tabselec]);
663     Panel1.Invalidate;
664   end;
665 end;
666 procedure TfraEditView.Panel1EndDrag(Sender, Target: TObject; X, Y: Integer);
667 {Se termina el arrastre, sea que se soltó en alguna parte, o se canceló.}
668 begin
669   tabSelec := -1;
670   Panel1.Invalidate;
671 end;
672 procedure TfraEditView.Panel1DragDrop(Sender, Source: TObject; X, Y: Integer);
673 {Se soltó la lengueta en el panel.}
674 begin
675   if TabIndex<0 then exit;
676   if tabSelec<0 then exit;
677   //Corrección
678   if tabSelec>TabIndex then tabSelec := tabSelec-1;
679   if tabSelec>editors.Count-1 then exit;
680 //  debugln('Panel1DragDrop: %d a %d', [TabIndex, tabSelec]);
681   editors.Move(TabIndex, tabSelec);
682   TabIndex := tabSelec;
683   Panel1.Invalidate;
684 end;
685 procedure TfraEditView.Panel1MouseUp(Sender: TObject; Button: TMouseButton;
686   Shift: TShiftState; X, Y: Integer);
687 begin
688   //Pasa el enfoque al editor que se ha seleccionado
689   if TabIndex<>-1 then begin
690     try
691       editors[TabIndex].SynEdit.SetFocus;
692     except
693 
694     end;
695   end;
696 end;
697 procedure TfraEditView.UpdateX1CoordTabs;
698 {Actualiza la coordenada x1, de las lenguetas, considerando el valor actual de
699 "xIniTabs". El valor x1, representa la coordenada en que se dibuajaría la lengueta.}
700 var
701   i, x1: integer;
702   edi: TSynEditor;
703 begin
704   {Este algoritmo debe ser igual a Panel1Paint(), para que no haya inconsistencias.}
705   x1 := xIniTabs;
706   for i := 0 to editors.Count-1 do begin
707     edi := editors[i];
708     edi.x1 := x1;   //Actualiza coordenada
709     //Calcula siguiente coordenada
710     x1 := x1 + edi.tabWidth + SEPAR_TABS;
711   end;
712 end;
713 procedure TfraEditView.MakeActiveTabVisible;
714 {Configura "xIniTabs", de modo que haga visible la pestaña del editor activo.}
715 var
716   x1, x2: integer;
717 begin
718   if Count=0 then exit;
719   UpdateX1CoordTabs;
720   x1 := ActiveEditor.x1;
721   x2 := ActiveEditor.x1 + ActiveEditor.tabWidth;
722   if x2 > self.Width then begin
723     //Pestaña sale de página, por la derecha
724     xIniTabs := xIniTabs - (x2-self.Width);
725   end else if x1 < Panel2.Width then begin
726     //Pestaña sale de página, por la izquierda
727     xIniTabs := xIniTabs + (Panel2.Width - x1);
728   end else begin
729 //    debugln('Pestaña se dibuja adentro');
730   end;
731 end;
732 procedure TfraEditView.Panel1Paint(Sender: TObject);
733 var
734   i, x1: Integer;
735   edi: TSynEditor;
736   cv: TCanvas;
737 begin
738   //Actualzia coordenadas
739   UpdateX1CoordTabs;
740   //Dibuja las pestañas
741   for i := 0 to editors.Count-1 do begin
742     edi := editors[i];
743     if i <> TabIndex then begin
744       //Dibuja todo menos al activo, lo deja para después.
745       DibLeng(edi, clBlack, false, edi.Caption);
746     end;
747   end;
748   //Dibuja al final al activo, para que aparezca encima
749   if TabIndex<>-1 then begin
750     edi := editors[TabIndex];
751     DibLeng(edi, clBlack, true, edi.Caption);
752   end;
753   //Dibuja la marca de movimiento de lengüeta
754   if (tabSelec>=0) and (tabSelec<editors.Count) then begin
755     edi := editors[tabSelec];
756     x1 := edi.x1+2;
757     cv := Panel1.canvas;
758     cv.Pen.Width := 5;
759     cv.Pen.Color := clGray;
760     cv.Line(x1 ,0, x1, Panel1.Height);
761   end else if tabSelec = editors.Count then begin
762     //Se marac al final de la última pestaña
763     edi := editors[editors.Count-1];  //el útlimo
764     x1 := edi.x1 + edi.tabWidth +2;
765     cv := Panel1.canvas;
766     cv.Pen.Width := 5;
767     cv.Pen.Color := clGray;
768     cv.Line(x1 ,0, x1, Panel1.Height);
769   end;
770 end;
771 procedure TfraEditView.InitTabs;
772 {Inicia el dibujo de las lenguetas}
773 begin
774   xIniTabs := panel2.Width;  //Empeiza dibujando al lado de las flechas
775   Panel1.OnMouseMove := @Panel1MouseMove;
776   panel1.OnMouseDown := @Panel1MouseDown;
777   panel1.OnMouseUp   := @Panel1MouseUp;
778   panel1.OnDragOver := @Panel1DragOver;
779   panel1.OnDragDrop := @Panel1DragDrop;
780   panel1.OnEndDrag := @Panel1EndDrag;
781 end;
782 //////////////////////////////////////////////////////////////
783 procedure TfraEditView.ChangeEditorState;
784 var
785   ed: TSynEditor;
786 begin
787   ed := ActiveEditor;
788   if OnChangeEditorState<>nil then OnChangeEditorState(ed);
789 end;
790 procedure TfraEditView.editChangeFileInform;
791 begin
792   if OnChangeFileInform<>nil then OnChangeFileInform;
793 end;
GetCanRedonull794 function TfraEditView.GetCanRedo: boolean;
795 var
796   ed: TSynEditor;
797 begin
798   if editors.Count = 0 then exit(false);
799   //Busca editor actual
800   ed := ActiveEditor;
801   Result := ed.CanRedo;
802 end;
TfraEditView.GetCanUndonull803 function TfraEditView.GetCanUndo: boolean;
804 var
805   ed: TSynEditor;
806 begin
807   if editors.Count = 0 then exit(false);
808   //Busca editor actual
809   ed := ActiveEditor;
810   Result := ed.CanUndo;
811 end;
TfraEditView.GetModifiednull812 function TfraEditView.GetModified: boolean;
813 var
814   ed: TSynEditor;
815 begin
816   if editors.Count = 0 then exit(false);
817   //Busca editor actual
818   ed := ActiveEditor;
819   Result := ed.Modified;
820 end;
LastIndexnull821 function TfraEditView.LastIndex: integer;
822 {Devuelve el índice de la última pestaña.}
823 begin
824   Result :=editors.Count - 1;
825 end;
TfraEditView.NewNamenull826 function TfraEditView.NewName(ext: string): string;
827 {Genera un nombre de archivo que no se repita enter las pestañas abiertas.}
828 var
829   n: Integer;
830 begin
831   n := 0;
832   repeat
833     inc(n);
834     Result := 'NewFile' + IntToStr(n) + ext;
835   until SearchEditorIdxByTab(Result)=-1;
836 end;
AddEditnull837 function TfraEditView.AddEdit(ext: string): TSynEditor;
838 {Agrega una nueva ventana de eición a la vista, y devuelve la referencia.}
839 var
840   ed: TSynEditor;
841   n: Integer;
842 begin
843   //Crea Editor.
844   ed := TSynEditor.Create(self, 'SinNombre', 'pas', Panel1);
845   ed.OnChangeEditorState := @ChangeEditorState;
846   ed.OnChangeFileInform := @editChangeFileInform;
847   ed.hl.IconList := ImgCompletion;
848   //Configura PageControl
849   ed.SynEdit.Parent := self;
850   ed.SynEdit.Align := alClient;
851   //Fija imágenes para marcadores
852   ed.SynEdit.BookMarkOptions.BookmarkImages := imgBookMarks;
853 
854   //Configura el borrado de la palabra actual
855   n := 46;
856   ed.SynEdit.Keystrokes.BeginUpdate;
857   ed.SynEdit.Keystrokes[n].Key := VK_DELETE;
858   ed.SynEdit.Keystrokes[n].Shift := [ssCtrl];
859   ed.SynEdit.Keystrokes[n].ShiftMask := [];
860   ed.SynEdit.Keystrokes.EndUpdate;
861   //Deshabilita Ctrl+N
862   n := ed.SynEdit.Keystrokes.FindCommand(ecInsertLine);
863   ed.SynEdit.Keystrokes[n].ShortCut := 0;   //Esto debe dehabilitarlo
864   n := 84;
865   ed.SynEdit.Keystrokes.BeginUpdate;
866   ed.SynEdit.Keystrokes[n].Key := VK_SUBTRACT;
867   ed.SynEdit.Keystrokes[n].Shift := [ssShift,ssAlt];
868   ed.SynEdit.Keystrokes[n].ShiftMask := [];
869   ed.SynEdit.Keystrokes.EndUpdate;
870   //Configura el desplegado con Alt+Shift+"+"
871   n := 85;
872   ed.SynEdit.Keystrokes.BeginUpdate;
873   ed.SynEdit.Keystrokes[n].Key := VK_ADD;
874   ed.SynEdit.Keystrokes[n].Shift := [ssShift,ssAlt];
875   ed.SynEdit.Keystrokes[n].ShiftMask := [];
876   ed.SynEdit.Keystrokes.EndUpdate;
877 
878   //Crea un "plugin" de edición síncrona
879   fSynchro := TSynPluginSyncroEdit.Create(self);
880   fSynchro.Editor := ed.SynEdit;
881 
882   //Configura múltiples cursores
883   fMultiCaret := TSynPluginMultiCaret.Create(self);
884   with fMultiCaret do begin
885     Editor := ed.SynEdit;
886     with KeyStrokes do begin
887       Add.Command    := ecPluginMultiCaretSetCaret;
888       Add.Key        := VK_INSERT;
889       Add.Shift      := [ssShift, ssCtrl];
890       Add.ShiftMask  := [ssShift,ssCtrl,ssAlt];
891 //      Add.Command    := ecPluginMultiCaretUnsetCaret;
892 //      Add.Key        := VK_DELETE;
893 //      Add.Shift      := [ssShift, ssCtrl];
894 //      Add.ShiftMask  := [ssShift,ssCtrl,ssAlt];
895     end;
896   end;
897 
898   ed.Caption := NewName(ext);   //Pone nombre diferente
899   ed.FileName := '';  //Pone sin nombre para saber que no se ha guardado
900   if OnRequireSynEditConfig<>nil then  //Configura
901     OnRequireSynEditConfig(ed.SynEdit);
902   editors.Add(ed);   //agrega a la lista
903   TabIndex := LastIndex;
904   //Configura desplazamiento para asegurarse que la pestaña se mostrará visible
905   MakeActiveTabVisible;
906   //Actualiza referencias
907   Result := ed;
908 end;
909 procedure TfraEditView.DeleteEdit;
910 {Elimina al editor activo.}
911 begin
912   if TabIndex=-1 then exit;
913   editors.Delete(TabIndex);
914   //Hay que actualiza TabIndex
915   if editors.Count = 0 then begin
916     //Era el único
917     FTabIndex := -1;
918     FrameResize(self);  //para ubciar mensaje de fondo
919   end else begin
920     //Había al menos 2
921     if TabIndex > editors.Count - 1 then begin
922       //Quedó apuntando fuera
923       FTabIndex := editors.Count - 1;   //limita
924       //No es necesario ocultar el anterior, porque se eliminó
925       editors[FTabIndex].SynEdit.Visible := true;  //muestra el nuevo
926     end else begin
927       //Queda apuntando al siguiente. No es necesario modificar.
928       //No es necesario ocultar el anterior, porque se eliminó
929       editors[FTabIndex].SynEdit.Visible := true;  //muestra el nuevo
930     end;
931   end;
932   MakeActiveTabVisible;
933   if OnSelectEditor<>nil then OnSelectEditor;
934   RefreshTabs;
935 end;
936 procedure TfraEditView.SetTabViewMode(AValue: integer);
937 begin
938   if FTabViewMode = AValue then Exit;
939   FTabViewMode := AValue;
940   RefreshTabs;
941 end;
942 ///Manejo de pestañas
TfraEditView.Countnull943 function TfraEditView.Count: integer;
944 begin
945   Result := editors.Count;
946 end;
ActiveEditornull947 function TfraEditView.ActiveEditor: TSynEditor;
948 {Devuelve el editor SynEditor, activo, es decir el que se encuentra en la lengueta
949 activa. }
950 var
951   i: Integer;
952 begin
953   if editors.Count = 0 then exit(nil);
954   i := TabIndex;
955   Result := editors[i];   //Solo funcionará si no se desordenan las enguetas
956 end;
TfraEditView.SearchEditorIdxnull957 function TfraEditView.SearchEditorIdx(filname: string): integer;
958 {Busca entre las ventanas abiertas al archivo indicado, y devuelve el índice del
959 editor. Si no lo encuentra devuelve -1}
960 var
961   ed: TSynEditor;
962   i: integer;
963 begin
964   for i:=0 to editors.Count-1 do begin
965     ed := editors[i];
966     if Upcase(ed.FileName) = UpCase(filname) then exit(i);
967   end;
968   exit(-1);
969 end;
SearchEditorIdxByTabnull970 function TfraEditView.SearchEditorIdxByTab(tabName: string): integer;
971 var
972   ed: TSynEditor;
973   i: integer;
974 begin
975   for i:=0 to editors.Count-1 do begin
976     ed := editors[i];
977     if Upcase(ed.Caption) = UpCase(tabName) then exit(i);
978   end;
979   exit(-1);
980 end;
SelectEditornull981 function TfraEditView.SelectEditor(filname: string): boolean;
982 {Activa el editor que corresponde al archivo indicado. Si no encuentra el archivo,
983 devuelve FALSE.}
984 var
985   edIdx: integer;
986 begin
987   if editors.Count=0 then exit(false);  //no hay ventanas
988   if filname = '@' then begin
989     //"@", indica el editor actual
990     exit(true);
991   end;
992   edIdx := SearchEditorIdx(filname);
993   if edIdx = -1 then begin
994     exit(false);
995   end else begin
996     TabIndex := edIdx;
997     exit(true);
998   end;
999 end;
1000 procedure TfraEditView.SelectNextEditor;
1001 {Selecciona al siguiente editor.}
1002 begin
1003   if Count = 0 then exit;
1004   if TabIndex=-1 then exit;
1005   if TabIndex = LastIndex then TabIndex := 0 else TabIndex := TabIndex + 1;
1006   SetFocus;
1007   MakeActiveTabVisible;
1008 end;
1009 procedure TfraEditView.SelectPrevEditor;
1010 {Selecciona al editor anterior.}
1011 begin
1012   if Count = 0 then exit;
1013   if TabIndex=-1 then exit;
1014   if TabIndex = 0 then TabIndex := LastIndex else TabIndex := TabIndex -1;
1015   SetFocus;
1016   MakeActiveTabVisible;
1017 end;
HasFocusnull1018 function TfraEditView.HasFocus: boolean;
1019 {Indica si alguno de los editores, tiene el enfoque.}
1020 var
1021   i: Integer;
1022 begin
1023   for i:=0 to editors.Count-1 do begin
1024     if editors[i].SynEdit.Focused then exit(true);
1025   end;
1026   exit(false);
1027 end;
1028 procedure TfraEditView.SetFocus;
1029 begin
1030 //  inherited SetFocus;
1031   if TabIndex = -1 then exit;
1032   if editors[TabIndex].SynEdit.Visible then begin
1033     editors[TabIndex].SynEdit.SetFocus;
1034   end;
1035 end;
1036 procedure TfraEditView.Undo;
1037 var
1038   ed: TSynEditor;
1039 begin
1040   if editors.Count = 0 then exit;
1041   //Busca editor actual
1042   ed := ActiveEditor;
1043   ed.Undo;
1044 end;
1045 procedure TfraEditView.Redo;
1046 var
1047   ed: TSynEditor;
1048 begin
1049   if editors.Count = 0 then exit;
1050   //Busca editor actual
1051   ed := ActiveEditor;
1052   ed.Redo;
1053 end;
1054 procedure TfraEditView.SelectAll;
1055 var
1056   ed: TSynEditor;
1057 begin
1058   if editors.Count = 0 then exit;
1059   //Busca editor actual
1060   ed := ActiveEditor;
1061   ed.SelectAll;
1062 end;
1063 procedure TfraEditView.ConfigureSyntax(ed: TSynEditor; Complete: boolean = true);
1064 var
1065   synFile: String;
1066   ext: string;
1067 begin
1068   ext := ExtractFileExt(ed.FileName);
1069   case Upcase(ext) of
1070   '.PAS': begin
1071       //Es Pascal
1072       synFile := patSyntax + DirectorySeparator + 'PicPas_PIC16.xml';
1073       if not FileExists(synFile) then begin
1074         MsgErr(MSG_NOSYNFIL, [synFile]);
1075         exit;
1076       end;
1077       ed.LoadSyntaxFromFile(synFile);
1078       if Complete then begin
1079         //Configura eventos de apertura para nombres de unidades.
1080         if OnRequireSetCompletion<>nil then OnRequireSetCompletion(ed);
1081       end;
1082     end;
1083   '.ASM','.LST': begin
1084       //Es Ensamblador
1085       synFile := patSyntax + DirectorySeparator + 'PicPas_AsmPic.xml';
1086       if not FileExists(synFile) then begin
1087         MsgErr(MSG_NOSYNFIL, [synFile]);
1088         exit;
1089       end;
1090       ed.LoadSyntaxFromFile(synFile);
1091       if Complete then begin
1092         //Configura eventos de apertura.
1093      end;
1094    end;
1095   '.C': begin
1096      //Es C
1097      synFile := patSyntax + DirectorySeparator + 'PicPas_C.xml';
1098      if not FileExists(synFile) then begin
1099        MsgErr(MSG_NOSYNFIL, [synFile]);
1100        exit;
1101      end;
1102      ed.LoadSyntaxFromFile(synFile);
1103      if Complete then begin
1104         //Configura eventos de apertura.
1105       end;
1106    end;
1107   end;
1108 end;
1109 //Administración de archivos
1110 procedure TfraEditView.NewPasFile;
1111 {Abre una nueva ventana de edición.}
1112 var
1113   ed: TSynEditor;
1114 begin
1115   ed := AddEdit('.pas');
1116   ed.FileName := tmpPath + DirectorySeparator + ed.Caption;
1117   ConfigureSyntax(ed);
1118   AgregArcReciente(ed.FileName);
1119 end;
1120 procedure TfraEditView.NewLstFile;
1121 {Abre una nueva ventana de edición.}
1122 var
1123   ed: TSynEditor;
1124 begin
1125   ed := AddEdit('.lst');
1126   ed.FileName := tmpPath + DirectorySeparator + ed.Caption;
1127   ConfigureSyntax(ed);
1128 //  AgregArcReciente(ed.FileName);
1129 end;
TfraEditView.LoadFilenull1130 function TfraEditView.LoadFile(fileName: string): boolean;
1131 //Carga un archivo en el editor. Si encuentra algún error. Devuelve FALSE.
1132 var
1133   ed: TSynEditor;
1134 begin
1135   Result := true;   //por defecto
1136   if SelectEditor(filename) then exit; //Ya estaba abierto
1137   ed := AddEdit('');   //Dispara OnSelecEditor
1138   if Pos(DirectorySeparator, fileName) = 0 then begin
1139     //Es ruta relativa, la vuelve abosulta
1140     fileName := patApp + fileName;
1141   end;
1142   ed.LoadFile(fileName);
1143   if ed.Error='' then begin
1144     AgregArcReciente(fileName);
1145   end else begin
1146     Result := false;  //Hubo error
1147   end;
1148   //Carga la sintaxis apropiada
1149   ConfigureSyntax(ed);
1150   //ed.LoadSyntaxFromPath;  //para que busque el archivo apropiado
1151   ed.Caption := ExtractFileName(fileName);
1152   {Dispara otra vez, para actualizar bien el nombre del archivo, en el Caption de la
1153   ventana principal.}
1154   if OnSelectEditor<>nil then OnSelectEditor;
1155 end;
TfraEditView.SelectOrLoadnull1156 function TfraEditView.SelectOrLoad(fileName: string): boolean;
1157 {Selecciona la ventana del editor que contiene al archivo solicitado. Si no lo tiene
1158 abierto, lo intenta abrir. Si falla, devuelve FALSE}
1159 begin
1160   //Se ha identificado el archivo con el error
1161   if SelectEditor(filename) then begin
1162     //Lo tenía abierto.
1163     exit(true);
1164   end else begin
1165     //No está abierto el archivo, lo abrimos
1166     Result := LoadFile(filename);
1167   end;
1168 end;
TfraEditView.SelectOrLoadnull1169 function TfraEditView.SelectOrLoad(const srcPos: TSrcPos; highlightLine: boolean): boolean;
1170 //Versión de SelectOrLoad(), que además posiciona el cursor en la coordenada indicada
1171 begin
1172   Result := SelectOrLoad(srcPos.fil);
1173   if Result then begin
1174     if (srcpos.row>=0) and (srcpos.col>=0)  then begin
1175       //posiciona curosr
1176       ActiveEditor.SynEdit.CaretY := srcPos.row;
1177 //      ActiveEditor.SynEdit.CaretX := srcPos.col;
1178       ActiveEditor.SynEdit.LogicalCaretXY := Point(srcPos.col, srcPos.row);
1179       //Define línea con error
1180       if highlightLine then ActiveEditor.linErr := srcPos.row;
1181       ActiveEditor.SynEdit.Invalidate;  //refresca
1182       SetFocus;
1183     end;
1184   end;
1185 end;
TfraEditView.OpenDialognull1186 function TfraEditView.OpenDialog: boolean;
1187 //Muestra el cuadro de diálogo para abrir un archivo. Si hay error devuelve FALSE.
1188 var arc0: string;
1189 begin
1190   OpenDialog1.Filter:= MSG_PASFILES + '|*.pas|' + MSG_ALLFILES + '|*.*';
1191   if not OpenDialog1.Execute then exit(true);    //se canceló
1192   arc0 := OpenDialog1.FileName;
1193   LoadFile(arc0);  //legalmente debería darle en UTF-8
1194   Result := true;   //sale sin incidencias
1195 end;
1196 procedure TfraEditView.SaveFile;
1197 //Guarda el editor actual
1198 begin
1199   if ActiveEditor=nil then exit;
1200   if ActiveEditor.FileName='' then begin
1201     //Es un archivo nuevo
1202     ActiveEditor.SaveAsDialog(SaveDialog1);
1203   end else begin
1204     ActiveEditor.SaveFile;
1205   end;
1206   //Actualiza por si acaso, era un archivo nuevo
1207   AgregArcReciente(ActiveEditor.FileName);
1208 end;
1209 procedure TfraEditView.SaveAll;
1210 {Guarda todas las ventanas abiertas en el editor.}
1211 var
1212   i: Integer;
1213 begin
1214   for i:=0 to editors.Count-1 do begin
1215     if editors[i].Modified then begin
1216       //Actualiza por si acaso, era un archivo nuevo
1217       AgregArcReciente(editors[i].FileName);
1218     end;
1219     if editors[i].FileName<>'' then begin
1220       //No deberái pasar que el archivo esté sin nombre.
1221       editors[i].SaveFile;
1222     end;
1223   end;
1224 end;
TfraEditView.SaveAsDialognull1225 function TfraEditView.SaveAsDialog: boolean;
1226 {Muestra la ventana para grabar un archivo. Si se cancela, devuelve TRUE.}
1227 begin
1228   if ActiveEditor=nil then exit(true);
1229   Result := ActiveEditor.SaveAsDialog(SaveDialog1);
1230   if Result then exit;   //se canceló
1231   if OnSelectEditor<>nil then OnSelectEditor;
1232 end;
TfraEditView.CloseEditornull1233 function TfraEditView.CloseEditor: boolean;
1234 {Cierra el editor actual. Si se cancela el mensaje de "Grabar", devuelve FALSE}
1235 begin
1236   if ActiveEditor=nil then exit(true);
1237   if ActiveEditor.SaveQuery(SaveDialog1) then
1238     exit(false);  //cancelado
1239   DeleteEdit;
1240   exit(true);
1241 end;
CloseAllnull1242 function TfraEditView.CloseAll(out lstClosedFiles: string): boolean;
1243 {Cierra todas las ventanas, pidiendo confirmación. Si se cancela, devuelve TRUE.
1244 Se devuelve en "lstOpenedFiles" una lista con los archivos que estaban abiertos.}
1245 begin
1246   lstClosedFiles := '';
1247   while editors.Count>0 do begin
1248     lstClosedFiles := lstClosedFiles + ActiveEditor.FileName + LineEnding;
1249     if ActiveEditor=nil then exit(true);
1250     if ActiveEditor.SaveQuery(SaveDialog1) then exit(true);  //cancelado
1251     DeleteEdit;
1252   end;
1253   exit(false);
1254 end;
1255 procedure TfraEditView.LoadLastFileEdited;
1256 {Carga el último archivo de la lista de recientes}
1257 begin
1258   if mnRecents.Count = 0 then exit;
1259   ActualMenusReciente(self);
1260   mnRecents.Items[0].Click;
1261 end;
1262 procedure TfraEditView.LoadListFiles(lst: string);
1263 var
1264   a: TStringDynArray;
1265   i: Integer;
1266   filName: String;
1267 begin
1268   a := Explode(LineEnding, lst);
1269   for i:=0 to high(a) do begin
1270      filName := trim(a[i]);
1271      if filName = '' then continue;
1272      LoadFile(filName);
1273   end;
1274 end;
1275 procedure TfraEditView.RecentClick(Sender: TObject);
1276 //Se selecciona un archivo de la lista de recientes
1277 var
1278   cap, recFile: string;
1279 begin
1280   cap := TMenuItem(Sender).Caption;
1281   recFile := MidStr(cap, 4,150);
1282   if not FileExistsUTF8(recFile) then exit;
1283   LoadFile(recFile);
1284 end;
1285 procedure TfraEditView.ActualMenusReciente(Sender: TObject);
1286 {Actualiza el menú de archivos recientes con la lista de los archivos abiertos
1287 recientemente. }
1288 var
1289   i: Integer;
1290 begin
1291   if mnRecents = nil then exit;
1292   if RecentFiles = nil then exit;
1293   //proteciión
1294   if RecentFiles.Count = 0 then begin
1295     mnRecents[0].Caption := MSG_NOFILES;
1296     mnRecents[0].Enabled:=false;
1297     for i:= 1 to mnRecents.Count-1 do begin
1298       mnRecents[i].Visible:=false;
1299     end;
1300     exit;
1301   end;
1302   //hace visible los ítems
1303   mnRecents[0].Enabled:=true;
1304   for i:= 0 to mnRecents.Count-1 do begin
1305     if i<RecentFiles.Count then
1306       mnRecents[i].Visible:=true
1307     else
1308       mnRecents[i].Visible:=false;
1309   end;
1310   //pone etiquetas a los menús, incluyendo un atajo numérico
1311   for i:=0 to RecentFiles.Count-1 do begin
1312     mnRecents[i].Caption := '&'+IntToStr(i+1)+' '+RecentFiles[i];
1313   end;
1314 end;
1315 procedure TfraEditView.AgregArcReciente(arch: string);
1316 //Agrega el nombre de un archivo reciente
1317 var hay: integer; //bandera-índice
1318     i: integer;
1319 begin
1320   if RecentFiles = nil then exit;
1321   //verifica si ya existe
1322   hay := -1;   //valor inicial
1323   for i:= 0 to RecentFiles.Count-1 do
1324     if RecentFiles[i] = arch then hay := i;
1325   if hay = -1 then  //no existe
1326     RecentFiles.Insert(0,arch)  //agrega al inicio
1327   else begin //ya existe
1328     RecentFiles.Delete(hay);     //lo elimina
1329     RecentFiles.Insert(0,arch);  //lo agrega al inicio
1330   end;
1331   while RecentFiles.Count>MaxRecents do  //mantiene tamaño máximo
1332     RecentFiles.Delete(MaxRecents);
1333 end;
1334 procedure TfraEditView.UpdateSynEditConfig;
1335 {Indica que se desea cambiar la configuración de todos los SynEdit abiertos.}
1336 var
1337   i: Integer;
1338 begin
1339   //Pide configuración para todos los editores abiertos
1340   for i:=0 to editors.Count-1 do begin
1341     if OnRequireSynEditConfig<>nil then begin
1342       OnRequireSynEditConfig(editors[i].SynEdit);
1343     end;
1344     //Actualiza resaltador
1345     ConfigureSyntax(editors[i]);
1346   end;
1347 end;
1348 procedure TfraEditView.UpdateSynEditCompletion;
1349 var
1350   i: Integer;
1351 begin
1352   //Pide configurar completado para todos los editores abiertos
1353   for i:=0 to editors.Count-1 do begin
1354     if OnRequireSetCompletion<>nil then OnRequireSetCompletion(editors[i]);
1355   end;
1356 end;
1357 
1358 //Inicialización
1359 procedure TfraEditView.InitMenuRecents(menRecents0: TMenuItem; RecentList: TStringList;
1360       MaxRecents0: integer=5);
1361 //Configura un menú, con el historial de los archivos abiertos recientemente
1362 //"nRecents", es el número de archivos recientes que se guardará
1363 var
1364   i: Integer;
1365 begin
1366   mnRecents := menRecents0;
1367   RecentFiles := RecentList;  //gaurda referencia a lista
1368   MaxRecents := MaxRecents0;
1369   //configura menú
1370   mnRecents.OnClick:=@ActualMenusReciente;
1371   for i:= 1 to MaxRecents do begin
1372     AddItemToMenu(mnRecents, '&'+IntToStr(i), @RecentClick);
1373   end;
1374 end;
1375 constructor TfraEditView.Create(AOwner: TComponent);
1376 begin
1377   inherited;
1378   editors:= TEditorList.Create(true);
1379   panel1.OnPaint := @Panel1Paint;
1380   FTabIndex := -1;
1381   InitTabs;
1382   tabSelec := -1;
1383 end;
1384 destructor TfraEditView.Destroy;
1385 begin
1386   editors.Destroy;
1387   inherited Destroy;
1388 end;
1389 //Menú
1390 procedure TfraEditView.mnNewTabClick(Sender: TObject);
1391 begin
1392   NewPasFile;
1393   SetFocus;
1394 end;
1395 procedure TfraEditView.UpDown1Click(Sender: TObject; Button: TUDBtnType);
1396 begin
1397   case Button of
1398   btNext: SelectNextEditor;
1399   btPrev: SelectPrevEditor;
1400   end;
1401 end;
1402 procedure TfraEditView.mnCloseTabClick(Sender: TObject);
1403 begin
1404   CloseEditor;
1405   SetFocus;
1406 end;
1407 procedure TfraEditView.mnCloseAllClick(Sender: TObject);
1408 begin
1409   while self.Count>0 do begin
1410     if not CloseEditor then
1411       break;  //Se canceló
1412   end;
1413   SetFocus;
1414 end;
1415 procedure TfraEditView.mnCloseOthersClick(Sender: TObject);
1416 var
1417   nBefore, i, nAfter: Integer;
1418 begin
1419   //Cierra anteriores
1420   nBefore := TabIndex;
1421   for i:= 1 to nBefore do begin
1422     TabIndex := 0;
1423     if not CloseEditor then
1424       break;  //Se canceló
1425   end;
1426   //Cierra posteriores
1427   nAfter := Count - TabIndex - 1;
1428   for i:= 1 to nAfter do begin
1429     TabIndex := Count-1;
1430     if not CloseEditor then
1431       break;  //Se canceló
1432   end;
1433   SetFocus;
1434 end;
1435 procedure TfraEditView.FrameResize(Sender: TObject);
1436 begin
1437   //Configura ubciacio de etiquetas
1438   if Count>0 then exit;   //Está oculto
1439   lblBackground.Left := self.Width div 2 - lblBackground.Width div 2;
1440   lblBackground.Top := self.Height div 2;
1441 end;
1442 
1443 end.
1444 //1482
1445