unit MvcModel;
//*******************************************************
// The model class and the view base class for example
// program demonstrating the Model/View/Controller (MVC)
// paradigm.
//*******************************************************
// Copyright (C) 1998 John Wiley & Sons, Inc.
// All rights reserved. See additional copyright
// information in Readme.txt.
//*******************************************************

interface

uses
    SysUtils;

const
    NUM_VALUES = 10;

type
    TMvcModel = class;
    TMvcView = class;

    TMvcModel = class(TObject)
        private
            // The model data.
            ExpenseValues : array [1..NUM_VALUES] of Single;
            ExpenseNames  : array [1..NUM_VALUES] of String;

            // The views.
            TopView      : TMvcView;

            procedure NotifyViews(skip : TMvcView);

        public
            constructor Create;
            procedure AddView(view : TMvcView);
            procedure RemoveView(target : TMvcView);
            function ExpenseValue(i : Integer) : Single;
            function ExpenseName(i : Integer) : String;
            function TotalExpenses : Single;
            function NumValues : Integer;
            procedure SetExpenseValue(i : Integer; value : Single; view : TMvcView);
            procedure SetExpenseName(i : Integer; value : String; view : TMvcView);
    end;

    TMvcView = class(TObject)
        public
            // Used to build a linked list of views.
            NextView : TMvcView;

        protected
            TheModel : TMvcModel;

        public
            constructor Create(model : TMvcModel);
            destructor Destroy; override;
            procedure DataModified; virtual; abstract;
    end;

implementation

// -----------------------------------------------
// Model implementation.
// -----------------------------------------------

// Create some random data.
constructor TMvcModel.Create;
var
    i : Integer;
begin
    inherited Create;
    Randomize;
    for i := 1 to NUM_VALUES do
        ExpenseValues[i] := Random(4000) + 3000;

    ExpenseNames[1]  := 'Advertising';
    ExpenseNames[2]  := 'Binary Transmission Lab';
    ExpenseNames[3]  := 'Computer Center';
    ExpenseNames[4]  := 'Destructive Evaluation Lab';
    ExpenseNames[5]  := 'Executive Offices';
    ExpenseNames[6]  := 'Finance';
    ExpenseNames[7]  := 'Geographic Systems';
    ExpenseNames[8]  := 'Human Resources';
    ExpenseNames[9]  := 'Interactive Technology Lab';
    ExpenseNames[10] := 'JASPER';
end;

// Add the new view to the top of the list.
procedure TMvcModel.AddView(view : TMvcView);
begin
    view.NextView := TopView;
    TopView := view;
end;

// Remove the view from the list.
procedure TMvcModel.RemoveView(target : TMvcView);
var
    view : TMvcView;
begin
    if (target = TopView) then
    begin
        TopView := TopView.NextView;
    end else begin
        view := TopView;
        while (view <> nil) do
        begin
            if (view.NextView = target) then break;
            view := view.NextView;
        end;
        if (view <> nil) then
            view.NextView := view.NextView.NextView;
    end;
end;

// Return an expense value.
function TMvcModel.ExpenseValue(i : Integer) : Single;
begin
    if ((i < 1) or (i > NUM_VALUES)) then
        raise ERangeError.CreateFmt(
            '%d is not within the valid range of 1..%d',
            [i, NUM_VALUES]);

    Result := ExpenseValues[i];
end;

// Return an expense name.
function TMvcModel.ExpenseName(i : Integer) : String;
begin
    if ((i < 1) or (i > NUM_VALUES)) then
        raise ERangeError.CreateFmt(
            '%d is not within the valid range of 1..%d',
            [i, NUM_VALUES]);

    Result := ExpenseNames[i];
end;

// Return the expense total.
function TMvcModel.TotalExpenses : Single;
var
    i : Integer;
begin
    Result := 0;
    for i := 1 to NUM_VALUES do
        Result := Result + ExpenseValues[i];
end;

// Return the number of expense items.
function TMvcModel.NumValues : Integer;
begin
    Result := NUM_VALUES;
end;

// Set an expense value.
procedure TMvcModel.SetExpenseValue(i : Integer; value : Single; view : TMvcView);
begin
    if ((i < 1) or (i > NUM_VALUES)) then
        raise ERangeError.CreateFmt(
            '%d is not within the valid range of 1..%d',
            [i, NUM_VALUES]);

    ExpenseValues[i] := value;
    NotifyViews(view);
end;

// Set an expense name.
procedure TMvcModel.SetExpenseName(i : Integer; value : String; view : TMvcView);
begin
    if ((i < 1) or (i > NUM_VALUES)) then
        raise ERangeError.CreateFmt(
            '%d is not within the valid range of 1..%d',
            [i, NUM_VALUES]);

    ExpenseNames[i] := value;
    NotifyViews(view);
end;

// Notify views that the data has changed.
procedure TMvcModel.NotifyViews(skip : TMvcView);
var
    view : TMvcView;
begin
    view := TopView;
    while (view <> nil) do
    begin
        if (view <> skip) then view.DataModified;
        view := view.NextView;
    end;
end;

// -----------------------------------------------
// View implementation.
// -----------------------------------------------

// Create the object and register with its model.
constructor TMvcView.Create(model : TMvcModel);
begin
    inherited Create;
    TheModel := model;
    TheModel.AddView(Self);
    DataModified;
end;

// Remove the view from its model.
destructor TMvcView.Destroy;
begin
    if (TheModel <> nil) then TheModel.RemoveView(Self);
    inherited Destroy;
end;

end.
