/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2014 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.server.ngclient;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeoutException;
import org.json.JSONObject;
import org.sablo.eventthread.IEventDispatcher;
import org.sablo.websocket.CurrentWindow;
import com.servoy.j2db.IBasicFormManager.History;
import com.servoy.j2db.IBasicMainContainer;
import com.servoy.j2db.IFormController;
import com.servoy.j2db.dataprocessing.IDataServer;
import com.servoy.j2db.dataprocessing.PrototypeState;
import com.servoy.j2db.dataprocessing.TagResolver;
import com.servoy.j2db.persistence.Form;
import com.servoy.j2db.persistence.Solution;
import com.servoy.j2db.scripting.JSWindow;
import com.servoy.j2db.scripting.RuntimeWindow;
import com.servoy.j2db.server.ngclient.component.WebFormController;
import com.servoy.j2db.server.shared.IPerfomanceRegistry;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.Pair;
import com.servoy.j2db.util.Text;
import com.servoy.j2db.util.UUID;
import com.servoy.j2db.util.Utils;
/**
* @author jcompagner
*
*/
public class NGRuntimeWindow extends RuntimeWindow implements IBasicMainContainer
{
private final History history;
private int x = -1;
private int y = -1;
private int width = -1;
private int height = -1;
private boolean visible;
private String formName;
private Integer navigatorID = null;
/**
* @param application
* @param windowName
* @param windowType
* @param parentWindow
*/
protected NGRuntimeWindow(INGApplication application, String windowName, int windowType, RuntimeWindow parentWindow)
{
super(application, windowName, windowType, parentWindow);
this.history = new History(application, this);
}
@Override
public INGApplication getApplication()
{
return (INGApplication)super.getApplication();
}
@Override
public String getContainerName()
{
return getName();
}
@Override
public IWebFormController getController()
{
if (formName == null) return null;
return getApplication().getFormManager().getForm(formName);
}
public IWebFormController getNavigator()
{
if (navigatorID != null && navigatorID > 0)
{
Form navigatorForm = getApplication().getFlattenedSolution().getForm(navigatorID);
navigatorID = null;
if (navigatorForm != null)
{
return getApplication().getFormManager().getForm(navigatorForm.getName());
}
}
return null;
}
public void setNavigator(Integer navigatorID)
{
this.navigatorID = navigatorID;
}
@Override
public void setController(IFormController form)
{
if (form != null)
{
this.formName = form.getName();
switchForm((WebFormController)form);
}
else
{
this.formName = null;
}
}
@Override
public History getHistory()
{
return history;
}
@Override
public void resetBounds()
{
this.storeBounds = false;
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("resetBounds",
new Object[] { this.getName() });
}
@Override
public void setLocation(int x, int y)
{
if (this.x != x || this.y != y)
{
this.x = x;
this.y = y;
Map<String, Integer> location = new HashMap<>();
location.put("x", x);
location.put("y", y);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setLocation",
new Object[] { this.getName(), location });
}
}
public void updateLocation(int x, int y)
{
this.x = x;
this.y = y;
}
@Override
public int getX()
{
return x;
}
@Override
public int getY()
{
return y;
}
@Override
public void setSize(int width, int height)
{
if (this.width != width || this.width != width)
{
this.width = width;
this.height = height;
Map<String, Integer> size = new HashMap<>();
size.put("width", width);
size.put("height", height);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setSize",
new Object[] { this.getName(), size });
}
}
public void updateSize(int width, int height)
{
this.width = width;
this.height = height;
}
@Override
public int getWidth()
{
updateSizeIfNeeded();
return width;
}
@Override
public int getHeight()
{
updateSizeIfNeeded();
return height;
}
private void updateSizeIfNeeded()
{
if (width == -1 || height == -1)
{
try
{
Object result = getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeServiceCall("getSize",
new Object[] { this.getName() });
if (result instanceof JSONObject)
{
updateSize(((JSONObject)result).optInt("width", -1), ((JSONObject)result).optInt("height", -1));
}
}
catch (IOException ex)
{
Debug.warn(ex);
}
}
}
@Override
public void setInitialBounds(int x, int y, int width, int height)
{
super.setInitialBounds(x, y, width, height);
Map<String, Integer> initialBounds = new HashMap<>();
initialBounds.put("x", this.initialBounds.x);
initialBounds.put("y", this.initialBounds.y);
initialBounds.put("width", this.initialBounds.width);
initialBounds.put("height", this.initialBounds.height);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setInitialBounds",
new Object[] { this.getName(), initialBounds });
}
@Override
public void setTitle(String title, boolean delayed)
{
super.setTitle(title);
sendTitle(title);
}
@Override
public void setTitle(String title)
{
sendTitle(title);
}
private void sendTitle(String title)
{
String titleString = "";
if (windowType == JSWindow.WINDOW)
{
Solution solution = getApplication().getSolution();
String solutionTitle = solution.getTitleText();
if (solutionTitle == null)
{
titleString = solution.getName();
}
else if (!solutionTitle.equals("<empty>")) //$NON-NLS-1$
{
titleString = solutionTitle;
}
titleString = getApplication().getI18NMessageIfPrefixed(titleString);
if (title != null && !title.trim().equals("") && !"<empty>".equals(title) && title != null) //$NON-NLS-1$ //$NON-NLS-2$
{
String nameString = getApplication().getI18NMessageIfPrefixed(title);
IWebFormController formController = getController();
if (formController != null)
{
String name2 = Text.processTags(nameString, formController.getFormUI().getDataAdapterList());
if (name2 != null) nameString = name2;
}
else
{
String name2 = Text.processTags(nameString, TagResolver.createResolver(new PrototypeState(null)));
if (name2 != null) nameString = name2;
}
if (!nameString.trim().equals("")) //$NON-NLS-1$
{
if ("".equals(titleString)) //$NON-NLS-1$
{
titleString += nameString;
}
else
{
titleString += " - " + nameString; //$NON-NLS-1$
}
}
}
String appName = "Servoy NG Client"; //$NON-NLS-1$
boolean branding = Utils.getAsBoolean(getApplication().getSettings().getProperty("servoy.branding", "false")); //$NON-NLS-1$ //$NON-NLS-2$
String appTitle = getApplication().getSettings().getProperty("servoy.branding.windowtitle"); //$NON-NLS-1$
if (branding && appTitle != null)
{
appName = appTitle;
}
if (titleString.equals("")) //$NON-NLS-1$
{
titleString = appName;
}
else
{
titleString += " - " + appName; //$NON-NLS-1$
}
}
else
{
titleString = getApplication().getI18NMessageIfPrefixed(title);
}
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setTitle",
new Object[] { this.getName(), titleString });
}
@Override
public boolean isVisible()
{
return visible;
}
@Override
public void toFront()
{
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("toFront",
new Object[] { this.getName() });
}
@Override
public void toBack()
{
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("toBack",
new Object[] { this.getName() });
}
@Override
public Object getWrappedObject()
{
return this;
}
@Override
public void setOpacity(float opacity)
{
super.setOpacity(opacity);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setOpacity",
new Object[] { getName(), opacity });
}
@Override
public void setResizable(boolean resizable)
{
super.setResizable(resizable);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setResizable",
new Object[] { getName(), resizable });
}
@Override
public void setUndecorated(boolean undecorated)
{
super.setUndecorated(undecorated);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setUndecorated",
new Object[] { getName(), undecorated });
}
@Override
public void setTransparent(boolean isTransparent)
{
super.setTransparent(isTransparent);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setTransparent",
new Object[] { getName(), isTransparent });
}
@Override
public void hideUI()
{
visible = false;
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("hide",
new Object[] { getName() });
// assume that just after hiding the window, currentController in js is the main window controller
JSWindow parent = getParent();
IFormController formController = null;
if (parent != null)
{
formController = parent.getImpl().getController();
getApplication().getRuntimeWindowManager().setCurrentWindowName(parent.getImpl().getName());
}
else if (getApplication().getRuntimeWindowManager().getMainApplicationWindow() != null)
{
if (getApplication().getRuntimeWindowManager().getMainApplicationWindow() != null)
formController = getApplication().getRuntimeWindowManager().getMainApplicationWindow().getController();
}
if (formController instanceof IWebFormController) getApplication().getFormManager().setCurrentControllerJS((IWebFormController)formController);
// resume
if (windowType == JSWindow.MODAL_DIALOG && getApplication().getWebsocketSession().getEventDispatcher() != null)
{
getApplication().getWebsocketSession().getEventDispatcher().resume(this);
}
}
@Override
public void setStoreBounds(boolean storeBounds)
{
super.setStoreBounds(storeBounds);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("setStoreBounds",
new Object[] { getName(), String.valueOf(storeBounds) });
}
@Override
protected void doOldShow(String formName, boolean closeAll, boolean legacyV3Behavior)
{
String currentWindowName = getApplication().getRuntimeWindowManager().getCurrentWindowName();
IWebFormController controller = getApplication().getFormManager().getForm(formName);
if (controller != null)
{
getApplication().getFormManager().showFormInContainer(formName, this, getTitle(), true, windowName);
this.formName = formName;
controller.getFormUI().setParentWindowName(getName());
//show panel as main
switchForm(controller);
}
String titleArg = getTitle();
titleArg = titleArg == null ? formName : titleArg;
titleArg = getApplication().getI18NMessageIfPrefixed(titleArg);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("show",
new Object[] { getName(), formName, titleArg });
if (windowType == JSWindow.MODAL_DIALOG && getApplication().getWebsocketSession().getEventDispatcher() != null)
{
try
{
try
{
CurrentWindow.get().sendChanges();
}
catch (IOException e)
{
Debug.log(e);
}
IPerfomanceRegistry perfRegistry = null;
try
{
perfRegistry = (getApplication().getApplicationServerAccess() != null
? getApplication().getApplicationServerAccess().getFunctionPerfomanceRegistry() : null);
}
catch (RemoteException e)
{
Debug.error(e);
}
Pair<UUID, UUID> perfId = null;
if (perfRegistry != null && perfRegistry.isEnabled())
perfId = perfRegistry.getPerformanceData(getApplication().getSolutionName()).startSubAction("$windowService.show",
System.currentTimeMillis(), IDataServer.METHOD_CALL_WAITING_FOR_USER_INPUT, getApplication().getClientID());
try
{
getApplication().getWebsocketSession().getEventDispatcher().suspend(this, IEventDispatcher.EVENT_LEVEL_DEFAULT,
IEventDispatcher.NO_TIMEOUT);
}
finally
{
if (perfId != null) perfRegistry.getPerformanceData(getApplication().getSolutionName()).endSubAction(perfId);
}
// this is now a hide of this window, set back the window name just before this show.
getApplication().getRuntimeWindowManager().setCurrentWindowName(currentWindowName);
}
catch (CancellationException e)
{
throw e; // full browser refresh while waiting for modal to close?
}
catch (TimeoutException e)
{
Debug.error("Modal dialog suspend timed out. This should never happen");
}
}
}
private void switchForm(IWebFormController currentForm)
{
visible = true;
// set the parent and current window ,
currentForm.getFormUI().setParentWindowName(getName());
getApplication().getFormManager().getFormAndSetCurrentWindow(formName);
Map<String, Object> mainForm = new HashMap<String, Object>();
Map<String, Integer> size = new HashMap<>();
size.put("width", currentForm.getForm().getSize().width);
size.put("height", currentForm.getForm().getSize().height);
mainForm.put("size", size);
mainForm.put("name", currentForm.getName());
Map<String, Object> navigatorForm = getNavigatorProperties(currentForm);
NGClientWindow.getCurrentWindow().touchForm(currentForm.getForm(), null, true);
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("switchForm",
new Object[] { getName(), mainForm, navigatorForm });
sendTitle(title);
}
/**
* Get the navigator properties based on the navigatorID of the form.
* @param formController
* @return a map which contains navigator properties such as name, size.
*/
private Map<String, Object> getNavigatorProperties(IWebFormController formController)
{
Map<String, Object> navigatorForm = new HashMap<String, Object>();
int navigatorId = formController.getForm().getNavigatorID();
if (formController.getFormUI() instanceof WebListFormUI && navigatorId == Form.NAVIGATOR_DEFAULT)
{
navigatorId = Form.NAVIGATOR_NONE;
}
switch (navigatorId)
{
case Form.NAVIGATOR_NONE :
break;
case Form.NAVIGATOR_DEFAULT :
{
navigatorForm.put("name", "servoycore/navigator/default_navigator_container.html");
Map<String, Integer> navSize = new HashMap<>();
navSize.put("width", 70);
navigatorForm.put("size", navSize);
break;
}
case Form.NAVIGATOR_IGNORE :
{
if (history.getIndex() > 0)
{
String prevForm = history.getFormName(history.getIndex() - 1);
if (prevForm != null)
{
navigatorForm = getApplication().getFormManager().getForm(prevForm).getNavigatorProperties();
}
}
break;
}
default :
{
Form navForm = getApplication().getFlattenedSolution().getForm(navigatorId);
if (navForm != null)
{
getApplication().getFormManager().getForm(navForm.getName()).getFormUI().setParentWindowName(getName());
navigatorForm.put("name", navForm.getName());
Map<String, Integer> navSize = new HashMap<>();
navSize.put("width", navForm.getSize().width);
navSize.put("height", navForm.getSize().height);
navigatorForm.put("size", navSize);
NGClientWindow.getCurrentWindow().touchForm(getApplication().getFlattenedSolution().getFlattenedForm(navForm), null, true);
}
}
}
if (navigatorForm.isEmpty()) // Form.NAVIGATOR_NONE
{
// just make it an empty object.
Map<String, Integer> navSize = new HashMap<>();
navSize.put("width", 0);
navigatorForm.put("size", navSize);
}
formController.setNavigatorProperties(navigatorForm);
return navigatorForm;
}
@Override
public void destroy()
{
super.destroy();
if (visible) hideUI();
IWebFormController controller = getController();
if (controller != null && controller.getFormUI().getParentWindowName() == getName())
{
controller.getFormUI().setParentWindowName(null);
}
getApplication().getWebsocketSession().getClientService(NGRuntimeWindowManager.WINDOW_SERVICE).executeAsyncServiceCall("destroy",
new Object[] { getName() });
}
}