This file is indexed.

/usr/src/castle-game-engine-5.2.0/ui/opengl/castlenotifications.pas is in castle-game-engine-src 5.2.0-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
{
  Copyright 2002-2014 Michalis Kamburelis.

  This file is part of "Castle Game Engine".

  "Castle Game Engine" is free software; see the file COPYING.txt,
  included in this distribution, for details about the copyright.

  "Castle Game Engine" is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  ----------------------------------------------------------------------------
}

{ Notifications displayed in the OpenGL window (TCastleNotifications). }
unit CastleNotifications;

interface

uses CastleUIControls, Classes, SysUtils, CastleUtils, CastleGLUtils,
  CastleFonts, CastleTimeUtils, CastleVectors, CastleStringUtils,
  FGL, CastleColors;

type
  THorizontalPosition = (hpLeft, hpMiddle, hpRight);
  TVerticalPosition = (vpDown, vpMiddle, vpUp);

  { Internal type. @exclude }
  TNotification = class
    Text: string;
    Time: TMilisecTime; {< appear time }
  end;

  { Internal type. @exclude }
  TNotificationList = class(specialize TFPGObjectList<TNotification>)
    procedure DeleteFirst(DelCount: Integer);
  end;

  { Notifications displayed in the OpenGL window.
    The idea is to display messages about something happening
    at the bottom / top of the screen. These messages disappear by themselves
    after some short time.

    Similar to older FPS games messages, e.g. DOOM, Quake, Duke Nukem 3D.
    Suitable for game messages like "Picked up 20 ammo"
    or "Player Foo joined game".

    This is a TUIControl descendant, so to use it --- just add it
    to TCastleWindowCustom.Controls or TCastleControlCustom.Controls.
    Call @link(Show) to display a message. }
  TCastleNotifications = class(TUIControl)
  private
    { Messages, ordered from oldest (new mesages are added at the end).}
    Messages: TNotificationList;
    FHorizontalPosition: THorizontalPosition;
    FVerticalPosition: TVerticalPosition;
    FColor: TCastleColor;
    FMaxMessages: integer;
    FTimeout: TMilisecTime;
    FHorizontalMargin, FVerticalMargin: Integer;
    FHistory: TCastleStringList;
    FCollectHistory: boolean;
    FPositionX: Integer;
    FPositionY: Integer;
  public
    const
      DefaultMaxMessages = 4;
      DefaultMessagesTimeout = 5000;
      DefaultHorizontalPosition = hpMiddle;
      DefaultVerticalPosition = vpDown;
      DefaultHorizontalMargin = 10;
      DefaultVerticalMargin = 1;

    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    { Show new message. An overloaded version that takes a single string will
      detect newlines in the string automatically so a message may be multi-line.
      The messages will be automatically broken to fit on the screen width with
      given font.
      @groupBegin }
    procedure Show(const s: string); overload;
    procedure Show(s: TStringList); overload;
    { @groupEnd }

    { Clear all messages. }
    procedure Clear;

    procedure Update(const SecondsPassed: Single;
      var HandleInput: boolean); override;

    procedure Render; override;
    function GetExists: boolean; override;

    { Color used to draw messages. Default value is yellow. }
    property Color: TCastleColor read FColor write FColor;

    { All the messages passed to @link(Show), collected only if CollectHistory.
      May be @nil when not CollectHistory. }
    property History: TCastleStringList read FHistory;

    { Position shift, relative to position set by HorizontalPosition and VerticalPosition.
      TODO: we should make positioning common for all controls, using Left, Bottom, or some Autoxxx
      spec. }
    property PositionX: Integer read FPositionX write FPositionX;
    property PositionY: Integer read FPositionY write FPositionY;
  published
    { How many message lines should be visible on the screen, at maximum.  }
    property MaxMessages: integer
      read FMaxMessages write FMaxMessages default DefaultMaxMessages;

    { How long a given message should be visible on the screen, in miliseconds.
      Message stops being visible when this timeout passed,
      or when we need more space for new messages (see MaxMessages). }
    property Timeout: TMilisecTime
      read FTimeout write FTimeout default DefaultMessagesTimeout;

    property HorizontalPosition: THorizontalPosition read FHorizontalPosition
      write FHorizontalPosition default DefaultHorizontalPosition;
    property VerticalPosition: TVerticalPosition read FVerticalPosition
      write FVerticalPosition default DefaultVerticalPosition;

    { Margins, in pixels, from the border of the container (window or such).
      @groupBegin }
    property HorizontalMargin: Integer read FHorizontalMargin write FHorizontalMargin
      default DefaultHorizontalMargin;
    property VerticalMargin: Integer read FVerticalMargin write FVerticalMargin
      default DefaultVerticalMargin;
    { @groupEnd }

    { Turn this on to have all the messages you pass to @link(Show) be collected
      inside @link(History) string list. @link(History) is expanded by @link(Show),
      it is cleared by @link(Clear), just like the notifications on screen.
      However, unlike the visible messages, it has unlimited size
      (messages there are not removed when MaxMessages or @link(Timeout)
      take action), and messages inside are not broken to honour screen width.

      This is useful if you want to show the player a history of messages
      (in case they missed the message in game). }
    property CollectHistory: boolean read FCollectHistory write FCollectHistory
      default false;
  end;

procedure Register;

implementation

uses CastleLog, CastleControls;

procedure Register;
begin
  RegisterComponents('Castle', [TCastleNotifications]);
end;

procedure TNotificationList.DeleteFirst(DelCount: Integer);
var
  I: Integer;
begin
  { Could be optimized better, but this is simple and works correctly
    with TFPGObjectList.FreeObjects = true management.
    This is called only for really small DelCount values, so no problem. }
  for I := 1 to DelCount do Delete(0);
end;

{ TCastleNotifications ------------------------------------------------------- }

constructor TCastleNotifications.Create(AOwner: TComponent);
begin
  inherited;
  Messages := TNotificationList.Create;
  FHistory := TCastleStringList.Create;

  MaxMessages := DefaultMaxMessages;
  Timeout := DefaultMessagesTimeout;
  FHorizontalPosition := DefaultHorizontalPosition;
  FVerticalPosition := DefaultVerticalPosition;
  FHorizontalMargin := DefaultHorizontalMargin;
  FVerticalMargin := DefaultVerticalMargin;
  FColor := Yellow;
end;

destructor TCastleNotifications.Destroy;
begin
  FreeAndNil(Messages);
  FreeAndNil(FHistory);
  inherited;
end;

procedure TCastleNotifications.Show(S: TStringList);

  procedure AddStrings(S: TStrings);
  var
    N: TNotification;
    i: integer;
  begin
    { Below could be optimized. But we use this only for a small number
      of messages, so no need to. }
    for i := 0 to S.Count - 1 do
    begin
      if Messages.Count = MaxMessages then Messages.Delete(0);
      N := TNotification.Create;
      N.Text := S[i];
      N.Time := GetTickCount;
      Messages.Add(N);
    end;
  end;

var
  Broken: TStringList;
begin
  if Log then
    WriteLog('Time message', S.Text);

  { TODO: It's a bummer that we need UIFont created (which means:
    OpenGL context must be initialized) to make BreakLines,
    while BreakLines only really uses font metrics (doesn't need OpenGL
    font resources). }
  if ContainerSizeKnown and GLInitialized then
  begin
    Broken := TStringList.Create;
    try
      UIFont.BreakLines(s, Broken, ContainerWidth - HorizontalMargin * 2);
      AddStrings(Broken);
    finally Broken.Free end;
  end else
    AddStrings(S);

  if CollectHistory then
    History.AddList(S);

  VisibleChange;
end;

procedure TCastleNotifications.Show(const s: string);
var
  strs: TStringList;
begin
  strs := TStringList.Create;
  try
    strs.Text := s;
    Show(strs);
  finally strs.Free end;
end;

procedure TCastleNotifications.Clear;
begin
  Messages.Clear;
  if CollectHistory then
    History.Clear;
  VisibleChange;
end;

procedure TCastleNotifications.Render;
var
  i: integer;
  x, y: integer;
begin
  for i := 0 to Messages.Count-1 do
  begin
    { calculate x relative to 0..ContainerWidth, then convert to 0..GLMaxX }
    case HorizontalPosition of
      hpLeft  : x := HorizontalMargin;
      hpRight : x :=  ContainerWidth-UIFont.TextWidth(messages[i].Text) - HorizontalMargin;
      hpMiddle: x := (ContainerWidth-UIFont.TextWidth(messages[i].Text)) div 2;
    end;

    { calculate y relative to 0..ContainerHeight, then convert to 0..GLMaxY }
    case VerticalPosition of
      vpDown  : y := (Messages.Count-i-1) * UIFont.RowHeight + UIFont.Descend + VerticalMargin;
      vpMiddle: y := (ContainerHeight - Messages.Count * UIFont.RowHeight) div 2 + i*UIFont.RowHeight;
      vpUp   :  y :=  ContainerHeight-(i+1)*UIFont.RowHeight - VerticalMargin;
    end;

    UIFont.Print(PositionX + x, PositionY + y, Color, Messages[i].Text);
  end;
end;

function TCastleNotifications.GetExists: boolean;
begin
  { optimization, do not even set 2D projection when no messages }
  Result := (inherited GetExists) and (Messages.Count <> 0);
end;

procedure TCastleNotifications.Update(const SecondsPassed: Single;
  var HandleInput: boolean);
{ Check which messages should time out. }
var
  gtc: TMilisecTime;
  i: integer;
begin
  inherited;
  gtc := GetTickCount;
  for i := Messages.Count - 1 downto 0 do
    if TimeTickSecondLater(Messages[i].Time, gtc, Timeout) then
    begin { delete messages 0..I }
      Messages.DeleteFirst(I + 1);
      VisibleChange;
      break;
    end;
end;

end.