eclipse plugin: Refactor the way the plugin uses the paths.
Now there exists a specialized class just for the paths. That is so we can easily model the installation based paths.
This commit is contained in:
parent
3e9974051a
commit
6b1de38e75
10 changed files with 127 additions and 64 deletions
|
@ -17,7 +17,6 @@ import org.eclipse.xtext.parsetree.NodeUtil;
|
|||
import org.eclipse.xtext.resource.XtextResource;
|
||||
import org.eclipse.xtext.ui.editor.hyperlinking.HyperlinkHelper;
|
||||
import org.eclipse.xtext.ui.editor.hyperlinking.IHyperlinkAcceptor;
|
||||
import org.wesnoth.Constants;
|
||||
import org.wesnoth.Logger;
|
||||
import org.wesnoth.preferences.Preferences;
|
||||
import org.wesnoth.preprocessor.Define;
|
||||
|
@ -96,11 +95,11 @@ public class WMLHyperlinkHelper extends HyperlinkHelper
|
|||
|
||||
if (filePath.startsWith("~")) // user addon relative location //$NON-NLS-1$
|
||||
filePath = filePath.replaceFirst("~", //$NON-NLS-1$
|
||||
Preferences.getString(Constants.P_WESNOTH_USER_DIR).replace('\\', '/') +
|
||||
Preferences.Paths.getUserDir( ).replace('\\', '/') +
|
||||
"/data/"); //$NON-NLS-1$
|
||||
else if (filePath.startsWith("core")) // data/core relative location //$NON-NLS-1$
|
||||
filePath = filePath.replaceFirst("core", //$NON-NLS-1$
|
||||
Preferences.getString(Constants.P_WESNOTH_WORKING_DIR).replace('\\', '/') +
|
||||
Preferences.Paths.getWorkingDir( ).replace('\\', '/') +
|
||||
"/data/core/"); //$NON-NLS-1$
|
||||
|
||||
FileLocationOpenerHyperlink macroTarget = new FileLocationOpenerHyperlink();
|
||||
|
@ -129,7 +128,7 @@ public class WMLHyperlinkHelper extends HyperlinkHelper
|
|||
mapLocation = mapLocation.substring(1, value.getLength() - 1);
|
||||
|
||||
mapLocation = mapLocation.replaceFirst("~", //$NON-NLS-1$
|
||||
Preferences.getString(Constants.P_WESNOTH_USER_DIR).replace('\\','/') +
|
||||
Preferences.Paths.getUserDir( ).replace('\\','/') +
|
||||
"/data/"); //$NON-NLS-1$
|
||||
|
||||
ObjectStorageAdapter adapter = (ObjectStorageAdapter)EcoreUtil.getAdapter(value.eAdapters(),
|
||||
|
|
|
@ -44,7 +44,7 @@ public class WesnothPlugin extends AbstractUIPlugin
|
|||
Logger.getInstance().startLogger();
|
||||
|
||||
if (PlatformUI.isWorkbenchRunning()){
|
||||
if (WorkspaceUtils.checkConditions(false) == false)
|
||||
if (WorkspaceUtils.checkPathsAreSet(false) == false)
|
||||
{
|
||||
Display.getDefault().asyncExec(new Runnable() {
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.eclipse.core.resources.IncrementalProjectBuilder;
|
|||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.wesnoth.Constants;
|
||||
import org.wesnoth.Logger;
|
||||
import org.wesnoth.Messages;
|
||||
import org.wesnoth.preferences.Preferences;
|
||||
|
@ -74,7 +73,7 @@ public class WesnothProjectBuilder extends IncrementalProjectBuilder
|
|||
monitor.beginTask(String.format(Messages.WesnothProjectBuilder_1, getProject().getName()), 100);
|
||||
|
||||
monitor.subTask(Messages.WesnothProjectBuilder_3);
|
||||
if (Preferences.getString(Constants.P_WESNOTH_USER_DIR).isEmpty())
|
||||
if ( Preferences.Paths.getUserDir( ).isEmpty( ) )
|
||||
{
|
||||
Logger.getInstance().log(Messages.WesnothProjectBuilder_4,
|
||||
Messages.WesnothProjectBuilder_5);
|
||||
|
@ -95,7 +94,7 @@ public class WesnothProjectBuilder extends IncrementalProjectBuilder
|
|||
monitor.subTask(Messages.WesnothProjectBuilder_8);
|
||||
Map<String, String> properties = new HashMap<String, String>();
|
||||
properties.put("wesnoth.user.dir", //$NON-NLS-1$
|
||||
Preferences.getString(Constants.P_WESNOTH_USER_DIR) + Path.SEPARATOR);
|
||||
Preferences.Paths.getUserDir( ));
|
||||
Logger.getInstance().log(Messages.WesnothProjectBuilder_10);
|
||||
|
||||
String result = AntUtils.runAnt(
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*******************************************************************************/
|
||||
package org.wesnoth.preferences;
|
||||
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
import org.wesnoth.Constants;
|
||||
|
@ -107,8 +108,85 @@ public class Preferences extends AbstractPreferenceInitializer
|
|||
{
|
||||
if (StringUtils.isNullOrEmpty(installName) ||
|
||||
installName.equalsIgnoreCase("default"))
|
||||
return "";
|
||||
return ""; //$NON-NLS-1$
|
||||
|
||||
return "inst_" + installName + "_";
|
||||
return "inst_" + installName + "_"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for accessing the paths used in the plugin based
|
||||
* on the install
|
||||
*/
|
||||
public static class Paths
|
||||
{
|
||||
/**
|
||||
* Returns the addons directory
|
||||
* @return Returns the addons directory
|
||||
*/
|
||||
public static String getAddonsDir()
|
||||
{
|
||||
return getUserDir( ) + "/data/add-ons/"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the campaign directory
|
||||
* @return Returns the campaign directory
|
||||
*/
|
||||
public static String getCampaignDir()
|
||||
{
|
||||
return getWorkingDir( ) + "/data/campaigns/"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public static String getCoreDir()
|
||||
{
|
||||
return getWorkingDir( ) + "data/core/"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <b>schema.cfg</b> file path
|
||||
* @return Returns the schema.cfg file path
|
||||
*/
|
||||
public static String getSchemaPath()
|
||||
{
|
||||
return getWorkingDir( ) + "/data/schema.cfg"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user's directory
|
||||
* @return Returns the user's directory
|
||||
*/
|
||||
public static String getUserDir()
|
||||
{
|
||||
return getString( Constants.P_WESNOTH_USER_DIR ) + Path.SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the working directory that contains the
|
||||
* <b>data</b> folder
|
||||
* @return Returns the working directory
|
||||
*/
|
||||
public static String getWorkingDir()
|
||||
{
|
||||
return getString( Constants.P_WESNOTH_WORKING_DIR ) + Path.SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory that contains the wml tools
|
||||
* ( wmlscope, wmllint, wmlindent, wesnoth_addons_manager, etc)
|
||||
* @return Returns the directory that contains the wml tools
|
||||
*/
|
||||
public static String getWMLToolsDir()
|
||||
{
|
||||
return getString( Constants.P_WESNOTH_WMLTOOLS_DIR ) + Path.SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the wesnoth executable
|
||||
* @return Returns the path to the wesnoth executable
|
||||
*/
|
||||
public static String getWesnothExecutablePath()
|
||||
{
|
||||
return getString( Constants.P_WESNOTH_EXEC_PATH );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public class WesnothWorkbenchAdvisor extends WorkbenchAdvisorHack {
|
|||
@Override
|
||||
public void postStartup()
|
||||
{
|
||||
if (WorkspaceUtils.checkConditions(false) == false)
|
||||
if (WorkspaceUtils.checkPathsAreSet(false) == false)
|
||||
{
|
||||
WorkspaceUtils.setupWorkspace(true);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.wesnoth.Constants;
|
||||
import org.wesnoth.Logger;
|
||||
import org.wesnoth.Messages;
|
||||
import org.wesnoth.preferences.Preferences;
|
||||
|
@ -47,8 +46,7 @@ public class SchemaParser
|
|||
*/
|
||||
public void parseSchema(boolean force)
|
||||
{
|
||||
parseSchema(force, Preferences.getString(
|
||||
Constants.P_WESNOTH_WORKING_DIR) + "/data/schema.cfg"); //$NON-NLS-1$
|
||||
parseSchema( force, Preferences.Paths.getSchemaPath( ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,7 +15,6 @@ import java.util.List;
|
|||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.ui.console.MessageConsole;
|
||||
import org.wesnoth.Constants;
|
||||
import org.wesnoth.Logger;
|
||||
import org.wesnoth.Messages;
|
||||
import org.wesnoth.preferences.Preferences;
|
||||
|
@ -123,8 +122,9 @@ public class GameUtils
|
|||
public static void startGame(List<String> extraArgs)
|
||||
{
|
||||
List<String> args = new ArrayList<String>();
|
||||
String wesnothExec = Preferences.getString(Constants.P_WESNOTH_EXEC_PATH);
|
||||
String workingDir = Preferences.getString(Constants.P_WESNOTH_WORKING_DIR);
|
||||
String wesnothExec = Preferences.Paths.getWesnothExecutablePath( );
|
||||
String workingDir = Preferences.Paths.getWorkingDir( );
|
||||
|
||||
if (wesnothExec.isEmpty() || workingDir.isEmpty())
|
||||
{
|
||||
GUIUtils.showErrorMessageBox(Messages.GameUtils_7);
|
||||
|
@ -136,7 +136,7 @@ public class GameUtils
|
|||
|
||||
// add the user's data directory path
|
||||
args.add("--config-dir"); //$NON-NLS-1$
|
||||
args.add(Preferences.getString(Constants.P_WESNOTH_USER_DIR));
|
||||
args.add(Preferences.Paths.getUserDir( ));
|
||||
|
||||
// we need to add the working dir (backward compatibility)
|
||||
args.add(workingDir);
|
||||
|
|
|
@ -104,11 +104,11 @@ public class PreprocessorUtils
|
|||
try{
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
|
||||
arguments.add("--config-dir"); //$NON-NLS-1$
|
||||
arguments.add(Preferences.getString(Constants.P_WESNOTH_USER_DIR));
|
||||
arguments.add( "--config-dir" ); //$NON-NLS-1$
|
||||
arguments.add( Preferences.Paths.getUserDir( ) );
|
||||
|
||||
arguments.add("--data-dir"); //$NON-NLS-1$
|
||||
arguments.add(Preferences.getString(Constants.P_WESNOTH_WORKING_DIR));
|
||||
arguments.add( "--data-dir" ); //$NON-NLS-1$
|
||||
arguments.add( Preferences.Paths.getWorkingDir( ) );
|
||||
|
||||
if (macrosFile != null && macrosFile.isEmpty() == false)
|
||||
{
|
||||
|
@ -154,7 +154,7 @@ public class PreprocessorUtils
|
|||
|
||||
Logger.getInstance().log(Messages.PreprocessorUtils_10 + filePath);
|
||||
ExternalToolInvoker wesnoth = new ExternalToolInvoker(
|
||||
Preferences.getString(Constants.P_WESNOTH_EXEC_PATH),
|
||||
Preferences.Paths.getWesnothExecutablePath( ),
|
||||
arguments);
|
||||
wesnoth.runTool();
|
||||
if (waitForIt)
|
||||
|
|
|
@ -22,7 +22,6 @@ import org.eclipse.core.resources.IResource;
|
|||
import org.eclipse.core.resources.WorkspaceJob;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.ui.IEditorReference;
|
||||
import org.eclipse.ui.console.MessageConsole;
|
||||
|
@ -50,8 +49,7 @@ public class WMLTools
|
|||
if (!checkPrerequisites(null, "wmlindent")) // wmlindent only check first //$NON-NLS-1$
|
||||
return null;
|
||||
|
||||
File wmllintFile = new File(Preferences.getString(
|
||||
Constants.P_WESNOTH_WMLTOOLS_DIR) + "/wmlindent"); //$NON-NLS-1$
|
||||
File wmllintFile = new File( Preferences.Paths.getWMLToolsDir( ) + "/wmlindent" ); //$NON-NLS-1$
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
arguments.add(wmllintFile.getAbsolutePath());
|
||||
|
||||
|
@ -83,8 +81,8 @@ public class WMLTools
|
|||
if (!checkPrerequisites(resourcePath, "wesnoth/wmlparser2.py")) //$NON-NLS-1$
|
||||
return null;
|
||||
|
||||
File wmlparserFile = new File(Preferences.getString(
|
||||
Constants.P_WESNOTH_WMLTOOLS_DIR) + "/wesnoth/wmlparser2.py"); //$NON-NLS-1$
|
||||
File wmlparserFile = new File(Preferences.Paths.getWMLToolsDir( ) +
|
||||
"/wesnoth/wmlparser2.py" ); //$NON-NLS-1$
|
||||
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
|
||||
|
@ -97,16 +95,16 @@ public class WMLTools
|
|||
arguments.add("-n"); //$NON-NLS-1$
|
||||
|
||||
// wesnoth executable's path
|
||||
arguments.add("-w"); //$NON-NLS-1$
|
||||
arguments.add(Preferences.getString(Constants.P_WESNOTH_EXEC_PATH));
|
||||
arguments.add( "-w" ); //$NON-NLS-1$
|
||||
arguments.add( Preferences.Paths.getWesnothExecutablePath( ) );
|
||||
|
||||
// add user data directory
|
||||
arguments.add("-c"); //$NON-NLS-1$
|
||||
arguments.add(Preferences.getString(Constants.P_WESNOTH_USER_DIR));
|
||||
arguments.add( "-c"); //$NON-NLS-1$
|
||||
arguments.add( Preferences.Paths.getUserDir( ) );
|
||||
|
||||
// add the working data directory
|
||||
arguments.add("-a"); //$NON-NLS-1$
|
||||
arguments.add(Preferences.getString(Constants.P_WESNOTH_WORKING_DIR));
|
||||
arguments.add( "-a" ); //$NON-NLS-1$
|
||||
arguments.add( Preferences.Paths.getWorkingDir( ) );
|
||||
|
||||
// input file
|
||||
arguments.add("-i"); //$NON-NLS-1$
|
||||
|
@ -141,8 +139,7 @@ public class WMLTools
|
|||
if (!checkPrerequisites(resourcePath, "wmllint")) //$NON-NLS-1$
|
||||
return null;
|
||||
|
||||
File wmllintFile = new File(Preferences.getString(
|
||||
Constants.P_WESNOTH_WMLTOOLS_DIR) + "/wmllint"); //$NON-NLS-1$
|
||||
File wmllintFile = new File( Preferences.Paths.getWMLToolsDir( ) + "/wmllint" ); //$NON-NLS-1$
|
||||
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
|
||||
|
@ -162,9 +159,8 @@ public class WMLTools
|
|||
arguments.add("--nospellcheck"); //$NON-NLS-1$
|
||||
|
||||
// add default core directory
|
||||
arguments.add(Preferences.getString(Constants.P_WESNOTH_WORKING_DIR) +
|
||||
Path.SEPARATOR + "data/core"); //$NON-NLS-1$
|
||||
arguments.add(resourcePath);
|
||||
arguments.add( Preferences.Paths.getCoreDir( ) );
|
||||
arguments.add( resourcePath );
|
||||
|
||||
return runPythonScript(arguments, null, true, true, stdout,stderr);
|
||||
}
|
||||
|
@ -194,8 +190,7 @@ public class WMLTools
|
|||
if (!checkPrerequisites(resourcePath, "wmlscope")) //$NON-NLS-1$
|
||||
return null;
|
||||
|
||||
File wmlscopeFile = new File(Preferences.getString(
|
||||
Constants.P_WESNOTH_WMLTOOLS_DIR) + "/wmlscope"); //$NON-NLS-1$
|
||||
File wmlscopeFile = new File(Preferences.Paths.getWMLToolsDir( ) + "/wmlscope"); //$NON-NLS-1$
|
||||
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
|
||||
|
@ -217,9 +212,8 @@ public class WMLTools
|
|||
arguments.add("--unresolved"); //$NON-NLS-1$
|
||||
|
||||
// add default core directory
|
||||
arguments.add(Preferences.getString(Constants.P_WESNOTH_WORKING_DIR) +
|
||||
Path.SEPARATOR + "data/core"); //$NON-NLS-1$
|
||||
arguments.add(resourcePath);
|
||||
arguments.add( Preferences.Paths.getCoreDir( ) );
|
||||
arguments.add( resourcePath );
|
||||
|
||||
return runPythonScript(arguments, null, true, true, stdout, stderr);
|
||||
}
|
||||
|
@ -392,7 +386,7 @@ public class WMLTools
|
|||
if (!checkPrerequisites(containerPath, "wesnoth_addon_manager")) //$NON-NLS-1$
|
||||
return null;
|
||||
|
||||
File wmllintFile = new File(Preferences.getString(Constants.P_WESNOTH_WMLTOOLS_DIR)
|
||||
File wmllintFile = new File(Preferences.Paths.getWMLToolsDir( )
|
||||
+ "/wesnoth_addon_manager"); //$NON-NLS-1$
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
arguments.add(wmllintFile.getAbsolutePath());
|
||||
|
@ -469,14 +463,13 @@ public class WMLTools
|
|||
|
||||
if (wmlTool != null)
|
||||
{
|
||||
if (Preferences.getString(Constants.P_WESNOTH_WMLTOOLS_DIR).equals("")) //$NON-NLS-1$
|
||||
if ( Preferences.Paths.getWMLToolsDir( ).equals( "" ) ) //$NON-NLS-1$
|
||||
{
|
||||
GUIUtils.showWarnMessageBox(Messages.WMLTools_45 +
|
||||
Messages.WMLTools_46);
|
||||
return false;
|
||||
}
|
||||
File wmlToolFile = new File(Preferences.getString(Constants.P_WESNOTH_WMLTOOLS_DIR) +
|
||||
Path.SEPARATOR + wmlTool);
|
||||
File wmlToolFile = new File( Preferences.Paths.getWMLToolsDir( ) + wmlTool );
|
||||
|
||||
if (!wmlToolFile.exists())
|
||||
{
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.eclipse.ui.IWorkbenchWindow;
|
|||
import org.eclipse.ui.IWorkingSet;
|
||||
import org.eclipse.ui.IWorkingSetManager;
|
||||
import org.eclipse.ui.dialogs.PreferencesUtil;
|
||||
import org.wesnoth.Constants;
|
||||
import org.wesnoth.Logger;
|
||||
import org.wesnoth.Messages;
|
||||
import org.wesnoth.WesnothPlugin;
|
||||
|
@ -306,9 +305,8 @@ public class WorkspaceUtils
|
|||
if (resource == null)
|
||||
return null;
|
||||
|
||||
String result = Preferences.getString(Constants.P_WESNOTH_USER_DIR) +
|
||||
Path.SEPARATOR + "data/add-ons/"; //$NON-NLS-1$
|
||||
result += (resource.getProject().getName() + Path.SEPARATOR);
|
||||
String result = Preferences.Paths.getAddonsDir( );
|
||||
result += resource.getProject().getName() + Path.SEPARATOR;
|
||||
result += resource.getProjectRelativePath().toOSString();
|
||||
return result;
|
||||
}
|
||||
|
@ -331,11 +329,11 @@ public class WorkspaceUtils
|
|||
Messages.Activator_5);
|
||||
}
|
||||
|
||||
if (!checkConditions(false))
|
||||
if (!checkPathsAreSet(false))
|
||||
{
|
||||
PreferenceDialog pref = PreferencesUtil.createPreferenceDialogOn(
|
||||
WesnothPlugin.getShell(), "org.wesnoth.preferences.InstallsPage", null, null); //$NON-NLS-1$
|
||||
if (pref.open() == Window.CANCEL || !checkConditions(true))
|
||||
if (pref.open() == Window.CANCEL || !checkPathsAreSet(true))
|
||||
{
|
||||
GUIUtils.showErrorMessageBox(Messages.WorkspaceUtils_7 +
|
||||
Messages.WorkspaceUtils_8);
|
||||
|
@ -378,8 +376,8 @@ public class WorkspaceUtils
|
|||
|
||||
// automatically import 'special' folders as projects
|
||||
List<File> files = new ArrayList<File>();
|
||||
String addonsDir = Preferences.getString(Constants.P_WESNOTH_USER_DIR)+"/data/add-ons/"; //$NON-NLS-1$
|
||||
String campaignsDir = Preferences.getString(Constants.P_WESNOTH_WORKING_DIR) + "/data/campaigns/"; //$NON-NLS-1$
|
||||
String addonsDir = Preferences.Paths.getAddonsDir( );
|
||||
String campaignsDir = Preferences.Paths.getCampaignDir( );
|
||||
|
||||
File[] tmp = null;
|
||||
if (GUIUtils.showMessageBox(Messages.WorkspaceUtils_18 +
|
||||
|
@ -490,20 +488,18 @@ public class WorkspaceUtils
|
|||
* @param displayWarning true to display a messagebox warning
|
||||
* the user if conditions are not met
|
||||
*/
|
||||
public static boolean checkConditions(boolean displayWarning)
|
||||
public static boolean checkPathsAreSet(boolean displayWarning)
|
||||
{
|
||||
String execDir = Preferences.getString(Constants.P_WESNOTH_EXEC_PATH);
|
||||
String userDir = Preferences.getString(Constants.P_WESNOTH_USER_DIR);
|
||||
String wmltoolsDir = Preferences.getString(Constants.P_WESNOTH_WMLTOOLS_DIR);
|
||||
String workingDir = Preferences.getString(Constants.P_WESNOTH_WORKING_DIR);
|
||||
|
||||
if (!validPath(execDir) || !validPath(userDir) ||
|
||||
!validPath(wmltoolsDir) || !validPath(workingDir))
|
||||
if ( !validPath( Preferences.Paths.getWesnothExecutablePath( ) ) ||
|
||||
!validPath( Preferences.Paths.getUserDir( ) ) ||
|
||||
!validPath( Preferences.Paths.getWMLToolsDir( ) ) ||
|
||||
!validPath( Preferences.Paths.getWorkingDir( ) ))
|
||||
{
|
||||
if (displayWarning)
|
||||
GUIUtils.showWarnMessageBox(Messages.WorkspaceUtils_33);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue