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