/src/org/mt4j/MTApplication.java

http://mt4j.googlecode.com/ · Java · 1321 lines · 594 code · 182 blank · 545 comment · 114 complexity · 10e6e4395c6b1d61d13dbebe5b6ac02a MD5 · raw file

  1. /***********************************************************************
  2. * mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
  3. *
  4. * This program 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 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. ***********************************************************************/
  18. package org.mt4j;
  19. import java.awt.Component;
  20. import java.awt.Dimension;
  21. import java.awt.DisplayMode;
  22. import java.awt.GraphicsDevice;
  23. import java.awt.GraphicsEnvironment;
  24. import java.awt.Toolkit;
  25. import java.awt.event.WindowAdapter;
  26. import java.io.FileInputStream;
  27. import java.io.FileNotFoundException;
  28. import java.io.InputStream;
  29. import java.util.ArrayDeque;
  30. import java.util.ArrayList;
  31. import java.util.Deque;
  32. import java.util.Iterator;
  33. import java.util.List;
  34. import java.util.Properties;
  35. import javax.media.opengl.GL;
  36. import javax.swing.ImageIcon;
  37. import org.mt4j.components.css.util.CSSStyleManager;
  38. import org.mt4j.input.InputManager;
  39. import org.mt4j.input.inputData.AbstractCursorInputEvt;
  40. import org.mt4j.input.inputData.ActiveCursorPool;
  41. import org.mt4j.input.inputData.InputCursor;
  42. import org.mt4j.input.inputProcessors.globalProcessors.AbstractGlobalInputProcessor;
  43. import org.mt4j.input.inputSources.AbstractInputSource;
  44. import org.mt4j.sceneManagement.IPreDrawAction;
  45. import org.mt4j.sceneManagement.ISceneChangeListener;
  46. import org.mt4j.sceneManagement.Iscene;
  47. import org.mt4j.sceneManagement.SceneChangeEvent;
  48. import org.mt4j.sceneManagement.transition.ITransition;
  49. import org.mt4j.util.MT4jSettings;
  50. import org.mt4j.util.SettingsMenu;
  51. import org.mt4j.util.animation.AnimationManager;
  52. import org.mt4j.util.animation.ani.AniAnimation;
  53. import org.mt4j.util.logging.ILogger;
  54. import org.mt4j.util.logging.Log4jLogger;
  55. import org.mt4j.util.logging.MTLoggerFactory;
  56. import org.mt4j.util.math.Tools3D;
  57. import org.mt4j.util.opengl.GLFBO;
  58. import processing.core.PApplet;
  59. /**
  60. * Use this class to create a new multitouch application.
  61. * <br>The best way to create your application would be to extend this class and
  62. * put the <code>main</code> method into that class.
  63. * In the <code>main</code> method call the <code>initialize()</code> method.
  64. * Then override the <code>startUp()</code> method which is called
  65. * automatically after the initialize method. The <code>startUp()</code> method can be used to
  66. * create your scenes (extend the <code>AbstractScene</code> class) and add them to
  67. * the application by calling <code>addScene</code> method.
  68. *
  69. * <p>Internally, the main method of processings PApplet class is called with the class name
  70. * of the extended PApplet class as an argument. The PApplet class then instantiates the given
  71. * class and calls its setup() and then repeatedly its run() method.
  72. *
  73. * @author Christopher Ruff
  74. */
  75. public abstract class MTApplication extends PApplet {
  76. /** The Constant logger. */
  77. private static ILogger logger;
  78. public static String CUSTOM_OPENGL_GRAPHICS = "org.mt4j.util.opengl.CustomPGraphicsOpenGL"; //PApplet.OPENGL
  79. /** The Constant serialVersionUID. */
  80. private static final long serialVersionUID = 1L;
  81. /** The scene change locked. */
  82. private boolean sceneChangeLocked;
  83. // private static MTApplication mtApp = null;
  84. /** The scene list. */
  85. private List<Iscene> sceneList;
  86. /** The current scene. */
  87. private Iscene currentScene;
  88. /** The animation mgr. */
  89. private AnimationManager animMgr;
  90. /** The time last frame. */
  91. private long timeLastFrame ;
  92. /** The already run. */
  93. private boolean alreadyRun;
  94. /** The input manager. */
  95. private InputManager inputManager;
  96. /** The scene changed listeners. */
  97. private List<ISceneChangeListener> sceneChangedListeners;
  98. /** The invoke later actions. */
  99. private Deque<Runnable> invokeLaterActions;
  100. /** The scene stack. */
  101. private ArrayDeque<Iscene> sceneStack;
  102. private Thread renderThread;
  103. public static String separator = "/";
  104. public static char separatorChar = '/';
  105. private static boolean settingsLoadedFromFile = false; //cant initialize in constructor, need it before that!
  106. private ImageIcon mt4jIcon;
  107. private CSSStyleManager cssStyleManager;
  108. private ArrayDeque<IPreDrawAction> preDrawActions;
  109. // private static boolean fullscreen;
  110. /*
  111. public static void main(String[] args){
  112. // MTApplication app = new MTApplication();
  113. PApplet.main(new String[] {
  114. // "--present",
  115. // "--exclusive",
  116. "--bgcolor=#000000",
  117. "--hide-stop",
  118. "org.mt4j.MTApplication"
  119. }
  120. );
  121. }
  122. @Override
  123. public void setup(){
  124. size(800,600, OPENGL); //TODO REMOVE
  125. logger.debug("Setup");
  126. System.out.println("Setup called");
  127. smooth();
  128. hint(ENABLE_OPENGL_2X_SMOOTH );
  129. smooth();
  130. noSmooth();
  131. background(0);
  132. GL gl = Tools3D.getGL(this);
  133. // gl.glEnable(GL.GL_MULTISAMPLE);
  134. // gl.glEnable(GL.GL_MULTISAMPLE_EXT);
  135. }
  136. @Override
  137. public void draw(){
  138. // background(255);
  139. fill(250,0,0,255);
  140. stroke(250,0,0,255);
  141. line(0,10, 280,20);
  142. GL gl = Tools3D.beginGL(this);
  143. // GL gl = ((PGraphicsOpenGL)this.g).beginGL();
  144. // gl.glEnable(GL.GL_LINE_SMOOTH );
  145. gl.glDisable(GL.GL_LINE_SMOOTH );
  146. // gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST);
  147. // Enable Blending
  148. gl.glEnable(GL.GL_BLEND);
  149. // Specifies pixel arithmetic
  150. gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
  151. gl.glLineWidth(1);
  152. gl.glColor4d(0.0, 0.0, 0.0, 1);
  153. gl.glBegin(GL.GL_LINE_STRIP);
  154. gl.glVertex3d(0, 20, 0);
  155. gl.glVertex3d(280, 30, 0);
  156. gl.glEnd();
  157. gl.glBegin(GL.GL_LINE_STRIP);
  158. gl.glVertex3d(0, 20, 0);
  159. gl.glVertex3d(711, 230, 0);
  160. gl.glVertex3d(200, 300, 0);
  161. gl.glVertex3d(100, 330, 0);
  162. gl.glEnd();
  163. // ((PGraphicsOpenGL)this.g).endGL();
  164. Tools3D.endGL(this);
  165. if (this.mousePressed){
  166. fill(150);
  167. rect(mouseX, mouseY, 10,10);
  168. }
  169. }
  170. */
  171. /*
  172. //TODO test to make window undecorated - seems to mess up some textures (maybe because opengl re-initialization)
  173. //put frame.setLocation(-1600, 0); at the end of setup() to position the frame
  174. public void init(){
  175. // to make a frame not displayable, you can
  176. // use frame.removeNotify()
  177. frame.removeNotify();
  178. frame.setUndecorated(true);
  179. // addNotify, here i am not sure if you have
  180. // to add notify again.
  181. frame.addNotify();
  182. super.init();
  183. }
  184. */
  185. /**
  186. * Dont instiatiate this class directly!
  187. * It gets instantiated by the PApplet class via
  188. * java reflection.
  189. */
  190. public MTApplication(){
  191. sceneList = new ArrayList<Iscene>();
  192. currentScene = null;
  193. animMgr = AnimationManager.getInstance();
  194. alreadyRun = false;
  195. sceneChangedListeners = new ArrayList<ISceneChangeListener>();
  196. invokeLaterActions = new ArrayDeque<Runnable>();
  197. sceneStack = new ArrayDeque<Iscene>();
  198. sceneChangeLocked = false;
  199. cssStyleManager = new CSSStyleManager(this);
  200. preDrawActions = new ArrayDeque<IPreDrawAction>();
  201. }
  202. /**
  203. * Initializes the processings settings.
  204. * Call this method in your main method prior to anything else!
  205. */
  206. public static void initialize(){
  207. initialize(new CurrentClassGetter().getClassName());
  208. }
  209. public static void initialize(boolean showSettingsMenu){
  210. initialize(new CurrentClassGetter().getClassName(), showSettingsMenu);
  211. }
  212. public static void initialize(String classToInstantiate){
  213. initialize(classToInstantiate, false);
  214. }
  215. /**
  216. * Initializes the processings settings.
  217. * Call this method in your main method prior to anything else!
  218. * We have to provide the fully qualified name to the class that
  219. * we are calling this from. (Should be our MTAplication extended class)
  220. * This is needed because processing will use the reflection api to instantiate
  221. * an instance of the MTApplication class.
  222. * <br>E.g.: <code>initialize("myPackage.myMainClass");</code>
  223. *
  224. * @param classToInstantiate the class to instantiate
  225. * @param showSettingsMenu show settings menu
  226. */
  227. public static void initialize(String classToInstantiate, boolean showSettingsMenu){
  228. //Initialize Loggin facilities - IMPORTANT TO DO THIS ASAP!
  229. MTLoggerFactory.setLoggerProvider(new Log4jLogger()); //FIXME TEST
  230. // MTLoggerFactory.setLoggerProvider(new JavaLogger()); //FIXME TEST
  231. logger = MTLoggerFactory.getLogger(MTApplication.class.getName());
  232. logger.setLevel(ILogger.INFO);
  233. logger.debug(classToInstantiate + " is the class instatiated by PApplet class.");
  234. //FIXME TEST
  235. if (showSettingsMenu){
  236. settingsLoadedFromFile = true;
  237. SettingsMenu menu = new SettingsMenu(classToInstantiate);
  238. menu.setVisible(true);
  239. }else{
  240. getSettingsFromFile();
  241. // Launch processing PApplet main() function
  242. if (MT4jSettings.getInstance().isFullscreen()){
  243. if (MT4jSettings.getInstance().isFullscreenExclusive()){
  244. PApplet.main(new String[] {
  245. "--display=" + MT4jSettings.getInstance().getDisplay(),
  246. "--present",
  247. "--exclusive",
  248. "--bgcolor=#000000",
  249. "--hide-stop",
  250. classToInstantiate}
  251. );
  252. }else{
  253. PApplet.main(new String[] {
  254. "--display=" + MT4jSettings.getInstance().getDisplay(),
  255. "--present",
  256. "--bgcolor=#000000",
  257. "--hide-stop",
  258. classToInstantiate}
  259. );
  260. }
  261. }else{
  262. PApplet.main(new String[] {
  263. "--display=" + MT4jSettings.getInstance().getDisplay(),
  264. classToInstantiate });
  265. }
  266. }
  267. }
  268. private static void getSettingsFromFile(){
  269. //Load some properties from Settings.txt file
  270. Properties properties = new Properties();
  271. try {
  272. try {
  273. FileInputStream fi = new FileInputStream(MT4jSettings.getInstance().getDefaultSettingsPath() + "Settings.txt");
  274. properties.load(fi);
  275. } catch (FileNotFoundException e) {
  276. logger.debug("Couldnt load Settings.txt from the File system. Trying to load it as a resource..");
  277. InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("Settings.txt");
  278. if (in != null){
  279. properties.load(in);
  280. }else{
  281. logger.debug("Couldnt load Settings.txt as a resource. Using defaults.");
  282. throw new FileNotFoundException("Couldnt load Settings.txt as a resource");
  283. }
  284. }
  285. MT4jSettings.fullscreen = Boolean.parseBoolean(properties.getProperty("Fullscreen", Boolean.valueOf(MT4jSettings.getInstance().isFullscreen()).toString()).trim());
  286. //Use java's fullscreen exclusive mode (real fullscreen) or just use an undecorated window at fullscreen size
  287. MT4jSettings.getInstance().fullscreenExclusive = Boolean.parseBoolean(properties.getProperty("FullscreenExclusive", Boolean.valueOf(MT4jSettings.getInstance().isFullscreenExclusive()).toString()).trim());
  288. //Which display to use for fullscreen
  289. MT4jSettings.getInstance().display = Integer.parseInt(properties.getProperty("Display", String.valueOf(MT4jSettings.getInstance().getDisplay())).trim());
  290. MT4jSettings.getInstance().windowWidth = Integer.parseInt(properties.getProperty("DisplayWidth", String.valueOf(MT4jSettings.getInstance().getWindowWidth())).trim());
  291. MT4jSettings.getInstance().windowHeight = Integer.parseInt(properties.getProperty("DisplayHeight", String.valueOf(MT4jSettings.getInstance().getWindowHeight())).trim());
  292. //FIXME at fullscreen really use the screen dimension? -> we need to set the native resoultion ourselves!
  293. //so we can have a lower fullscreen resolution than the screen dimensions
  294. if (MT4jSettings.getInstance().isFullscreen() && !MT4jSettings.getInstance().isFullscreenExclusive()){
  295. Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  296. MT4jSettings.getInstance().windowWidth = screenSize.width;
  297. MT4jSettings.getInstance().windowHeight = screenSize.height;
  298. }
  299. /*
  300. //Comment this to not change the window width to the screen width in fullscreen mode
  301. else{
  302. }
  303. */
  304. MT4jSettings.getInstance().maxFrameRate = Integer.parseInt(properties.getProperty("MaximumFrameRate", String.valueOf(MT4jSettings.getInstance().getMaxFrameRate())).trim());
  305. MT4jSettings.getInstance().renderer = Integer.parseInt(properties.getProperty("Renderer", String.valueOf(MT4jSettings.getInstance().getRendererMode())).trim());
  306. MT4jSettings.getInstance().numSamples = Integer.parseInt(properties.getProperty("OpenGLAntialiasing", String.valueOf(MT4jSettings.getInstance().getNumSamples())).trim());
  307. MT4jSettings.getInstance().vSync = Boolean.parseBoolean(properties.getProperty("Vertical_sync", Boolean.valueOf(MT4jSettings.getInstance().isVerticalSynchronization()).toString()).trim());
  308. //Set frametitle
  309. String frameTitle = properties.getProperty("Frametitle", MT4jSettings.getInstance().getFrameTitle().trim());
  310. MT4jSettings.getInstance().frameTitle = frameTitle;
  311. } catch (Exception e) {
  312. logger.error("Error while loading Settings.txt. Using defaults.");
  313. }
  314. settingsLoadedFromFile = true;
  315. }
  316. protected void switchResolution() {
  317. logger.debug("Switching resolution..");
  318. try {
  319. frame.enableInputMethods(false);
  320. frame.setIgnoreRepaint(true);
  321. final GraphicsDevice myGraphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
  322. // Get the current display mode
  323. final DisplayMode previousDisplayMode= myGraphicsDevice.getDisplayMode();
  324. // final int width = 1280;
  325. // final int height = 768;
  326. final int width = MT4jSettings.getInstance().getWindowWidth();
  327. final int height = MT4jSettings.getInstance().getWindowHeight();
  328. int bitDepth = 32;
  329. int refreshRate = myGraphicsDevice.getDisplayMode().getRefreshRate();
  330. myGraphicsDevice.setFullScreenWindow(this.frame);
  331. // Check if display mode changes are supported by the OS
  332. if (myGraphicsDevice.isDisplayChangeSupported()) {
  333. // Get all available display modes
  334. DisplayMode[] displayModes = myGraphicsDevice.getDisplayModes();
  335. DisplayMode multiBitsDepthSupportedDisplayMode = null;
  336. DisplayMode refreshRateUnknownDisplayMode = null;
  337. DisplayMode multiBitsDepthSupportedAndRefreshRateUnknownDisplayMode = null;
  338. DisplayMode matchingDisplayMode = null;
  339. DisplayMode currentDisplayMode;
  340. // Look for the display mode that matches with our parameters
  341. // Look for some display modes that are close to these parameters
  342. // and that could be used as substitutes
  343. // On some machines, the refresh rate is unknown and/or multi bit
  344. // depths are supported. If you try to force a particular refresh
  345. // rate or a bit depth, you might find no available display mode
  346. // that matches exactly with your parameters
  347. for (int i = 0; i < displayModes.length && matchingDisplayMode == null; i++) {
  348. currentDisplayMode = displayModes[i];
  349. if (currentDisplayMode.getWidth() == width &&
  350. currentDisplayMode.getHeight() == height) {
  351. if (currentDisplayMode.getBitDepth() == bitDepth) {
  352. if (currentDisplayMode.getRefreshRate() == refreshRate) {
  353. matchingDisplayMode = currentDisplayMode;
  354. } else if (currentDisplayMode.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN) {
  355. refreshRateUnknownDisplayMode = currentDisplayMode;
  356. }
  357. } else if (currentDisplayMode.getBitDepth() == DisplayMode.BIT_DEPTH_MULTI) {
  358. if (currentDisplayMode.getRefreshRate() == refreshRate) {
  359. multiBitsDepthSupportedDisplayMode = currentDisplayMode;
  360. } else if (currentDisplayMode.getRefreshRate() == DisplayMode.REFRESH_RATE_UNKNOWN) {
  361. multiBitsDepthSupportedAndRefreshRateUnknownDisplayMode = currentDisplayMode;
  362. }
  363. }
  364. }
  365. }
  366. DisplayMode nextDisplayMode = null;
  367. if (matchingDisplayMode != null) {
  368. nextDisplayMode = matchingDisplayMode;
  369. } else if (multiBitsDepthSupportedDisplayMode != null) {
  370. nextDisplayMode = multiBitsDepthSupportedDisplayMode;
  371. } else if (refreshRateUnknownDisplayMode != null) {
  372. nextDisplayMode = refreshRateUnknownDisplayMode;
  373. } else if (multiBitsDepthSupportedAndRefreshRateUnknownDisplayMode != null) {
  374. nextDisplayMode = multiBitsDepthSupportedAndRefreshRateUnknownDisplayMode;
  375. } else {
  376. // isFullScreenSupported = false;
  377. logger.error("No matching fullscreen display mode found!");
  378. }
  379. if (nextDisplayMode != null){
  380. /*
  381. DisplayMode myDisplayMode = new DisplayMode(
  382. width,
  383. height,
  384. myGraphicsDevice.getDisplayMode().getBitDepth(),
  385. DisplayMode.REFRESH_RATE_UNKNOWN);
  386. myGraphicsDevice.setDisplayMode(myDisplayMode);
  387. */
  388. myGraphicsDevice.setDisplayMode(nextDisplayMode);
  389. Component[] myComponents = frame.getComponents();
  390. for (int i = 0; i < myComponents.length; i++) {
  391. if (myComponents[i] instanceof PApplet) {
  392. myComponents[i].setLocation(0, 0);
  393. }
  394. }
  395. frame.addWindowListener(new WindowAdapter() {
  396. @Override
  397. public void windowClosing(java.awt.event.WindowEvent e) {
  398. // If required, restore the previous display mode
  399. myGraphicsDevice.setDisplayMode(previousDisplayMode);
  400. // If required, get back to the windowed mode
  401. if (myGraphicsDevice.getFullScreenWindow() == frame) {
  402. myGraphicsDevice.setFullScreenWindow(null);
  403. }
  404. }
  405. });
  406. }
  407. }
  408. }
  409. catch (Exception e) {
  410. e.printStackTrace();
  411. }
  412. }
  413. /**
  414. * ***********************************************************
  415. * Processings setup. this is called once when the applet is started
  416. * Used to define some initial settings
  417. * **********************************************************.
  418. */
  419. @Override
  420. public void setup(){
  421. //TOGGLES ALWAYS ON TOP MODE
  422. //this.frame.setAlwaysOnTop(true);
  423. if (logger == null){
  424. //Initialize Loggin facilities - IMPORTANT TO DO THIS ASAP!//////
  425. MTLoggerFactory.setLoggerProvider(new Log4jLogger());
  426. logger = MTLoggerFactory.getLogger(MTApplication.class.getName());
  427. logger.setLevel(ILogger.INFO);
  428. }
  429. logger.debug("-> setup called");
  430. //Check if OS 32/64 Bit
  431. String bit = System.getProperty("sun.arch.data.model");
  432. logger.info("Platform: \"" + System.getProperty("os.name") + "\" -> Version: \"" + System.getProperty("os.version") + "\" -> JVM Bit: \"" + bit + "\"");
  433. MT4jSettings.getInstance().architecture = bit.contains("64")? MT4jSettings.ARCHITECTURE_64_BIT : MT4jSettings.ARCHITECTURE_32_BIT;
  434. if (!settingsLoadedFromFile){
  435. getSettingsFromFile();
  436. }
  437. // Applet size - size() must be the first command in setup() method
  438. if (MT4jSettings.getInstance().getRendererMode() == MT4jSettings.OPENGL_MODE)
  439. this.size(MT4jSettings.getInstance().getWindowWidth(), MT4jSettings.getInstance().getWindowHeight(), MTApplication.CUSTOM_OPENGL_GRAPHICS);
  440. else if (MT4jSettings.getInstance().getRendererMode() == MT4jSettings.P3D_MODE)
  441. this.size(MT4jSettings.getInstance().getWindowWidth(), MT4jSettings.getInstance().getWindowHeight(), PApplet.P3D);
  442. //Switch to different resolution in fullscreen exclusive mode if neccessary
  443. Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  444. if (MT4jSettings.getInstance().isFullscreen() && MT4jSettings.getInstance().isFullscreenExclusive() && MT4jSettings.getInstance().getWindowWidth() != screenSize.width && MT4jSettings.getInstance().getWindowHeight() != screenSize.height){
  445. switchResolution();
  446. }
  447. /*
  448. //Processing Bug? seems to always use 2 samples
  449. if (MT4jSettings.getInstance().getNumSamples() <= 0){
  450. hint(DISABLE_OPENGL_2X_SMOOTH);
  451. }else if (MT4jSettings.getInstance().getNumSamples() == 2){
  452. //Nothing to set, Processing default anyway
  453. }else if (MT4jSettings.getInstance().getNumSamples() == 4){
  454. hint(DISABLE_OPENGL_2X_SMOOTH);
  455. hint(ENABLE_OPENGL_4X_SMOOTH);
  456. }
  457. */
  458. // pContext.hint( PApplet.ENABLE_OPENGL_4X_SMOOTH ); // ENABLES OPENGL EXTRA SMOOTHING -> DOESENT GET CONSISTENT RESULTS ON ALL MACHINES! DISABLE WHEN PROBLEMS OCCUR!
  459. //hint(ENABLE_DEPTH_SORT); // Enable primitive z-sorting of triangles and lines in P3D and OPENGL. This can slow performance considerably, and the algorithm is not yet perfect.
  460. //hint(DISABLE_ERROR_REPORT); // Speeds up the OPENGL renderer setting by not checking for errors while running.
  461. //hint(ENABLE_ACCURATE_TEXTURES); //Enables better texture accuracy for the P3D renderer. This option will do a better job of dealing with textures in perspective.
  462. // Save this applets rendering thread for reference
  463. this.renderThread = Thread.currentThread();
  464. //System.out.println("Current Thread: "+ Thread.currentThread());
  465. // Set frame icon image
  466. try {
  467. //Set the window frame's title
  468. frame.setTitle(MT4jSettings.getInstance().getFrameTitle());
  469. // this.mt4jIcon = new ImageIcon(MT4jSettings.getInstance().getDefaultImagesPath() +
  470. // "MT4j.gif");
  471. this.mt4jIcon = new ImageIcon(Thread.currentThread().getContextClassLoader().getResource(MT4jSettings.getInstance().getDefaultImagesPath() +
  472. "MT4j.gif"));
  473. this.frame.setIconImage(mt4jIcon.getImage());
  474. }catch (Exception e){
  475. e.printStackTrace();
  476. }
  477. logger.info("MT4j window dimensions: \"" + MT4jSettings.getInstance().getWindowWidth() + " X " + MT4jSettings.getInstance().getWindowHeight() + "\"");
  478. // //Set background color
  479. // pContext.background(MT4jSettings.getInstance().getBackgroundClearColor());
  480. background(150);
  481. //Set the framerate
  482. frameRate(MT4jSettings.getInstance().getMaxFrameRate());
  483. logger.info("Maximum framerate: \"" + MT4jSettings.getInstance().getMaxFrameRate() + "\"");
  484. //FIXME TODO add in settings.txt?
  485. hint(MTApplication.DISABLE_OPENGL_ERROR_REPORT);
  486. MT4jSettings.getInstance().programStartTime = System.currentTimeMillis();
  487. //Apply some opengl settings like V-Syncing or multi-Sampling
  488. this.applyOpenGLStartSettings();
  489. //Create a new inputsourcePool
  490. this.setInputManager(new InputManager(this));
  491. AniAnimation.init(this); //Initialize Ani animation library
  492. /*
  493. * Resizable Window test
  494. * Problems:
  495. * - all textures, shaders etc get destroyed because a new gl context is created
  496. * - cursor coordiantes are calculated wrong? we prolly have to update Papplet width/height
  497. frame.setResizable(true);
  498. frame.addComponentListener(new ComponentAdapter() {
  499. public void componentResized(ComponentEvent e) {
  500. if(e.getSource() == frame) {
  501. frame.setSize(frame.getWidth(), minHeight);
  502. }
  503. }
  504. } );
  505. */
  506. //Call startup at the end of setup(). Should be overridden in extending classes
  507. this.startUp();
  508. }
  509. /**
  510. * Apply open gl start settings.
  511. */
  512. private void applyOpenGLStartSettings(){
  513. //TODO pa.smooth() / pa.noSmooth() ver?ndert auch line_smooth!
  514. //f?r test ob multisampling lines ohne Line_smooth okay rendered m?ssen
  515. //sicherheitshalber auch die pa.smoot() etc abgefangen werden und line_smooth immer disabled sein!
  516. //TODO check line drawing and abstractvisible at stencil in this context (line_smooth)
  517. //TODO
  518. // - if multisampling enabled dont do line smoothing at all
  519. // - OR: disable multisampling each time before doing line_smoothing! (better but expensive?)
  520. // -> info: disabling multisampling isnt possible at runtime..
  521. // - or disable mutisample before drawing with line_smooth!
  522. //TOOD dont use lines to smooth some objects then (fonts, etc)
  523. if (MT4jSettings.getInstance().isOpenGlMode() ){
  524. GL gl = Tools3D.getGL(this);
  525. logger.info("OpenGL Version: \"" + gl.glGetString(GL.GL_VERSION) + "\"" + " - Vendor: \"" + gl.glGetString(GL.GL_VENDOR) + "\"" + " - Renderer: \"" + gl.glGetString(GL.GL_RENDERER) + "\"");
  526. // logger.info("Shading language version: \"" + gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION) + "\"");
  527. logger.info("Non power of two texture sizes allowed: \"" + Tools3D.supportsNonPowerOfTwoTexture(this) + "\"");
  528. logger.info("OpenGL Framebuffer Object Extension available: \"" + GLFBO.isSupported(this) + "\"");
  529. //Set VSyncing on -> to avoid tearing
  530. //-> check if gfx card settings allow apps to set it!
  531. //-> Use with caution! only use with fps rate == monitor Hz!
  532. //and fps never drop below Hz! -> else choppy!
  533. //-> only works with opengl!
  534. Tools3D.setVSyncing(this, MT4jSettings.getInstance().isVerticalSynchronization());
  535. logger.info("Vertical Sync enabled: \"" + MT4jSettings.getInstance().isVerticalSynchronization() + "\"");
  536. if ( MT4jSettings.getInstance().isMultiSampling()){
  537. gl.glEnable(GL.GL_MULTISAMPLE);
  538. // gl.glDisable(GL.GL_MULTISAMPLE);
  539. logger.info("OpenGL multi-sampling enabled.");
  540. }
  541. gl.glEnable(GL.GL_LINE_SMOOTH);
  542. // gl.glDisable(GL.GL_LINE_SMOOTH);
  543. }
  544. }
  545. public void setOpenGLErrorReportingEnabled(boolean reportErros){
  546. if (reportErros){
  547. hint(MTApplication.ENABLE_OPENGL_ERROR_REPORT);
  548. }else{
  549. hint(MTApplication.DISABLE_OPENGL_ERROR_REPORT);
  550. }
  551. }
  552. /**
  553. * ********************************************************************************************
  554. * Processings draw() gets called repeatedly by processings PApplet Class - unless noloop() is called
  555. * ********************************************************************************************.
  556. */
  557. @Override
  558. public void draw(){
  559. this.runApplication();
  560. }
  561. /**
  562. * Is called at the end of the setup() method.
  563. * <br>Override this method in your extended MTApplication class!
  564. */
  565. public abstract void startUp();
  566. /**
  567. * Registers an action to be processed before the next frame
  568. * in the main drawing thread.
  569. *
  570. * @param action the action
  571. */
  572. public void registerPreDrawAction(final IPreDrawAction action){
  573. synchronized (preDrawActions) {
  574. // this.preDrawActions.addLast(action);
  575. invokeLater(new Runnable() {
  576. public void run() {
  577. preDrawActions.addLast(action);
  578. }
  579. });
  580. }
  581. }
  582. /**
  583. * Unregisters an PreDrawAction.
  584. *
  585. * @param action the action
  586. */
  587. public void unregisterPreDrawAction(final IPreDrawAction action){
  588. synchronized (preDrawActions) {
  589. if (preDrawActions.contains(action)){
  590. // this.preDrawActions.remove(action);
  591. invokeLater(new Runnable() {
  592. public void run() {
  593. preDrawActions.remove(action);
  594. }
  595. });
  596. }
  597. }
  598. }
  599. /**
  600. * Main run loop.
  601. * <li>Updates the time passed since the last time drawn.
  602. * <li>Updates any animations with the new time delta.
  603. * <li>Updates and draws the current scene.
  604. * <li>Updates and draws the current scene transitions.
  605. */
  606. private void runApplication(){
  607. //Process preDrawActions
  608. synchronized (preDrawActions) {
  609. for (Iterator<IPreDrawAction> iter = preDrawActions.iterator(); iter.hasNext();) {
  610. IPreDrawAction action = iter.next();
  611. action.processAction();
  612. if (!action.isLoop()){
  613. iter.remove();
  614. }
  615. }
  616. }
  617. //Use nanoTime
  618. if (!alreadyRun){
  619. alreadyRun = true;
  620. timeLastFrame = System.nanoTime();
  621. }
  622. long nanos = System.nanoTime();
  623. long timeDelta = (nanos - timeLastFrame) / 1000000L;
  624. timeLastFrame = nanos;
  625. /*
  626. //Use currentTimeMillis
  627. if (!alreadyRun){
  628. alreadyRun = true;
  629. timeLastFrame = System.currentTimeMillis();
  630. }
  631. long millis = System.currentTimeMillis();
  632. long timeDelta = millis - timeLastFrame;
  633. timeLastFrame = millis;
  634. */
  635. // System.out.println("TimeDelta: " + timeDelta);
  636. //Run invoke later actions
  637. synchronized (invokeLaterActions) {
  638. while (!invokeLaterActions.isEmpty()){
  639. invokeLaterActions.pollFirst().run();
  640. }
  641. }
  642. //Update animation manager
  643. animMgr.update(timeDelta);
  644. // /*
  645. //Handle scene transitions
  646. if (this.pendingTransition != null){
  647. //Run the transition
  648. this.pendingTransition.transition.drawAndUpdate(this.g, timeDelta);
  649. if (this.pendingTransition.transition.isFinished()){
  650. this.pendingTransition.transition.onLeave();
  651. this.doSceneChange(this.getCurrentScene(), this.pendingTransition.nextScene);
  652. this.pendingTransition = null;
  653. }
  654. }else{
  655. //Draw the current scene
  656. Iscene theCurrentScene = this.getCurrentScene();
  657. if (theCurrentScene != null){
  658. theCurrentScene.drawAndUpdate(this.g, timeDelta);
  659. }
  660. }
  661. // */
  662. /*
  663. //Update scene
  664. sceneMgr.updateCurrentScene(timeDelta);
  665. //Draw scene
  666. sceneMgr.drawCurrentScene();
  667. */
  668. }
  669. /**
  670. * Checks if is render thread is current.
  671. *
  672. * @return true, if is render thread current
  673. */
  674. public boolean isRenderThreadCurrent(){
  675. return Thread.currentThread().equals(renderThread);
  676. }
  677. /**
  678. * Invokes the specified runnable at the beginning the next rendering loop in the rendering thread.
  679. * This is especially useful for executing opengl commands from another thread - which would lead to errors
  680. * if not synchronized with the rendering thread.
  681. *
  682. * @param runnable the runnable
  683. */
  684. public void invokeLater(Runnable runnable){
  685. synchronized (invokeLaterActions) {
  686. invokeLaterActions.addLast(runnable);
  687. }
  688. }
  689. /**
  690. * Checks which scene is on top of the scene stack at the moment.
  691. * If no scene has been pushed on the stack, null is returned.
  692. *
  693. * @return the iscene
  694. */
  695. public Iscene peekScene(){
  696. return sceneStack.peek();
  697. }
  698. public int getSceneStackCount(){
  699. return sceneStack.size();
  700. }
  701. /**
  702. * Pushes the current scene on the scene stack.
  703. */
  704. public void pushScene(){
  705. if (getCurrentScene() == null){
  706. logger.debug("Scene stack is empty! No scene to put on the stack!");
  707. }else{
  708. logger.debug("Putting scene: " + getCurrentScene().getName() + " on the stack.");
  709. sceneStack.offerFirst(getCurrentScene());
  710. }
  711. }
  712. /**
  713. * Pops the scene thats currently ontop of the scene stack and changes back to it.
  714. * If the stack is empty no error is thrown and no scene change will happen.
  715. */
  716. public boolean popScene(){
  717. // Iscene stackScene = sceneStack.pollFirst();
  718. Iscene stackScene = sceneStack.peek();
  719. if (stackScene != null){
  720. logger.debug("Popping scene: " + stackScene.getName() + " back from the stack.");
  721. boolean changed = this.changeScene(stackScene);
  722. if (changed){
  723. sceneStack.pollFirst();
  724. return true;
  725. }else{
  726. return false;
  727. }
  728. }else{
  729. logger.warn("Scene stack is empty! No scene to pop from the stack!");
  730. return false;
  731. }
  732. }
  733. private boolean inDoSceneChange = false;
  734. private TransitionInfo pendingTransition;
  735. /**
  736. * The Class TransitionInfo. Holding info about a scene change transition.
  737. * @author Christopher Ruff
  738. */
  739. private class TransitionInfo{
  740. ITransition transition;
  741. Iscene lastScene;
  742. Iscene nextScene;
  743. boolean destroyLastSceneAfterTransition = false;
  744. public TransitionInfo(ITransition transition, Iscene lastScene, Iscene nextScene){
  745. this.transition = transition;
  746. this.lastScene = lastScene;
  747. this.nextScene = nextScene;
  748. }
  749. }
  750. /**
  751. * Initiates the scene change. Checks if the old scene has a transition
  752. * and sets it to be used in the main loop.
  753. *
  754. * @param oldScene the old scene
  755. * @param newScene the new scene
  756. */
  757. private boolean initiateSceneChange(Iscene oldScene, Iscene newScene){
  758. //FIXME TEST!
  759. if (oldScene.equals(newScene)){
  760. logger.error("Trying to change from and to the same scene.");
  761. return false;
  762. }
  763. //Lock scene changes to only 1 at a time. At sending the bridge events during the
  764. //scene change, it could occur that a scene change could be triggered again which we prevent
  765. if (!sceneChangeLocked){
  766. sceneChangeLocked = true;
  767. Iscene lastScene = this.getCurrentScene();
  768. //Remove pending animations //
  769. //FIXME problemes, if new animations are defined in a scenes constructor, they get removed here..
  770. //AnimationManager.getInstance().clear();
  771. //Flush events so that enqueued input ended get sent to the last scene
  772. //(Problem: they have been removed from active cursor pool already so they dont
  773. //appear there and no ended and started evts are sent to the scenes!
  774. //IF input started or updated should be flushed with this they should appear in active
  775. //cursor list after that and be sended the right events
  776. //- maybe only flush input_ended?
  777. for (AbstractInputSource abstractInputSource : getInputManager().getInputSources()) {
  778. abstractInputSource.flushEvents();
  779. }
  780. //Check which cursors are still active and clone their last evt as INPUT_ENDED
  781. //so the scene can complete its state (i.e. buttons are be released etc)
  782. this.sendEndedEvents(lastScene);
  783. //Disable the last scene's global input processors
  784. this.getInputManager().disableGlobalInputProcessors(lastScene);
  785. // /*
  786. if (lastScene.getTransition() != null){
  787. ITransition t = lastScene.getTransition();
  788. this.pendingTransition = new TransitionInfo(t, lastScene, newScene);
  789. t.onEnter();
  790. t.setup(lastScene, newScene);
  791. return true;
  792. }else{
  793. return this.doSceneChange(lastScene, newScene);
  794. }
  795. // */
  796. //doSceneChange(oldScene, newScene);
  797. }else{
  798. logger.debug("Couldnt change scene -> Change is locked from another scene change.");
  799. return false;
  800. }
  801. }
  802. /**
  803. * Does the scene change after the transition (if existing) is completed.
  804. * @param oldScene the old scene
  805. * @param newScene the new scene
  806. */
  807. private boolean doSceneChange(Iscene oldScene, Iscene newScene){
  808. if (sceneChangeLocked && !inDoSceneChange){
  809. inDoSceneChange = true;
  810. //Maybe show loading progress for newScenne.Init first?
  811. oldScene.onLeave();
  812. //Initialize new Scene
  813. newScene.onEnter();
  814. //Enable input Processors previously registered with that scene
  815. this.getInputManager().enableGlobalInputProcessors(newScene);
  816. //Check which cursors are active and clone their last evt as INPUT_DETECTED
  817. //so the scene doesent get INPUT_UPDATED without the start events
  818. this.sendStartedEvents(newScene);
  819. //Set new current scene
  820. this.currentScene = newScene;
  821. //FIXME TEST -> Make it possible to destroy scenes after a transition
  822. //(During a transition the old scene cant be removed or destroyed because
  823. //its still the current scene!)
  824. if (pendingTransition != null){
  825. if (pendingTransition.destroyLastSceneAfterTransition){
  826. logger.debug("Destroying scene: " + pendingTransition.lastScene.getName() + " after the transition.");
  827. pendingTransition.lastScene.destroy();
  828. }
  829. }
  830. if (!this.sceneChangedListeners.isEmpty()){
  831. this.fireSceneChangeEvent(new SceneChangeEvent(this, oldScene, newScene));
  832. }
  833. logger.debug("Scene changed from: '" + oldScene + "' to: '" + newScene + "'");
  834. sceneChangeLocked = false;
  835. inDoSceneChange = false;
  836. return true;
  837. }else{
  838. return false;
  839. }
  840. }
  841. /**
  842. * Changes the scene to the specified scene.
  843. * <p>NOTE: This is not threadsafe while using OpenGL mode. If in openGL mode make,
  844. * sure to call this only from the same thread. If running in a different thread,
  845. * execute the scene change using the <code>invokeLater(Runnable runnable)</code> method
  846. * of the MTApplication instance!
  847. * <p>NOTE: If the scene is not already added to the application by invoking <code>addScene()</code>, the scene
  848. * is automatically added to the mtapplication.
  849. *
  850. * @param newScene the new scene
  851. */
  852. public synchronized boolean changeScene(Iscene newScene){
  853. if (!this.sceneList.contains(newScene)){
  854. this.addScene(newScene);
  855. }
  856. return this.initiateSceneChange(this.getCurrentScene(), newScene);
  857. }
  858. /**
  859. * Checks which cursors are active during the scene change and
  860. * sends input_ended events of the active cursors to last scene's global input processors
  861. * so actions in the last scene can be completed correctly.
  862. * This means that one cursor can have more than one input_ended and input_started event
  863. * in its event list!
  864. *
  865. * @param lastScene the last scene
  866. * @param newScene the new scene
  867. */
  868. private void sendEndedEvents(Iscene lastScene){
  869. logger.debug("Sending INPUT_ENDED events to the last scene, Active motions: " + ActiveCursorPool.getInstance().getActiveCursorCount());
  870. InputCursor[] activeCursors = ActiveCursorPool.getInstance().getActiveCursors();
  871. for (InputCursor inputCursor : activeCursors) {
  872. if (inputCursor.getCurrentEvent() != null) {
  873. AbstractCursorInputEvt lastEvt = inputCursor.getCurrentEvent();
  874. if (lastEvt.getId() != AbstractCursorInputEvt.INPUT_ENDED) {
  875. try {
  876. AbstractCursorInputEvt endedEvt = (AbstractCursorInputEvt) lastEvt.clone();
  877. endedEvt.setId(AbstractCursorInputEvt.INPUT_ENDED);
  878. endedEvt.onFired();
  879. this.sendEvtToSceneProcessors(lastScene, endedEvt);
  880. logger.debug("Sending INPUT_ENDED evt to scene: " + lastScene.getName() + " Cursor: " + endedEvt.getCursor());
  881. } catch (CloneNotSupportedException e) {
  882. e.printStackTrace();
  883. }
  884. }
  885. }
  886. }
  887. }
  888. /**
  889. * Checks which cursors are active during the scene change and
  890. * sends input_started to the new scene's global input processors so actions in the
  891. * last scene can be completed correctly.
  892. * This means that one cursor can have more than one input_ended and input_started event
  893. * in its event list!
  894. *
  895. * @param lastScene the last scene
  896. * @param newScene the new scene
  897. */
  898. private void sendStartedEvents(Iscene newScene){
  899. logger.debug("Sending INPUT_DETECTED events to the new scene, Active motions: " + ActiveCursorPool.getInstance().getActiveCursorCount());
  900. InputCursor[] activeCursors = ActiveCursorPool.getInstance().getActiveCursors();
  901. for (InputCursor inputCursor : activeCursors) {
  902. if (inputCursor.getCurrentEvent() != null) {
  903. //PROBLEM: if in lastscene last event in cursor was input_started enqueued
  904. //but not added to cursor yet,
  905. //shall we send it again in new scene? -> will input_started be sent twice?
  906. //- what if input started was enqueued during transition and not sent to any scene
  907. AbstractCursorInputEvt lastEvt = inputCursor.getCurrentEvent();
  908. /*
  909. if (//lastEvt.getId() != AbstractCursorInputEvt.INPUT_DETECTED
  910. true
  911. ){
  912. */
  913. try {
  914. AbstractCursorInputEvt startedEvt = (AbstractCursorInputEvt) lastEvt.clone();
  915. startedEvt.setId(AbstractCursorInputEvt.INPUT_STARTED);
  916. startedEvt.onFired();
  917. this.sendEvtToSceneProcessors(newScene, startedEvt);
  918. logger.debug("Sending INPUT_DETECTED evt to scene: " + newScene.getName() + " Cursor: " + startedEvt.getCursor());
  919. } catch (CloneNotSupportedException e) {
  920. e.printStackTrace();
  921. }
  922. // }
  923. }
  924. }
  925. }
  926. /**
  927. * Send evt to scene processors.
  928. *
  929. * @param scene the scene
  930. * @param evtToFire the evt to fire
  931. */
  932. private void sendEvtToSceneProcessors(Iscene scene, AbstractCursorInputEvt evtToFire){
  933. AbstractGlobalInputProcessor[] sceneInputProcessors = this.getInputManager().getGlobalInputProcessors(scene);
  934. for (AbstractGlobalInputProcessor a : sceneInputProcessors) {
  935. //Hack, because processInputEvt() is disabled at this moment! -> not anymore..
  936. // a.processInputEvtImpl(evtToFire);
  937. a.processInputEvent(evtToFire);
  938. }
  939. }
  940. /**
  941. * Gets the currently active scene.
  942. *
  943. * @return the current scene
  944. */
  945. public Iscene getCurrentScene(){
  946. return currentScene;
  947. }
  948. /*
  949. public void drawCurrentScene(){
  950. getCurrentScene().draw();
  951. }
  952. public void updateCurrentScene(long timeDelta){
  953. getCurrentScene().update(timeDelta);
  954. }
  955. */
  956. /**
  957. * Adds the scene to the list of scenes.
  958. * Also changes to that scene if it is the first one to be added.
  959. *
  960. * @param scene the scene
  961. */
  962. public void addScene(Iscene scene){
  963. if (this.getSceneCount() == 0){
  964. scene.onEnter();
  965. this.currentScene = scene;
  966. this.getInputManager().enableGlobalInputProcessors(scene);
  967. this.fireSceneChangeEvent(new SceneChangeEvent(this, this.currentScene, this.currentScene));
  968. }
  969. if (!sceneList.contains(scene))
  970. sceneList.add(scene);
  971. }
  972. /**
  973. * Adds all scenes.
  974. *
  975. * @param scenes the scenes
  976. */
  977. public void addAll(Iscene[] scenes){
  978. // if (this.getSceneCount() == 0 && scenes[0] != null){
  979. // this.currentScene = scenes[0];
  980. // }
  981. for (Iscene scene : scenes) {
  982. // sceneList.add(scene);
  983. this.addScene(scene);
  984. }
  985. }
  986. /**
  987. * Removes the scene from the list of scenes. Fails if the scene is the currently active scene.
  988. * If the scene isnt going to be used anymore, calling the scene's destroy() method is the better choice
  989. * than the removeScene method alone.
  990. *
  991. * @param scene the scene
  992. */
  993. public boolean removeScene(Iscene scene){
  994. if (sceneList.contains(scene)){
  995. if (scene.equals(this.currentScene)){
  996. logger.warn("Cant remove the scene if it is the currently active scene! (" + scene + ")");
  997. return false;
  998. }else{
  999. sceneList.remove(scene);
  1000. return true;
  1001. }
  1002. }
  1003. else{
  1004. return false;
  1005. }
  1006. // return true;
  1007. }
  1008. /**
  1009. * Destroy scene after transition. Workaround so that if a scene's destroy() method is called
  1010. * but the scene is in a transition (cant be removed then) we call destroy on the scene after
  1011. * the transition.
  1012. * Only has an impact if there is a pending transition with the specified scene as the last scene.
  1013. *
  1014. * @param scene the scene
  1015. */
  1016. public void destroySceneAfterTransition(Iscene scene){
  1017. if (pendingTransition != null && pendingTransition.lastScene.equals(scene)){
  1018. pendingTransition.destroyLastSceneAfterTransition = true;
  1019. }
  1020. }
  1021. /**
  1022. * Gets the registered scenes.
  1023. *
  1024. * @return the scenes
  1025. */
  1026. public Iscene[] getScenes(){
  1027. return sceneList.toArray(new Iscene[sceneList.size()]);
  1028. }
  1029. /**
  1030. * Gets the scene by name.
  1031. *
  1032. * @param name the name
  1033. *
  1034. * @return the scene
  1035. */
  1036. public Iscene getScene(String name){
  1037. Iscene returnScene = null;
  1038. for(Iscene scene : sceneList){
  1039. if (scene.getName().equals(name))
  1040. returnScene = scene;
  1041. }
  1042. return returnScene;
  1043. }
  1044. /**
  1045. * Gets the scene count.
  1046. *
  1047. * @return the scene count
  1048. */
  1049. public int getSceneCount(){
  1050. return sceneList.size();
  1051. }
  1052. /**
  1053. * Gets the input manager.
  1054. *
  1055. * @return the input manager
  1056. */
  1057. public InputManager getInputManager() {
  1058. return inputManager;
  1059. }
  1060. /**
  1061. * Sets the input manager.
  1062. *
  1063. * @param inputManager the new input manager
  1064. */
  1065. public void setInputManager(InputManager inputManager) {
  1066. this.inputManager = inputManager;
  1067. }
  1068. /////////////////////////
  1069. /**
  1070. * Fire scene change event.
  1071. *
  1072. * @param sc the sc
  1073. */
  1074. protected void fireSceneChangeEvent(SceneChangeEvent sc) {
  1075. for (ISceneChangeListener listener : sceneChangedListeners){
  1076. listener.processSceneChangeEvent(sc);
  1077. }
  1078. }
  1079. /**
  1080. * Adds a scene change listener.
  1081. *
  1082. * @param listener the listener
  1083. */
  1084. public synchronized void addSceneChangeListener(ISceneChangeListener listener){
  1085. if (!this.sceneChangedListeners.contains(listener)){
  1086. sceneChangedListeners.add(listener);
  1087. }
  1088. }
  1089. /**
  1090. * Removes the scene change listener.
  1091. *
  1092. * @param listener the listener
  1093. */
  1094. public synchronized void removeSceneChangeListener(ISceneChangeListener listener){
  1095. if (sceneChangedListeners.contains(listener)){
  1096. sceneChangedListeners.remove(listener);
  1097. }
  1098. }
  1099. /**
  1100. * Gets the scene change listeners.
  1101. *
  1102. * @return the scene change listeners
  1103. */
  1104. public synchronized ISceneChangeListener[] getSceneChangeListener(){
  1105. return sceneChangedListeners.toArray(new ISceneChangeListener[this.sceneChangedListeners.size()]);
  1106. }
  1107. /////////////////////////////////
  1108. /**
  1109. * Gets the class name.
  1110. *
  1111. * @author C.Ruff
  1112. */
  1113. public static class CurrentClassGetter extends SecurityManager {
  1114. /**
  1115. * Gets the class name.
  1116. *
  1117. * @return the class name
  1118. */
  1119. public String getClassName() {
  1120. return getClassContext()[2].getName(); //FIXME is this reliable to always work?
  1121. }
  1122. }
  1123. public CSSStyleManager getCssStyleManager() {
  1124. return this.cssStyleManager;
  1125. }
  1126. }