Crossfire JXClient, Trunk  R20561
Buffer.java
Go to the documentation of this file.
1 /*
2  * This file is part of JXClient, the Fullscreen Java Crossfire Client.
3  *
4  * JXClient is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * JXClient is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with JXClient; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * Copyright (C) 2005-2008 Yann Chachkoff.
19  * Copyright (C) 2006-2011 Andreas Kirschbaum.
20  */
21 
22 package com.realtime.crossfire.jxclient.gui.log;
23 
25 import java.awt.Color;
26 import java.awt.font.FontRenderContext;
27 import java.awt.geom.RectangularShape;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.concurrent.CopyOnWriteArrayList;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35 
41 public class Buffer {
42 
46  public static final int MAX_LINES = 250;
47 
51  public static final int MIN_LINE_HEIGHT = 8;
52 
56  @NotNull
58 
62  @NotNull
63  private final Fonts fonts;
64 
68  @NotNull
69  private final FontRenderContext context;
70 
74  private int renderWidth;
75 
79  @NotNull
80  private final List<Line> lines = new CopyOnWriteArrayList<>();
81 
85  private int totalHeight;
86 
91  @NotNull
92  private final Object sync = new Object();
93 
97  private int lastCount;
98 
102  @Nullable
103  private Color lastColor;
104 
108  @NotNull
109  private String lastText = "";
110 
117  public Buffer(@NotNull final Fonts fonts, @NotNull final FontRenderContext context, final int renderWidth) {
118  this.fonts = fonts;
119  this.context = context;
120  this.renderWidth = renderWidth;
121  }
122 
127  public void setRenderWidth(final int renderWidth) {
128  if (this.renderWidth == renderWidth) {
129  return;
130  }
131 
132  this.renderWidth = renderWidth;
133 
134  synchronized (sync) {
135  totalHeight = 0;
136  for (final Line line : lines) {
137  final int height = calculateHeight(line);
138  line.setHeight(height);
139  totalHeight += height;
140  }
141  }
142  }
143 
147  public void clear() {
148  final List<Line> removedLines;
149  synchronized (sync) {
150  removedLines = new ArrayList<>(lines);
151  totalHeight = 0;
152  lines.clear();
153  }
154  for (final BufferListener listener : listeners) {
155  listener.linesRemoved(removedLines);
156  }
157  lastCount = 0;
158  lastText = "";
159  lastColor = null;
160  }
161 
166  public void addLine(@NotNull final Line line) {
167  final int height = calculateHeight(line);
168  line.setHeight(height);
169  synchronized (sync) {
170  totalHeight += height;
171  lines.add(line);
172  }
173 
174  for (final BufferListener listener : listeners) {
175  listener.lineAdded();
176  }
177  }
178 
183  public void replaceLine(@NotNull final Line line) {
184  final int height = calculateHeight(line);
185  line.setHeight(height);
186  synchronized (sync) {
187  totalHeight += height;
188  final int lastIndex = lines.size()-1;
189  totalHeight -= lines.get(lastIndex).getHeight();
190  lines.set(lastIndex, line);
191  }
192 
193  for (final BufferListener listener : listeners) {
194  listener.lineReplaced();
195  }
196  }
197 
201  public void prune() {
202  final List<Line> removedLines;
203  synchronized (sync) {
204  if (lines.size() <= MAX_LINES) {
205  return;
206  }
207 
208  removedLines = new ArrayList<>(lines.size()-MAX_LINES);
209  while (lines.size() > MAX_LINES) {
210  final Line line = lines.remove(0);
211  removedLines.add(line);
212  totalHeight -= line.getHeight();
213  }
214  }
215  for (final BufferListener listener : listeners) {
216  listener.linesRemoved(removedLines);
217  }
218  }
219 
226  @NotNull
227  public Line getLine(final int line) {
228  synchronized (sync) {
229  return lines.get(line);
230  }
231  }
232 
237  public int getTotalHeight() {
238  synchronized (sync) {
239  return Math.max(totalHeight, 1);
240  }
241  }
242 
248  @NotNull
249  public Iterator<Line> iterator() {
250  assert Thread.holdsLock(sync);
251  return Collections.unmodifiableList(lines).iterator();
252  }
253 
259  @NotNull
260  public Iterator<Line> listIterator(final int line) {
261  assert Thread.holdsLock(sync);
262  return Collections.unmodifiableList(lines).listIterator(line);
263  }
264 
269  public int size() {
270  synchronized (sync) {
271  return lines.size();
272  }
273  }
274 
280  private int calculateHeight(@NotNull final Line line) {
281  int height = 0;
282  int x = 0;
283  int minY = 0;
284  int maxY = 0;
285  int beginIndex = 0;
286  int i = 0;
287  for (final Segment segment : line) {
288  final RectangularShape rectangle = segment.getSize(fonts, context);
289  final int width = (int)Math.round(rectangle.getWidth());
290  if (x != 0 && x+width > renderWidth) {
291  line.updateAttributes(beginIndex, i, height-minY, fonts, context);
292 
293  height += maxY-minY;
294  x = 0;
295  minY = 0;
296  maxY = 0;
297  beginIndex = i;
298  }
299 
300  segment.setX(x);
301  segment.setY(height);
302  segment.setWidth(width);
303 
304  x += width;
305  minY = (int)Math.min(minY, Math.round(rectangle.getY()));
306  maxY = (int)Math.max(maxY, Math.round(rectangle.getY()+rectangle.getHeight()));
307 
308  i++;
309  }
310 
311  line.updateAttributes(beginIndex, i, height-minY, fonts, context);
312  height += maxY-minY;
313 
314  return Math.max(MIN_LINE_HEIGHT, height);
315  }
316 
321  public void addBufferListener(@NotNull final BufferListener listener) {
322  listeners.add(listener);
323  }
324 
329  public void removeBufferListener(@NotNull final BufferListener listener) {
330  listeners.remove(listener);
331  }
332 
338  @NotNull
339  public Object getSyncObject() {
340  return sync;
341  }
342 
349  public boolean mergeLines(@NotNull final String text, @Nullable final Color color) {
350  if (lastCount > 0 && text.equals(lastText) && (lastColor == null ? color == null : lastColor.equals(color))) {
351  lastCount++;
352  return true;
353  }
354 
355  lastCount = 1;
356  lastText = text;
357  lastColor = color;
358  return false;
359 
360  }
361 
367  public int getLastCount() {
368  return lastCount;
369  }
370 
371 }
static final int MAX_LINES
The maximum number of lines the buffer can hold.
Definition: Buffer.java:46
Manages the contents of one text line.
Definition: Line.java:38
Line getLine(final int line)
Returns one Line by line index.
Definition: Buffer.java:227
final FontRenderContext context
The FontRenderContext to use.
Definition: Buffer.java:69
int getTotalHeight()
Returns the total height of all lines.
Definition: Buffer.java:237
void clear()
Clears all lines from the buffer.
Definition: Buffer.java:147
int calculateHeight(@NotNull final Line line)
Determines the height of a Line in pixels.
Definition: Buffer.java:280
Color lastColor
The color of the previously added line of text.
Definition: Buffer.java:103
void replaceLine(@NotNull final Line line)
Replaces the last Line of this buffer.
Definition: Buffer.java:183
int getHeight()
Returns the height of this line.
Definition: Line.java:95
Iterator< Line > iterator()
Returns an Iterator for the lines in this buffer.
Definition: Buffer.java:249
void addLine(@NotNull final Line line)
Appends a Line to the end of the buffer.
Definition: Buffer.java:166
final Object sync
Object to synchronized access to lines and totalHeight.
Definition: Buffer.java:92
void prune()
Prunes excess lines.
Definition: Buffer.java:201
Object getSyncObject()
Returns the object to synchronize on when calling iterator() or listIterator(int).
Definition: Buffer.java:339
void addBufferListener(@NotNull final BufferListener listener)
Adds a listener to notify of changes.
Definition: Buffer.java:321
static final int MIN_LINE_HEIGHT
The minimal height of a line in pixels.
Definition: Buffer.java:51
int size()
Returns the number of lines.
Definition: Buffer.java:269
One segment of a Line which should be displayed without changing text attributes. ...
Definition: Segment.java:34
void add(@NotNull final T listener)
Adds a listener.
boolean mergeLines(@NotNull final String text, @Nullable final Color color)
Checks whether a new text line should be merged with a preceding line.
Definition: Buffer.java:349
final EventListenerList2< BufferListener > listeners
The listeners to notify about changes.
Definition: Buffer.java:57
Buffer(@NotNull final Fonts fonts, @NotNull final FontRenderContext context, final int renderWidth)
Creates a new instance.
Definition: Buffer.java:117
int lastCount
The number of repetitions of the previously added line of text.
Definition: Buffer.java:97
String lastText
The contents of the previously added line of text.
Definition: Buffer.java:109
Manages the contents of the contents of a log window.
Definition: Buffer.java:41
Iterator< Line > listIterator(final int line)
Returns an Iterator for the lines in this buffer.
Definition: Buffer.java:260
final Fonts fonts
The Fonts instance for looking up fonts.
Definition: Buffer.java:63
Interface for listeners for changes of Buffer contents.
void setRenderWidth(final int renderWidth)
Updates the width to render.
Definition: Buffer.java:127
final List< Line > lines
The lines in display order.
Definition: Buffer.java:80
void remove(@NotNull final T listener)
Removes a listener.
int getLastCount()
Returns the number of merged lines.
Definition: Buffer.java:367
void removeBufferListener(@NotNull final BufferListener listener)
Removes a listener to be notified of changes.
Definition: Buffer.java:329
int totalHeight
The total height of all lines.
Definition: Buffer.java:85
int renderWidth
The width to render.
Definition: Buffer.java:74