blob: 330b3b6c3a5ca71be1e2ed7e8aa109b745af834e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 SAP AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Mathias Kinzler (SAP AG) - initial implementation
*******************************************************************************/
package org.eclipse.egit.ui.internal.repository;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.preferences.ConfigurationEditorComponent;
import org.eclipse.egit.ui.internal.properties.ListPropertySource;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.PropertyDescriptor;
import org.eclipse.ui.views.properties.PropertySheetPage;
/**
* Properties for repository and user configuration (read-only).
* <p>
* Depending on which mode is selected, either the user configuration, the
* repository configuration, or the effective configuration is shown.
*/
public class RepositoryPropertySource implements IPropertySource {
static final String CHANGEMODEACTIONID = "ChangeMode"; //$NON-NLS-1$
static final String SINGLEVALUEACTIONID = "SingleValueToggle"; //$NON-NLS-1$
static final String EDITACTIONID = "Edit"; //$NON-NLS-1$
private static final String SYSTEM_ID_PREFIX = "system"; //$NON-NLS-1$
private static final String USER_ID_PREFIX = "user"; //$NON-NLS-1$
private static final String REPO_ID_PREFIX = "repo"; //$NON-NLS-1$
private static final String EFFECTIVE_ID_PREFIX = "effe"; //$NON-NLS-1$
private static class EditAction extends Action {
private RepositoryPropertySource source;
public EditAction(String text, ImageDescriptor image,
RepositoryPropertySource source) {
super(text, image);
this.source = source;
}
public EditAction setSource(RepositoryPropertySource source) {
this.source = source;
return this;
}
@Override
public String getId() {
return EDITACTIONID;
}
@Override
public void run() {
final StoredConfig config;
DisplayMode mode = source.getCurrentMode();
switch (mode) {
case EFFECTIVE:
return;
case SYSTEM:
config = source.systemConfig;
break;
case USER:
config = source.userHomeConfig;
break;
case REPO:
config = source.repositoryConfig;
break;
default:
return;
}
new EditDialog(source.myPage.getSite().getShell(),
(FileBasedConfig) config, mode.getText()).open();
source.myPage.refresh();
}
@Override
public int getStyle() {
return IAction.AS_PUSH_BUTTON;
}
}
private final PropertySheetPage myPage;
private final FileBasedConfig systemConfig;
private final FileBasedConfig userHomeConfig;
private final StoredConfig repositoryConfig;
private final StoredConfig effectiveConfig;
private ActionContributionItem changeModeAction;
private ActionContributionItem editAction;
private ActionContributionItem singleValueToggleAction;
/**
* @param repository
* the repository
* @param page
* the page showing the properties
*/
public RepositoryPropertySource(Repository repository,
PropertySheetPage page) {
myPage = page;
effectiveConfig = repository.getConfig();
systemConfig = SystemReader.getInstance().openSystemConfig(null, FS.DETECTED);
userHomeConfig = SystemReader.getInstance().openUserConfig(null, FS.DETECTED);
if (effectiveConfig instanceof FileBasedConfig) {
File configFile = ((FileBasedConfig) effectiveConfig).getFile();
repositoryConfig = new FileBasedConfig(configFile, repository
.getFS());
} else {
repositoryConfig = effectiveConfig;
}
synchronized (myPage) {
// check if the actions are already there, if not, create them
IActionBars bars = myPage.getSite().getActionBars();
changeModeAction = (ActionContributionItem) bars
.getToolBarManager().find(CHANGEMODEACTIONID);
singleValueToggleAction = (ActionContributionItem) bars
.getToolBarManager().find(SINGLEVALUEACTIONID);
editAction = ((ActionContributionItem) bars.getToolBarManager()
.find(EDITACTIONID));
if (editAction != null)
((EditAction) editAction.getAction()).setSource(this);
if (changeModeAction != null) {
return;
}
changeModeAction = new ActionContributionItem(new Action(
DisplayMode.REPO.getText(), IAction.AS_DROP_DOWN_MENU) {
@Override
public String getId() {
return CHANGEMODEACTIONID;
}
@Override
public void run() {
MenuManager mgr = new MenuManager();
ToolItem item = (ToolItem) changeModeAction.getWidget();
ToolBar control = item.getParent();
final Menu ctxMenu = mgr.createContextMenu(control);
for (final DisplayMode aMode : DisplayMode.values()) {
mgr.add(new Action(aMode.getText()) {
@Override
public void run() {
if (aMode == getCurrentMode()) {
// the currently checked menu item can be
// hit again
return;
}
changeModeAction.getAction().setText(
aMode.getText());
boolean enabled = true;
switch (aMode) {
case EFFECTIVE:
enabled = false;
break;
case SYSTEM:
enabled = systemConfig.getFile() != null
&& systemConfig.getFile()
.canWrite();
break;
default:
// Nothing; enabled is true
break;
}
editAction.getAction().setEnabled(enabled);
myPage.refresh();
}
@Override
public boolean isEnabled() {
return aMode != DisplayMode.SYSTEM
|| systemConfig
.getFile() != null;
}
@Override
public boolean isChecked() {
return aMode == getCurrentMode();
}
@Override
public int getStyle() {
return IAction.AS_RADIO_BUTTON;
}
});
}
ctxMenu.setVisible(true);
}
@Override
public String getToolTipText() {
return UIText.RepositoryPropertySource_SelectModeTooltip;
}
@Override
public int getStyle() {
return IAction.AS_DROP_DOWN_MENU;
}
});
editAction = new ActionContributionItem(new EditAction(
UIText.RepositoryPropertySource_EditConfigButton,
UIIcons.EDITCONFIG, this));
singleValueToggleAction = new ActionContributionItem(new Action(
UIText.RepositoryPropertySource_SingleValueButton) {
@Override
public String getId() {
return SINGLEVALUEACTIONID;
}
@Override
public void run() {
myPage.refresh();
}
@Override
public int getStyle() {
return IAction.AS_CHECK_BOX;
}
@Override
public String getToolTipText() {
return UIText.RepositoryPropertySource_SuppressMultipleValueTooltip;
}
});
bars.getToolBarManager().add(new Separator());
bars.getToolBarManager().add(changeModeAction);
bars.getToolBarManager().add(singleValueToggleAction);
bars.getToolBarManager().add(editAction);
// bars.getToolBarManager().update(true) is not good enough; the
// layout of the bar doesn't get updated.
bars.updateActionBars();
}
}
private DisplayMode getCurrentMode() {
String actionText = changeModeAction.getAction().getText();
for (DisplayMode aMode : DisplayMode.values()) {
if (aMode.getText().equals(actionText))
return aMode;
}
return null;
}
private boolean getSingleValueMode() {
if (singleValueToggleAction != null)
return singleValueToggleAction.getAction().isChecked();
return false;
}
private Object getValueFromConfig(Config config, String keyString) {
int i = keyString.indexOf('.');
if (i < 0) {
return ""; //$NON-NLS-1$
}
int j = keyString.lastIndexOf('.');
String section = keyString.substring(0, i);
String subsection = i == j ? null : keyString.substring(i + 1, j);
String name = keyString.substring(j + 1);
String[] valueList = null;
if (getSingleValueMode()) {
valueList = new String[] { config.getString(section, subsection,
name) };
} else {
valueList = config.getStringList(section, subsection, name);
}
if (valueList == null || valueList.length == 0) {
return null;
}
if (valueList.length == 1) {
return valueList[0];
}
return new ListPropertySource(Arrays.asList(valueList));
}
@Override
public Object getEditableValue() {
return null;
}
@Override
public IPropertyDescriptor[] getPropertyDescriptors() {
try {
systemConfig.load();
userHomeConfig.load();
repositoryConfig.load();
effectiveConfig.load();
} catch (IOException e) {
showExceptionMessage(e);
} catch (ConfigInvalidException e) {
showExceptionMessage(e);
}
List<IPropertyDescriptor> resultList = new ArrayList<>();
StoredConfig config;
String category;
String prefix;
boolean recursive = false;
switch (getCurrentMode()) {
case EFFECTIVE:
prefix = EFFECTIVE_ID_PREFIX;
category = UIText.RepositoryPropertySource_EffectiveConfigurationCategory;
config = effectiveConfig;
recursive = true;
break;
case REPO: {
prefix = REPO_ID_PREFIX;
String location = ""; //$NON-NLS-1$
if (repositoryConfig instanceof FileBasedConfig) {
location = ((FileBasedConfig) repositoryConfig).getFile()
.getAbsolutePath();
}
category = NLS
.bind(
UIText.RepositoryPropertySource_RepositoryConfigurationCategory,
location);
config = repositoryConfig;
break;
}
case USER: {
prefix = USER_ID_PREFIX;
String location = userHomeConfig.getFile().getAbsolutePath();
category = NLS
.bind(UIText.RepositoryPropertySource_GlobalConfigurationCategory,
location);
config = userHomeConfig;
break;
}
case SYSTEM: {
prefix = SYSTEM_ID_PREFIX;
if (systemConfig.getFile() == null) {
return new IPropertyDescriptor[0];
}
String location = systemConfig.getFile().getAbsolutePath();
category = NLS
.bind(UIText.RepositoryPropertySource_GlobalConfigurationCategory,
location);
config = systemConfig;
break;
}
default:
return new IPropertyDescriptor[0];
}
for (String key : config.getSections()) {
for (String sectionItem : config.getNames(key, recursive)) {
String sectionId = key + "." + sectionItem; //$NON-NLS-1$
PropertyDescriptor desc = new PropertyDescriptor(prefix
+ sectionId, sectionId);
desc.setCategory(category);
resultList.add(desc);
}
for (String sub : config.getSubsections(key)) {
for (String sectionItem : config.getNames(key, sub, recursive)) {
String sectionId = key + "." + sub + "." + sectionItem; //$NON-NLS-1$ //$NON-NLS-2$
PropertyDescriptor desc = new PropertyDescriptor(prefix
+ sectionId, sectionId);
desc.setCategory(category);
resultList.add(desc);
}
}
}
return resultList.toArray(new IPropertyDescriptor[0]);
}
@Override
public Object getPropertyValue(Object id) {
String actId = ((String) id);
Object value = null;
if (actId.startsWith(SYSTEM_ID_PREFIX)) {
value = getValueFromConfig(systemConfig,
actId.substring(SYSTEM_ID_PREFIX.length()));
} else if (actId.startsWith(USER_ID_PREFIX)) {
value = getValueFromConfig(userHomeConfig,
actId.substring(USER_ID_PREFIX.length()));
} else if (actId.startsWith(REPO_ID_PREFIX)) {
value = getValueFromConfig(repositoryConfig,
actId.substring(REPO_ID_PREFIX.length()));
} else if (actId.startsWith(EFFECTIVE_ID_PREFIX)) {
value = getValueFromConfig(effectiveConfig,
actId.substring(EFFECTIVE_ID_PREFIX.length()));
}
if (value == null)
// the text editor needs this to work
return ""; //$NON-NLS-1$
return value;
}
@Override
public boolean isPropertySet(Object id) {
return false;
}
@Override
public void resetPropertyValue(Object id) {
// no editing here
}
@Override
public void setPropertyValue(Object id, Object value) {
// no editing here
}
private void showExceptionMessage(Exception e) {
Activator.handleError(UIText.RepositoryPropertySource_ErrorHeader, e,
true);
}
private enum DisplayMode {
/* The effective configuration as obtained from the repository */
EFFECTIVE(UIText.RepositoryPropertySource_EffectiveConfigurationAction),
/* System wide configuration */
SYSTEM(UIText.RepositoryPropertySource_SystemConfigurationMenu),
/* The user specific configuration */
USER(UIText.RepositoryPropertySource_GlobalConfigurationMenu),
/* The repository specific configuration */
REPO(UIText.RepositoryPropertySource_RepositoryConfigurationButton);
/**
* @return the human-readable String for this mode
*/
String getText() {
return this.text;
}
private final String text;
private DisplayMode(String text) {
this.text = text;
}
}
/**
* Wraps the editor component into a dialog
*/
private final static class EditDialog extends TitleAreaDialog {
private final FileBasedConfig myConfig;
private final String myTitle;
ConfigurationEditorComponent editor;
public EditDialog(Shell shell, FileBasedConfig config, String title) {
super(shell);
myConfig = config;
setShellStyle(getShellStyle() | SWT.SHELL_TRIM);
myTitle = title;
setHelpAvailable(false);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite main = (Composite) super.createDialogArea(parent);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true,
true).applyTo(main);
editor = new ConfigurationEditorComponent(main, myConfig, true, 5) {
@Override
protected void setErrorMessage(String message) {
EditDialog.this.setErrorMessage(message);
}
@Override
protected void setDirty(boolean dirty) {
getButton(IDialogConstants.OK_ID).setEnabled(dirty);
}
};
Control result = editor.createContents();
applyDialogFont(main);
return result;
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell
.setText(UIText.RepositoryPropertySource_EditConfigurationTitle);
newShell.setSize(700, 600);
}
@Override
public void create() {
super.create();
setTitle(myTitle);
setMessage(UIText.RepositoryPropertySource_EditorMessage);
getButton(IDialogConstants.OK_ID).setEnabled(false);
}
@Override
protected void okPressed() {
try {
editor.save();
super.okPressed();
} catch (IOException e) {
Activator.handleError(e.getMessage(), e, true);
}
}
}
}