Crossfire JXClient, Trunk  R20561
JXCWindowRenderer.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.misc;
23 
43 import java.awt.Color;
44 import java.awt.Component;
45 import java.awt.Container;
46 import java.awt.Dimension;
47 import java.awt.DisplayMode;
48 import java.awt.Frame;
49 import java.awt.Graphics;
50 import java.awt.GraphicsConfiguration;
51 import java.awt.GraphicsDevice;
52 import java.awt.GraphicsEnvironment;
53 import java.awt.Insets;
54 import java.awt.Point;
55 import java.awt.Rectangle;
56 import java.awt.Toolkit;
57 import java.awt.Window;
58 import java.awt.event.ComponentEvent;
59 import java.awt.event.ComponentListener;
60 import java.awt.event.KeyEvent;
61 import java.awt.event.MouseEvent;
62 import java.awt.image.BufferStrategy;
63 import java.io.IOException;
64 import java.io.Writer;
65 import java.text.DateFormat;
66 import java.text.SimpleDateFormat;
67 import java.util.Collection;
68 import java.util.Date;
69 import java.util.Iterator;
70 import java.util.List;
71 import java.util.ListIterator;
72 import java.util.concurrent.CopyOnWriteArrayList;
73 import javax.swing.JFrame;
74 import javax.swing.JLayeredPane;
75 import javax.swing.JViewport;
76 import javax.swing.RootPaneContainer;
77 import javax.swing.event.MouseInputListener;
78 import org.jetbrains.annotations.NotNull;
79 import org.jetbrains.annotations.Nullable;
80 
85 public class JXCWindowRenderer {
86 
90  private static final int DEFAULT_NUM_LOOK_OBJECTS = 50;
91 
95  private static final int DEFAULT_MAP_WIDTH = 9;
96 
100  private static final int DEFAULT_MAP_HEIGHT = 9;
101 
105  @Nullable
106  private JFrame frame;
107 
112  @NotNull
113  private final Container layeredPane = new JLayeredPane() {
114 
118  private static final long serialVersionUID = 1L;
119 
120  @Override
121  public void paint(@NotNull final Graphics g) {
122  super.paint(g);
124  }
125 
126  };
127 
131  @NotNull
132  private final MouseTracker mouseTracker;
133 
137  @NotNull
139 
143  @Nullable
144  private final Writer debugScreen;
145 
149  @NotNull
150  private final GraphicsEnvironment graphicsEnvironment;
151 
155  @NotNull
156  private final GraphicsDevice graphicsDevice;
157 
161  @NotNull
162  private final DisplayMode defaultDisplayMode;
163 
167  @NotNull
168  private final Rectangle maximumWindowBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds(); // This call hangs on Solaris when called from the EDT.
169 
173  @NotNull
174  private final MouseInputListener mouseInputListener = new MouseInputListener() {
175 
176  @Override
177  public void mouseClicked(final MouseEvent e) {
178  mouseTracker.mouseClicked(findElement(e.getComponent(), e), e);
179  }
180 
181  @Override
182  public void mousePressed(final MouseEvent e) {
183  mouseTracker.mousePressed(findElement(e.getComponent(), e), e);
184  }
185 
186  @Override
187  public void mouseReleased(final MouseEvent e) {
188  mouseTracker.mouseReleased(findElement(e.getComponent(), e), e);
189  }
190 
191  @Override
192  public void mouseEntered(final MouseEvent e) {
193  mouseTracker.mouseEntered(findElement(e.getComponent(), e), e);
194  }
195 
196  @Override
197  public void mouseExited(final MouseEvent e) {
198  mouseTracker.mouseExited(e);
199  }
200 
201  @Override
202  public void mouseDragged(final MouseEvent e) {
203  mouseTracker.mouseDragged(findElement(e.getComponent(), e), e);
204  }
205 
206  @Override
207  public void mouseMoved(final MouseEvent e) {
208  mouseTracker.mouseMoved(findElement(e.getComponent(), e), e);
209  }
210 
211  };
212 
218  @Nullable
219  private BufferStrategy bufferStrategy;
220 
224  private int windowWidth;
225 
229  private int windowHeight;
230 
235  @NotNull
236  private final List<Gui> openDialogs = new CopyOnWriteArrayList<>();
237 
241  @NotNull
243 
247  @Nullable
248  private Gui currentGui;
249 
254  @NotNull
255  private final Collection<GUIMap> maps = new CopyOnWriteArrayList<>();
256 
261  @NotNull
262  private final Collection<GUIFloorList> floorLists = new CopyOnWriteArrayList<>();
263 
267  @Nullable
268  private Component tooltip;
269 
273  private int offsetX;
274 
278  private int offsetY;
279 
283  private boolean isFullScreen;
284 
288  private boolean wasDisplayed;
289 
293  @NotNull
295 
299  @NotNull
300  private final DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS ");
301 
305  @NotNull
306  private final ComponentListener componentListener = new ComponentListener() {
307 
308  @Override
309  public void componentResized(final ComponentEvent e) {
310  final RootPaneContainer tmpFrame = frame;
311  assert tmpFrame != null;
312  final int width = tmpFrame.getContentPane().getWidth();
313  final int height = tmpFrame.getContentPane().getHeight();
314  updateWindowSize(width, height);
316  }
317 
318  @Override
319  public void componentMoved(final ComponentEvent e) {
320  // ignore
321  }
322 
323  @Override
324  public void componentShown(final ComponentEvent e) {
326  }
327 
328  @Override
329  public void componentHidden(final ComponentEvent e) {
330  // ignore
331  }
332 
333  };
334 
341  public JXCWindowRenderer(@NotNull final MouseTracker mouseTracker, @NotNull final CrossfireServerConnection crossfireServerConnection, @Nullable final Writer debugScreen) {
342  this.mouseTracker = mouseTracker;
343  this.crossfireServerConnection = crossfireServerConnection;
344  this.debugScreen = debugScreen;
345  graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
346  graphicsDevice = graphicsEnvironment.getDefaultScreenDevice();
347  defaultDisplayMode = getDisplayMode();
348  debugScreenWrite("getMaxWindowDimension: maximum window bounds="+maximumWindowBounds);
349  }
350 
359  public boolean setFullScreenMode(@NotNull final JFrame frame, @Nullable final Resolution resolution) {
360  debugScreenWrite("setFullScreenMode: resolution="+(resolution == null ? "default" : resolution));
361 
362  final DisplayMode currentDisplayMode = getDisplayMode();
363  if (frame == this.frame && isFullScreen && bufferStrategy != null && (resolution == null || resolution.getWidth() == windowWidth && resolution.getHeight() == windowHeight)) {
364  debugScreenWrite("setResolutionPre: no change needed");
365  debugScreenWrite("setResolutionPre: success");
366  return true;
367  }
368 
369  setResolutionPre(frame);
370 
371  final Dimension dimension;
372  if (resolution == null) {
373  dimension = new Dimension(currentDisplayMode.getWidth(), currentDisplayMode.getHeight());
374  debugScreenWrite("setFullScreenMode: full-screen requested, dimension="+dimension+" [using current display resolution]");
375  } else {
376  dimension = new Dimension(resolution.getWidth(), resolution.getHeight());
377  debugScreenWrite("setFullScreenMode: full-screen requested, dimension="+dimension+" [using user-specified resolution]");
378  }
379  frame.setPreferredSize(dimension);
380  frame.setResizable(false);
381  frame.setUndecorated(true);
382 
383  // full-screen switch must happen before display mode change
384  if (!isFullScreenSupported()) {
385  setFullScreenWindow(null);
386  debugScreenWrite("setFullScreenMode: failure");
387  return false;
388  }
389 
390  setFullScreenWindow(frame);
391 
392  if (resolution == null || resolution.equalsDisplayMode(currentDisplayMode)) {
393  debugScreenWrite("setFullScreenMode: requested resolution matches screen resolution");
394  } else {
395  if (!isDisplayChangeSupported()) {
396  setFullScreenWindow(null);
397  debugScreenWrite("setFullScreenMode: failure");
398  return false;
399  }
400 
401  final DisplayMode newDisplayMode = new DisplayMode(resolution.getWidth(), resolution.getHeight(), DisplayMode.BIT_DEPTH_MULTI, DisplayMode.REFRESH_RATE_UNKNOWN);
402  try {
403  setDisplayMode(newDisplayMode);
404  } catch (final IllegalArgumentException ex) {
405  debugScreenWrite("setFullScreenMode: setting screen resolution failed: "+ex.getMessage());
406  setDisplayMode(defaultDisplayMode);
407  setFullScreenWindow(null);
408  debugScreenWrite("setFullScreenMode: failure");
409  return false;
410  }
411  }
412 
413  setResolutionPost(frame, dimension);
414  if (this.frame != null) {
415  this.frame.removeComponentListener(componentListener);
416  }
417  this.frame = frame;
418  this.frame.addComponentListener(componentListener);
419  addMouseTracker(frame);
421  return true;
422  }
423 
432  public void setWindowMode(@NotNull final JFrame frame, @Nullable final Resolution resolution, @NotNull final Resolution minResolution, final boolean fixedSize) {
433  debugScreenWrite("setWindowMode: resolution="+(resolution == null ? "default" : resolution)+", fixedSize="+fixedSize);
434 
435  final DisplayMode currentDisplayMode = getDisplayMode();
436  if (frame == this.frame && !isFullScreen && bufferStrategy != null && (resolution == null || resolution.getWidth() == windowWidth && resolution.getHeight() == windowHeight)) {
437  debugScreenWrite("setResolutionPre: no change needed");
438  debugScreenWrite("setResolutionPre: success");
439  return;
440  }
441 
442  setResolutionPre(frame);
443 
444  debugScreenWrite("setResolutionPre: windowed mode requested");
445  frame.setUndecorated(false);
446  frame.setResizable(!fixedSize);
447  final Point centerPoint = getCenterPoint();
448  final Dimension dimension;
449  //noinspection IfMayBeConditional
450  if (resolution == null) {
451  dimension = new Dimension(currentDisplayMode.getWidth(), currentDisplayMode.getHeight());
452  } else {
453  dimension = resolution.asDimension();
454  }
455  final int x = centerPoint.x-dimension.width/2;
456  final int y = centerPoint.y-dimension.height/2;
457  if (!wasDisplayed) {
458  frame.setLocation(x, y); // try to minimize display movements
459  }
460  frame.setVisible(true);
461  final Insets frameInsets = frame.getInsets();
462  debugScreenWrite("setResolutionPre: frame insets="+frameInsets);
463 
464  final Dimension maxDimension = getMaxWindowDimension(frameInsets);
465  debugScreenWrite("setResolutionPre: maximal window dimension="+maxDimension);
466  if (dimension.width > maxDimension.width || dimension.height > maxDimension.height) {
467  //noinspection VariableNotUsedInsideIf
468  if (resolution == null) {
469  dimension.width = Math.max(minResolution.getWidth()+frameInsets.left+frameInsets.right, maxDimension.width);
470  dimension.height = Math.max(minResolution.getHeight()+frameInsets.top+frameInsets.bottom, maxDimension.height);
471  debugScreenWrite("setResolutionPre: window size exceeds maximum allowed size, reducing window size to "+dimension.width+"x"+dimension.height);
472  } else {
473  debugScreenWrite("setResolutionPre: window size exceeds maximum allowed size, ignoring");
474  }
475  }
476 
477  if (wasDisplayed) {
478  debugScreenWrite("setResolutionPre: resizing window to "+dimension);
479  frame.setPreferredSize(dimension);
480  frame.setSize(dimension);
481  } else {
482  wasDisplayed = true;
483  final int x2 = centerPoint.x-dimension.width/2-frameInsets.left;
484  final int y2 = centerPoint.y-dimension.height/2-frameInsets.top;
485  debugScreenWrite("setResolutionPre: moving window to "+x2+"/"+y2+" "+dimension.width+"x"+dimension.height);
486  frame.setBounds(x2, y2, dimension.width+frameInsets.left+frameInsets.right, dimension.height+frameInsets.top+frameInsets.bottom);
487  }
488 
489  setResolutionPost(frame, dimension);
490  if (this.frame != null) {
491  this.frame.removeComponentListener(componentListener);
492  }
493  this.frame = frame;
494  this.frame.addComponentListener(componentListener);
495  addMouseTracker(frame);
497  }
498 
504  private void setResolutionPre(@NotNull final Window frame) {
505  // disable full-screen since switching from full-screen to full-screen
506  // does not work reliably
507  if (isFullScreen) {
508  setDisplayMode(defaultDisplayMode);
509  }
510  setFullScreenWindow(null);
511 
512  debugScreenWrite("setResolutionPre: disposing frame");
513  frame.dispose();
514  }
515 
522  private void setResolutionPost(@NotNull final Window frame, @NotNull final Dimension dimension) {
523  debugScreenWrite("setResolutionPost: creating buffer strategy");
524  frame.createBufferStrategy(2);
525  bufferStrategy = frame.getBufferStrategy();
526 
527  final Insets insets = frame.getInsets();
528  offsetX = insets.left;
529  offsetY = insets.top;
530  debugScreenWrite("setResolutionPost: offset="+offsetX+"x"+offsetY+" "+insets);
531 
532  debugScreenWrite("setResolutionPost: requesting focus");
533  frame.requestFocusInWindow();
534 
535  updateWindowSize(dimension.width, dimension.height);
536 
537  frame.add(layeredPane);
538  if (currentGui == null) {
539  frame.validate();
540  } else {
541  addToLayeredPane(currentGui, 0, -1);
542  if (windowWidth > 0 && windowHeight > 0) {
543  assert currentGui != null;
544  currentGui.setSize(windowWidth, windowHeight);
545  }
546 
547  frame.validate();
549  }
550 
551  debugScreenWrite("setResolutionPost: success");
552  }
553 
559  private void updateWindowSize(final int windowWidth, final int windowHeight) {
560  if (this.windowWidth == windowWidth && this.windowHeight == windowHeight) {
561  return;
562  }
563  this.windowWidth = windowWidth;
564  this.windowHeight = windowHeight;
565  debugScreenWrite("updateWindowSize: gui size="+this.windowWidth+"x"+this.windowHeight);
566  if (currentGui != null) {
567  currentGui.setSize(windowWidth, windowHeight);
568  }
569  if (frame != null) {
570  frame.validate();
571  }
572  }
573 
579  @NotNull
580  private Dimension getMaxWindowDimension(@NotNull final Insets frameInsets) {
581  final GraphicsConfiguration graphicsConfiguration = graphicsDevice.getDefaultConfiguration();
582  final Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfiguration);
583  debugScreenWrite("getMaxWindowDimension: screen insets="+screenInsets);
584 
585  final int maxWidth = maximumWindowBounds.width-screenInsets.left-screenInsets.right-frameInsets.left-frameInsets.right;
586  final int maxHeight = maximumWindowBounds.height-screenInsets.top-screenInsets.bottom-frameInsets.top-frameInsets.bottom;
587  debugScreenWrite("getMaxWindowDimension: maximum window dimension="+maxWidth+"x"+maxHeight);
588  return new Dimension(maxWidth, maxHeight);
589  }
590 
594  public void endRendering() {
595  if (isFullScreen && frame != null) {
596  if (currentGui != null) {
597  removeFromLayeredPane(currentGui);
598  }
599  final Resolution minResolution = new Resolution(1, 1);
600  assert frame != null;
601  setWindowMode(frame, null, minResolution, false);
602  assert frame != null;
603  removeMouseTracker(frame);
604  assert frame != null;
606  assert frame != null;
607  frame.removeComponentListener(componentListener);
608  frame = null;
609  }
610  }
611 
616  public void redraw(@NotNull final Graphics g) {
617  layeredPane.paint(g);
618  }
619 
624  public void clearGUI(@NotNull final Gui gui) {
625  setCurrentGui(gui);
626  for (int ig = 0; ig < 3; ig++) {
627  assert bufferStrategy != null;
628  final Graphics g = bufferStrategy.getDrawGraphics();
629  redrawBlack(g);
630  g.dispose();
631  assert bufferStrategy != null;
632  bufferStrategy.show();
633  }
634  }
635 
640  private void redrawBlack(@NotNull final Graphics g) {
641  g.setColor(Color.BLACK);
642  assert frame != null;
643  final int width = frame.getWidth();
644  assert frame != null;
645  final int height = frame.getHeight();
646  g.fillRect(0, 0, width, height);
647  }
648 
657  public boolean openDialog(@NotNull final Gui dialog, final boolean autoCloseOnDeactivate) {
658  if (dialog == currentGui) {
659  return false;
660  }
661 
662  if (!openDialogs.isEmpty() && openDialogs.get(openDialogs.size()-1) == dialog) {
663  return false;
664  }
665 
666  if (!openDialogsRemove(dialog)) {
667  dialog.activateDefaultElement();
668  final GuiAutoCloseListener guiAutoCloseListener = autoCloseOnDeactivate ? () -> closeDialog(dialog) : null;
669  dialog.setGuiAutoCloseListener(guiAutoCloseListener);
670  }
671  openDialogsAdd(dialog);
673  return true;
674  }
675 
680  public void raiseDialog(@NotNull final Gui dialog) {
681  if (dialog == currentGui) {
682  return;
683  }
684 
685  if (!openDialogs.isEmpty() && openDialogs.get(openDialogs.size()-1) == dialog) {
686  return;
687  }
688 
689  if (!isDialogOpen(dialog)) {
690  return;
691  }
692 
693  if (!openDialogsRemove(dialog)) {
694  assert false;
695  }
696  openDialogsAdd(dialog);
698  }
699 
705  public boolean isDialogOpen(@NotNull final Gui dialog) {
706  return openDialogs.contains(dialog);
707  }
708 
714  @NotNull
715  public Iterable<Gui> getOpenDialogs() {
716  return OpenDialogsIterator::new;
717  }
718 
723  @SuppressWarnings("NullableProblems")
724  public void setCurrentGui(@NotNull final Gui gui) {
726  if (frame != null && currentGui != null) {
727  removeFromLayeredPane(currentGui);
728  }
729  currentGui = gui;
730  //noinspection VariableNotUsedInsideIf
731  if (frame != null) {
732  addToLayeredPane(currentGui, 0, -1);
733  }
734 
735  if (windowWidth > 0 && windowHeight > 0) {
736  assert currentGui != null;
737  currentGui.setSize(windowWidth, windowHeight);
738  }
739  if (frame != null) {
740  frame.validate();
741  }
742  });
744  }
745 
752  public boolean closeDialog(@NotNull final Gui dialog) {
753  if (!openDialogsRemove(dialog)) {
754  return false;
755  }
756 
757  dialog.setActiveElementActive(false);
759  return true;
760  }
761 
767  public boolean toggleDialog(@NotNull final Gui dialog) {
768  if (dialog == currentGui) {
769  return true;
770  }
771 
772  if (openDialogsRemove(dialog)) {
773  dialog.setActiveElementActive(false);
775  return false;
776  }
777 
778  dialog.setGuiAutoCloseListener(null);
779  openDialogsAdd(dialog);
780  dialog.activateDefaultElement();
782  return true;
783  }
784 
789  public void setTooltip(@Nullable final Component tooltip) {
790  if (this.tooltip != null) {
791  layeredPane.remove(this.tooltip);
792  }
793  this.tooltip = tooltip;
794  if (this.tooltip != null) {
795  layeredPane.add(this.tooltip, 2, -1);
796  }
797  }
798 
803  public void setGuiState(@NotNull final RendererGuiState rendererGuiState) {
804  if (this.rendererGuiState == rendererGuiState) {
805  return;
806  }
807 
808  this.rendererGuiState = rendererGuiState;
810  for (final Gui dialog : openDialogs) {
811  removeFromLayeredPane(dialog);
812  if (!dialog.isHidden(rendererGuiState)) {
813  addToLayeredPane(dialog, 1, 0);
814  }
815  }
816  if (frame != null) {
817  frame.validate();
818  }
819  });
821  for (final RendererGuiStateListener listener : rendererGuiStateListeners) {
822  listener.guiStateChanged(rendererGuiState);
823  }
824  }
825 
830  @NotNull
832  return rendererGuiState;
833  }
834 
840  public void addGuiStateListener(@NotNull final RendererGuiStateListener listener) {
841  rendererGuiStateListeners.add(listener);
842  }
843 
849  private void openDialogsAdd(@NotNull final Gui dialog) {
850  if (openDialogs.contains(dialog)) {
851  raiseDialog(dialog);
852  return;
853  }
854 
855  dialog.autoSize(windowWidth, windowHeight);
856 
857  final Point mouse = frame == null ? null : frame.getMousePosition(true);
858  if (mouse == null || dialog.isHidden(rendererGuiState)) {
859  openDialogs.add(dialog);
860  if (!dialog.isHidden(rendererGuiState)) {
861  openDialogInt(dialog);
862  }
863  } else {
864  if (dialog.isWithinDrawingArea(mouse.x, mouse.y)) {
865  final MouseEvent mouseEvent = new MouseEvent(frame, 0, System.currentTimeMillis(), 0, mouse.x, mouse.y, 0, false);
866  mouseTracker.mouseExited(mouseEvent);
867  openDialogs.add(dialog);
868  assert !dialog.isHidden(rendererGuiState);
869  openDialogInt(dialog);
870  mouseTracker.mouseEntered(findElement(mouseEvent), mouseEvent);
871  } else {
872  openDialogs.add(dialog);
873  assert !dialog.isHidden(rendererGuiState);
874  openDialogInt(dialog);
875  }
876  }
877  }
878 
883  private void openDialogInt(@NotNull final Gui dialog) {
884  addToLayeredPane(dialog, 1, 0);
885  final Dimension preferredSize = dialog.getPreferredSize();
886  final Dimension size;
887  //noinspection IfMayBeConditional
888  if (preferredSize == null) {
889  size = new Dimension(320, 200);
890  } else {
891  size = new Dimension(Math.min(preferredSize.width, windowWidth), Math.min(preferredSize.height, windowHeight));
892  }
893  dialog.setSize(size);
894  if (frame != null) {
895  frame.validate();
896  }
897  }
898 
905  private boolean openDialogsRemove(@NotNull final Gui dialog) {
906  if (!openDialogs.contains(dialog)) {
907  return false;
908  }
909 
910  final Point mouse = frame == null ? null : frame.getMousePosition(true);
911  if (mouse == null) {
912  openDialogs.remove(dialog);
913  removeFromLayeredPane(dialog);
914  if (frame != null) {
915  frame.validate();
916  // @todo too aggressive?
917  assert frame != null;
918  frame.repaint();
919  }
920  } else {
921  if (dialog.isWithinDrawingArea(mouse.x, mouse.y)) {
922  final MouseEvent mouseEvent = new MouseEvent(frame, 0, System.currentTimeMillis(), 0, mouse.x, mouse.y, 0, false);
923  mouseTracker.mouseExited(mouseEvent);
924  openDialogs.remove(dialog);
925  removeFromLayeredPane(dialog);
926  if (frame != null) {
927  frame.validate();
928  // @todo too aggressive?
929  assert frame != null;
930  frame.repaint();
931  }
932  mouseTracker.mouseEntered(findElement(mouseEvent), mouseEvent);
933  } else {
934  openDialogs.remove(dialog);
935  removeFromLayeredPane(dialog);
936  if (frame != null) {
937  frame.validate();
938  // @todo too aggressive?
939  assert frame != null;
940  frame.repaint();
941  }
942  }
943  }
944 
945  return true;
946  }
947 
953  public boolean deactivateCommandInput() {
954  for (final Gui dialog : openDialogs) {
955  if (!dialog.isHidden(rendererGuiState)) {
956  if (dialog.deactivateCommandInput()) {
957  return true;
958  }
959  if (dialog.isModal()) {
960  return false;
961  }
962  }
963  }
964 
965  assert currentGui != null;
967  }
968 
973  @Nullable
975  for (final Gui dialog : openDialogs) {
976  if (!dialog.isHidden(rendererGuiState)) {
977  final Buffer buffer = getActiveMessageBuffer(dialog);
978  if (buffer != null) {
979  return buffer;
980  }
981  if (dialog.isModal()) {
982  return null;
983  }
984  }
985  }
986 
987  assert currentGui != null;
989  }
990 
996  @Nullable
997  private static Buffer getActiveMessageBuffer(@NotNull final Gui gui) {
998  final GUILog buffer = gui.getFirstElement(GUIMessageLog.class);
999  return buffer == null ? null : buffer.getBuffer();
1000  }
1001 
1006  public void setSelectedHostname(@NotNull final String serverName) {
1007  assert currentGui != null;
1008  final GUIMetaElementList metaElementList = currentGui.getFirstElement(GUIMetaElementList.class);
1009  if (metaElementList != null) {
1010  metaElementList.setSelectedHostname(serverName);
1011  }
1012  }
1013 
1020  @Nullable
1022  // check main gui
1023  assert currentGui != null;
1024  final GUIText textArea1 = activateCommandInput(currentGui);
1025  if (textArea1 != null) {
1026  return textArea1;
1027  }
1028 
1029  // check visible dialogs
1030  for (final Gui dialog : openDialogs) {
1031  if (!dialog.isHidden(rendererGuiState)) {
1032  final GUIText textArea2 = activateCommandInput(dialog);
1033  if (textArea2 != null) {
1034  openDialog(dialog, false); // raise dialog
1035  return textArea2;
1036  }
1037  }
1038  if (dialog.isModal()) {
1039  return null;
1040  }
1041  }
1042 
1043  return null;
1044  }
1045 
1051  public boolean handleKeyPress(@NotNull final KeyEvent2 e) {
1052  assert currentGui != null;
1053  return currentGui.handleKeyPress(e);
1054  }
1055 
1059  private class OpenDialogsIterator implements Iterator<Gui> {
1060 
1065  @NotNull
1066  private final ListIterator<Gui> it = openDialogs.listIterator(openDialogs.size());
1067 
1068  @Override
1069  public boolean hasNext() {
1070  return it.hasPrevious();
1071  }
1072 
1073  @SuppressWarnings("IteratorNextCanNotThrowNoSuchElementException")
1074  @NotNull
1075  @Override
1076  public Gui next() {
1077  return it.previous();
1078  }
1079 
1080  @Override
1081  public void remove() {
1082  throw new UnsupportedOperationException("remove() not implemented");
1083  }
1084 
1085  }
1086 
1091  public int getWindowWidth() {
1092  return windowWidth;
1093  }
1094 
1099  public int getWindowHeight() {
1100  return windowHeight;
1101  }
1102 
1109  @Nullable
1110  private static AbstractGUIElement findElement(@NotNull final Component component, @NotNull final MouseEvent mouseEvent) {
1111  for (Component result = component; result != null; result = result.getParent()) {
1112  if (result instanceof AbstractGUIElement) {
1113  return (AbstractGUIElement)result;
1114  }
1115  if (result instanceof JViewport) {
1116  final JViewport viewport = (JViewport)result;
1117  final Point position = viewport.getViewPosition();
1118  mouseEvent.translatePoint(-position.x, -position.y);
1119  }
1120  }
1121  return null;
1122  }
1123 
1131  @Nullable
1132  private AbstractGUIElement findElement(@NotNull final MouseEvent e) {
1133  final MouseEvent ce = e;//convertEvent(e);
1134  AbstractGUIElement elected = null;
1135 
1136  final int eX = ce.getX();
1137  final int eY = ce.getY();
1138  for (final Gui dialog : openDialogs) {
1139  if (!dialog.isHidden(rendererGuiState)) {
1140  elected = getElementFromPoint(dialog, eX-dialog.getX(), eY-dialog.getY());
1141  //noinspection VariableNotUsedInsideIf
1142  if (elected != null) {
1143  break;
1144  }
1145  }
1146  if (dialog.isModal()) {
1147  return null;
1148  }
1149  }
1150 
1151  if (elected == null) {
1152  assert currentGui != null;
1153  elected = getElementFromPoint(currentGui, eX, eY);
1154  }
1155 
1156  return elected;
1157  }
1158 
1168  @Nullable
1169  private AbstractGUIElement getElementFromPoint(@NotNull final Gui gui, final int eX, final int eY) {
1170  final int x = eX-offsetX;
1171  final int y = eY-offsetY;
1172  return gui.getElementFromPoint(x, y);
1173  }
1174 
1179  private void debugScreenWrite(@NotNull final CharSequence message) {
1180  if (debugScreen == null) {
1181  return;
1182  }
1183 
1184  try {
1185  debugScreen.append(simpleDateFormat.format(new Date()));
1186  debugScreen.append(message);
1187  debugScreen.append("\n");
1188  debugScreen.flush();
1189  } catch (final IOException ex) {
1190  System.err.println("Cannot write screen debug: "+ex.getMessage());
1191  System.exit(1);
1192  throw new AssertionError(ex);
1193  }
1194  }
1195 
1202  private void addToLayeredPane(@NotNull final Component component, final int layer, final int index) {
1203  layeredPane.add(component, layer, index);
1204  addMouseTrackerRecursively(component);
1205  addComponent(component);
1206  }
1207 
1212  private void removeFromLayeredPane(@NotNull final Component component) {
1213  layeredPane.remove(component);
1214  removeMouseTrackerRecursively(component);
1215  removeComponent(component);
1216  }
1217 
1222  private void addMouseTracker(@NotNull final Component component) {
1223  component.addMouseListener(mouseInputListener);
1224  component.addMouseMotionListener(mouseInputListener);
1225  }
1226 
1231  private void removeMouseTracker(@NotNull final Component component) {
1232  component.removeMouseListener(mouseInputListener);
1233  component.removeMouseMotionListener(mouseInputListener);
1234  }
1235 
1241  private void addMouseTrackerRecursively(@NotNull final Component component) {
1242  addMouseTracker(component);
1243  if (component instanceof Container) {
1244  final Container container = (Container)component;
1245  for (int i = 0; i < container.getComponentCount(); i++) {
1246  addMouseTrackerRecursively(container.getComponent(i));
1247  }
1248  }
1249  }
1250 
1256  private void removeMouseTrackerRecursively(@NotNull final Component component) {
1257  removeMouseTracker(component);
1258  if (component instanceof Container) {
1259  final Container container = (Container)component;
1260  for (int i = 0; i < container.getComponentCount(); i++) {
1261  removeMouseTrackerRecursively(container.getComponent(i));
1262  }
1263  }
1264  }
1265 
1270  private void addComponent(@NotNull final Component component) {
1271  if (component instanceof GUIMap) {
1272  final GUIMap map = (GUIMap)component;
1273  maps.add(map);
1274  }
1275  if (component instanceof GUIFloorList) {
1276  final GUIFloorList floorList = (GUIFloorList)component;
1277  floorLists.add(floorList);
1278  }
1279  if (component instanceof Container) {
1280  final Container container = (Container)component;
1281  for (int i = 0; i < container.getComponentCount(); i++) {
1282  addComponent(container.getComponent(i));
1283  }
1284  }
1285  }
1286 
1291  private void removeComponent(@NotNull final Component component) {
1292  if (component instanceof GUIMap) {
1293  final GUIMap map = (GUIMap)component;
1294  maps.remove(map);
1295  }
1296  if (component instanceof GUIFloorList) {
1297  final GUIFloorList floorList = (GUIFloorList)component;
1298  floorLists.remove(floorList);
1299  }
1300  if (component instanceof Container) {
1301  final Container container = (Container)component;
1302  for (int i = 0; i < container.getComponentCount(); i++) {
1303  removeComponent(container.getComponent(i));
1304  }
1305  }
1306  }
1307 
1312  public void updateServerSettings() {
1313  if (frame == null || !frame.isVisible()) {
1314  return;
1315  }
1316 
1317  final Dimension mapSize = getMapSize();
1318  crossfireServerConnection.setPreferredMapSize(mapSize.width, mapSize.height);
1319  crossfireServerConnection.setPreferredNumLookObjects(getNumLookObjects());
1320  }
1321 
1326  @NotNull
1327  private Dimension getMapSize() {
1328  int width = DEFAULT_MAP_WIDTH;
1329  int height = DEFAULT_MAP_HEIGHT;
1330  for (final GUIMap map : maps) {
1331  width = Math.max(width, map.getPreferredMapWidth());
1332  height = Math.max(height, map.getPreferredMapHeight());
1333  }
1334  return new Dimension(width, height);
1335  }
1336 
1341  private int getNumLookObjects() {
1342  int minNumLookObjects = Integer.MAX_VALUE;
1343  for (final GUIFloorList floorList : floorLists) {
1344  minNumLookObjects = Math.min(minNumLookObjects, floorList.getNumLookObjects());
1345  }
1346  if (minNumLookObjects < Integer.MAX_VALUE) {
1347  return minNumLookObjects;
1348  }
1349 
1350  return DEFAULT_NUM_LOOK_OBJECTS;
1351  }
1352 
1359  @Nullable
1360  public static GUIText activateCommandInput(@NotNull final Gui gui) {
1361  final GUIText textArea = gui.getFirstElement(GUIText.class);
1362  if (textArea == null) {
1363  return null;
1364  }
1365 
1366  if (!textArea.getName().equals("command")) {
1367  return null;
1368  }
1369 
1370  textArea.setActive(true);
1371  return textArea;
1372  }
1373 
1378  private boolean isDisplayChangeSupported() {
1379  final boolean result = graphicsDevice.isDisplayChangeSupported();
1380  debugScreenWrite("isDisplayChangeSupported()="+(result ? "yes" : "no"));
1381  return result;
1382  }
1383 
1388  private void setDisplayMode(@NotNull final DisplayMode displayMode) {
1389  debugScreenWrite("setDisplayMode("+displayMode.getWidth()+"x"+displayMode.getHeight()+")");
1390  graphicsDevice.setDisplayMode(displayMode);
1391  }
1392 
1397  @NotNull
1398  private DisplayMode getDisplayMode() {
1399  final DisplayMode displayMode = graphicsDevice.getDisplayMode();
1400  debugScreenWrite("getDisplayMode()="+displayMode.getWidth()+"x"+displayMode.getHeight());
1401  return displayMode;
1402  }
1403 
1409  private boolean isFullScreenSupported() {
1410  final boolean result = graphicsDevice.isFullScreenSupported();
1411  debugScreenWrite("isFullScreenSupported()="+(result ? "yes" : "no"));
1412  return result;
1413  }
1414 
1420  private void setFullScreenWindow(@Nullable final Window window) {
1421  //noinspection VariableNotUsedInsideIf
1422  isFullScreen = window != null;
1423  debugScreenWrite("setFullScreenWindow("+(isFullScreen ? "enter full-screen mode" : "leave full-screen mode")+")");
1424  graphicsDevice.setFullScreenWindow(window);
1425  }
1426 
1431  @NotNull
1432  private Point getCenterPoint() {
1433  final Point result = graphicsEnvironment.getCenterPoint();
1434  debugScreenWrite("getCenterPoint()="+result);
1435  return result;
1436  }
1437 
1438 }
JXCWindowRenderer(@NotNull final MouseTracker mouseTracker, @NotNull final CrossfireServerConnection crossfireServerConnection, @Nullable final Writer debugScreen)
Creates a new instance.
void setPreferredMapSize(int preferredMapWidth, int preferredMapHeight)
Sets the preferred map size.
boolean setFullScreenMode(@NotNull final JFrame frame, @Nullable final Resolution resolution)
Tries to switch to the given resolution.
Interface for clients interested in auto-close events of Gui instances.
void addGuiStateListener(@NotNull final RendererGuiStateListener listener)
Adds a gui state listener to be notified about rendererGuiState changes.
void mouseClicked(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Handles a mouse clicked event.
void endRendering()
Ends rendering and reverts the display settings.
boolean isDisplayChangeSupported()
Returns the graphicsDevice supports low-level display changes.
void setResolutionPost(@NotNull final Window frame, @NotNull final Dimension dimension)
Tries to switch to the given resolution.
void removeComponent(@NotNull final Component component)
Removes a Component.
void setCurrentGui(@NotNull final Gui gui)
Sets the Gui to display.
Combines a list of GUIElements to for a gui.
Definition: Gui.java:43
boolean closeDialog(@NotNull final Gui dialog)
Closes a dialog.
final DateFormat simpleDateFormat
A formatter for timestamps.
void setSelectedHostname(@NotNull final String serverName)
Select an entry by server name.
final Rectangle maximumWindowBounds
The maximal size of a window.
void updateWindowSize(final int windowWidth, final int windowHeight)
Updates the window size for rendering from the main window size.
void mouseDragged(@Nullable final GUIElement element, @NotNull final MouseEvent e)
Handles a mouse dragged event.
final GraphicsEnvironment graphicsEnvironment
The used GraphicsEnvironment.
static AbstractGUIElement findElement(@NotNull final Component component, @NotNull final MouseEvent mouseEvent)
Finds the gui element a given Component is part of.
boolean isDialogOpen(@NotNull final Gui dialog)
Returns whether a given dialog is currently visible.
static GUIText activateCommandInput(@NotNull final Gui gui)
Returns the first command text field of a gui and make it active.
Iterable< Gui > getOpenDialogs()
Returns all open dialogs in reverse painting order; the first element is the top-most dialog...
RendererGuiState getGuiState()
Returns the current gui state.
final Container layeredPane
The JLayeredPane added as the top-level component to frame.
void updateServerSettings()
Updates server based settings to current screen size.
final CrossfireServerConnection crossfireServerConnection
The CrossfireServerConnection to monitor.
AbstractGUIElement findElement(@NotNull final MouseEvent e)
Finds the gui element for a given MouseEvent.
void setActive(final boolean active)
Sets the active state of a GUI element.
static final int DEFAULT_NUM_LOOK_OBJECTS
The default number of ground view objects.
A gui element implementing the message window.
void mouseEntered(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Handles a mouse entered event.
static final int DEFAULT_MAP_HEIGHT
The default map height to request from the server.
Represents a pressed or released key.
Definition: KeyEvent2.java:33
void removeFromLayeredPane(@NotNull final Component component)
Removes a component from layeredPane.
final Collection< GUIFloorList > floorLists
The GUIItemList instances that currentGui and openDialogs contain and that display floor items...
Buffer getActiveMessageBuffer()
Returns the active message buffer.
static Buffer getActiveMessageBuffer(@NotNull final Gui gui)
Returns the active message buffer for a Gui instance.
Dimension getMaxWindowDimension(@NotNull final Insets frameInsets)
Returns the maximum dimension of a frame to fit on the screen.
int getWindowHeight()
Returns the height of the client area.
GUIText activateCommandInput()
Activates the command input text field.
Interface defining an abstract GUI element.
Definition: GUIElement.java:32
boolean handleKeyPress(@NotNull final KeyEvent2 e)
Dispatches a key press KeyEvent.
Definition: Gui.java:365
final MouseTracker mouseTracker
The MouseTracker instance.
boolean deactivateCommandInput()
Deactivates the command input text field.
void setWindowMode(@NotNull final JFrame frame, @Nullable final Resolution resolution, @NotNull final Resolution minResolution, final boolean fixedSize)
Tries to switch to the given resolution.
boolean handleKeyPress(@NotNull final KeyEvent2 e)
Dispatches a key press KeyEvent.
String getName()
Returns the internal name of this gui element.The name is used in skin files for identifying an eleme...
void redraw(@NotNull final Graphics g)
Paints the view into the given graphics instance.
void mousePressed(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Handles a mouse pressed event.
A GUIList instance that displays GUIItemItem instances.
Buffer getBuffer()
Returns the Buffer instance containing the text messages.
Definition: GUILog.java:251
A GUIList that tracks a Metaserver instance.
void addToLayeredPane(@NotNull final Component component, final int layer, final int index)
Adds a component to layeredPane.
Dimension getMapSize()
Returns the map size in squares.
final List< Gui > openDialogs
Currently opened dialogs.
void redrawBlack(@NotNull final Graphics g)
Repaints all to black.
final ComponentListener componentListener
The ComponentListener attached to frame.
void openDialogsAdd(@NotNull final Gui dialog)
Adds a dialog to openDialogs.
BufferStrategy bufferStrategy
The current BufferStrategy.
void setPreferredNumLookObjects(int preferredNumLookObjects)
Sets the maximum number of objects in the ground view.
void removeMouseTrackerRecursively(@NotNull final Component component)
Removes mouseTracker recursively from all children of a Component.
boolean wasDisplayed
Records whether the frame has been displayed before.
final MouseInputListener mouseInputListener
A MouseInputListener that forwards to mouseTracker.
final Writer debugScreen
The Writer to write screen debug to or.
void setDisplayMode(@NotNull final DisplayMode displayMode)
Sets the display mode of the graphicsDevice.
An Iterator that returns all open dialogs in painting order.
void clearGUI(@NotNull final Gui gui)
Sets a gui to display and clears the display.
RendererGuiState rendererGuiState
The current gui state.
Utility class for Swing related functions.
Manages the contents of the contents of a log window.
Definition: Buffer.java:41
void mouseExited(@NotNull final MouseEvent e)
Handles a mouse exited event.
boolean deactivateCommandInput()
Deactivates the command text input field of this dialog.
Definition: Gui.java:397
DisplayMode getDisplayMode()
Returns the current display mode of the graphicsDevice.
void openDialogInt(@NotNull final Gui dialog)
Opens a dialog.
int getNumLookObjects()
Returns the number of ground view objects to request from the server.
final GraphicsDevice graphicsDevice
The used GraphicsDevice.
void removeMouseTracker(@NotNull final Component component)
Removes mouseTracker from a Component.
boolean toggleDialog(@NotNull final Gui dialog)
Toggles a dialog: if the dialog is not shown, show it; else hide it.
boolean isFullScreenSupported()
Returns whether the graphicsDevice supports full-screen exclusive mode.
void mouseReleased(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Handles a mouse released event.
Abstract base class for GUI elements to be shown in Guis.
boolean openDialogsRemove(@NotNull final Gui dialog)
Removes a dialog to openDialogs.
void addMouseTrackerRecursively(@NotNull final Component component)
Adds mouseTracker recursively to all children of a Component.
void setTooltip(@Nullable final Component tooltip)
Sets the tooltip to use, or.
Adds encoding/decoding of crossfire protocol packets to a ServerConnection.
final EventListenerList2< RendererGuiStateListener > rendererGuiStateListeners
Listeners to be notified about rendererGuiState changes.
boolean isFullScreen
Records whether full-screen mode is active.
void setResolutionPre(@NotNull final Window frame)
Tries to switch to the given resolution.
Information about JXClient&#39;s screen/window resolution.
Definition: Resolution.java:35
void setGuiState(@NotNull final RendererGuiState rendererGuiState)
Sets the current gui state.
AbstractGUIElement getElementFromPoint(@NotNull final Gui gui, final int eX, final int eY)
Determines the GUIElement for a given coordinate with a given Gui instance.
Tracks mouse actions and delivers mouse events to affected GUIElement.
void addMouseTracker(@NotNull final Component component)
Adds mouseTracker to a Component.
final Collection< GUIMap > maps
All GUIMap instances that currentGui and openDialogs contain.
int getWindowWidth()
Returns the width of the client area.
void debugScreenWrite(@NotNull final CharSequence message)
Writes a message to the screen debug.
Point getCenterPoint()
Returns the Point where windows should be centered.
void setSelectedHostname(@NotNull final String serverName)
Selects a server entry.
void raiseDialog(@NotNull final Gui dialog)
Raises an already opened dialog.
Abstract base class for gui elements implementing text fields.
Definition: GUILog.java:48
final DisplayMode defaultDisplayMode
The default screen mode that was active when the client did start.
void paintActiveComponent(@NotNull final Graphics g)
Marks the active component in a Graphics instance.
void addComponent(@NotNull final Component component)
Adds a Component.
static final int DEFAULT_MAP_WIDTH
The default map width to request from the server.
int windowWidth
The width of the client area in pixels.
void setFullScreenWindow(@Nullable final Window window)
Enter full-screen mode, or return to windowed mode.
boolean openDialog(@NotNull final Gui dialog, final boolean autoCloseOnDeactivate)
Opens a dialog.
static void invokeAndWait(@NotNull final Runnable runnable)
Calls SwingUtilities#invokeAndWait(Runnable) if not on the EDT or calls the Runnable directly if on t...
final ListIterator< Gui > it
The backing list iterator; it returns the elements in reversed order.
Abstract base class for text input fields.
Definition: GUIText.java:57
int windowHeight
The height of the client area in pixels.
Interface for listeners interested in gui state changes.
void mouseMoved(@Nullable final AbstractGUIElement element, @NotNull final MouseEvent e)
Handles a mouse moved event.