FrameTabSessions.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. {Frame que contiene un control similar a un TPageControl pero que abre ventanas de
  2. sesión (un panel de texto y una pantalla de Terminal.)
  3. Este frame es similar al usado en los compiladores PicPas y P65Pas.}
  4. unit FrameTabSessions;
  5. {$mode objfpc}{$H+}
  6. interface
  7. uses
  8. Classes, SysUtils, FileUtil, LazUTF8, LazFileUtils, Forms, Controls, Dialogs,
  9. ComCtrls, ExtCtrls, Graphics, LCLProc, Menus, LCLType, StdCtrls,
  10. fgl, Types, SynEdit, SynEditKeyCmds, Globales,
  11. uResaltTerm, FrameTabSession, SynFacilUtils, SynFacilBasic, MisUtils;
  12. type
  13. { TPage }
  14. TPage = class
  15. procedure SetVisible(state: boolean); virtual; abstract;
  16. end;
  17. TSessionTabEvent = procedure(ed: TfraTabSession) of object;
  18. { TfraTabSessions }
  19. TfraTabSessions = class(TFrame)
  20. published
  21. ImgCompletion: TImageList;
  22. lblNewSession: TLabel;
  23. lblOpenSession: TLabel;
  24. mnCloseOthers: TMenuItem;
  25. mnCloseAll: TMenuItem;
  26. mnNewTab: TMenuItem;
  27. mnCloseTab: TMenuItem;
  28. mnNewTab1: TMenuItem;
  29. OpenDialog1: TOpenDialog;
  30. panHeader: TPanel;
  31. Panel2: TPanel;
  32. panContent: TPanel;
  33. PopUpTabs: TPopupMenu;
  34. UpDown1: TUpDown;
  35. procedure lblNewSessionMouseDown(Sender: TObject; Button: TMouseButton;
  36. Shift: TShiftState; X, Y: Integer);
  37. procedure lblNewSessionMouseEnter(Sender: TObject);
  38. procedure lblNewSessionMouseLeave(Sender: TObject);
  39. procedure lblOpenSessionMouseDown(Sender: TObject; Button: TMouseButton;
  40. Shift: TShiftState; X, Y: Integer);
  41. procedure lblOpenSessionMouseEnter(Sender: TObject);
  42. procedure lblOpenSessionMouseLeave(Sender: TObject);
  43. procedure mnCloseOthersClick(Sender: TObject);
  44. procedure mnCloseAllClick(Sender: TObject);
  45. procedure mnCloseTabClick(Sender: TObject);
  46. procedure mnNewTabClick(Sender: TObject);
  47. procedure UpDown1Click(Sender: TObject; Button: TUDBtnType);
  48. private //Métodos para dibujo de las lenguetas
  49. xIniTabs : integer; //Coordenada inicial desde donde se dibujan las lenguetas
  50. tabDrag : integer;
  51. tabSelec : integer;
  52. procedure MakeActiveTabVisible;
  53. procedure Panel1DragDrop(Sender, Source: TObject; X, Y: Integer);
  54. procedure Panel1DragOver(Sender, Source: TObject; X, Y: Integer;
  55. State: TDragState; var Accept: Boolean);
  56. procedure Panel1EndDrag(Sender, Target: TObject; X, Y: Integer);
  57. procedure RefreshTabs;
  58. procedure SetTabIndex(AValue: integer);
  59. procedure DibLeng(x1, x2: integer; coltex: TColor; Activo: boolean; txt: string
  60. ); //dibuja una lengueta
  61. procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton;
  62. Shift: TShiftState; X, Y: Integer);
  63. procedure Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer
  64. );
  65. procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton;
  66. Shift: TShiftState; X, Y: Integer);
  67. procedure UpdateX1CoordTabs;
  68. procedure Panel1Paint(Sender: TObject);
  69. procedure InitTabs;
  70. private
  71. FTabIndex : integer;
  72. function LastIndex: integer;
  73. function NewName(prefix, ext: string): string;
  74. procedure DeleteEdit;
  75. public //Manejo de pestañas y páginas
  76. pages : TPages;
  77. property TabIndex: integer read FTabIndex write SetTabIndex; //panel actualmente activo
  78. function Count: integer;
  79. function ActivePage: TfraTabSession;
  80. function SearchEditorIdxByTab(tabName: string): integer;
  81. procedure SelectNextEditor;
  82. procedure SelectPrevEditor;
  83. function HasFocus: boolean;
  84. procedure SetFocus; override;
  85. procedure UpdateTabWidth(pag: TfraTabSession);
  86. public //Eventos
  87. OnSelectEditor: procedure of object; //Cuando cambia la selección de editor
  88. OnRequireSynEditConfig: procedure(ed: TsynEdit) of object;
  89. OnRequireSetCompletion: procedure(ed: TfraTabSession) of object;
  90. public
  91. {Evento general asociado a una página del control.
  92. El parámetro "event" es una cadena con el nombre del evento.
  93. La página se pasa en "page" como "TObject" para soportar cualquier tipo de "frame"
  94. como página.
  95. El parámetro "res" es la respuesta que se da al evento.}
  96. OnPageEvent: procedure(event: string; page: TObject; out res: string) of object;
  97. procedure PageEvent(event: string; page: TObject; out res: string);
  98. public //Administración de páginas
  99. tmpPath: string; //ruta usada para crear archivos temporales para los editores
  100. function AddPage(ext: string): TfraTabSession;
  101. function ClosePage: boolean;
  102. function CloseAll(out lstClosedFiles: string): boolean;
  103. public //Inicialización
  104. procedure UpdateSynEditConfig;
  105. procedure UpdateSynEditCompletion;
  106. constructor Create(AOwner: TComponent); override;
  107. destructor Destroy; override;
  108. procedure SetLanguage;
  109. end;
  110. implementation
  111. {$R *.lfm}
  112. const
  113. SEPAR_TABS = 2; //Separación adicional, entre pestañas
  114. WIDTH_ADD_TAB = 40; //Ancho de botón "Agregar página"
  115. { TfraTabSessions }
  116. procedure TfraTabSessions.SetLanguage;
  117. begin
  118. //{$I ..\language\tra_FrameEditView.pas}
  119. end;
  120. procedure TfraTabSessions.RefreshTabs;
  121. begin
  122. if pages.Count=0 then begin
  123. panHeader.Visible := false;
  124. end else begin
  125. panHeader.Visible := true;
  126. end;
  127. panHeader.Invalidate; //para refrescar
  128. //Botones de desplazamiento horizontal
  129. if pages.Count > 1 then begin
  130. UpDown1.Enabled := true;
  131. end else begin
  132. UpDown1.Enabled := false;
  133. end;
  134. end;
  135. procedure TfraTabSessions.SetTabIndex(AValue: integer);
  136. {Define la sesión que se hará visible}
  137. var
  138. res: string;
  139. begin
  140. if AValue>pages.Count-1 then AValue := pages.Count-1;
  141. if FTabIndex = AValue then Exit;
  142. if FTabIndex<>-1 then begin //Que no sea la primera vez
  143. pages[FTabIndex].SetHide; //Oculta la sesión anterior.
  144. end;
  145. FTabIndex := AValue; //cambia valor
  146. // pages[FTabIndex].Visible := true; //Muestra la nueva sesión.
  147. PageEvent('req_activate', pages[FTabIndex], res);
  148. if OnSelectEditor<>nil then OnSelectEditor; //Dispara evento.
  149. RefreshTabs;
  150. end;
  151. //Métodos pàra el dibujo de lenguetas
  152. procedure TfraTabSessions.DibLeng(x1, x2: integer; coltex: TColor; Activo: boolean;
  153. txt: string);
  154. {Dibuja la lengueta en la posición indicada. Si "txt" es '+', se dibuja la lengueta
  155. para agregar página.}
  156. procedure GetX1X2(const xrmin: integer; y: integer; out xr1, xr2: integer);
  157. {devuelve las coordenadas x1 y x2 de la línea "y" de la lengueta}
  158. begin
  159. case y of
  160. 0: begin //Primera fila
  161. xr1 := x1+4;
  162. xr2 := xrmin -4;
  163. end;
  164. 1: begin
  165. xr1 := x1+2;
  166. xr2 := xrmin -2;
  167. end;
  168. 2: begin
  169. xr1 := x1+1;
  170. xr2 := xrmin ;
  171. end;
  172. 3: begin
  173. xr1 := x1+1;
  174. xr2 := xrmin + 1;
  175. end;
  176. else //otras filas
  177. xr1 := x1;
  178. xr2 := xrmin + (y div 2);
  179. end;
  180. end;
  181. var
  182. cv: TCanvas;
  183. y1, y2, alto, xr1, xr2, xrmin, xrmin2, i: Integer;
  184. r: TRect;
  185. colBorde: TColor;
  186. begin
  187. //Lee coordenadas horizontales
  188. alto := panHeader.Height;
  189. y1 := 0;
  190. y2 := y1 + alto;
  191. //Inicia dibujo
  192. cv := panHeader.canvas;
  193. cv.Font.Size:= FONT_TAB_SIZE;
  194. cv.Font.Bold := false;
  195. cv.Font.Color := clBlack;
  196. cv.Font.Color := coltex; //Color de texto
  197. //Fija Línea y color de fondo
  198. cv.Pen.Style := psSolid;
  199. cv.Pen.Width := 1;
  200. if Activo then cv.Pen.Color := clWhite else cv.Pen.Color := clMenu;
  201. //Dibuja fondo de lengueta. El dibujo es línea por línea
  202. xrmin := x2 - (alto div 4); //Corrige inicio, para que el punto medio de la pendiente, caiga en x2.
  203. xrmin2 := x2 + (alto div 4)+1; //Corrige inicio, para que el punto medio de la pendiente, caiga en x2.
  204. for i:=0 to alto-1 do begin
  205. GetX1X2(xrmin, i, xr1, xr2);
  206. cv.Line(xr1, i, xr2, i);
  207. end;
  208. //Dibuja borde de lengueta
  209. colBorde := clGray;
  210. cv.Pen.Color := colBorde;
  211. cv.Line(x1,y1+4,x1,y2); //lateral izquierdo
  212. cv.Line(x1+4,y1, xrmin-4, y1); //superior
  213. cv.Line(xrmin+2, y1+4, xrmin2, y2); //lateral derecho
  214. //Bordes
  215. GetX1X2(xrmin, 0, xr1, xr2);
  216. cv.Pixels[xr1,0] := colBorde;
  217. cv.Pixels[xr2,0] := colBorde;
  218. GetX1X2(xrmin, 1, xr1, xr2);
  219. cv.Pixels[xr1,1] := colBorde;
  220. cv.Pixels[xr1+1,1] := colBorde;
  221. cv.Pixels[xr2,1] := colBorde;
  222. cv.Pixels[xr2-1,1] := colBorde;
  223. GetX1X2(xrmin, 2, xr1, xr2);
  224. cv.Pixels[xr1,2] := colBorde;
  225. cv.Pixels[xr2,2] := colBorde;
  226. cv.Pixels[xr2-1,2] := colBorde;
  227. GetX1X2(xrmin, 3, xr1, xr2);
  228. cv.Pixels[xr1,3] := colBorde;
  229. cv.Pixels[xr2,3] := colBorde;
  230. if txt = '+' then begin //Lengueta para agregar
  231. //Elimina objetos y pone texto
  232. r.Top := y1;
  233. r.Bottom := y2;
  234. r.Left := x1+8; //Deja espacio para el ícono
  235. r.Right := x2-7; //Deja espacio para el botón de cierre
  236. cv.Font.Size:= 12;
  237. cv.Font.Bold := true;
  238. cv.TextRect(r, x1+13, 0, '+');
  239. end else begin //Lengueta normal
  240. //Dibuja ícono
  241. ImgCompletion.Draw(cv, x1+4, 6, 1);
  242. //Elimina objetos y pone texto
  243. r.Top := y1;
  244. r.Bottom := y2;
  245. r.Left := x1+20; //Deja espacio para el ícono
  246. r.Right := x2-7; //Deja espacio para el botón de cierre
  247. cv.TextRect(r, x1+23, 4 ,txt);
  248. end;
  249. end;
  250. procedure TfraTabSessions.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
  251. Shift: TShiftState; X, Y: Integer);
  252. var
  253. x2, i: Integer;
  254. edi: TfraTabSession;
  255. res: string;
  256. begin
  257. {Se asuma que las lenguetas ya tienen su coordenada x1, actualizada, porque ya
  258. han sido dibujadas, así que no llamaremos a UpdateX1CoordTabs.}
  259. for i := 0 to pages.Count-1 do begin
  260. edi := pages[i];
  261. x2 := edi.x1 + edi.tabWidth;
  262. if (X>edi.x1) and (X<x2) then begin
  263. TabIndex := i; //Selecciona
  264. if Shift = [ssRight] then begin
  265. PopUpTabs.PopUp;
  266. end else if Shift = [ssMiddle] then begin
  267. //Cerrar el archivo
  268. ClosePage;
  269. end else if Shift = [ssLeft] then begin
  270. //Solo selección
  271. MakeActiveTabVisible;
  272. //Inicia el arrastre
  273. panHeader.BeginDrag(false, 10);
  274. tabDrag := i; //gaurda el índice del arrastrado
  275. end;
  276. exit;
  277. end;
  278. end;
  279. //Verifica si se pulsó en el botón '+'
  280. if pages.Count>0 then begin //Solo cuando se dibuja.
  281. if (X>x2) and (X<x2 + WIDTH_ADD_TAB) then begin
  282. PageEvent('req_new_page', nil, res);
  283. SetFocus;
  284. end;
  285. end;
  286. end;
  287. procedure TfraTabSessions.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,
  288. Y: Integer);
  289. begin
  290. // debugln('Move');
  291. end;
  292. procedure TfraTabSessions.Panel1DragOver(Sender, Source: TObject; X, Y: Integer;
  293. State: TDragState; var Accept: Boolean);
  294. var
  295. x2, i, x2Mid: Integer;
  296. edi: TfraTabSession;
  297. begin
  298. Accept := true;
  299. //Ve a cual lengüeta selecciona
  300. tabSelec := -1;
  301. for i := 0 to pages.Count-1 do begin
  302. edi := pages[i];
  303. x2Mid := edi.x1 + edi.tabWidth div 2;
  304. x2 := edi.x1 + edi.tabWidth;
  305. if (X>edi.x1) and (X<x2) then begin
  306. if X<x2Mid then begin
  307. //Está en la primera mitad.
  308. tabSelec := i; //Selecciona
  309. end else begin
  310. //En la mitad final, selecciona el siguiente
  311. tabSelec := i+1; //Selecciona
  312. end;
  313. end;
  314. end;
  315. //Genera marca en la lengüeta
  316. if tabSelec<>-1 then begin
  317. // debugln('leng selec: %d', [tabselec]);
  318. panHeader.Invalidate;
  319. end;
  320. end;
  321. procedure TfraTabSessions.Panel1EndDrag(Sender, Target: TObject; X, Y: Integer);
  322. {Se termina el arrastre, sea que se soltó en alguna parte, o se canceló.}
  323. begin
  324. tabSelec := -1;
  325. panHeader.Invalidate;
  326. end;
  327. procedure TfraTabSessions.Panel1DragDrop(Sender, Source: TObject; X, Y: Integer);
  328. {Se soltó la lengueta en el panel.}
  329. begin
  330. if TabIndex<0 then exit;
  331. if tabSelec<0 then exit;
  332. //Corrección
  333. if tabSelec>TabIndex then tabSelec := tabSelec-1;
  334. if tabSelec>pages.Count-1 then exit;
  335. // debugln('Panel1DragDrop: %d a %d', [TabIndex, tabSelec]);
  336. pages.Move(TabIndex, tabSelec);
  337. TabIndex := tabSelec;
  338. panHeader.Invalidate;
  339. end;
  340. procedure TfraTabSessions.Panel1MouseUp(Sender: TObject; Button: TMouseButton;
  341. Shift: TShiftState; X, Y: Integer);
  342. begin
  343. //Pasa el enfoque al editor que se ha seleccionado
  344. if TabIndex=-1 then exit;
  345. try
  346. if pages[TabIndex].Visible then begin //Si el "frame" es visible.
  347. pages[TabIndex].edPCom.SetFocus;
  348. end;
  349. except
  350. end;
  351. end;
  352. procedure TfraTabSessions.UpdateX1CoordTabs;
  353. {Actualiza la coordenada x1, de las lenguetas, considerando el valor actual de
  354. "xIniTabs". El valor x1, representa la coordenada en que se dibuajaría la lengueta.}
  355. var
  356. i, x1: integer;
  357. pag: TfraTabSession;
  358. begin
  359. {Este algoritmo debe ser igual a Panel1Paint(), para que no haya inconsistencias.}
  360. x1 := xIniTabs;
  361. for i := 0 to pages.Count-1 do begin
  362. pag := pages[i];
  363. pag.x1 := x1; //Actualiza coordenada
  364. //Calcula siguiente coordenada
  365. x1 := x1 + pag.tabWidth + SEPAR_TABS;
  366. end;
  367. end;
  368. procedure TfraTabSessions.MakeActiveTabVisible;
  369. {Configura "xIniTabs", de modo que haga visible la pestaña del editor activo.
  370. Solo trabaja sobre la prestaña o lengueta. No maneja a la ventana de la sesión.}
  371. var
  372. x1, x2: integer;
  373. begin
  374. if Count=0 then exit;
  375. UpdateX1CoordTabs;
  376. x1 := ActivePage.x1;
  377. x2 := ActivePage.x1 + ActivePage.tabWidth;
  378. if x2 > self.Width then begin
  379. //Pestaña sale de página, por la derecha
  380. xIniTabs := xIniTabs - (x2-self.Width);
  381. end else if x1 < Panel2.Width then begin
  382. //Pestaña sale de página, por la izquierda
  383. xIniTabs := xIniTabs + (Panel2.Width - x1);
  384. end else begin
  385. // debugln('Pestaña se dibuja adentro');
  386. end;
  387. end;
  388. procedure TfraTabSessions.Panel1Paint(Sender: TObject);
  389. var
  390. i, x1, xfin: Integer;
  391. cv: TCanvas;
  392. pag: TfraTabSession;
  393. begin
  394. //Actualiza coordenadas
  395. UpdateX1CoordTabs;
  396. //Dibuja las pestañas
  397. for i := 0 to pages.Count-1 do begin
  398. pag := pages[i];
  399. if i <> TabIndex then begin
  400. //Dibuja todo menos al activo, lo deja para después.
  401. DibLeng(pag.x1, pag.x1 + pag.tabWidth, clBlack, false, pag.Caption);
  402. end;
  403. end;
  404. //Dibuja lengueta "+"
  405. if pages.Count>0 then begin
  406. //Solo se dibuja si hay página seleccionada.
  407. pag := pages[pages.Count-1]; //ültima página
  408. xfin := pag.x1 + pag.tabWidth;
  409. DibLeng(xfin, xfin + WIDTH_ADD_TAB, clBlack, false, '+');
  410. end;
  411. //Dibuja al final al activo, para que aparezca encima
  412. if TabIndex<>-1 then begin
  413. pag := pages[TabIndex];
  414. DibLeng(pag.x1, pag.x1 + pag.tabWidth, clBlack, true, pag.Caption);
  415. end;
  416. //Dibuja la marca de movimiento de lengüeta
  417. if (tabSelec>=0) and (tabSelec<pages.Count) then begin
  418. pag := pages[tabSelec];
  419. x1 := pag.x1+2;
  420. cv := panHeader.canvas;
  421. cv.Pen.Width := 5;
  422. cv.Pen.Color := clGray;
  423. cv.Line(x1 ,0, x1, panHeader.Height);
  424. end else if tabSelec = pages.Count then begin
  425. //Se marca al final de la última pestaña
  426. pag := pages[pages.Count-1]; //el útlimo
  427. x1 := pag.x1 + pag.tabWidth +2;
  428. cv := panHeader.canvas;
  429. cv.Pen.Width := 5;
  430. cv.Pen.Color := clGray;
  431. cv.Line(x1 ,0, x1, panHeader.Height);
  432. end;
  433. end;
  434. procedure TfraTabSessions.UpdateTabWidth(pag: TfraTabSession);
  435. var
  436. w: Integer;
  437. begin
  438. panHeader.Canvas.Font.Size := FONT_TAB_SIZE; {Fija atrubutos de texto, para que el
  439. cálculo con "TextWidth", de ancho sea correcto}
  440. w := panHeader.Canvas.TextWidth(pag.Caption) + 30;
  441. if w < MIN_WIDTH_TAB then w := MIN_WIDTH_TAB;
  442. pag.tabWidth := w;
  443. panHeader.Invalidate; //Para refrescar el dibujo
  444. end;
  445. procedure TfraTabSessions.PageEvent(event: string; page: TObject; out
  446. res: string);
  447. {Forma corta de llamar al evento OnPageEvent. }
  448. begin
  449. if OnPageEvent=nil then begin
  450. //Fija salida por seguridad.
  451. res := '';
  452. end else begin
  453. //Llama al evento
  454. OnPageEvent(event, page, res);
  455. end;
  456. end;
  457. procedure TfraTabSessions.InitTabs;
  458. {Configura eventos para el control de las lenguetas}
  459. begin
  460. xIniTabs := panel2.Width; //Empeiza dibujando al lado de las flechas
  461. panHeader.OnMouseMove := @Panel1MouseMove;
  462. panHeader.OnMouseDown := @Panel1MouseDown;
  463. panHeader.OnMouseUp := @Panel1MouseUp;
  464. panHeader.OnDragOver := @Panel1DragOver;
  465. panHeader.OnDragDrop := @Panel1DragDrop;
  466. panHeader.OnEndDrag := @Panel1EndDrag;
  467. end;
  468. //////////////////////////////////////////////////////////////
  469. function TfraTabSessions.LastIndex: integer;
  470. {Devuelve el índice de la última pestaña.}
  471. begin
  472. Result :=pages.Count - 1;
  473. end;
  474. function TfraTabSessions.NewName(prefix, ext: string): string;
  475. {Genera un nombre de archivo, a partir de "prefix", que no se repita entre las pestañas
  476. abiertas y que no exista en disco.}
  477. var
  478. n: Integer;
  479. begin
  480. n := 0;
  481. repeat
  482. inc(n);
  483. Result := prefix + IntToStr(n) + ext;
  484. until (SearchEditorIdxByTab(Result)=-1) and (not FileExists(Result));
  485. end;
  486. function TfraTabSessions.AddPage(ext: string): TfraTabSession;
  487. {Agrega una nueva ventana de eición a la vista, y devuelve la referencia.}
  488. var
  489. page: TfraTabSession;
  490. res: string;
  491. begin
  492. //Crea página.
  493. page := TfraTabSession.Create(nil); //No se define "Owner" porque se administrará dentro de muestra propia lista.
  494. page.Parent := self.panContent;
  495. page.Align := alClient;
  496. page.Name := 'Page'+IntToStr(pages.Count); //Nombre único. Se usaría NewName(ext), pero incuye un caracter punto.
  497. page.Caption := NewName('Page', ext); //Fija nombre de la pestaña. El nombre del archivo lo decidirá el frame.
  498. UpdateTabWidth(page); //Cambia el título Hay que actualizar ancho de lengueta.
  499. pages.Add(page); //Agrega a la lista.
  500. //Evento de requerimiento de inicialización de página.
  501. PageEvent('req_init', page, res);
  502. //Activa la página. Debe hacerse después de llamar a 'req_init'.
  503. TabIndex := LastIndex; //Selecciona la última sesión agregada
  504. //Configura desplazamiento para asegurarse que la pestaña se mostrará visible.
  505. MakeActiveTabVisible;
  506. //Actualiza referencias.
  507. Result := page;
  508. end;
  509. procedure TfraTabSessions.DeleteEdit;
  510. {Elimina al editor activo.}
  511. begin
  512. if TabIndex=-1 then exit;
  513. pages.Delete(TabIndex);
  514. //Hay que actualiza TabIndex
  515. if pages.Count = 0 then begin
  516. //Era el único
  517. FTabIndex := -1;
  518. end else begin
  519. //Había al menos 2
  520. if TabIndex > pages.Count - 1 then begin
  521. //Quedó apuntando fuera
  522. FTabIndex := pages.Count - 1; //limita
  523. //No es necesario ocultar el anterior, porque se eliminó
  524. pages[FTabIndex].Visible := true; //muestra el nuevo
  525. end else begin
  526. //Queda apuntando al siguiente. No es necesario modificar.
  527. //No es necesario ocultar el anterior, porque se eliminó
  528. pages[FTabIndex].Visible := true; //muestra el nuevo
  529. end;
  530. end;
  531. MakeActiveTabVisible;
  532. if OnSelectEditor<>nil then OnSelectEditor;
  533. RefreshTabs;
  534. end;
  535. ///Manejo de pestañas
  536. function TfraTabSessions.Count: integer;
  537. begin
  538. Result := pages.Count;
  539. end;
  540. function TfraTabSessions.ActivePage: TfraTabSession;
  541. {Devuelve el editor SynEditor, activo, es decir el que se encuentra en la lengueta
  542. activa. }
  543. var
  544. i: Integer;
  545. begin
  546. if pages.Count = 0 then exit(nil);
  547. i := TabIndex;
  548. Result := pages[i]; //Solo funcionará si no se desordenan las enguetas
  549. end;
  550. function TfraTabSessions.SearchEditorIdxByTab(tabName: string): integer;
  551. var
  552. ed: TfraTabSession;
  553. i: integer;
  554. begin
  555. for i:=0 to pages.Count-1 do begin
  556. ed := pages[i];
  557. if Upcase(ed.Caption) = UpCase(tabName) then exit(i);
  558. end;
  559. exit(-1);
  560. end;
  561. procedure TfraTabSessions.SelectNextEditor;
  562. {Selecciona al siguiente editor.}
  563. begin
  564. if Count = 0 then exit;
  565. if TabIndex=-1 then exit;
  566. if TabIndex = LastIndex then TabIndex := 0 else TabIndex := TabIndex + 1;
  567. SetFocus;
  568. MakeActiveTabVisible;
  569. end;
  570. procedure TfraTabSessions.SelectPrevEditor;
  571. {Selecciona al editor anterior.}
  572. begin
  573. if Count = 0 then exit;
  574. if TabIndex=-1 then exit;
  575. if TabIndex = 0 then TabIndex := LastIndex else TabIndex := TabIndex -1;
  576. SetFocus;
  577. MakeActiveTabVisible;
  578. end;
  579. function TfraTabSessions.HasFocus: boolean;
  580. {Indica si alguno de los editores, tiene el enfoque.}
  581. var
  582. i: Integer;
  583. begin
  584. for i:=0 to pages.Count-1 do begin
  585. if pages[i].edPCom.Focused then exit(true);
  586. end;
  587. exit(false);
  588. end;
  589. procedure TfraTabSessions.SetFocus;
  590. begin
  591. // inherited SetFocus;
  592. if TabIndex = -1 then exit;
  593. if pages[TabIndex].Visible then begin //Si el "frame" es visible.
  594. pages[TabIndex].edPCom.SetFocus;
  595. end;
  596. end;
  597. function TfraTabSessions.ClosePage: boolean;
  598. {Cierra la página actual.
  599. Si se cierra la página, o no hay página actual, se devuelve TRUE.
  600. Si no se puede cerrar, devuelve FALSE}
  601. var
  602. res: string;
  603. begin
  604. if ActivePage=nil then exit(true);
  605. PageEvent('query_close', ActivePage, res);
  606. if (res='N') or (res='') then exit(false); //Cancelado. No se debe cerrar.
  607. //Hay que proceder con el cierre
  608. DeleteEdit;
  609. exit(true);
  610. end;
  611. function TfraTabSessions.CloseAll(out lstClosedFiles: string): boolean;
  612. {Cierra todas las ventanas, pidiendo confirmación. Si se cancela, devuelve FALSE.
  613. Se devuelve en "lstOpenedFiles" una lista con los archivos que estaban abiertos.}
  614. var
  615. res: string;
  616. begin
  617. lstClosedFiles := '';
  618. while pages.Count>0 do begin
  619. lstClosedFiles := lstClosedFiles + ActivePage.ePCom.FileName + LineEnding;
  620. if ActivePage = nil then exit(true);
  621. PageEvent('query_close', ActivePage, res);
  622. if (res='N') or (res='') then exit(false); //Cancelado. No se debe cerrar.
  623. DeleteEdit;
  624. end;
  625. exit(true);
  626. end;
  627. procedure TfraTabSessions.UpdateSynEditConfig;
  628. {Indica que se desea cambiar la configuración de todos los SynEdit abiertos.}
  629. var
  630. i: Integer;
  631. begin
  632. //Pide configuración para todos los editores abiertos
  633. for i:=0 to pages.Count-1 do begin
  634. if OnRequireSynEditConfig<>nil then begin
  635. OnRequireSynEditConfig(pages[i].edPCom);
  636. end;
  637. end;
  638. end;
  639. procedure TfraTabSessions.UpdateSynEditCompletion;
  640. var
  641. i: Integer;
  642. begin
  643. //Pide configurar completado para todos los editores abiertos
  644. for i:=0 to pages.Count-1 do begin
  645. if OnRequireSetCompletion<>nil then OnRequireSetCompletion(pages[i]);
  646. end;
  647. end;
  648. //Inicialización
  649. constructor TfraTabSessions.Create(AOwner: TComponent);
  650. begin
  651. inherited;
  652. pages:= TPages.Create(true);
  653. panHeader.OnPaint := @Panel1Paint;
  654. FTabIndex := -1;
  655. InitTabs;
  656. tabSelec := -1;
  657. RefreshTabs;
  658. end;
  659. destructor TfraTabSessions.Destroy;
  660. begin
  661. pages.Destroy;
  662. inherited Destroy;
  663. end;
  664. //Menú
  665. procedure TfraTabSessions.mnNewTabClick(Sender: TObject);
  666. var
  667. res: string;
  668. begin
  669. PageEvent('req_new_page', nil, res);
  670. SetFocus;
  671. end;
  672. procedure TfraTabSessions.UpDown1Click(Sender: TObject; Button: TUDBtnType);
  673. begin
  674. case Button of
  675. btNext: SelectNextEditor;
  676. btPrev: SelectPrevEditor;
  677. end;
  678. end;
  679. procedure TfraTabSessions.mnCloseTabClick(Sender: TObject);
  680. begin
  681. ClosePage;
  682. SetFocus;
  683. end;
  684. procedure TfraTabSessions.mnCloseAllClick(Sender: TObject);
  685. begin
  686. while self.Count>0 do begin
  687. if not ClosePage then
  688. break; //Se canceló
  689. end;
  690. SetFocus;
  691. end;
  692. procedure TfraTabSessions.mnCloseOthersClick(Sender: TObject);
  693. var
  694. nBefore, i, nAfter: Integer;
  695. begin
  696. //Cierra anteriores
  697. nBefore := TabIndex;
  698. for i:= 1 to nBefore do begin
  699. TabIndex := 0;
  700. if not ClosePage then
  701. break; //Se canceló
  702. end;
  703. //Cierra posteriores
  704. nAfter := Count - TabIndex - 1;
  705. for i:= 1 to nAfter do begin
  706. TabIndex := Count-1;
  707. if not ClosePage then
  708. break; //Se canceló
  709. end;
  710. SetFocus;
  711. end;
  712. //Eventos del botón "Nueva Sesión".
  713. procedure TfraTabSessions.lblNewSessionMouseEnter(Sender: TObject);
  714. begin
  715. lblNewSession.Font.Bold := true;
  716. end;
  717. procedure TfraTabSessions.lblNewSessionMouseLeave(Sender: TObject);
  718. begin
  719. lblNewSession.Font.Bold := false;
  720. end;
  721. procedure TfraTabSessions.lblNewSessionMouseDown(Sender: TObject;
  722. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  723. var
  724. res: string;
  725. begin
  726. PageEvent('req_new_page', nil, res);
  727. SetFocus;
  728. end;
  729. //Eventos del botón "Abrir Sesión".
  730. procedure TfraTabSessions.lblOpenSessionMouseEnter(Sender: TObject);
  731. begin
  732. lblOpenSession.Font.Bold := true;
  733. end;
  734. procedure TfraTabSessions.lblOpenSessionMouseLeave(Sender: TObject);
  735. begin
  736. lblOpenSession.Font.Bold := false;
  737. end;
  738. procedure TfraTabSessions.lblOpenSessionMouseDown(Sender: TObject;
  739. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  740. var
  741. res: string;
  742. begin
  743. PageEvent('req_open_page', nil, res);
  744. SetFocus;
  745. end;
  746. end.
  747. //1482