Sign in Join
Wiki copytemporary

How To Write Lazarus Component/ko: what changed

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

OldNewLine
11컴포넌츠 빌드방법에 대한 기본 안내서이다. 윈도우7 라자루스 0.9.30버전에서 테스트 되었다.
22
33## 단계 1: 패키지 생성
44
55- On the Lazarus IDE menu, click **Package \> New Package** to run the Package Manager.
66
77![](/wiki-images/7/7c/Package_menu.png)
88
99- A **Save dialog** file will appear. Chose a folder and a filename and press save. If the IDE prompts for using lowercase filenames press 'yes'.
1010
1111- Congratulations: You have just created your first package!
1212
1313![](/wiki-images/3/37/How_to_write_lazarus_component_package_maker.png)
1414
1515## 단계 2: 유닛 생성
1616
1717You can create a new unit or use an existing file.
1818
1919### 새로운 유닛 생성
2020
2121- Use the **Add button \> New component**.
2222
2323![](/wiki-images/5/56/Package_new_component.png)
2424
2525- Choose a component like TComboBox.
2626- Choose *customcontrol1.pas* as filename.
2727- Click OK.
2828
2929``` pascal
3030unit CustomControl1;
3131
3232{$mode objfpc}{$H+}
3333
3434interface
3535
3636uses
3737 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
3838
3939type
4040 TCustomControl1 = class(TComboBox)
4141 private
4242 { Private declarations }
4343 protected
4444 { Protected declarations }
4545 public
4646 { Public declarations }
4747 published
4848 { Published declarations }
4949 end;
5050
5151procedure Register;
5252
5353implementation
5454
5555procedure Register;
5656begin
5757 RegisterComponents('Standard',[TCustomControl1]);
5858end;
5959
6060end.
6161```
6262
6363- Install the package by clicking the 'install' button at the top of the package editor.
6464
6565![](/wiki-images/3/36/Package_install.png)
6666
6767- Then the IDE will ask you, if the IDE should be rebuilt. Say yes.
6868
6969![](/wiki-images/9/9b/Package_rebuild.png)
7070
7171- Restart Lazarus and see your new component in the component palette. Congratulations: You have just installed your first package with your first package component.
7272
7373![](/wiki-images/3/35/Package_installed.png)
7474
7575> **Note:** If you do not see your new component in the component palette, it is most likely that you are not running the re-compiled version of Lazarus. You can set where Lazarus builds to in: Tools -\> Options -\> Files -\> Lazarus directory. Instead of calling lazarus directly, you also can use startlazarus, which starts the newly created lazarus, for example the lazarus executable in the ~/.lazarus directory, if you don't have write access to the directory lazarus was installed into.
7676
7777### 기존 유닛 추가
7878
7979If you already had a unit you can add it to the package:
8080
8181![](/wiki-images/1/11/Package_existing_unit.png)
8282
8383- Click the **Add** button, go to the **Add Files** tab. At the Unit file name, browse to your existing file. Click the **Add files to package**. If the packet manager complains that the unit is not in the unitpath, click yes to add the directory to the unit path.
8484- Click the **Add** button again, go to the **Add Files** tab and browse to the *.lrs* file and click OK (See Step 3 about creating this icon file).
8585- Click the **Add** button again, go to the **New Requirement** tab, in the Package name select **LCL** and click OK.
8686
8787End result should look like this:
8888
89-![`Package Maker`](/wiki-images/d/d9/How_to_write_lazarus_component_package_maker_done.png)
89+```text
90+Package Maker
91+```
9092
9193- Click on under the Files tree in the Package Manager. In the File Properties, make sure *Register unit* is checked.
9294- Click the Options button. Select the IDE Integration tab. At the Package Type make sure Designtime and Runtime is selected.
9395- Click the Compile button to check to see if the files compile without errors.
9496- Click the Install button, Lazarus will rebuild and restart automatically.
9597
9698The component is created and ready to be used:
9799
98-![`Component Created`](/wiki-images/3/3c/How_to_write_lazarus_component_created.png)
100+```text
101+Component Created
102+```
99103
100104## 단계 3: 패키지 아이콘 생성
101105
102106아이콘은 PNG 파일로 24x24 사이즈입니다.
103107
104108FPC is capable of using standard resource files `.rc` or compiled resources `.res` since fpc version 2.6
105109
106110The `.lrs`
107111
108112### 라자루스 이미지 에디터 사용
109113
110114You can use [[Lazarus Image Editor]] to create the icons in .lrs format.
111115
112116### lazres 사용
113117
114118`lazres`는 라자루스툴 디렉토리에 있는 located를 사용합니다.
115119
116120You may need to compile lazres at first use. Simply open the *lazres.lpi* in the IDE and at the menu click **run \> build**.
117121
118122**Create lrs file**
119123
120124lrs 파일 생성:
121125
122126`~/lazarus/tools/lazres samplepackage.lrs TMyCom.png`
123127
124128Where *samplepackage* is the name of your package and *TMyCom* is the name of your component.
125129
126130You can add more than one image to the lrs file by appending the image filename at the end. Eg. ~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyOtherCom.png ...
127131
128132**샘플**
129133
130134Following is a sample of the resulting *samplepackage.lrs* file.
131135
132136 LazarusResources.Add('TMyCom','PNG',[
133137 #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#24#0#0#0#24#8#2#0#0#0'o'#21#170#175
134138 +#0#0#0#4'gAMA'#0#0#177#143#11#252'a'#5#0#0#0'|IDAT8O'#237#212#209#10#192' '#8
135139 +#5'P'#247#231#251's'#215#138#133#164#166'\'#220#195'`'#209'c'#157'L'#173#131
136140 +#153#169'd4'#168'dP'#137'r_'#235'5'#136'@Zmk'#16'd9'#144#176#232#164'1'#247
137141 +'I'#8#160'IL'#206'C'#179#144#12#199#140'.'#134#244#141'~'#168#247#209'S~;'#29
138142 +'V+'#196#201'^'#10#15#150'?'#255#18#227#206'NZ>42'#181#159#226#144#15'@'#201
139143 +#148#168'e'#224'7f<@4'#130'u_YD'#23#213#131#134'Q]'#158#188#135#0#0#0#0'IEND'
140144 +#174'B`'#130
141145 ]);
142146
143147Make sure to include your resource file in your new component source by adding the following at the bottom your component's unit and including "LResources" in the uses clause
144148
145149``` pascal
146150initialization
147151 {$I samplepackage.lrs}
148152```
149153
150154## 리컴파일링 패키지
151155
152156You need to rebuild the package everytime you make changes to the *mycom.pas* file. To rebuild the package, open the *samplepackage.lpk* file in the Package Manager and click the Install button.
153157
154158## 패키지 제거
155159
156160- To remove installed components, on the IDE menu, click Package \> Configure installed packages. The following image shows the Installed Packages tool.
157161
158-![`Installed Components`](/wiki-images/0/0e/How_to_write_lazarus_component_installed_components.png)
162+```text
163+Installed Components
164+```
159165
160166- Select the package you want to uninstall and click Uninstall selection.
161167
162168If something goes wrong with a package (e.g. package directory is deleted without first uninstalling it), Lazarus may not allow you to uninstall packages. To fix the problem, at the IDE menu click Tools \> Build Lazarus. Lazarus will rebuild all packages and restart. You should now be able to uninstall problematic packages.
163169
164170## mycom.pas 개선
165171
166172- The codes in *mycom.pas* above gives you the basics on what you need to create a component. The following is an enhanced version with some tips on how to write procedures and events for components.
167173- *OnChange2* 이벤트를 만드는 방법을 보여줍니다
168174- The *OnSample* shows how to create custom events
169175- *MyText* and *MyText2* shows different ways to write properties.
170176- You can use TComboBox instead of TCustomComboBox as the base class, which publishes all properties as TComboBox.
171177- If TCustomComboBox is used as the base class, you'll notice a lot of properties and events will be missing at the Object Inspector in the IDE. To add those properties and events just copy and paste the properties as listed below *// properties from TComboBox*. These list of properties can be got from the TComboBox declaration in the StdCtrls unit. Omit any property which you want to handle yourself.
172178
173179``` pascal
174180unit mycom;
175181
176182{$mode objfpc}{$H+}
177183
178184interface
179185
180186uses
181187 Classes, SysUtils, StdCtrls, Forms, Dialogs,
182188 LCLType,LCLIntf,lresources,LCLProc;
183189
184190type
185191
186192 TSampleEvent = procedure(MyText: String) of Object;
187193
188194 TMyCom = class (TCustomComboBox)
189195 private
190196 FMyText: String;
191197 FOnChange2: TNotifyEvent;
192198 FOnSample: TSampleEvent;
193199 public
194200 constructor Create(TheOwner: TComponent); override;
195201 procedure CreateWnd; override;
196202 procedure Change; override;
197203 protected
198204 function GetMyText2: String;
199205 procedure SetMyText2(MyText: String);
200206 published
201207 property MyText: String read FMyText write FMyText;
202208 property MyText2: String read GetMyText2 write SetMyText2;
203209 property OnChange2: TNotifyEvent read FOnChange2 write FOnChange2;
204210 property OnSample: TSampleEvent read FOnSample write FOnSample;
205211
206212 // properties from TComboBox
207213 property Align;
208214 property Anchors;
209215 property ArrowKeysTraverseList;
210216 property AutoComplete;
211217 property AutoCompleteText;
212218 property AutoDropDown;
213219 property AutoSelect;
214220 property AutoSize;
215221 property BidiMode;
216222 property BorderSpacing;
217223 property CharCase;
218224 property Color;
219225 property Ctl3D;
220226 property Constraints;
221227 property DragCursor;
222228 property DragMode;
223229 property DropDownCount;
224230 property Enabled;
225231 property Font;
226232 property ItemHeight;
227233 property ItemIndex;
228234 property Items;
229235 property ItemWidth;
230236 property MaxLength;
231237 property OnChange;
232238 property OnChangeBounds;
233239 property OnClick;
234240 property OnCloseUp;
235241 property OnContextPopup;
236242 property OnDblClick;
237243 property OnDragDrop;
238244 property OnDragOver;
239245 property OnDrawItem;
240246 property OnEndDrag;
241247 property OnDropDown;
242248 property OnEditingDone;
243249 property OnEnter;
244250 property OnExit;
245251 property OnGetItems;
246252 property OnKeyDown;
247253 property OnKeyPress;
248254 property OnKeyUp;
249255 property OnMeasureItem;
250256 property OnMouseDown;
251257 property OnMouseMove;
252258 property OnMouseUp;
253259 property OnStartDrag;
254260 property OnSelect;
255261 property OnUTF8KeyPress;
256262 property ParentBidiMode;
257263 property ParentColor;
258264 property ParentCtl3D;
259265 property ParentFont;
260266 property ParentShowHint;
261267 property PopupMenu;
262268 property ReadOnly;
263269 property ShowHint;
264270 property Sorted;
265271 property Style;
266272 property TabOrder;
267273 property TabStop;
268274 property Text;
269275 property Visible;
270276 end;
271277
272278procedure Register;
273279
274280implementation
275281
276282procedure Register;
277283begin
278284 RegisterComponents('Sample',[TMyCom]);
279285end;
280286
281287constructor TMyCom.Create(TheOwner: TComponent);
282288begin
283289 inherited Create(TheOwner);
284290 Self.Style := csDropDownList;
285291end;
286292
287293procedure TMyCom.CreateWnd;
288294begin
289295 inherited CreateWnd;
290296 Items.Assign(Screen.Fonts);
291297end;
292298
293299procedure TMyCom.Change;
294300begin
295301 inherited;
296302 if Assigned(FOnChange2) then FOnChange2(Self);
297303 if Assigned(FOnSample) then FOnSample(FMyText);
298304end;
299305
300306function TMyCom.GetMyText2: String;
301307begin
302308 Result:=FMyText;
303309end;
304310
305311procedure TMyCom.SetMyText2(MyText: String);
306312begin
307313 FMyText:=MyText;
308314end;
309315
310316initialization
311317 {$I samplepackage.lrs}
312318
313319end.
314320```
315321
316322## 내장 (시각)컨포넌트 사용
317323
318324It's possible to use standard components embedded in your own components (look for example at TLabeledEdit or TButtonPanel). Let's say you want to create a custom panel with a TLabel on it. With the steps described above the base package and source files can be created. Now to add a TLabel to the component, do the following:
319325
320326- Add a private attribute for the label component (FEmbeddedLabel: TLabel;).
321327- Add a published read-only property for the label component (property EmbeddedLabel: TLabel read FEmbeddedLabel;)
322328- Create the label in the component's (overridden) constructor (FEmbeddedLabel := TLabel.Create(self); )
323329- Set the parent of the component (FEmbeddedLabel.Parent := self;)
324330- If the component to be embedded is not a 'subcomponent' by default (like TBoundLabel, TPanelBitBtn etc.) then add the call to SetSubComponent. This is necessary for the IDE so it knows that it has to store the properties of the embedded component as well. TLabel is not a subcomponent by default so the call to the method must be added (FEmbeddedLabel.SetSubComponent(true);).
325331
326332To sum it up you would get something like this (only the essential parts are shown):
327333
328334``` pascal
329335TEnhancedPanel = class(TCustomControl)
330336private
331337 { The new attribute for the embedded label }
332338 FEmbeddedLabel: TLabel;
333339
334340public
335341 { The constructor must be overriden so the label can be created }
336342 constructor Create(AOwner: TComponent); override;
337343
338344published
339345 { Make the label visible in the IDE }
340346 property EmbeddedLabel: TLabel read FEmbeddedLabel;
341347end;
342348
343349implementation
344350
345351constructor TEnhancedPanel.Create(AOwner: TComponent);
346352begin
347353 inherited Create(AOwner);
348354
349355 // Set default width and height
350356 with GetControlClassDefaultSize do
351357 SetInitialBounds(0, 0, CX, CY);
352358
353359 // Add the embedded label
354360 FEmbeddedLabel := TLabel.Create(Self); // Add the embedded label
355361 FEmbeddedLabel.Parent := self; // Show the label in the panel
356362 FEmbeddedLabel.SetSubComponent(true); // Tell the IDE to store the modified properties
357363 FLabel.Name := 'EmbeddedLabel';
358364 FLabel.Caption := 'Howdy World!';
359365
360366 // Make sure the embedded label can not be selected/deleted within the IDE
361367 FLabel.ControlStyle := FLabel.ControlStyle - [csNoDesignSelectable];
362368
363369 // Set other properties if necessary
364370 //...
365371
366372end;
367373```
368374
369375## Using custom paint procedure
370376
371377You can always subclass a component inside your program. For example, this implements a custom Paint procedure to a TLabel:
372378
373379``` pascal
374380type
375381 TMyLabel = class(TLabel)
376382 public
377383 procedure Paint; override;
378384 end;
379385{...}
380386implementation
381387{...}
382388procedure TMyLabel.Paint;
383389begin
384390 // your code to implement Paint, for example
385391 Canvas.TextOut(0,0,Caption);
386392end;
387393```
388394
389395Now you can create a MyLabel inside your program, at run time, with that overridden Paint procedure instead of the standard one.
390396
391397For most components, and for most methods, it would be recommendable to call inherited procedure iniside it:
392398
393399``` pascal
394400procedure TMyLabel.Paint;
395401begin
396402
397403 inherited Paint; /////////////////////
398404
399405 // your code to implement Paint, for example
400406 Canvas.TextOut(0,0,Caption);
401407end;
402408```
403409
404410But inherited behavior is not desirable in this case, since the second writing action would overlap the first (inherited) one.
405411
406412## IDE와 컴포넌트를 통합
407413
408414### Property Editors
409415
410416Property editors provide custom dialogs to edit properties in the object inspector. For most properties, like strings, string lists, images, enumerated types and others, there are already default property editors, but if a custom component has a special kind of property it may require a custom dialog to edit the property. Each property editor is a class, which should descend from TPropertyEditor or one of it's descendents and implement methods from this base class. They should be registered in the 'Register' procedure by using the function RegisterPropertyEditor from the unit PropEdits. It is a standard to name property editors with the property name followed by 'Property', for example TFieldProperty for the property editor of the TField property.
411417
412418``` pascal
413419 TPropertyEditor = class
414420 public
415421 function AllEqual: Boolean; Virtual;
416422 function AutoFill: Boolean; Virtual;
417423 procedure Edit; Virtual; // Activated by double-clicking the property value
418424 procedure ShowValue; Virtual; // Activated by control-clicking the property value
419425 function GetAttributes: TPropertyAttributes; Virtual;
420426 function GetEditLimit: Integer; Virtual;
421427 function GetName: ShortString; Virtual;
422428 procedure GetProperties(Proc: TGetPropEditProc); Virtual;
423429 function GetHint(HintType: TPropEditHint; x, y: integer): String; Virtual;
424430 function GetDefaultValue: AnsiString; Virtual;
425431 procedure GetValues(Proc: TGetStrProc); Virtual;
426432 procedure SetValue(const NewValue: AnsiString); Virtual;
427433 procedure UpdateSubProperties; Virtual;
428434 function SubPropertiesNeedsUpdate: Boolean; Virtual;
429435 function IsDefaultValue: Boolean; Virtual;
430436 function IsNotDefaultValue: Boolean; Virtual;
431437 // ... shortened
432438 end;
433439```
434440
435441A good example for a property editor is the TFont property editor.
436442
437443One of the most common cases for property editors is properties which are classes, because classes have many fields and can have a variety of formats, it's not possible for Lazarus to have the object inspector edit field able to edit it, like is done for strings and numeric types. For classes a convention is to have the value field show permanently the name of the class in parentheses, for example „(TFont)“ and the „...“ button shows a dialog to edit this class. This behavior, except for the dialog, is implemented by a standard property editor for classes called TClassPropertyEditor, which can be inherited from when writing property editors for classes:
438444
439445``` pascal
440446TClassPropertyEditor = class(TPropertyEditor)
441447public
442448 constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); Override;
443449 function GetAttributes: TPropertyAttributes; Override;
444450 procedure GetProperties(Proc: TGetPropEditProc); Override;
445451 function GetValue: AnsiString; Override;
446452 property SubPropsTypeFilter: TTypeKinds Read FSubPropsTypeFilter
447453 Write SetSubPropsTypeFilter
448454 Default tkAny;
449455end;
450456```
451457
452458Going back to the TFont example, inheriting from TClassPropertyEditor already offers part of the desired behavior and then the TFontPropertyEditor class only needs to implement showing the dialog in the Edit method and set the attributes for the editor:
453459
454460``` pascal
455461 TFontPropertyEditor = class(TClassPropertyEditor)
456462 public
457463 procedure Edit; Override;
458464 function GetAttributes: TPropertyAttributes; Override;
459465 end;
460466
461467procedure TFontPropertyEditor.Edit;
462468var
463469 FontDialog: TFontDialog;
464470begin
465471 FontDialog := TFontDialog.Create(NIL);
466472 try
467473 FontDialog.Font := TFont(GetObjectValue(TFont));
468474 FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
469475 if FontDialog.Execute then SetPtrValue(FontDialog.Font);
470476 finally
471477 FontDialog.Free;
472478 end;
473479end;
474480
475481function TFontPropertyEditor.GetAttributes: TPropertyAttributes;
476482begin
477483 Result := [paMultiSelect, paSubProperties, paDialog, paReadOnly];
478484end;
479485```
480486
481487### 컴포넌트 에디터
482488
483489Component editors control the behavior of right-clicking and double clicking components in the form designer. Each component editor is a class, which should descend from TComponentEditor or one of it's descendents and implement methods from this base class. They should be registered in the 'Register' procedure by using the function RegisterComponentEditor from the unit ComponentEditors. It is a standard to name component editors with the component name followed by 'Editor', for example TStringGridComponentEditor for the property editor of the TStringGrid component. Although user component editors should be based in TComponentEditor, most of it's methods are actually from an ancestor, so it is necessary to also know TBaseComponentEditor:
484490
485491``` pascal
486492 TBaseComponentEditor = class
487493 protected
488494 public
489495 constructor Create(AComponent: TComponent;
490496 ADesigner: TComponentEditorDesigner); Virtual;
491497 procedure Edit; Virtual; Abstract;
492498 procedure ExecuteVerb(Index: Integer); Virtual; Abstract;
493499 function GetVerb(Index: Integer): String; Virtual; Abstract;
494500 function GetVerbCount: Integer; Virtual; Abstract;
495501 procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); Virtual; Abstract;
496502 procedure Copy; Virtual; Abstract;
497503 function IsInInlined: Boolean; Virtual; Abstract;
498504 function GetComponent: TComponent; Virtual; Abstract;
499505 function GetDesigner: TComponentEditorDesigner; Virtual; Abstract;
500506 function GetHook(out Hook: TPropertyEditorHook): Boolean; Virtual; Abstract;
501507 procedure Modified; Virtual; Abstract;
502508 end;
503509```
504510
505511The most important method of a component editor is Edit, which is called when the component is double clicked. When the context menu for the component is invoked the GetVerbCount and GetVerb methods are called to build the menu. If one of the verbs (which means menu items in this case) are selected, ExecuteVerb is called. There is a default component editor (TDefaultEditor) which implements Edit to search the properties of the component for the most appropriate one to be edited. It usually chooses an event, which is edited by adding it's skeleton code in the code editor and setting the cursor to be in place to add code for it.
506512
507513Other important methods from TBasComponentEditor are: ExecuteVerb(Index), which executes one of the extra menu items placed on the right-click popup menu; GetVerb – To return the name of each extra popup menu item. Note that t is the responsibility of the component editor to place special menu item caption characters like & to create a keyboard accelerator and “-” to create a separator; GetVerbCount – Returns the amount of items to be added to the popup menu. The index for the routines GetVerb and ExecuteVerb is zero based, going from 0 to GetVerbCount – 1; PrepareItem – Called for each verb after the menu item was created. Allows the menu item to be customized such as by adding subitems, adding a checkbox or even hiding it by setting Visible to false; Copy - Called when the component is being copied to the clipboard. The component data for use by Lazarus will always be added and cannot be modified. This method is instead for adding a different kind of clipboard information to paste the component in other applications, but which won't affect the Lazarus paste.
508514
509515A simple and interesting example is the TCheckListBox component editor which creates a dialog to edit. More convenient then implementing all methods from TBaseComponentEditor is inheriting from TComponentEditor, and this is what TCheckListBoxEditor does. This base class adds empty implementations for most methods and some default ones for others. For Edit it calls ExecuteVerb(0), so if the first item will be identical to the double-click action, which is a convention for editor, there is no need to implement Edit. This basic action for the double-click and first menu item is often a dialog, and for TCheckListBox this is also done:
510516
511517``` pascal
512518 TCheckListBoxComponentEditor = class(TComponentEditor)
513519 protected
514520 procedure DoShowEditor;
515521 public
516522 procedure ExecuteVerb(Index: Integer); override;
517523 function GetVerb(Index: Integer): String; override;
518524 function GetVerbCount: Integer; override;
519525 end;
520526
521527procedure TCheckGroupComponentEditor.DoShowEditor;
522528var
523529 Dlg: TCheckGroupEditorDlg;
524530begin
525531 Dlg := TCheckGroupEditorDlg.Create(NIL);
526532 try
527533 // .. shortened
528534 Dlg.ShowModal;
529535 // .. shortened
530536 finally
531537 Dlg.Free;
532538 end;
533539end;
534540
535541procedure TCheckGroupComponentEditor.ExecuteVerb(Index: Integer);
536542begin
537543 case Index of
538544 0: DoShowEditor;
539545 end;
540546end;
541547
542548function TCheckGroupComponentEditor.GetVerb(Index: Integer): String;
543549begin
544550 Result := 'CheckBox Editor...';
545551end;
546552
547553function TCheckGroupComponentEditor.GetVerbCount: Integer;
548554begin
549555 Result := 1;
550556end;
551557```
552558
553559## 같이 보기
554560
555561- [[Extending the IDE]]
556562- [[Lazarus Packages]] - A guide for creating a package under Lazarus
557563- [[Components and Code examples]]
558564- [[Lazarus Components]]
559565- [[IDE Window: Add to Package]]
560566
561567You can post questions regarding this page [here](http://forum.lazarus.freepascal.org/index.php/topic,5789.0.html)
562568
563569---
564570*From the Free Pascal wiki, page "How To Write Lazarus Component/ko", as it stood on 2020-02-17 after a change by Trev, 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).*

Back to history