/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* For information about the authors of this project Have a look
* at the AUTHORS file in the root of this project.
*/
package net.sourceforge.fullsync.ui;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import net.sourceforge.fullsync.ExceptionHandler;
import net.sourceforge.fullsync.Util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
class AboutDialog extends Dialog implements AsyncUIUpdate {
private static final String FULLSYNC_LICENSES_DIRECTORY = "net/sourceforge/fullsync/licenses/";
private static final long ANIMATION_DELAY = 750;
private int stIndex;
private Timer stTimer;
private Combo componentCombo;
private StyledText licenseText;
private List<String> licenseNames;
private List<String> licenseTexts;
AboutDialog(final Shell parent, int style) {
super(parent, style);
try {
final Shell dialogShell = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE);
GridLayout dialogShellLayout = new GridLayout(1, true);
dialogShellLayout.marginTop = 0;
dialogShellLayout.marginHeight = 0;
dialogShellLayout.marginWidth = 0;
dialogShellLayout.marginBottom = 5;
dialogShellLayout.verticalSpacing = 5;
dialogShellLayout.horizontalSpacing = 5;
dialogShell.setLayout(dialogShellLayout);
dialogShell.setText(Messages.getString("AboutDialog.About_FullSync")); //$NON-NLS-1$
Composite logoComposite = new LogoHeaderComposite(dialogShell, SWT.FILL);
GridData logoCompositeLData = new GridData();
logoCompositeLData.grabExcessHorizontalSpace = true;
logoCompositeLData.horizontalAlignment = GridData.FILL;
logoComposite.setLayoutData(logoCompositeLData);
final TabFolder tabs = new TabFolder(dialogShell, SWT.FILL);
GridData tabLData = new GridData(GridData.FILL_BOTH);
tabs.setLayoutData(tabLData);
tabs.setLayout(new FillLayout());
final TabItem tabGeneral = new TabItem(tabs, SWT.NONE);
tabGeneral.setText(Messages.getString("AboutDialog.Tab_About")); //$NON-NLS-1$
stTimer = new Timer(false);
tabGeneral.setControl(initAboutTab(tabs));
dialogShell.addDisposeListener(e -> stTimer.cancel());
dialogShell.getDisplay().asyncExec(() -> {
// fix the size of the General tab to show all child elements
Point generalTabSize = tabGeneral.getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT);
Rectangle tabsClientSize = tabs.getClientArea();
int width = generalTabSize.x - tabsClientSize.width;
int height = generalTabSize.y - tabsClientSize.height;
Point dlgSize = dialogShell.getSize();
width = dlgSize.x + width;
height = dlgSize.y + height;
dialogShell.setSize(width, height);
Rectangle parentBounds = parent.getBounds();
int dlgX = (parentBounds.x + (parentBounds.width / 2)) - (width / 2);
int dlgY = (parentBounds.y + (parentBounds.height / 2)) - (height / 2);
dialogShell.setLocation(dlgX, dlgY);
});
TabItem tabLicenses = new TabItem(tabs, SWT.NONE);
tabLicenses.setText(Messages.getString("AboutDialog.Tab_Licenses")); //$NON-NLS-1$
tabLicenses.setControl(initLicensesTab(tabs));
TabItem tabChangelog = new TabItem(tabs, SWT.NONE);
tabChangelog.setText(Messages.getString("AboutDialog.Tab_Changelog")); //$NON-NLS-1$
tabChangelog.setControl(initChangelogTab(tabs));
// ok button
Button buttonOk = new Button(dialogShell, SWT.PUSH | SWT.CENTER);
buttonOk.setText(Messages.getString("AboutDialog.Ok")); //$NON-NLS-1$
buttonOk.addListener(SWT.Selection, e -> dialogShell.close());
GridData buttonOkLData = new GridData();
buttonOkLData.horizontalAlignment = GridData.CENTER;
buttonOkLData.heightHint = UISettings.BUTTON_HEIGHT;
buttonOkLData.widthHint = UISettings.BUTTON_WIDTH;
buttonOkLData.grabExcessHorizontalSpace = true;
buttonOk.setLayoutData(buttonOkLData);
// layout the dialog and show it
dialogShell.layout(true);
dialogShell.open();
buttonOk.setFocus();
Display display = dialogShell.getDisplay();
while (!dialogShell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
catch (Exception e) {
ExceptionHandler.reportException(e);
}
}
private Control initChangelogTab(Composite parent) {
final Composite tab = new Composite(parent, SWT.FILL);
tab.setLayout(new GridLayout(1, true));
ChangeLogBox changeLogBox = new ChangeLogBox(tab, SWT.NONE, "");
GridData changelogBoxLData = new GridData(GridData.FILL_BOTH);
changelogBoxLData.heightHint = 300;
changeLogBox.setLayoutData(changelogBoxLData);
return tab;
}
private Composite initAboutTab(Composite parent) {
final Composite tab = new Composite(parent, SWT.FILL);
tab.setLayout(new GridLayout(1, true));
// version label
String version = Util.getFullSyncVersion();
Label labelVersion = new Label(tab, SWT.FILL);
labelVersion.setText(Messages.getString("AboutDialog.Version", version)); //$NON-NLS-1$
GridData lvd = new GridData(SWT.FILL);
lvd.grabExcessHorizontalSpace = true;
labelVersion.setLayoutData(lvd);
// copyright text
Link copyright = new Link(tab, SWT.FILL);
String copyrightText = Util.getResourceAsString("net/sourceforge/fullsync/copyright.txt"); //$NON-NLS-1$
copyrightText = copyrightText.replaceAll("\\{version\\}", version); //$NON-NLS-1$
copyright.setText(copyrightText);
GridData lcd = new GridData(SWT.FILL);
lcd.grabExcessHorizontalSpace = true;
copyright.setLayoutData(lcd);
copyright.addListener(SWT.Selection, e -> GuiController.launchProgram(e.text));
// separator
Label labelSeparator1 = new Label(tab, SWT.SEPARATOR | SWT.HORIZONTAL);
GridData labelSeparatorLData = new GridData();
labelSeparatorLData.horizontalAlignment = SWT.FILL;
labelSeparatorLData.grabExcessHorizontalSpace = true;
labelSeparator1.setLayoutData(labelSeparatorLData);
// credits
final Label labelThanks = new Label(tab, SWT.CENTER);
GridData labelThanksLData = new GridData();
labelThanksLData.grabExcessHorizontalSpace = true;
labelThanksLData.horizontalAlignment = GridData.CENTER;
labelThanks.setLayoutData(labelThanksLData);
labelThanks.setText("\n\n\n"); //$NON-NLS-1$
labelThanks.setAlignment(SWT.CENTER);
final String[] specialThanks;
String sp = Util.getResourceAsString("net/sourceforge/fullsync/special-thanks.txt"); //$NON-NLS-1$
String[] res = sp.split("\n"); //$NON-NLS-1$
if (null != res) {
specialThanks = res;
}
else {
sp = ""; //$NON-NLS-1$
specialThanks = new String[] { sp, sp, sp };
}
stTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Display display = Display.getDefault();
display.syncExec(() -> {
if (!labelThanks.isDisposed()) {
int firstLine = stIndex % specialThanks.length;
int secondLine = (stIndex + 1) % specialThanks.length;
int thirdLine = (stIndex + 2) % specialThanks.length;
labelThanks.setText(specialThanks[firstLine] + '\n' + specialThanks[secondLine] + '\n' + specialThanks[thirdLine]);
labelThanks.pack(true);
tab.layout(new Control[] { labelThanks });
++stIndex;
stIndex %= specialThanks.length;
}
});
}
}, ANIMATION_DELAY, ANIMATION_DELAY);
// separator
Label labelSeparator2 = new Label(tab, SWT.SEPARATOR | SWT.HORIZONTAL);
GridData labelSeparator2LData = new GridData();
labelSeparator2LData.grabExcessHorizontalSpace = true;
labelSeparator2LData.horizontalAlignment = SWT.FILL;
labelSeparator2.setLayoutData(labelSeparator2LData);
// buttons composite
Composite compositeBottom = new Composite(tab, SWT.NONE);
GridLayout compositeBottomLayout = new GridLayout();
GridData compositeBottomLData = new GridData();
compositeBottomLData.horizontalAlignment = SWT.FILL;
compositeBottom.setLayoutData(compositeBottomLData);
compositeBottomLayout.makeColumnsEqualWidth = true;
compositeBottomLayout.numColumns = 2;
compositeBottom.setLayout(compositeBottomLayout);
// website link
Link websiteLink = new Link(compositeBottom, SWT.NONE);
websiteLink.setText(String.format("<a>%s</a>", Messages.getString("AboutDialog.WebSite"))); //$NON-NLS-1$ //$NON-NLS-2$
GridData websiteLinkLData = new GridData();
websiteLink.addListener(SWT.Selection, e -> GuiController.launchProgram(Util.getWebsiteURL()));
websiteLinkLData.grabExcessHorizontalSpace = false;
websiteLinkLData.horizontalAlignment = SWT.CENTER;
websiteLink.setLayoutData(websiteLinkLData);
// twitter link
Composite compositeTwitter = new Composite(compositeBottom, SWT.NONE);
compositeTwitter.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
compositeTwitter.setLayout(new GridLayout(2, false));
Image twitterBird = GuiController.getInstance().getImage("twitter_bird_blue_16.png"); //$NON-NLS-1$
Label twitterBirdLabel = new Label(compositeTwitter, SWT.NONE);
Rectangle twitterBirdBounds = twitterBird.getBounds();
twitterBirdLabel.setSize(twitterBirdBounds.width, twitterBirdBounds.height);
twitterBirdLabel.setImage(twitterBird);
Link twitterLink = new Link(compositeTwitter, SWT.NONE);
twitterLink.setText("<a>@FullSyncNews</a>"); //$NON-NLS-1$
GridData twitterLinkLData = new GridData();
twitterLink.addListener(SWT.Selection, e -> GuiController.launchProgram(Util.getTwitterURL()));
twitterLink.setLayoutData(twitterLinkLData);
tab.pack();
tab.layout();
return tab;
}
private Composite initLicensesTab(Composite parent) throws IOException {
Composite tab = new Composite(parent, SWT.FILL);
tab.setLayout(new GridLayout(2, false));
Label component = new Label(tab, SWT.NONE);
component.setText(Messages.getString("AboutDialog.Component")); //$NON-NLS-1$
componentCombo = new Combo(tab, SWT.DROP_DOWN | SWT.READ_ONLY);
GridData componentComboLData = new GridData(SWT.FILL, SWT.NONE, true, false);
componentCombo.setLayoutData(componentComboLData);
licenseText = new StyledText(tab, SWT.BORDER | SWT.READ_ONLY | SWT.V_SCROLL | SWT.H_SCROLL);
licenseText.setAlwaysShowScrollBars(false);
GridData licenseTextLData = new GridData(GridData.FILL_BOTH);
licenseTextLData.horizontalSpan = 2;
licenseText.setLayoutData(licenseTextLData);
componentCombo.addModifyListener(e -> {
if (null != licenseTexts) {
int index = componentCombo.getSelectionIndex();
licenseText.setText(licenseTexts.get(index));
}
});
GuiController.backgroundExec(this);
return tab;
}
private static class LicenseEntry {
public String name;
public String license;
}
@Override
public void execute() throws Exception {
int numLicenses = 0;
List<LicenseEntry> licenses = new ArrayList<LicenseEntry>();
for (String name : Util.loadDirectoryFromClasspath(AboutDialog.class, FULLSYNC_LICENSES_DIRECTORY)) {
if (name.endsWith(".txt")) { //$NON-NLS-1$
++numLicenses;
LicenseEntry entry = new LicenseEntry();
entry.name = name.substring(0, name.length() - 4);
entry.license = Util.getResourceAsString(FULLSYNC_LICENSES_DIRECTORY + name);
licenses.add(entry);
}
}
Collections.sort(licenses, (o1, o2) -> o1.name.compareToIgnoreCase(o2.name));
licenseNames = new ArrayList<String>(numLicenses);
licenseTexts = new ArrayList<String>(numLicenses);
for (LicenseEntry lic : licenses) {
licenseNames.add(lic.name);
licenseTexts.add(lic.license);
}
}
@Override
public void updateUI(boolean succeeded) {
int idx = 0;
int fsIdx = 0;
for (String licenseName : licenseNames) {
componentCombo.add(licenseName);
if ("FullSync".equals(licenseName)) { //$NON-NLS-1$
fsIdx = idx;
}
++idx;
}
licenseNames = null;
componentCombo.select(fsIdx);
licenseText.setText(licenseTexts.get(fsIdx));
}
}