/*******************************************************************************
* Copyright (C) 2015 Thomas Wolf <thomas.wolf@paranor.ch>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.egit.ui.internal.preferences;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimeZone;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.internal.SWTUtils;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.jface.preference.ComboFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.StringFieldEditor;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.util.GitDateFormatter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
/** Preferences concerning date formats in EGit. */
public class DateFormatPreferencePage extends FieldEditorPreferencePage
implements IWorkbenchPreferencePage {
private static final PersonIdent SAMPLE = new PersonIdent("", "", //$NON-NLS-1$ //$NON-NLS-2$
new Date(System.currentTimeMillis() - 24 * 3600 * 1000),
getDifferentTimeZone());
private static final Map<GitDateFormatter.Format, FormatInfo> DATA = initializeData();
private ComboFieldEditor formatChooser;
private StringFieldEditor dateFormat;
private Label dateFormatPreview;
private Label formatExplanation;
private String lastCustomValue;
/** Creates a new instance. */
public DateFormatPreferencePage() {
super(GRID);
initializeData();
setTitle(UIText.DateFormatPreferencePage_title);
}
@Override
public void init(IWorkbench workbench) {
// Nothing to do
}
@Override
protected IPreferenceStore doGetPreferenceStore() {
return Activator.getDefault().getPreferenceStore();
}
@Override
protected void createFieldEditors() {
String[][] values = new String[DATA.size()][2];
int i = 0;
for (Map.Entry<GitDateFormatter.Format, FormatInfo> entry : DATA
.entrySet()) {
values[i][0] = entry.getValue().name;
values[i][1] = entry.getKey() == null
? UIPreferences.DATE_FORMAT_CUSTOM : entry.getKey().name();
i++;
}
final Composite pane = getFieldEditorParent();
formatChooser = new ComboFieldEditor(
UIPreferences.DATE_FORMAT_CHOICE,
UIText.DateFormatPreferencePage_formatChooser_label,
values,
pane);
addField(formatChooser);
dateFormat = new StringFieldEditor(
UIPreferences.DATE_FORMAT,
UIText.DateFormatPreferencePage_formatInput_label,
StringFieldEditor.UNLIMITED,
StringFieldEditor.VALIDATE_ON_KEY_STROKE, pane) {
@Override
protected boolean doCheckState() {
// Validate the contents. If we're disabled, we're showing some
// built-in format string, which we always consider as valid.
if (!getTextControl(pane).isEnabled()) {
return true;
}
try {
updatePreview(
new SimpleDateFormat(getStringValue().trim()));
return true;
} catch (IllegalArgumentException e) {
dateFormatPreview.setText(""); //$NON-NLS-1$
return false;
}
}
@Override
protected void doLoad() {
// Set explicitly below
}
@Override
protected void doStore() {
// Never store invalid values, or built-in values
if (getTextControl(pane).isEnabled() && doCheckState()) {
super.doStore();
}
}
@Override
public void setStringValue(String value) {
super.setStringValue(value);
refreshValidState();
}
};
dateFormat.setEmptyStringAllowed(false);
dateFormat.setErrorMessage(
UIText.DateFormatPreferencePage_invalidDateFormat_message);
addField(dateFormat);
// We know that the layout will have two columns
Label dpLabel = SWTUtils.createLabel(pane,
UIText.DateFormatPreferencePage_datePreview_label);
dpLabel.setLayoutData(SWTUtils.createGridData(SWT.DEFAULT, SWT.DEFAULT,
false, false));
dateFormatPreview = SWTUtils.createLabel(pane, null, 1);
Label dummyLabel = SWTUtils.createLabel(pane, ""); //$NON-NLS-1$
dummyLabel.setLayoutData(SWTUtils.createGridData(SWT.DEFAULT,
SWT.DEFAULT, false, false));
formatExplanation = new Label(pane, SWT.LEFT | SWT.WRAP);
GridData layout = SWTUtils.createGridData(SWT.DEFAULT, SWT.DEFAULT,
false, true);
formatExplanation.setLayoutData(layout);
// Setup based on initial values. We don't get any events by the editors
// on initial load!
lastCustomValue = getPreferenceStore()
.getString(UIPreferences.DATE_FORMAT);
String initialValue = getPreferenceStore()
.getString(UIPreferences.DATE_FORMAT_CHOICE);
updateFields(initialValue);
}
@Override
protected void initialize() {
super.initialize();
// When the chooser's selection changes, update the dateFormat &
// enablement
formatChooser.setPropertyChangeListener(new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (FieldEditor.VALUE.equals(event.getProperty())) {
GitDateFormatter.Format format = fromString(
(String) event.getOldValue());
if (format == null) {
lastCustomValue = dateFormat.getStringValue();
}
updateFields((String) event.getNewValue());
}
}
});
}
private void updateFields(String newSelection) {
GitDateFormatter.Format format = fromString(newSelection);
FormatInfo info = DATA.get(format);
formatExplanation.setText(info.explanation);
if (format == null) {
dateFormat.getTextControl(getFieldEditorParent()).setEnabled(true);
dateFormat.setStringValue(lastCustomValue);
} else {
dateFormat.getTextControl(getFieldEditorParent()).setEnabled(false);
dateFormat.setStringValue(info.format);
updatePreview(format);
}
}
@Override
protected void performDefaults() {
super.performDefaults();
// We don't get property changed events when the default values are
// restored...
lastCustomValue = getPreferenceStore()
.getDefaultString(UIPreferences.DATE_FORMAT);
updateFields(getPreferenceStore()
.getDefaultString(UIPreferences.DATE_FORMAT_CHOICE));
}
private GitDateFormatter.Format fromString(String value) {
try {
return GitDateFormatter.Format.valueOf(value);
} catch (IllegalArgumentException | NullPointerException e) {
return null;
}
}
private void updatePreview(SimpleDateFormat format) {
dateFormatPreview.setText(format.format(SAMPLE.getWhen()));
}
private void updatePreview(GitDateFormatter.Format format) {
dateFormatPreview
.setText(new GitDateFormatter(format).formatDate(SAMPLE));
}
private static Map<GitDateFormatter.Format, FormatInfo> initializeData() {
Map<GitDateFormatter.Format, FormatInfo> d = new LinkedHashMap<>();
d.put(GitDateFormatter.Format.DEFAULT,
new FormatInfo(
UIText.DateFormatPreferencePage_choiceGitDefault_label,
"EEE MMM dd HH:mm:ss yyyy Z", //$NON-NLS-1$
UIText.DateFormatPreferencePage_helpGitDefault_label));
d.put(GitDateFormatter.Format.LOCAL,
new FormatInfo(
UIText.DateFormatPreferencePage_choiceGitLocal_label,
"EEE MMM dd HH:mm:ss yyyy", //$NON-NLS-1$
UIText.DateFormatPreferencePage_helpGitLocal_label));
d.put(GitDateFormatter.Format.RELATIVE,
new FormatInfo(
UIText.DateFormatPreferencePage_choiceGitRelative_label,
UIText.DateFormatPreferencePage_gitRelative_format_text,
UIText.DateFormatPreferencePage_helpGitRelative_label));
d.put(GitDateFormatter.Format.ISO,
new FormatInfo(
UIText.DateFormatPreferencePage_choiceGitIso_label,
"yyyy-MM-dd HH:mm:ss Z", //$NON-NLS-1$
UIText.DateFormatPreferencePage_helpGitIso_label));
d.put(GitDateFormatter.Format.RFC,
new FormatInfo(
UIText.DateFormatPreferencePage_choiceGitRfc_label,
"EEE, dd MMM yyyy HH:mm:ss Z", //$NON-NLS-1$
UIText.DateFormatPreferencePage_helpGitRfc_label));
d.put(GitDateFormatter.Format.SHORT,
new FormatInfo(
UIText.DateFormatPreferencePage_choiceGitShort_label,
"yyyy-MM-dd", //$NON-NLS-1$
UIText.DateFormatPreferencePage_helpGitShort_label));
DateFormat systemFormat = DateFormat
.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
String localeLocalFormat = (systemFormat instanceof SimpleDateFormat)
? ((SimpleDateFormat) systemFormat).toPattern()
: UIText.DateFormatPreferencePage_gitLocaleLocal_format_text;
String localeFormat = (systemFormat instanceof SimpleDateFormat)
? localeLocalFormat + " Z" //$NON-NLS-1$
: UIText.DateFormatPreferencePage_gitLocale_format_text;
d.put(GitDateFormatter.Format.LOCALE,
new FormatInfo(
UIText.DateFormatPreferencePage_choiceGitLocale_label,
localeFormat,
UIText.DateFormatPreferencePage_helpGitLocale_label));
d.put(GitDateFormatter.Format.LOCALELOCAL, new FormatInfo(
UIText.DateFormatPreferencePage_choiceGitLocaleLocal_label,
localeLocalFormat,
UIText.DateFormatPreferencePage_helpGitLocaleLocal_label));
d.put(null,
new FormatInfo(
UIText.DateFormatPreferencePage_choiceCustom_label, "", //$NON-NLS-1$
UIText.DateFormatPreferencePage_helpCustom_label));
return d;
}
private static TimeZone getDifferentTimeZone() {
TimeZone localTimeZone = TimeZone.getDefault();
int offset = (localTimeZone.getRawOffset() / 3600 / 1000 - 6);
// 6h to the West, full hour. Now get back to a sane range:
if (offset < -12) {
offset += 24;
}
String[] zoneIds = TimeZone.getAvailableIDs(offset * 3600 * 1000);
if (zoneIds.length == 0) {
// Huh?
return localTimeZone;
}
return TimeZone.getTimeZone(zoneIds[0]);
}
private static final class FormatInfo {
private final String name;
private final String format;
private final String explanation;
public FormatInfo(String name, String dateFormat, String explanation) {
this.name = name;
this.format = dateFormat;
this.explanation = explanation;
}
}
}