📄 cpuobservers.pas
字号:
//////////////////////////////////////////////////////////////////////
// //
// CpuObservers.pas: CPU Observer Framework //
// Observer superclass and observer list management //
// //
// The contents of this file are subject to the Bottled Light //
// Public License Version 1.0 (the "License"); you may not use this //
// file except in compliance with the License. You may obtain a //
// copy of the License at http://www.bottledlight.com/BLPL/ //
// //
// Software distributed under the License is distributed on an //
// "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or //
// implied. See the License for the specific language governing //
// rights and limitations under the License. //
// //
// The Original Code is the Mappy VM User Interface, released //
// April 1st, 2003. The Initial Developer of the Original Code is //
// Bottled Light, Inc. Portions created by Bottled Light, Inc. are //
// Copyright (C) 2001-2003 Bottled Light, Inc. All Rights Reserved. //
// //
// Author(s): //
// Michael Noland (joat), michael@bottledlight.com //
// //
// Changelog: //
// 1.0: First public release (April 1st, 2003) //
// //
// Notes: //
// Mappy VM uses an observer model, where any number of data //
// viewers, or 'observers' can be registered, and they'll appear //
// in the viewers menu item for the user to activate or close //
// at will. When an observer is active, its UpdateObserver //
// method is called after each simulation step (either one cycle //
// or an entire frame). Observers also call UpdateObservers to //
// notify other observers when they've modified the MVM core //
// state in any way. //
// //
// This model has a couple of advantages, most notably less //
// usage and extensibility. Memory usage because observer forms //
// are created on the fly when needed, rather than kept around in //
// memory on the oft chance someone will use it. Extensability //
// because making a new observer is a case of creating a new form //
// and descending it from TCpuObserver, implementing two methods, //
// and adding a RegisterViewer call to the bottom of the unit. //
// //
// This idea of demand-creation of forms is also used for dialogs //
// such as Audio Options. They're also descended from the same //
// TCpuObserver, and behave in exactly the same way, except they //
// are invoked from a different menu item in the user interface. //
// //
// A slightly more recent addition is the decentralization of //
// loading/saving config data. Now all an observer has to do is //
// override two virutal methods and grab/store their config state //
// into a TIniFile (registry be damned!) //
// //
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
unit CpuObservers; ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
interface ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
uses
SysUtils, Classes, Contnrs, Graphics, Controls, Forms, Math, Menus,
IniFiles, console, nexus;
//////////////////////////////////////////////////////////////////////
type
TCpuObserver = class(TForm)
private
myItem: TMenuItem;
protected
procedure Shutdown;
procedure observerClose(Sender: TObject; var Action: TCloseAction);
public
class function OCaption: string; virtual; abstract;
procedure UpdateObserver; virtual; abstract;
procedure LoadSettings(ini: TIniFile); virtual;
procedure SaveSettings(ini: TIniFile); virtual;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
TCpuObserverClass = class of TCpuObserver;
//////////////////////////////////////////////////////////////////////
procedure RegisterViewer(typ: TCpuObserverClass);
procedure RegisterDialog(typ: TCpuObserverClass);
function CreateObserver(name: string; menuItem: TMenuItem): TCpuObserver;
function FindObserver(name: string): TCpuObserver;
procedure UpdateObservers;
procedure CloseObservers;
//////////////////////////////////////////////////////////////////////
var
observerList: TObjectList;
observers, guiObservers: TStringList;
//////////////////////////////////////////////////////////////////////
implementation ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
procedure RegisterViewer(typ: TCpuObserverClass);
begin
observers.AddObject(typ.OCaption, TObject(typ));
end;
//////////////////////////////////////////////////////////////////////
procedure RegisterDialog(typ: TCpuObserverClass);
begin
guiObservers.AddObject(typ.OCaption, TObject(typ));
end;
//////////////////////////////////////////////////////////////////////
function CreateObserver(name: string; menuItem: TMenuItem): TCpuObserver;
var
i: integer;
begin
Result := nil;
i := observers.IndexOf(name);
if i > -1 then
Application.CreateForm(TCpuObserverClass(observers.objects[i]), Result)
else begin
i := guiObservers.IndexOf(name);
Application.CreateForm(TCpuObserverClass(guiObservers.objects[i]), Result);
end;
if i > - 1 then begin
observerList.Add(Result);
Result.myItem := menuItem;
Result.myItem.Checked := true;
Result.Show;
end;
end;
//////////////////////////////////////////////////////////////////////
function FindObserver(name: string): TCpuObserver;
var
i: integer;
begin
for i := 0 to observerList.Count - 1 do begin
Result := observerList.Items[i] as TCpuObserver;
if Result.OCaption = name then Exit;
end;
Result := nil;
end;
//////////////////////////////////////////////////////////////////////
procedure UpdateObservers;
var
i: integer;
begin
// Update each open observer
for i := 0 to observerList.count - 1 do
(observerList.Items[i] as TCpuObserver).UpdateObserver;
end;
//////////////////////////////////////////////////////////////////////
procedure CloseObservers;
begin
while observerList.Count > 0 do (observerList.Items[0] as TCpuObserver).Close;
observerList.Clear;
end;
//////////////////////////////////////////////////////////////////////
// TCpuObserver //////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
constructor TCpuObserver.Create(AOwner: TComponent);
var
ini: TIniFile;
begin
inherited;
OnClose := observerClose;
// Set some things up
Caption := ocaption;
// Load the settings
ini := TIniFile.Create(appIniFile);
LoadSettings(ini);
ini.Free;
end;
//////////////////////////////////////////////////////////////////////
destructor TCpuObserver.Destroy;
begin
Shutdown;
inherited;
end;
//////////////////////////////////////////////////////////////////////
procedure TCpuObserver.Shutdown;
var
ini: TIniFile;
begin
// Save the settings
ini := TIniFile.Create(appIniFile);
SaveSettings(ini);
ini.Free;
end;
//////////////////////////////////////////////////////////////////////
procedure TCpuObserver.observerClose(Sender: TObject; var Action: TCloseAction);
var
ref: TCpuObserver;
index: integer;
begin
// Uncheck the observer entry in the view menu
ref := Sender as TCpuObserver;
ref.myItem.Checked := false;
// Close the observer and remove it from the list
Action := caFree;
index := observerList.IndexOf(Sender);
if index > -1 then observerList.Delete(index);
end;
//////////////////////////////////////////////////////////////////////
procedure TCpuObserver.LoadSettings(ini: TIniFile);
var
ws: TWindowState;
begin
ws := TWindowState(Min(2, Max(0, ini.ReadInteger(OCaption, 'WindowState', Ord(wsNormal)))));
if ws <> wsMaximized then begin
Width := ini.ReadInteger(OCaption, 'Width', Width);
Height := ini.ReadInteger(OCaption, 'Height', Height);
Left := ini.ReadInteger(OCaption, 'Left', Left);
Top := ini.ReadInteger(OCaption, 'Top', Top);
end;
WindowState := ws;
end;
//////////////////////////////////////////////////////////////////////
procedure TCpuObserver.SaveSettings(ini: TIniFile);
begin
ini.WriteInteger(OCaption, 'WindowState', Ord(WindowState));
ini.WriteInteger(OCaption, 'Left', Left);
ini.WriteInteger(OCaption, 'Top', Top);
ini.WriteInteger(OCaption, 'Width', Width);
ini.WriteInteger(OCaption, 'Height', Height);
end;
//////////////////////////////////////////////////////////////////////
initialization
observers := TStringList.Create;
guiObservers := TStringList.Create;
observerList := TObjectList.Create(false);
finalization
observerList.Free;
guiObservers.Free;
observers.Free;
end.
//////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -