Crossfire JXClient, Trunk
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-2017,2019-2023 Andreas Kirschbaum
20  * Copyright (C) 2010-2012,2014-2018,2020-2023 Nicolas Weeger
21  */
22 
23 package com.realtime.crossfire.jxclient.gui.log;
24 
26 import java.awt.Color;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Objects;
32 import java.util.concurrent.CopyOnWriteArrayList;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35 
41 public abstract class Buffer {
42 
46  @NotNull
48 
52  private final int maxLines;
53 
57  private int renderWidth;
58 
62  @NotNull
63  private final List<Line> lines = new CopyOnWriteArrayList<>();
64 
68  private int totalHeight;
69 
74  @NotNull
75  private final Object sync = new Object();
76 
80  private int lastCount;
81 
85  private int lastType = -1;
86 
90  private int lastSubtype = -1;
91 
95  @Nullable
96  private Color lastColor;
97 
101  @NotNull
102  private String lastText = "";
103 
107  private boolean showSentCommands;
108 
112  private boolean showTimestamps;
113 
119  protected Buffer(final int maxLines, final int renderWidth) {
120  if (maxLines < 2) {
121  throw new IllegalArgumentException("maxLines="+maxLines);
122  }
123 
124  this.maxLines = maxLines;
125  this.renderWidth = renderWidth;
126  }
127 
132  public void setRenderWidth(final int renderWidth) {
133  synchronized (sync) {
134  if (this.renderWidth == renderWidth) {
135  return;
136  }
137 
138  this.renderWidth = renderWidth;
139  layoutLines();
140  }
141  }
142 
147  public void setShowSentCommands(final boolean showSentCommands) {
148  synchronized (sync) {
149  if (this.showSentCommands == showSentCommands) {
150  return;
151  }
152 
153  this.showSentCommands = showSentCommands;
154  layoutLines();
155  }
156  }
157 
162  public void setShowTimestamps(final boolean showTimestamps) {
163  synchronized (sync) {
164  if (this.showTimestamps == showTimestamps) {
165  return;
166  }
167 
168  this.showTimestamps = showTimestamps;
169  layoutLines();
170  }
171  }
172 
176  public void clear() {
177  final List<Line> removedLines;
178  synchronized (sync) {
179  removedLines = new ArrayList<>(lines);
180  totalHeight = 0;
181  lines.clear();
182  }
183  if (!removedLines.isEmpty()) {
184  for (BufferListener listener : listeners) {
185  listener.linesRemoved(removedLines);
186  }
187  }
188  lastCount = 0;
189  lastText = "";
190  lastType = -1;
191  lastSubtype = -1;
192  lastColor = null;
193  }
194 
199  public void addLine(@NotNull final Line line) {
200  synchronized (sync) {
201  final int position = lines.isEmpty() ? 0 : lines.get(lines.size()-1).getEndPosition();
202  totalHeight += line.calculateHeight(position, renderWidth, showSentCommands, showTimestamps);
203  lines.add(line);
204  }
205 
206  for (BufferListener listener : listeners) {
207  listener.lineAdded();
208  }
209  }
210 
215  public void replaceLine(@NotNull final Line line) {
216  synchronized (sync) {
217  final int position = lines.size() < 2 ? 0 : lines.get(lines.size()-2).getEndPosition();
218  totalHeight += line.calculateHeight(position, renderWidth, showSentCommands, showTimestamps);
219  final int lastIndex = lines.size()-1;
220  totalHeight -= lines.get(lastIndex).getHeight();
221  lines.set(lastIndex, line);
222  }
223 
224  for (BufferListener listener : listeners) {
225  listener.lineReplaced();
226  }
227  }
228 
232  public void prune() {
233  final List<Line> removedLines;
234  synchronized (sync) {
235  if (lines.size() <= maxLines) {
236  return;
237  }
238 
239  removedLines = new ArrayList<>(lines.size()-maxLines);
240  int positionsRemoved = 0;
241  while (lines.size() > maxLines) {
242  final Line line = lines.remove(0);
243  removedLines.add(line);
244  totalHeight -= line.getHeight();
245  positionsRemoved -= line.getEndPosition()-line.getStartPosition();
246  }
247  for (final Line line : lines) {
248  line.updatePosition(positionsRemoved);
249  }
250  }
251  if (!removedLines.isEmpty()) {
252  for (BufferListener listener : listeners) {
253  listener.linesRemoved(removedLines);
254  }
255  }
256  }
257 
264  @NotNull
265  public Line getLine(final int line) {
266  synchronized (sync) {
267  return lines.get(line);
268  }
269  }
270 
275  public int getTotalHeight() {
276  synchronized (sync) {
277  return Math.max(totalHeight, 1);
278  }
279  }
280 
286  @NotNull
287  public Iterable<Line> lines(final int line) {
288  assert Thread.holdsLock(sync);
289  try {
290  return Collections.unmodifiableList(lines).subList(line, lines.size());
291  } catch (final IndexOutOfBoundsException ignored) {
292  return Collections.emptyList();
293  }
294  }
295 
300  public int size() {
301  synchronized (sync) {
302  return lines.size();
303  }
304  }
305 
310  public void addBufferListener(@NotNull final BufferListener listener) {
311  listeners.add(listener);
312  }
313 
318  public void removeBufferListener(@NotNull final BufferListener listener) {
319  listeners.remove(listener);
320  }
321 
326  @NotNull
327  public Object getSyncObject() {
328  return sync;
329  }
330 
339  public boolean mergeLines(@NotNull final String text, final int type, final int subtype, @Nullable final Color color) {
340  if (!text.isEmpty() && lastCount > 0 && text.equals(lastText) && lastType == type && lastSubtype == subtype && Objects.equals(lastColor, color)) {
341  lastCount++;
342  return true;
343  }
344 
345  lastCount = 1;
346  lastText = text;
347  lastType = type;
348  lastSubtype = subtype;
349  lastColor = color;
350  return false;
351 
352  }
353 
360  public int getLastCount() {
361  return lastCount;
362  }
363 
367  private void layoutLines() {
368  synchronized (sync) {
369  totalHeight = 0;
370  int position = 0;
371  for (Line line : lines) {
372  totalHeight += line.calculateHeight(position, renderWidth, showSentCommands, showTimestamps);
373  position = line.getEndPosition();
374  }
375  }
376  }
377 
389  public void addTextSegment(@NotNull final Line line, @NotNull final String text, final boolean bold, final boolean italic, final boolean underline, @NotNull final FontID fontID, @NotNull final Color color, @NotNull final Color selectionColor) {
390  line.addTextSegment(newTextSegment(text, bold, italic, underline, fontID, color, selectionColor));
391  }
392 
404  @NotNull
405  protected abstract TextSegment newTextSegment(@NotNull final String text, final boolean bold, final boolean italic, final boolean underline, @NotNull final FontID fontID, @NotNull final Color color, @NotNull final Color selectionColor);
406 
413  public int coordinateToPosition(final int x, final int y) {
414  int yLine = 0;
415  for (final Line line : lines) {
416  for (final TextSegment segment : line.segments()) {
417  if (y <= yLine+segment.getY()) {
418  if (y <= yLine+segment.getY()-segment.getHeight()) {
419  return segment.getPosition();
420  }
421  if (x <= segment.getX()+segment.getWidth()) {
422  final int length = segment.getText().length();
423  for (int i = 0; i < length; i++) {
424  final int width = getSegmentWidth(segment, i+1);
425  if (x < segment.getX()+width) {
426  return segment.getPosition()+i;
427  }
428  }
429  return segment.getPosition()+length;
430  }
431  }
432  }
433 
434  yLine += line.getHeight();
435  }
436  return lines.isEmpty() ? 0 : lines.get(lines.size()-1).getEndPosition();
437  }
438 
445  protected abstract int getSegmentWidth(@NotNull final TextSegment segment, final int characters);
446 
453  @NotNull
454  public String getText(final int beginSelection, final int endSelection) {
455  if (beginSelection >= endSelection) {
456  return "";
457  }
458 
459  final boolean showSentCommands;
460  synchronized (sync) {
462  }
463  int i = 0;
464  while (i < lines.size()) {
465  final Line line = lines.get(i);
466  if (line.isVisible(showSentCommands) && beginSelection < line.getEndPosition()) {
467  break;
468  }
469  i++;
470  }
471  final StringBuilder sb = new StringBuilder();
472  while (i < lines.size()) {
473  final Line line = lines.get(i);
474  if (line.getStartPosition() >= endSelection) {
475  break;
476  }
477  if (line.isVisible(showSentCommands)) {
478  if (sb.length() > 0) {
479  sb.append("\n");
480  }
481  line.appendSelection(sb, beginSelection, endSelection);
482  }
483  i++;
484  }
485  return sb.toString();
486  }
487 
488 }
com.realtime.crossfire.jxclient.gui.log.Buffer.sync
final Object sync
Definition: Buffer.java:75
com.realtime.crossfire.jxclient
com.realtime.crossfire.jxclient.gui.log.Buffer.lastColor
Color lastColor
Definition: Buffer.java:96
com.realtime.crossfire.jxclient.gui.log.Buffer.setShowTimestamps
void setShowTimestamps(final boolean showTimestamps)
Definition: Buffer.java:162
com.realtime.crossfire.jxclient.gui.log.Buffer.addTextSegment
void addTextSegment(@NotNull final Line line, @NotNull final String text, final boolean bold, final boolean italic, final boolean underline, @NotNull final FontID fontID, @NotNull final Color color, @NotNull final Color selectionColor)
Definition: Buffer.java:389
com.realtime.crossfire.jxclient.gui.log.Buffer.getLine
Line getLine(final int line)
Definition: Buffer.java:265
com.realtime.crossfire.jxclient.gui.log.Buffer.clear
void clear()
Definition: Buffer.java:176
com.realtime.crossfire.jxclient.gui.log.Buffer.newTextSegment
abstract TextSegment newTextSegment(@NotNull final String text, final boolean bold, final boolean italic, final boolean underline, @NotNull final FontID fontID, @NotNull final Color color, @NotNull final Color selectionColor)
com.realtime.crossfire.jxclient.gui.log.Line
Definition: Line.java:39
com.realtime.crossfire.jxclient.gui.log.Buffer
Definition: Buffer.java:41
com.realtime.crossfire.jxclient.gui.log.Line.isVisible
boolean isVisible(final boolean showSentCommands)
Definition: Line.java:221
com.realtime.crossfire.jxclient.gui.log.TextSegment
Definition: TextSegment.java:40
com.realtime.crossfire.jxclient.gui.log.Buffer.addLine
void addLine(@NotNull final Line line)
Definition: Buffer.java:199
com.realtime.crossfire.jxclient.util.EventListenerList2
Definition: EventListenerList2.java:37
com.realtime.crossfire.jxclient.gui.log.BufferListener
Definition: BufferListener.java:33
com.realtime.crossfire.jxclient.gui.log.Buffer.setRenderWidth
void setRenderWidth(final int renderWidth)
Definition: Buffer.java:132
com.realtime.crossfire.jxclient.gui.log.Buffer.lines
final List< Line > lines
Definition: Buffer.java:63
com.realtime.crossfire.jxclient.gui.log.Buffer.lastSubtype
int lastSubtype
Definition: Buffer.java:90
com.realtime.crossfire.jxclient.gui.log.Buffer.Buffer
Buffer(final int maxLines, final int renderWidth)
Definition: Buffer.java:119
com.realtime.crossfire.jxclient.gui.log.Line.appendSelection
void appendSelection(@NotNull final StringBuilder sb, final int beginSelection, final int endSelection)
Definition: Line.java:258
com.realtime.crossfire.jxclient.gui.log.Buffer.showSentCommands
boolean showSentCommands
Definition: Buffer.java:107
com.realtime.crossfire.jxclient.gui.log.Buffer.showTimestamps
boolean showTimestamps
Definition: Buffer.java:112
com.realtime.crossfire.jxclient.gui.log.Buffer.layoutLines
void layoutLines()
Definition: Buffer.java:367
com.realtime.crossfire.jxclient.gui.log.Buffer.renderWidth
int renderWidth
Definition: Buffer.java:57
com.realtime.crossfire.jxclient.gui.log.Buffer.lines
Iterable< Line > lines(final int line)
Definition: Buffer.java:287
com.realtime.crossfire.jxclient.gui.log.Buffer.removeBufferListener
void removeBufferListener(@NotNull final BufferListener listener)
Definition: Buffer.java:318
com.realtime.crossfire.jxclient.gui.log.FontID
Definition: FontID.java:29
com.realtime.crossfire.jxclient.gui.log.Buffer.lastCount
int lastCount
Definition: Buffer.java:80
com.realtime.crossfire.jxclient.gui.log.Buffer.maxLines
final int maxLines
Definition: Buffer.java:52
com.realtime.crossfire.jxclient.gui.log.Line.getEndPosition
int getEndPosition()
Definition: Line.java:158
com.realtime.crossfire.jxclient.gui.log.Buffer.addBufferListener
void addBufferListener(@NotNull final BufferListener listener)
Definition: Buffer.java:310
com.realtime.crossfire.jxclient.gui.log.Buffer.getSegmentWidth
abstract int getSegmentWidth(@NotNull final TextSegment segment, final int characters)
com.realtime.crossfire.jxclient.gui.log.Line.getHeight
int getHeight()
Definition: Line.java:140
com.realtime.crossfire.jxclient.gui.log.Buffer.lastText
String lastText
Definition: Buffer.java:102
com.realtime.crossfire.jxclient.gui.log.Buffer.getTotalHeight
int getTotalHeight()
Definition: Buffer.java:275
com.realtime.crossfire.jxclient.util
Definition: Codec.java:23
com.realtime.crossfire.jxclient.gui.log.Buffer.mergeLines
boolean mergeLines(@NotNull final String text, final int type, final int subtype, @Nullable final Color color)
Definition: Buffer.java:339
com.realtime.crossfire.jxclient.gui.log.Buffer.getLastCount
int getLastCount()
Definition: Buffer.java:360
com.realtime.crossfire.jxclient.gui.log.Line.getStartPosition
int getStartPosition()
Definition: Line.java:149
com.realtime.crossfire.jxclient.gui.log.Buffer.setShowSentCommands
void setShowSentCommands(final boolean showSentCommands)
Definition: Buffer.java:147
com.realtime.crossfire.jxclient.gui.log.Buffer.lastType
int lastType
Definition: Buffer.java:85
com.realtime.crossfire
com.realtime.crossfire.jxclient.gui.log.Buffer.getText
String getText(final int beginSelection, final int endSelection)
Definition: Buffer.java:454
com.realtime.crossfire.jxclient.gui.log.Buffer.size
int size()
Definition: Buffer.java:300
com.realtime
com
com.realtime.crossfire.jxclient.gui.log.Buffer.listeners
final EventListenerList2< BufferListener > listeners
Definition: Buffer.java:47
com.realtime.crossfire.jxclient.gui.log.Buffer.totalHeight
int totalHeight
Definition: Buffer.java:68
com.realtime.crossfire.jxclient.gui.log.Buffer.coordinateToPosition
int coordinateToPosition(final int x, final int y)
Definition: Buffer.java:413
com.realtime.crossfire.jxclient.gui.log.Buffer.getSyncObject
Object getSyncObject()
Definition: Buffer.java:327
com.realtime.crossfire.jxclient.gui.log.Buffer.replaceLine
void replaceLine(@NotNull final Line line)
Definition: Buffer.java:215
com.realtime.crossfire.jxclient.gui.log.Buffer.prune
void prune()
Definition: Buffer.java:232