/*
* Copyright 2010 The Rabbit Eclipse Plug-in Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package rabbit.ui.internal;
import rabbit.tracking.internal.TrackingPlugin;
import rabbit.ui.IPage;
import rabbit.ui.Preference;
import org.eclipse.core.runtime.IProduct;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ControlContribution;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DateTime;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A view to show metrics.
*/
public class RabbitView extends ViewPart {
/** Preference constant for saving/restoring the view state. */
private static final String PREF_RABBIT_VIEW = "rabbitView";
/** Preference constant for saving/restoring the view state. */
private static final String PREF_METRICS_WIDTH = "metricsPanelWidth";
/**
* Checks whether the two calendars has the same year, month, and day of
* month.
*
* @param date1 The calendar.
* @param date2 The other calendar.
* @return True if the dates has the same year, month, and day of month, false
* otherwise.
*/
public static boolean isSameDate(Calendar date1, Calendar date2) {
return date1.get(Calendar.YEAR) == date2.get(Calendar.YEAR)
&& date1.get(Calendar.MONTH) == date2.get(Calendar.MONTH)
&& date1.get(Calendar.DAY_OF_MONTH) == date2.get(Calendar.DAY_OF_MONTH);
}
/**
* Updates the date with the data from the widget.
*
* @param date The date to be updated.
* @param widget The widget to get the data from.
*/
public static void updateDate(Calendar date, DateTime widget) {
date.set(Calendar.YEAR, widget.getYear());
date.set(Calendar.MONTH, widget.getMonth());
date.set(Calendar.DAY_OF_MONTH, widget.getDay());
}
/**
* Updates the widget with the data from the date.
*
* @param widget The widget to be updated.
* @param date The date to get the data from.
*/
public static void updateDateTime(DateTime widget, Calendar date) {
widget.setYear(date.get(Calendar.YEAR));
widget.setMonth(date.get(Calendar.MONTH));
widget.setDay(date.get(Calendar.DAY_OF_MONTH));
}
/**
* Gets the version of Eclipse. Not completely reliable.
*
* @return The version String, such as 3.5..., or an empty String.
*/
private static String getProductVersion() {
try {
IProduct product = Platform.getProduct();
String aboutText = product.getProperty("aboutText");
String pattern = "Version: (.*)\n";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(aboutText);
return (m.find()) ? m.group(1) : "";
} catch (Exception e) {
return "";
}
}
/**
* A map containing page status (updated or not), if a page is not updated
* (value return false), then it will be updated before it's displayed (when a
* user clicks on a tree node).
*/
private Map<IPage, Boolean> pageStatus;
/** A map containing pages and the root composite of the page. */
private Map<IPage, Composite> pages;
/** A map containing pages and their tool bar items. */
private Map<IPage, IContributionItem[]> pageToolItems;
/** A tool bar for pages to create their tool bar items. */
private IToolBarManager extensionToolBar;
private FormToolkit toolkit;
/**
* The layout of {@link #displayPanel}, used to show/hide pages on user
* selection.
*/
private StackLayout stackLayout;
/** The composite to show the page that is selected by the user. */
private Composite displayPanel;
/** The preferences for the pages. */
private final Preference preferences;
/** True if this OS is Windows, false otherwise. */
private final boolean isWindowsOS = Platform.getOS().equals(Platform.OS_WIN32);
/** True if this OS is linux, false otherwise. */
private final boolean isLinux = Platform.getOS().equals(Platform.OS_LINUX);
/** File to save/restore the view state, may be null. */
private IMemento memento;
/** The form data of the sash dividing the two panels. */
private FormData sashFormData;
/**
* Constructs a new view.
*/
public RabbitView() {
pages = new HashMap<IPage, Composite>();
pageStatus = new HashMap<IPage, Boolean>();
pageToolItems = new HashMap<IPage, IContributionItem[]>();
toolkit = new FormToolkit(PlatformUI.getWorkbench().getDisplay());
stackLayout = new StackLayout();
preferences = new Preference();
}
@Override
public void createPartControl(Composite parent) {
Form form = toolkit.createForm(parent);
form.getBody().setLayout(new FormLayout());
sashFormData = new FormData();
sashFormData.width = 1;
sashFormData.top = new FormAttachment(0, 0);
sashFormData.left = new FormAttachment(0, 200);
sashFormData.bottom = new FormAttachment(100, 0);
final Sash sash = new Sash(form.getBody(), SWT.VERTICAL);
sash.setBackground(toolkit.getColors().getBorderColor());
sash.setLayoutData(sashFormData);
sash.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event e) {
((FormData) sash.getLayoutData()).left = new FormAttachment(0, e.x);
sash.getParent().layout();
}
});
// Extension list:
FormData leftData = new FormData();
leftData.top = new FormAttachment(0, 0);
leftData.left = new FormAttachment(0, 0);
leftData.right = new FormAttachment(sash, 0);
leftData.bottom = new FormAttachment(100, 0);
Form left = toolkit.createForm(form.getBody());
left.setText("Metrics");
left.setLayoutData(leftData);
left.getBody().setLayout(new FillLayout());
MetricsPanel list = new MetricsPanel(this);
list.createContents(left.getBody());
// Displaying area:
FormData rightData = new FormData();
rightData.top = new FormAttachment(0, 0);
rightData.left = new FormAttachment(sash, 0);
rightData.right = new FormAttachment(100, 0);
rightData.bottom = new FormAttachment(100, 0);
Composite right = toolkit.createComposite(form.getBody());
right.setLayoutData(rightData);
GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(right);
// Header:
Composite header = toolkit.createComposite(right);
GridLayout headerLayout = new GridLayout(2, false);
if (isLinux) { // Make GTK widgets have less spaces:
headerLayout.marginHeight = 0;
headerLayout.marginWidth = 0;
headerLayout.horizontalSpacing = 0;
headerLayout.verticalSpacing = 0;
}
header.setLayout(headerLayout);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).applyTo(header);
{
int toolbarStyle = (!isLinux) ? SWT.FLAT : SWT.NONE;
ToolBar bar = new ToolBar(header, toolbarStyle);
bar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
toolkit.adapt(bar, false, false);
extensionToolBar = new ToolBarManager(bar);
bar = new ToolBar(header, toolbarStyle);
toolkit.adapt(bar, false, false);
createToolBarItems(new ToolBarManager(bar));
}
displayPanel = toolkit.createComposite(right);
displayPanel.setLayout(stackLayout);
GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(
displayPanel);
// Greeting message:
Composite cmp = toolkit.createComposite(displayPanel);
cmp.setLayout(new GridLayout());
{
Label imgLabel = toolkit.createLabel(cmp, "", SWT.CENTER);
imgLabel.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, true));
imgLabel.setImage(getTitleImage());
Label helloLabel = toolkit.createLabel(cmp, "Welcome!", SWT.CENTER);
helloLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
}
stackLayout.topControl = cmp;
displayPanel.layout();
if (memento != null) {
restoreState(memento);
}
}
/**
* Displays the given page.
*
* @param page The page to display.
*/
public void display(IPage page) {
// Removes the extension tool bar items:
for (IContributionItem item : extensionToolBar.getItems()) {
item.setVisible(false);
}
Composite cmp = null;
if (page != null) {
// Updates the page:
cmp = pages.get(page);
if (cmp == null) {
cmp = toolkit.createComposite(displayPanel);
cmp.setLayout(new FillLayout());
page.createContents(cmp);
pages.put(page, cmp);
// Restores the state:
if (memento != null) {
page.onRestoreState(memento);
}
}
// Updates the extension tool bar items:
IContributionItem[] items = pageToolItems.get(page);
if (items == null) {
items = page.createToolBarItems(extensionToolBar);
pageToolItems.put(page, items);
} else {
for (IContributionItem item : items) {
item.setVisible(true);
}
}
// Updates the current visible page, mark others as not updated:
Boolean updated = pageStatus.get(page);
if (updated == null || !updated) {
pageStatus.put(page, Boolean.TRUE);
updatePage(page, preferences);
}
}
extensionToolBar.update(true);
stackLayout.topControl = cmp;
displayPanel.layout();
}
@Override
public void dispose() {
toolkit.dispose();
super.dispose();
}
@Override
public void init(IViewSite site, IMemento m) throws PartInitException {
super.init(site, m);
if (m != null) {
this.memento = m.getChild(PREF_RABBIT_VIEW);
}
}
@Override
public void saveState(IMemento memento) {
memento = memento.createChild(PREF_RABBIT_VIEW);
memento.putInteger(PREF_METRICS_WIDTH, sashFormData.left.offset);
for (IPage page : pages.keySet()) {
page.onSaveState(memento);
}
}
@Override
public void setFocus() {
}
private void createSpace(IToolBarManager toolBar) {
createString(toolBar, " ");
}
private void createString(IToolBarManager toolBar, final String str) {
toolBar.add(new ControlContribution(null) {
@Override
protected Control createControl(Composite parent) {
return toolkit.createLabel(parent, str);
}
});
}
/**
* Creates tool bar items for non windows operating systems.
*
* @param toolBar The tool bar.
*/
private void createToolBarForNonWindowsOS(IToolBarManager toolBar) {
CalendarAction.create(toolBar, getSite().getShell(), preferences
.getStartDate(), " From: ", " ");
CalendarAction.create(toolBar, getSite().getShell(), preferences
.getEndDate(), " To: ", " ");
}
/**
* Creates tool bar items for Windows operating system.
*
* @param toolBar The tool bar.
*/
private void createToolBarForWindowsOS(IToolBarManager toolBar) {
toolBar.add(new ControlContribution("rabbit.ui.fromDateTime") {
@Override
protected Control createControl(Composite parent) {
final Calendar dateToBind = preferences.getStartDate();
final DateTime fromDateTime = new DateTime(parent, SWT.DROP_DOWN
| SWT.BORDER);
fromDateTime
.setToolTipText("Select the start date for the data to be displayed");
updateDateTime(fromDateTime, dateToBind);
fromDateTime.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
updateDate(dateToBind, fromDateTime);
}
});
return fromDateTime;
}
});
createSpace(toolBar);
toolBar.add(new ControlContribution("rabbit.ui.toDateTime") {
@Override
protected Control createControl(Composite parent) {
final Calendar dateToBind = preferences.getEndDate();
final DateTime toDateTime = new DateTime(parent, SWT.DROP_DOWN
| SWT.BORDER);
toDateTime
.setToolTipText("Select the end date for the data to be displayed");
updateDateTime(toDateTime, dateToBind);
toDateTime.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
updateDate(dateToBind, toDateTime);
}
});
return toDateTime;
}
});
}
/**
* Creates the tool bar items.
*
* @param toolBar The tool bar.
*/
private void createToolBarItems(IToolBarManager toolBar) {
// Only Windows && Eclipse 3.5 has SWT.DROP_DOWN for DateTime.
// We don't support 3.3 and before anyway:
boolean isDropDownDateTimeSupported = !getProductVersion()
.startsWith("3.4");
if (isWindowsOS && isDropDownDateTimeSupported) {
createToolBarForWindowsOS(toolBar);
} else {
createToolBarForNonWindowsOS(toolBar);
}
if (isWindowsOS) { // Looks better:
createSpace(toolBar);
}
IAction refresh = new Action("Refresh") {
@Override
public void run() {
updateView();
}
};
/*
* Mainly for Eclipse 3.4 (no SWT.DROP_DOWN DateTime) on Windows. Things
* look ugly on Windows if some tool bar actions have text and some have
* icons, so in this case, no icons at all.
*/
if (!isWindowsOS || isDropDownDateTimeSupported) {
refresh.setImageDescriptor(SharedImages.REFRESH);
}
//
toolBar.add(refresh);
toolBar.update(true);
}
/**
* Restores the view state.
* @param memento The settings.
*/
private void restoreState(IMemento memento) {
memento = memento.getChild(PREF_RABBIT_VIEW);
if (memento == null) {
return;
}
Integer width = memento.getInteger(PREF_METRICS_WIDTH);
if (width != null && width > 0) {
sashFormData.left.offset = width;
}
}
private void updatePage(final IPage page, final Preference preference) {
Job job = page.updateJob(preference);
if (job == null)
return;
IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService)
getSite().getService(IWorkbenchSiteProgressService.class);
service.schedule(job);
}
/**
* Updates the pages to current preference.
*/
private void updateView() {
// Sync with today's data:
Calendar today = Calendar.getInstance();
if (isSameDate(today, preferences.getEndDate())
|| today.before(preferences.getEndDate())) {
TrackingPlugin.getDefault().saveCurrentData();
}
// Mark all invisible pages as "not yet updated":
for (Map.Entry<IPage, Composite> entry : pages.entrySet()) {
boolean isVisible = stackLayout.topControl == entry.getValue();
if (isVisible) {
// update current visible page.
updatePage(entry.getKey(), preferences);
}
pageStatus.put(entry.getKey(), Boolean.valueOf(isVisible));
}
}
}