You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@pivot.apache.org by Anna Karina Nava Soriano <an...@gmail.com> on 2010/07/09 23:15:06 UTC

pivot application not painting at loading

Hello everyone! I am newbie on Pivot.

I'm trying to migrate a complex web application to Java.  Pivot in my best choice due to its strong relation to Java and I'm currently reaching all the functionality :).

Due to complexity issues, related to some different applications I am going to need different applets interacting. I had already reached that goal.   And right now for testing some particular concept I only have the following two:
1. A Pivot application. Currently a simple one with only one single button (then it will be substituted by my real app)
2. a Loader from LWJGL (http://www.lwjgl.org/applet/) I'm using this and not some loader in pivot because it already meets all my needs.   This loader is indeed an applet that will load a second applet (in my case that one generating my pivot application) and it does it when it was done with jar downloading.

The thing is that all this is working but with one simple failure: when the loader (2) had done and switch to the applet (1) (which will indeed load the pivot application) it actually load it but it is not painting it, I know this be cause if I force repainting (resizing the browser window reaching the zone where the applet is) then the pivot application shows up normally.  And before resizing I got all the debug messages (in my java console) that I am printing to know if the startup method is being called entirely and it is!.  So the only problem is the first painting. 

I have looked into the Pivot's API and also googled to to find something that will help me to force painting at the end of the startup method but I had only found paint and repaint methods and used it with no results. Can anyone tell me if I am doing something wrong? I also look for something relative to this inside this mailing list and some issues on repainting and the only similar was fixed with "Be sure to wrap your TaskListener in a TaskAdapter. TaskListeners are called on the background thread. TaskAdapters ensure that they are called on the UI thread." The thing is that I am not taking care of any TaskListener,  should I? may be a simple example will be useful for me. 

CODE: my loader (1) has init, start, run, stop and destroy methods (among others) those important are:
______________________________________
AppletLoader :
______________________________________
	public void start() {
		if (secondApplet != null) {
			secondApplet.start();
		}
		else {
			if(loaderThread == null && !fatalError) {
				loaderThread = new Thread(this);
				loaderThread.setName("AppletLoader.loaderThread");
				loaderThread.start();

				animationThread = new Thread() {
					public void run() {
						while(loaderThread != null) {
							repaint();
							AppletLoader.this.sleep(100);
						}
                        repaint();
						animationThread = null;
					}
				};
				animationThread.setName("AppletLoader.animationthread");
				animationThread.start();
			}
		}
	}
        
	public void run() {
		state = STATE_CHECKING_CACHE;
 		...
		try {
.			....
			// if jars not available or need updating download them
			if (!cacheAvailable) {
				// downloads jars from the server
				downloadJars(path);		// 10-55%

				// Extract Pack and LZMA files
				extractJars(path);		// 55-65%

				// Extracts Native Files
				//extractNatives(path);	// 65-85%

				// add version information once jars downloaded successfully
				if (version != null) {
					percentage = 90;
					writeVersionFile(dir, latestVersion);
				}
			}

			// add the downloaded jars and natives to classpath
			updateClassPath(path);

			// switch to secondApplet
			switchApplet();

			state = STATE_DONE;

		} catch (AccessControlException ace) {
			.......
		}
	}
	
	protected void switchApplet() throws Exception {

		state = STATE_SWITCHING_APPLET;
		percentage = 100;

		debug_sleep(2000);

		Class appletClass = classLoader.loadClass(getParameter("al_main"));
		secondApplet = (Applet) appletClass.newInstance();

		secondApplet.setStub(this);
		secondApplet.setSize(getWidth(), getHeight());

		setLayout(new BorderLayout());
		add(secondApplet);
		validate();

		state = STATE_INITIALIZE_REAL_APPLET;
		secondApplet.init();

		state = STATE_START_REAL_APPLET;
		secondApplet.start();
	}

______________________________________
Pivot application:
______________________________________
@Override
    public void startup(Display disp, Map<String, String> properties)
    {
        
        label = new Label();
        label.setText("Welcome to the simple application!");
        label.getStyles().put("font", new Font("Arial", Font.BOLD, 20));
        label.getStyles().put("color", Color.DARK_GRAY);
        label.getStyles().put("horizontalAlignment", HorizontalAlignment.CENTER);
        label.getStyles().put("verticalAlignment", VerticalAlignment.CENTER);

        pushButton = new PushButton("Call to the BIG application!");
        pushButton.getButtonPressListeners().add(new ButtonPressListener()
        {
            @Override
            public void buttonPressed(Button button)
            {
                System.out.println("button pressed");
                org.apache.pivot.wtk.BrowserApplicationContext.eval("callingJavaScript();", main.this);
            }
        });

        boxPane = new BoxPane();
        boxPane.add(pushButton);
        boxPane.add(label);
        //setting box style:
        boxPane.getStyles().put("padding", 20);
        boxPane.getStyles().put("horizontalAlignment", "center");
        boxPane.getStyles().put("verticalAlignment", "center");
        boxPane.getStyles().put("backgroundColor", Color.GRAY);

        window = new Window();
        window.setContent(boxPane);
        window.getStyles().put("backgroundColor", Color.GRAY);
        window.setTitle("Hello World!");
        window.setMaximized(true);
        window.open(disp);
	//MY CODE ENDED IN THE PREVIOUS LINE  and the followings are all the attempts to repaint:
//        window.paint(window.getGraphics());
//        boxPane.paint(window.getGraphics());
//        boxPane.paint(boxPane.getGraphics());
//        window.paint(window.getGraphics());
//        window.requestFocus();
//        window.repaint();
//        disp.repaint();
//        disp.requestFocus();
//        disp.paint(disp.getGraphics());
//        window.paint(disp.getGraphics());
    }


IMPORTANT: I am not using WTKX, only java to define all objects for this case I rather in this way.  Obviously this pivot application when running alone is working so do the Loader (loading a simple applet, not pivot, it works)
Is there something related with the UI thread? 
Am I doing something wrong? 
Is a mistake omitting WTKX definition? 
Is there a way for me to know what is the method that is called when I resize and the second applet is repainted ONLY when the browser's window border reaches the zone when the second applet supposed to be.
Probably my computer? I'm running on an IMac (Intel-based) Mac OS X (10.6.3) and this thing happens in Firefox 3.6.6 and the same with Safari 4.0.5 both with the java console running (enabled) since applet start

It will be pretty helpful to me to know what is happening here, what is the difference in my repaint and that done when resizing.

Thanks in advance! I I really want to use Pivot in my project and if this is not possible, may me I am going to need to develop some loader in pivot with the same functionality but may be loading the other applets I will get the same problem.

Thanks all of you, any piece of advise will be helpful!

Re: pivot application not painting at loading

Posted by Greg Brown <gk...@mac.com>.
> Is there a way for me to extend BrowserApplicationContext.HostApplet? and override its methods? and in that way could use it as my current loader  adding its functionality?

It may be OK to make HostApplet non-final. Do you have the Pivot source code? You could prototype this change and see it it works.

> just another question... I don't know why the repaint method is not working, what will be the correct way of using it?

>>> //        window.paint(window.getGraphics());

As an app developer, you shouldn't often need to call paint() directly. The system ensures that paint() is called for you when you call repaint(), which marks a region of the screen for update. Multiple repaint() calls may be consolidated - however, this is all handled by AWT. Pivot just hooks into the Java2D paint system. If you wanted to draw a component (or some other Pivot visual) to a background image or something similar, you might call paint() yourself, but otherwise you should not need to do it.

Again, I suspect that the reason your paints are not working has something to do with the fact that you are loading the Pivot applet from within another applet. Unfortunately, applets are pretty finicky as it is - this just adds another variable, and I have no idea what the other applet might be doing under the hood.


Re: pivot application not painting at loading

Posted by Anna Karina Nava Soriano <an...@gmail.com>.
Thanks for your response!

I would launch my Pivot application via BrowserApplicationContext.HostApplet if it wasn't final, be cause I need to override some loading features, like progress bar, messages about the jars currently loading (and showing percentage), the logo, adding jars to the classpath, and the most important feature is that I need the launcher to load the Pivot application when its particular jars are downloaded and also still be running in background loading the rest of the jars needed for some other functionalities and manage those jars in some kind of queue that will add at the firs position those jars when the user require some particular functionality (showing a progress bar again) and with a callback return to the main pivot application or another pivot application in the same html ( if required ). 

Is there a way for me to extend BrowserApplicationContext.HostApplet? and override its methods? and in that way could use it as my current loader  adding its functionality?

I have seen that there is a way to perform background tasks in pivot,  so I could reach my current loader functionality if only I could extend  BrowserApplicationContext.HostApplet. Some hint with this?

just another question... I don't know why the repaint method is not working, what will be the correct way of using it?
>> //        window.paint(window.getGraphics());
>> //        boxPane.paint(window.getGraphics());
>> //        boxPane.paint(boxPane.getGraphics());
>> //        window.paint(window.getGraphics());
>> //        window.requestFocus();
>> //        window.repaint();
>> //        disp.repaint();
>> //        disp.requestFocus();
>> //        disp.paint(disp.getGraphics());
>> //        window.paint(disp.getGraphics());


Thank you very much for your help.

On Jul 10, 2010, at 1:37 AM, Greg Brown wrote:

> Hi Anna,
> 
> I'm not sure what the problem might be, but it is probably not related to TaskAdapter or the fact that you are not using WTKX. I'd guess it has something to do with loading the Pivot applet from within the LWJGL applet. This isn't something we have tested for or plan to support.
> 
> Is there any reason you can't simply launch your Pivot application via BrowserApplicationContext.HostApplet? Your Pivot application should still be able to communicate with other applets on the page that way.
> 
> Greg
> 
> 
> On Jul 9, 2010, at 5:15 PM, Anna Karina Nava Soriano wrote:
> 
>> Hello everyone! I am newbie on Pivot.
>> 
>> I'm trying to migrate a complex web application to Java.  Pivot in my best choice due to its strong relation to Java and I'm currently reaching all the functionality :).
>> 
>> Due to complexity issues, related to some different applications I am going to need different applets interacting. I had already reached that goal.   And right now for testing some particular concept I only have the following two:
>> 1. A Pivot application. Currently a simple one with only one single button (then it will be substituted by my real app)
>> 2. a Loader from LWJGL (http://www.lwjgl.org/applet/) I'm using this and not some loader in pivot because it already meets all my needs.   This loader is indeed an applet that will load a second applet (in my case that one generating my pivot application) and it does it when it was done with jar downloading.
>> 
>> The thing is that all this is working but with one simple failure: when the loader (2) had done and switch to the applet (1) (which will indeed load the pivot application) it actually load it but it is not painting it, I know this be cause if I force repainting (resizing the browser window reaching the zone where the applet is) then the pivot application shows up normally.  And before resizing I got all the debug messages (in my java console) that I am printing to know if the startup method is being called entirely and it is!.  So the only problem is the first painting. 
>> 
>> I have looked into the Pivot's API and also googled to to find something that will help me to force painting at the end of the startup method but I had only found paint and repaint methods and used it with no results. Can anyone tell me if I am doing something wrong? I also look for something relative to this inside this mailing list and some issues on repainting and the only similar was fixed with "Be sure to wrap your TaskListener in a TaskAdapter. TaskListeners are called on the background thread. TaskAdapters ensure that they are called on the UI thread." The thing is that I am not taking care of any TaskListener,  should I? may be a simple example will be useful for me. 
>> 
>> CODE: my loader (1) has init, start, run, stop and destroy methods (among others) those important are:
>> ______________________________________
>> AppletLoader :
>> ______________________________________
>> 	public void start() {
>> 		if (secondApplet != null) {
>> 			secondApplet.start();
>> 		}
>> 		else {
>> 			if(loaderThread == null && !fatalError) {
>> 				loaderThread = new Thread(this);
>> 				loaderThread.setName("AppletLoader.loaderThread");
>> 				loaderThread.start();
>> 
>> 				animationThread = new Thread() {
>> 					public void run() {
>> 						while(loaderThread != null) {
>> 							repaint();
>> 							AppletLoader.this.sleep(100);
>> 						}
>>                         repaint();
>> 						animationThread = null;
>> 					}
>> 				};
>> 				animationThread.setName("AppletLoader.animationthread");
>> 				animationThread.start();
>> 			}
>> 		}
>> 	}
>>         
>> 	public void run() {
>> 		state = STATE_CHECKING_CACHE;
>>  		...
>> 		try {
>> .			....
>> 			// if jars not available or need updating download them
>> 			if (!cacheAvailable) {
>> 				// downloads jars from the server
>> 				downloadJars(path);		// 10-55%
>> 
>> 				// Extract Pack and LZMA files
>> 				extractJars(path);		// 55-65%
>> 
>> 				// Extracts Native Files
>> 				//extractNatives(path);	// 65-85%
>> 
>> 				// add version information once jars downloaded successfully
>> 				if (version != null) {
>> 					percentage = 90;
>> 					writeVersionFile(dir, latestVersion);
>> 				}
>> 			}
>> 
>> 			// add the downloaded jars and natives to classpath
>> 			updateClassPath(path);
>> 
>> 			// switch to secondApplet
>> 			switchApplet();
>> 
>> 			state = STATE_DONE;
>> 
>> 		} catch (AccessControlException ace) {
>> 			.......
>> 		}
>> 	}
>> 	
>> 	protected void switchApplet() throws Exception {
>> 
>> 		state = STATE_SWITCHING_APPLET;
>> 		percentage = 100;
>> 
>> 		debug_sleep(2000);
>> 
>> 		Class appletClass = classLoader.loadClass(getParameter("al_main"));
>> 		secondApplet = (Applet) appletClass.newInstance();
>> 
>> 		secondApplet.setStub(this);
>> 		secondApplet.setSize(getWidth(), getHeight());
>> 
>> 		setLayout(new BorderLayout());
>> 		add(secondApplet);
>> 		validate();
>> 
>> 		state = STATE_INITIALIZE_REAL_APPLET;
>> 		secondApplet.init();
>> 
>> 		state = STATE_START_REAL_APPLET;
>> 		secondApplet.start();
>> 	}
>> 
>> ______________________________________
>> Pivot application:
>> ______________________________________
>> @Override
>>     public void startup(Display disp, Map<String, String> properties)
>>     {
>>         
>>         label = new Label();
>>         label.setText("Welcome to the simple application!");
>>         label.getStyles().put("font", new Font("Arial", Font.BOLD, 20));
>>         label.getStyles().put("color", Color.DARK_GRAY);
>>         label.getStyles().put("horizontalAlignment", HorizontalAlignment.CENTER);
>>         label.getStyles().put("verticalAlignment", VerticalAlignment.CENTER);
>> 
>>         pushButton = new PushButton("Call to the BIG application!");
>>         pushButton.getButtonPressListeners().add(new ButtonPressListener()
>>         {
>>             @Override
>>             public void buttonPressed(Button button)
>>             {
>>                 System.out.println("button pressed");
>>                 org.apache.pivot.wtk.BrowserApplicationContext.eval("callingJavaScript();", main.this);
>>             }
>>         });
>> 
>>         boxPane = new BoxPane();
>>         boxPane.add(pushButton);
>>         boxPane.add(label);
>>         //setting box style:
>>         boxPane.getStyles().put("padding", 20);
>>         boxPane.getStyles().put("horizontalAlignment", "center");
>>         boxPane.getStyles().put("verticalAlignment", "center");
>>         boxPane.getStyles().put("backgroundColor", Color.GRAY);
>> 
>>         window = new Window();
>>         window.setContent(boxPane);
>>         window.getStyles().put("backgroundColor", Color.GRAY);
>>         window.setTitle("Hello World!");
>>         window.setMaximized(true);
>>         window.open(disp);
>> 	//MY CODE ENDED IN THE PREVIOUS LINE  and the followings are all the attempts to repaint:
>> //        window.paint(window.getGraphics());
>> //        boxPane.paint(window.getGraphics());
>> //        boxPane.paint(boxPane.getGraphics());
>> //        window.paint(window.getGraphics());
>> //        window.requestFocus();
>> //        window.repaint();
>> //        disp.repaint();
>> //        disp.requestFocus();
>> //        disp.paint(disp.getGraphics());
>> //        window.paint(disp.getGraphics());
>>     }
>> 
>> 
>> IMPORTANT: I am not using WTKX, only java to define all objects for this case I rather in this way.  Obviously this pivot application when running alone is working so do the Loader (loading a simple applet, not pivot, it works)
>> Is there something related with the UI thread? 
>> Am I doing something wrong? 
>> Is a mistake omitting WTKX definition? 
>> Is there a way for me to know what is the method that is called when I resize and the second applet is repainted ONLY when the browser's window border reaches the zone when the second applet supposed to be.
>> Probably my computer? I'm running on an IMac (Intel-based) Mac OS X (10.6.3) and this thing happens in Firefox 3.6.6 and the same with Safari 4.0.5 both with the java console running (enabled) since applet start
>> 
>> It will be pretty helpful to me to know what is happening here, what is the difference in my repaint and that done when resizing.
>> 
>> Thanks in advance! I I really want to use Pivot in my project and if this is not possible, may me I am going to need to develop some loader in pivot with the same functionality but may be loading the other applets I will get the same problem.
>> 
>> Thanks all of you, any piece of advise will be helpful!
> 


Re: pivot application not painting at loading

Posted by Greg Brown <gk...@mac.com>.
Hi Anna,

I'm not sure what the problem might be, but it is probably not related to TaskAdapter or the fact that you are not using WTKX. I'd guess it has something to do with loading the Pivot applet from within the LWJGL applet. This isn't something we have tested for or plan to support.

Is there any reason you can't simply launch your Pivot application via BrowserApplicationContext.HostApplet? Your Pivot application should still be able to communicate with other applets on the page that way.

Greg


On Jul 9, 2010, at 5:15 PM, Anna Karina Nava Soriano wrote:

> Hello everyone! I am newbie on Pivot.
> 
> I'm trying to migrate a complex web application to Java.  Pivot in my best choice due to its strong relation to Java and I'm currently reaching all the functionality :).
> 
> Due to complexity issues, related to some different applications I am going to need different applets interacting. I had already reached that goal.   And right now for testing some particular concept I only have the following two:
> 1. A Pivot application. Currently a simple one with only one single button (then it will be substituted by my real app)
> 2. a Loader from LWJGL (http://www.lwjgl.org/applet/) I'm using this and not some loader in pivot because it already meets all my needs.   This loader is indeed an applet that will load a second applet (in my case that one generating my pivot application) and it does it when it was done with jar downloading.
> 
> The thing is that all this is working but with one simple failure: when the loader (2) had done and switch to the applet (1) (which will indeed load the pivot application) it actually load it but it is not painting it, I know this be cause if I force repainting (resizing the browser window reaching the zone where the applet is) then the pivot application shows up normally.  And before resizing I got all the debug messages (in my java console) that I am printing to know if the startup method is being called entirely and it is!.  So the only problem is the first painting. 
> 
> I have looked into the Pivot's API and also googled to to find something that will help me to force painting at the end of the startup method but I had only found paint and repaint methods and used it with no results. Can anyone tell me if I am doing something wrong? I also look for something relative to this inside this mailing list and some issues on repainting and the only similar was fixed with "Be sure to wrap your TaskListener in a TaskAdapter. TaskListeners are called on the background thread. TaskAdapters ensure that they are called on the UI thread." The thing is that I am not taking care of any TaskListener,  should I? may be a simple example will be useful for me. 
> 
> CODE: my loader (1) has init, start, run, stop and destroy methods (among others) those important are:
> ______________________________________
> AppletLoader :
> ______________________________________
> 	public void start() {
> 		if (secondApplet != null) {
> 			secondApplet.start();
> 		}
> 		else {
> 			if(loaderThread == null && !fatalError) {
> 				loaderThread = new Thread(this);
> 				loaderThread.setName("AppletLoader.loaderThread");
> 				loaderThread.start();
> 
> 				animationThread = new Thread() {
> 					public void run() {
> 						while(loaderThread != null) {
> 							repaint();
> 							AppletLoader.this.sleep(100);
> 						}
>                         repaint();
> 						animationThread = null;
> 					}
> 				};
> 				animationThread.setName("AppletLoader.animationthread");
> 				animationThread.start();
> 			}
> 		}
> 	}
>         
> 	public void run() {
> 		state = STATE_CHECKING_CACHE;
>  		...
> 		try {
> .			....
> 			// if jars not available or need updating download them
> 			if (!cacheAvailable) {
> 				// downloads jars from the server
> 				downloadJars(path);		// 10-55%
> 
> 				// Extract Pack and LZMA files
> 				extractJars(path);		// 55-65%
> 
> 				// Extracts Native Files
> 				//extractNatives(path);	// 65-85%
> 
> 				// add version information once jars downloaded successfully
> 				if (version != null) {
> 					percentage = 90;
> 					writeVersionFile(dir, latestVersion);
> 				}
> 			}
> 
> 			// add the downloaded jars and natives to classpath
> 			updateClassPath(path);
> 
> 			// switch to secondApplet
> 			switchApplet();
> 
> 			state = STATE_DONE;
> 
> 		} catch (AccessControlException ace) {
> 			.......
> 		}
> 	}
> 	
> 	protected void switchApplet() throws Exception {
> 
> 		state = STATE_SWITCHING_APPLET;
> 		percentage = 100;
> 
> 		debug_sleep(2000);
> 
> 		Class appletClass = classLoader.loadClass(getParameter("al_main"));
> 		secondApplet = (Applet) appletClass.newInstance();
> 
> 		secondApplet.setStub(this);
> 		secondApplet.setSize(getWidth(), getHeight());
> 
> 		setLayout(new BorderLayout());
> 		add(secondApplet);
> 		validate();
> 
> 		state = STATE_INITIALIZE_REAL_APPLET;
> 		secondApplet.init();
> 
> 		state = STATE_START_REAL_APPLET;
> 		secondApplet.start();
> 	}
> 
> ______________________________________
> Pivot application:
> ______________________________________
> @Override
>     public void startup(Display disp, Map<String, String> properties)
>     {
>         
>         label = new Label();
>         label.setText("Welcome to the simple application!");
>         label.getStyles().put("font", new Font("Arial", Font.BOLD, 20));
>         label.getStyles().put("color", Color.DARK_GRAY);
>         label.getStyles().put("horizontalAlignment", HorizontalAlignment.CENTER);
>         label.getStyles().put("verticalAlignment", VerticalAlignment.CENTER);
> 
>         pushButton = new PushButton("Call to the BIG application!");
>         pushButton.getButtonPressListeners().add(new ButtonPressListener()
>         {
>             @Override
>             public void buttonPressed(Button button)
>             {
>                 System.out.println("button pressed");
>                 org.apache.pivot.wtk.BrowserApplicationContext.eval("callingJavaScript();", main.this);
>             }
>         });
> 
>         boxPane = new BoxPane();
>         boxPane.add(pushButton);
>         boxPane.add(label);
>         //setting box style:
>         boxPane.getStyles().put("padding", 20);
>         boxPane.getStyles().put("horizontalAlignment", "center");
>         boxPane.getStyles().put("verticalAlignment", "center");
>         boxPane.getStyles().put("backgroundColor", Color.GRAY);
> 
>         window = new Window();
>         window.setContent(boxPane);
>         window.getStyles().put("backgroundColor", Color.GRAY);
>         window.setTitle("Hello World!");
>         window.setMaximized(true);
>         window.open(disp);
> 	//MY CODE ENDED IN THE PREVIOUS LINE  and the followings are all the attempts to repaint:
> //        window.paint(window.getGraphics());
> //        boxPane.paint(window.getGraphics());
> //        boxPane.paint(boxPane.getGraphics());
> //        window.paint(window.getGraphics());
> //        window.requestFocus();
> //        window.repaint();
> //        disp.repaint();
> //        disp.requestFocus();
> //        disp.paint(disp.getGraphics());
> //        window.paint(disp.getGraphics());
>     }
> 
> 
> IMPORTANT: I am not using WTKX, only java to define all objects for this case I rather in this way.  Obviously this pivot application when running alone is working so do the Loader (loading a simple applet, not pivot, it works)
> Is there something related with the UI thread? 
> Am I doing something wrong? 
> Is a mistake omitting WTKX definition? 
> Is there a way for me to know what is the method that is called when I resize and the second applet is repainted ONLY when the browser's window border reaches the zone when the second applet supposed to be.
> Probably my computer? I'm running on an IMac (Intel-based) Mac OS X (10.6.3) and this thing happens in Firefox 3.6.6 and the same with Safari 4.0.5 both with the java console running (enabled) since applet start
> 
> It will be pretty helpful to me to know what is happening here, what is the difference in my repaint and that done when resizing.
> 
> Thanks in advance! I I really want to use Pivot in my project and if this is not possible, may me I am going to need to develop some loader in pivot with the same functionality but may be loading the other applets I will get the same problem.
> 
> Thanks all of you, any piece of advise will be helpful!