Sign in Join
Wiki copytemporary

SynEdit/pl: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11**SynEdit** to pakiet edycji/notatek z [[Syntax highlighting/pl|podświetlaniem składni]] dostępny na [[SynEdit tab/pl|karcie SynEdit]] obsługujący wiele języków/składni.
22
33SynEdit zawarty w Lazarusie został utworzony na bazie [SynEdit 1.0.3](http://synedit.sourceforge.net/), i został w dużym stopniu zaadaptowany i rozszerzony. Zmiany są wymienione poniżej.
44
55Pakiet Lazarus zawiera komponent edytora źródeł o nazwie [[TSynEdit/pl|TSynEdit]], kilka podświetlaczy składni i inne komponenty używane do edycji źródeł.
66
77Licencjonowany na tych samych warunkach, co oryginalny SynEdit (MPL lub GPL).
88
99## Wersja oryginalna a Lazarus
1010
1111Wersją dla Lazarus opiekuje się głównie Martin Friebe. Martin napisał na forum, co zostało dodane do wersji Lazarusa od czasu pojawienia się tego forka:
1212
1313Duże rzeczy dodane do wersji Lazarus:
1414
1515- zwijanie bloków kodu
1616- konfigurowalny boczny margines i jego części (tzw. rynna)
1717- wspólny tekst pomiędzy kilkoma redaktorami
1818- obsługa kodowania utf-8
1919- wtyczka do edycji synchronizacji
2020- podstawowa obsługa RTL/LTR
2121- konfiguracja myszy za pomocą MouseActions
2222- przepisano różne moduły podświetlania/oznaczeń składni
2323
2424Bazy kodu wersji Delphi/Lazarus zostały niezależnie przeprojektowane. Pozostało bardzo niewiele do zrobienia.
2525
2626## Port SynEdit 2.0
2727
2828Istnieje alternatywny port wersji 2.0.x oryginalnego SynEdit. Nie jest aktywnie utrzymywany, ostatnie zatwierdzenie (obecnie jest marzec 2024) miało miejsce w 2023 roku, ale od 2011 roku ma on wersję 2.0.5.
2929
3030- [[SynEdit/port]]
3131- [Github](https://github.com/rnapoles/strona)
3232
3333## SynEdit w IDE
3434
3535SynEdit w Lazarusie jest pakietem wbudowanym, ponieważ samo IDE używa go do edycji kodów źródłowych. Z tego powodu nie można usunąć tego pakietu z listy instalacyjnej. Natomiast aby usunąć SynEdit z palety komponentów, można usunąć pakiet SynEditDsgn z instalacji.
3636
3737## Używanie SynEdit
3838
3939### Podświetlacze składni
4040
4141- Istnieje kilka standardowych poświetlaczy składni (zobacz [[SynEdit tab/pl|karta SynEdit]] w [[Component Palette|palecie komponentów]])
4242- Istnieją podświetlacze skryptowe, które można dostosować do wielu innych formatów plików:
4343 - [[TSynAnySyn]] (standardowy, jest domyślnie na palecie komponentów)
4444 - [[TSynPositionHighlighter]] (standardowy, **nie** jest obecny palecie komponentów)
4545 - [[TSynUniHighlighter]] (standardowy, **nie** jest obecny na palecie komponentów)
4646 - SynFacilSyn ([Github](https://github.com/t-edson/SynFacilSyn))
4747- Istnieje więcej podświetlaczy innych firm: SynCacheSyn, SynGeneralSyn, SynRCSyn, SynRubySyn, SynSDDSyn, SynSMLSyn, SynSTSyn, SynTclTkSyn, SynUnrealSyn, SynURISyn, SynVBScriptSyn, SynVrml97Syn, [Spójrz tutaj](http://bugs.freepascal.org/view.php?id=18248).
4848- Możesz napisać nowy podświetlacz, zobacz informacje na [[SynEdit Highlighter/pl|SynEdit Highlighter]].
4949
5050### Znaczniki
5151
5252Znaczniki umożliwiają dodatkowe kolorowanie [[SynEdit Markup]]
5353
5454### Oznaczenia
5555
5656Znaczniki zapewniają dodatkowe kolorowanie
5757
5858### Edycja istniejącego podświetlacza
5959
6060Czasami możesz chcieć edytować istniejące podświetlacze składni (tak jak chciałem to zrobić kilka dni temu), które już istnieją. W tym przykładzie będziemy edytować podświetlacz dla kodu pascala (nazwa klasy: [[TSynPasSyn]]; pakiet: SynEdit V1.0; moduł: SynHighlighterPas.pas).
6161
6262Powiedzmy, że chcemy osiągnąć to, aby nasza aplikacja (w tym przypadku Lazarus) rozróżniał trzy typy komentarzy, które istnieją w Pascalu:
6363
6464``` pascal
6565 (* standardowe *)
6666 { klamry }
6767 // ukośniki
6868```
6969
7070Może to być pomocne, jeśli chcesz rozróżnić różne typy swoich komentarzy (np. „Opis”, „Notatka”, „Referencja” itp.) i chcesz, aby każdy wyróżniony był innym kolorem.
7171
7272> **Note:** Na wypadek, gdybyś coś zepsuł, sugeruję dodanie kilku komentarzy „NEW” i „/NEW”, ale nie jest to konieczne
7373
7474- Najpierw otwórz moduł „SynHighlighterPas”, który powinien znajdować się w katalogu SynEdit.
7575- Ponieważ nie chcemy powodować niezgodności, tworzymy nowy rodzaj typu wyliczającego, który pomoże nam później zidentyfikować nasz komentarz:
7676
7777Np. pod deklaracją „tkTokenKind” napisz:
7878
7979``` pascal
8080 {NEW}
8181 TtckCommentKind = (tckAnsi, tckBor, tckSlash);
8282 {/NEW}
8383```
8484
8585- W deklaracji „TSynPasSyn” wyszukaj „FTokenID” i dodaj pomiędzy „FTokenID” a następnym polem
8686
8787``` pascal
8888 {NEW}
8989 FCommentID: TtckCommentKind;
9090 {/NEW}
9191 //Tworzy to nowe pole, w którym możemy zapisać informację, jakiego rodzaju mamy komentarz
9292```
9393
9494- W deklaracji „TSynPasSyn” wyszukaj „fCommentAttri” i dodaj następujący tekst między „fCommentAttri” a następnym polem
9595
9696``` pascal
9797 {NEW}
9898 fCommentAttri_Ansi: TSynHighlighterAttributes;
9999 fCommentAttri_Bor: TSynHighlighterAttributes;
100100 fCommentAttri_Slash: TSynHighlighterAttributes;
101101 {/NEW}
102102 //Dzięki temu możemy zwrócić różne atrybuty dla każdego typu komentarza
103103```
104104
105105- Następnie wyszukaj definicję konstruktora „TSynPasSyn”, która powinna brzmieć „constructor TSynPasSyn.Create(AOwner: TComponent);”.
106106- Musimy utworzyć nasze nowe atrybuty, dlatego dodajemy nasze atrybuty gdzieś w konstruktorze (sugeruję po domyślnym „fCommentAttri”)
107107
108108``` pascal
109109 (...)
110110 AddAttribute(fCommentAttri);
111111 {NEW}
112112 fCommentAttri_Ansi := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Ansi', SYNS_XML_AttrComment+'_Ansi'); //Ostatnie dwa ciągi to podpis i przechowywana nazwa
113113 //Jeśli chcesz mieć domyślne ustawienia swojego atrybutu, możesz np. dodać to:
114114 //fCommentAttri_Ansi.Background := clBlack; //Ustawiłoby to „Tło” na „clBlack” jako domyślne
115115 AddAttribute(fCommentAttri_Ansi);
116116 fCommentAttri_Bor := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Bor', SYNS_XML_AttrComment+'_Bor');
117117 AddAttribute(fCommentAttri_Bor);
118118 fCommentAttri_Slash := TSynHighlighterAttributes.Create(SYNS_AttrComment+'_Slash', SYNS_XML_AttrComment+'_Slash');
119119 AddAttribute(fCommentAttri_Slash);
120120 {/NEW}
121121 (...)
122122```
123123
124124- Bardziej skomplikowane działanie polega teraz na wyszukaniu miejsc, w których „FTokenID” jest ustawione na „tkComment” i jednocześnie obsługuje nasz „podtyp” (oczywiście już je dla Ciebie znalazłem :)
125125
126126``` pascal
127127procedure TSynPasSyn.BorProc;
128128(...)
129129 fTokenID := tkComment;
130130 {NEW}
131131 FCommentID:=tckBor;
132132 {/NEW}
133133 if rsIDEDirective in fRange then
134134(...)
135135```
136136
137137``` pascal
138138procedure TSynPasSyn.AnsiProc;
139139begin
140140 fTokenID := tkComment;
141141 {NEW}
142142 FCommentID:=tckAnsi;
143143 {/NEW}
144144(...)
145145```
146146
147147``` pascal
148148procedure TSynPasSyn.RoundOpenProc;
149149(...)
150150 fTokenID := tkComment;
151151 {NEW}
152152 FCommentID:=tckAnsi;
153153 {/NEW}
154154 fStringLen := 2; // długość "(*"
155155(...)
156156```
157157
158158``` pascal
159159procedure TSynPasSyn.SlashProc;
160160begin
161161 if fLine[Run+1] = '/' then begin
162162 fTokenID := tkComment;
163163 {NEW}
164164 FCommentID:=tckSlash;
165165 {/NEW}
166166 if FAtLineStart then begin
167167(...)
168168```
169169
170170``` pascal
171171procedure TSynPasSyn.SlashContinueProc;
172172(...)
173173 fTokenID := tkComment;
174174 {NEW}
175175 FCommentID:=tckSlash;
176176 {/NEW}
177177 while not(fLine[Run] in [#0, #10, #13]) do
178178(...)
179179```
180180
181181- Teraz musimy tylko pobrać informacje po wywołaniu „GetTokenAttribute” i zwrócić właściwy atrybut, dlatego edytujemy „GetTokenAttribute” w następujący sposób:
182182
183183``` pascal
184184function TSynPasSyn.GetTokenAttribute: TSynHighlighterAttributes;
185185begin
186186 case GetTokenID of
187187 tkAsm: Result := fAsmAttri;
188188 {OLD
189189 tkComment: Result := fCommentAttri; //To jest komentarz i stanowi kopię zapasową, więc zostanie zignorowany
190190 /OLD}
191191 {NEW}
192192 tkComment: begin
193193 if (FCommentID = tckAnsi) then
194194 Result := fCommentAttri_Ansi //To jest typ standardowy AnsiComment
195195 else
196196 if (FCommentID = tckBor) then
197197 Result := fCommentAttri_Bor //To jest typ klamrowy BorComment
198198 else
199199 if (FCommentID = tckSlash) then
200200 Result := fCommentAttri_Slash //To jest typ ukośnikowy SlashComment
201201 else
202202 Result := fCommentAttri //Jeśli nasz kod w jakiś sposób zawiódł, przywróć ustawienia domyślne
203203 end;
204204 {/NEW}
205205 tkIDEDirective: begin
206206(...)
207207```
208208
209209Jeśli używasz lazarusa, po prostu zainstaluj ponownie pakiet SynEdit, jeśli nie, przekompiluj swój projekt/pakiet.
210210
211211**GOTOWE ! Mówię poważnie! Twój kod jest teraz gotowy na rozróżnianie różnych typów komentarzy.**
212212
213213Lazarus-IDE automatycznie wykrywa, jakie atrybuty istnieją i pokazuje je w opcjach, na przykład zapisuje je, jeśli je zmienisz. Jeśli Twoja aplikacja/IDE tego nie robi, będziesz musiał ustawić Kolor/Czcionkę/itp. nowych atrybutów gdzieś ręcznie (np. w konstruktorze TSynPasSyn)
214214
215215### Realizacja wtyczek
216216
217217Istnieją 3 wtyczki uzupełniające dla SynEdit:
218218
219219[[TSynCompletion]]
220220
221221- Oferuje listę słów w rozwijanym menu za pomocą kombinacji klawiszy skrótu (domyślnie: Ctrl-Spacja).
222222- Używane w IDE do uzupełniania identyfikatora.
223223- Zawarte w przykładach.
224224- Dostępne na palecie komponentów (od wersji 0.9.3x).
225225
226226Przykładowy kod wywołujący programowo wyskakujące okienko uzupełniania (tj. bez naciskania skrótu klawiaturowego):
227227
228228``` pascal
229229YourSynEdit.CommandProcessor(YourSynCompletion.ExecCommandID, '', nil)
230230```
231231
232232[[TSynAutoComplete]]
233233
234234- Zastępuje bieżący token fragmentem tekstu. **Nie** jest to interaktywne. **Nie** posiada menu rozwijanego.
235235- Zawarte w przykładach.
236236- Dostępne na palecie komponentów.
237237
238238[[TSynEditAutoComplete]]
239239
240240- Podstawowy moduł szablonów. **Nie** posiada menu rozwijanego.
241241- Używany przez IDE do szablonów kodu. IDE zawiera dodatkowy kod rozszerzający tę funkcję (rozwijane i synchronizujące makra dodawane są przez IDE).
242242- **Nie** ujęte w przykładach.
243243
244244Todo: Należy udokumentować różnice pomiędzy 2. i 3. poziomem. Może uda się je połączyć.
245245
246246### Logiczna/fizyczna pozycja karetki
247247
248248SynEdit oferuje położenie karetki (migającego kursora tekstowego) w 2 różnych formach:
249249
250250- Fizyczne X/Y: Odpowiada pozycji wizualnej (na płótnie) karetki,
251251- Logiczne X/Y: Odpowiada przesunięciu bajtów tekstu.
252252
253253Obydwa są oparte na 1. Obecnie współrzędne Y są zawsze takie same. To może się zmienić w przyszłości.
254254
255255Współrzędna fizyczna
256256To pozycja w siatce wyświetlania (pomijająca przewijanie). Przy czym:
257257
258258litera „a” i „â” zajmują JEDNĄ komórkę w siatce, zwiększając fizyczne x o 1. Mimo że w utf8 kodowanie „a” zajmuje jeden bajt, a „â” zajmuje kilka bajtów.
259259
260260jednakże znak tabulacji (#9), poza tym, że jest tylko jednym bajtem i jednym znakiem, może zająć kilka komórek w siatce, zwiększając fizyczne x o więcej niż jeden. Istnieją również znaki w językach chińskim i wschodnim, które zajmują 2 pozycje siatki (znak Google o pełnej szerokości i o połowie szerokości)
261261
262262Współrzędna logiczna
263263To przesunięcie bajtu w ciągu znaków przechowującym linię.
264264
265265litera „a” ma 1 bajt i zwiększa się o 1
266266
267267litera „â” ma 2 (lub 3) bajty i zwiększa się o tą wartość
268268
269269tab ma 1 bajt i zwiększa się o 1.
270270
271271Żaden z tych współrzędnych nie podaje pozycji w znakach/punktach kodowych UTF8 (tak jak np. Utf8Copy czy Utf8Length).
272272
273273Fizyczna współrzędna X jest zawsze liczona od lewej strony tekstu, nawet jeśli jest on przewijany. Aby uzyskać grid-x aktualnie przewijanej kontrolki, wykonaj:
274274
275275
276276grid-X-in-visible-part-of-synedit := PhysicalX - SynEdit.LeftChar + 1
277277
278278grid-y-in-visible-part-of-synedit := SynEdit.RowToScreenRow(PhysicalY); // obejmuje zwijanie bloków
279279
280280użyj ScreenRowToRow do odwrotnych wyliczeń
281281
282282### Zmiana tekstu za pomocą kodu
283283
284-> **Warning:** Zmiana tekstu za pomocą właściwości <u>SynEdit.Lines</u> <u>nie działa w przypadku funkcji cofnij/ponów. </u>
284+> **Warning:** Zmiana tekstu za pomocą właściwości SynEdit.Lines nie działa w przypadku funkcji cofnij/ponów.
285285
286286Dostęp do tekstu można uzyskać za pośrednictwem SynEdit.Lines. Jest to właściwość oparta na TStrings oferująca dostęp do odczytu/zapisu dla każdej linii. Pierwszy wiersz ma index 0.
287287
288288``` pascal
289289 SynEdit.Lines[0] := 'Text'; // pierwsza line
290290```
291291
292292Za pomocą SynEdit.Lines można ustawić początkową wersję tekstu (np. wczytaną z pliku). Należy pamiętać, że SynEdit.Lines.Add/SynEdit.Lines.Append nie obsługuje podziału wierszy wewnątrz dodanych ciągów. Należy dodawać linie jedna po drugiej.
293293
294294Aby zmodyfikować zawartość SynEdit i pozwolić użytkownikowi na cofnięcie akcji, użyj następujących metod:
295295
296296``` pascal
297297 procedure InsertTextAtCaret(aText: String; aCaretMode: TSynCaretAdjustMode = scamEnd);
298298 property TextBetweenPoints[aStartPoint, aEndPoint: TPoint]: String // Logiczna pozycja
299299 read GetTextBetweenPoints write SetTextBetweenPointsSimple;
300300 property TextBetweenPointsEx[aStartPoint, aEndPoint: TPoint; CaretMode: TSynCaretAdjustMode]: String
301301 write SetTextBetweenPointsEx;
302302 procedure SetTextBetweenPoints(aStartPoint, aEndPoint: TPoint;
303303 const AValue: String;
304304 aFlags: TSynEditTextFlags = [];
305305 aCaretMode: TSynCaretAdjustMode = scamIgnore;
306306 aMarksMode: TSynMarksAdjustMode = smaMoveUp;
307307 aSelectionMode: TSynSelectionMode = smNormal );
308308```
309309
310310Examples:
311311
312312``` pascal
313313 // Wstaw tekst w miejscu karetki
314314 SynEdit.InsertTextAtCaret('Tekst');
315315 // Zamień tekst od punktu (x=2,y=10) do punktu (x=4,y=20) na Str
316316 SynEdit.TextBetweenPoints[Point(2,10), Point(4,20)] := Str;
317317```
318318
319319``` pascal
320320 // Usuń/zamień pojedynczy znak w pozycji karetki
321321 var
322322 p1, p2: TPoint;
323323 begin
324324 p1 := SynEdit.LogicalCaretXY;
325325 p2 := p1;
326326 // Oblicz pozycję bajtu następnego znaku
327327 p2.x := p2.x + UTF8CharacterLength(@SynEdit.LineText[p2.x]);
328328 // p1 wskazuje pierwszy bajt znaku, który ma zostać zastąpiony
329329 // p2 wskazuje pierwszy bajt znaku po ostatnim znaku, który można zastąpić
330330 // Zamień na „Tekst” (lub użyj pustego ciągu, aby usunąć)
331331 SynEdit.TextBetweenPoints[p1, p2] := 'Tekst';
332332```
333333
334334### Zwiń/rozwiń za pomocą kodu
335335
336336- To jest wciąż w budowie.
337337- Działa to tylko wtedy, gdy bieżący poddświetlacz składni obsługuje zwijanie kodu (szczegóły w [[SynEdit Highlighter]]).
338338- Należy również pamiętać, że niektóre podświetlacze obsługują kilka niezależnych drzew zwijania. Np. w Pascalu możesz zwinąć słowa kluczowe (begin, end, class, procedura itp.), które jest głównym zwijaniem, ale możesz zwinąć też \$ifdef lub \$region, które jest zwijaniem drugorzędnym.
339339- Zwijanie bieżącego zaznaczenia różni się także od zwijania słów kluczowych.
340340
341341Metody zwijania kodu:
342342
3433431\) TSynEdit.CodeFoldAction
344344
345345Pasuje na podanej linii. Jeśli jest więcej niż jeden, zwija najbardziej do środka (najbardziej po prawej). Uwaga: to nie działa w przypadku zaznaczania ani w przypadku zakładek, które całkowicie się ukrywają / Wymagają przetestowania dla innych operacji zwijania.
346346
3473472\) TSynEdit.FindNextUnfoldedLine
348348
3493493\) TSynEdit.FoldAll / TSynEdit.UnfoldAll
350350
351351### Zakładki
352352
353353- [Temat na forum dotyczący dodawania znaków marginesu (rynny) i kolorowych linii](http://forum.lazarus.freepascal.org/index.php/topic,14948.msg79794.html)
354354
355355### Więcej informacji
356356
357357Dyskusje na forum, które zawierają informacje o SynEdit:
358358
359359- [Wyszukaj/zamień; wstawka korektorska; pozycja logiczna/fizyczna](http://forum.lazarus.freepascal.org/index.php/topic,19520.msg111158.html#msg111158)
360360- [Różnica między SynEdit/SynMemo](http://forum.lazarus.freepascal.org/index.php/topic,19645.msg111962.html#msg111962)
361361- [Działania myszy; wyłącz wklejanie środkowym kliknięciem](http://forum.lazarus.freepascal.org/index.php/topic,23997.msg144100.html#msg144100)
362362- [Informacje o TSynEditMarkup](http://forum.lazarus.freepascal.org/index.php?topic=24842)
363363
364364### Przykładowe aplikacje
365365
366366Przykładowe zastosowania znajdziesz w folderze "lazarus/examples/synedit".
367367
368368### Dodawanie skrótów klawiszowych do wycinania/kopiowania/wklejania/itp
369369
370370Skróty klawiszowe można wdrożyć za pomocą poleceń SynEdit.
371371
372372``` pascal
373373uses
374374 SynEdit, SynEditKeyCmds;
375375
376376procedure TForm1.SynEdit1KeyDown(Sender: TObject; var Key: Word;
377377 Shift: TShiftState);
378378begin
379379 if (Shift = [ssCtrl]) then
380380 case Key of
381381 VK_C: SynEdit1.CommandProcessor(TSynEditorCommand(ecCopy), ' ', nil);
382382 VK_V: SynEdit1.CommandProcessor(TSynEditorCommand(ecPaste), ' ', nil);
383383 VK_X: SynEdit1.CommandProcessor(TSynEditorCommand(ecCut), ' ', nil);
384384 end;
385385end;
386386```
387387
388388## Dalszy rozwój, dyskusje
389389
390390- RTL (od prawej do lewej): rozpoczęty przez Mazena (częściowo zaimplementowany w systemie Windows)
391391- SynEdit używa tylko UTF8; wersja ASCII/ANSI już nie istnieje. Czcionka jest wstępnie wybrana w zależności od systemu. Użytkownik może wybrać inną czcionkę, ale musi wtedy wybrać czcionkę o stałej szerokości.
392392 - automatyczny wybór czcionki o stałej szerokości: W tej chwili SynEdit zaczyna od czcionki „courier”. W tej chwili LCL TFont nie udostępnia właściwości umożliwiającej filtrowanie czcionek o stałej szerokości.
393393 - automatyczny wybór czcionki UTF-8: To samo co powyżej o stałej szerokości, ale także z czcionką UTF-8, tak aby np. umlauty były wyświetlane poprawnie.
394394- Martwe klucze. Większość klawiatur obsługuje wpisywanie dwóch lub więcej klawiszy w celu utworzenia jednego znaku specjalnego (takiego jak znaki akcentowane lub umlaut). (Jest to obsługiwane przez zestaw widżetów LCL)
395395- [[Redesign of the SynEdit component|Przeprojektowanie komponentu SynEdit]]. Podstawowym celem jest bardziej niezawodne wyświetlanie i nawigacja w tekście. Bardziej modułowe podejście pozwala również na lepszą integrację rozszerzeń i wyspecjalizowanych elementów sterujących do użytku poza Lazarusem.
396396- [Zawijanie słów](http://bugs.freepascal.org/view.php?id=30395). Jest to eksperymentalna implementacja oparta na idei klas TextTrimmer/TabExpansion. Powiązany problem z bugtrakerem zawiera klasę i wyjaśnienie zmian wymaganych w innych plikach, aby działał.
397397- Haki w przetwarzaniu kluczy/poleceń SynEdit. Na forum: <http://forum.lazarus-ide.org/index.php/topic,35592.msg243316.html#msg243316>
398398
399399## Zobacz także
400400
401401- [[SynEdit Highlighter/pl|Podświetlacz SynEdit]]
402402- [„Edytuj istniejący podświetlacz”: niemiecki wątek na forum pod adresem „lazarusforum.de”](http://www.lazarusforum.de/viewtopic.php?f=5&t=8723)
403403- [[ATSynEdit]]
404404
405405---
406406*From the Free Pascal wiki, page "SynEdit/pl", as it stood on 2024-03-12 after a change by Slawek, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
407407Category: Components
408408Category: Lazarus
409409Category: SynEdit

Back to history