/******************************************************************************* * Copyright (c) 2011 Subgraph. * 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 * * Contributors: * Subgraph - initial API and implementation ******************************************************************************/ package com.subgraph.vega.internal.ui.http; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.jface.action.ContributionItem; import org.eclipse.jface.action.StatusLineLayoutData; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.IPageListener; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IViewReference; import org.eclipse.ui.IWindowListener; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import com.subgraph.vega.ui.http.Activator; import com.subgraph.vega.ui.http.intercept.InterceptView; import com.subgraph.vega.ui.http.intercept.queue.InterceptQueueView; public class ProxyStatusLineContribution extends ContributionItem { private final static int BLINK_INTERVAL = 500; private final Logger logger = Logger.getLogger("proxy"); private CLabel label; private Image proxyAlert = Activator.getImageDescriptor("icons/proxy_alert.png").createImage(); private Image proxyRunning = Activator.getImageDescriptor("icons/proxy_running.png").createImage(); private Image proxyStopped = Activator.getImageDescriptor("icons/proxy_stopped.png").createImage(); private final Timer alertBlinkTimer = new Timer(); private TimerTask alertBlinkTask; private boolean alertEnabled; private String runningMessage; private int viewCnt = 0; /** Count of currently visible views that display interceptor information */ private int queueCnt = 0; private Date lastViewChange = new Date(); public ProxyStatusLineContribution() { super(); createListeners(); } @Override public void fill(Composite parent) { new Label(parent, SWT.SEPARATOR); label = new CLabel(parent, SWT.SHADOW_NONE); setProxyStopped(); StatusLineLayoutData layoutData = new StatusLineLayoutData(); layoutData.widthHint = 220; label.setLayoutData(layoutData); label.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { synchronized(alertBlinkTimer) { if(queueCnt != 0) { handleAlertClick(); } } } }); } private void createListeners() { IWindowListener listener = new IWindowListener() { @Override public void windowActivated(IWorkbenchWindow window) { } @Override public void windowDeactivated(IWorkbenchWindow window) { } @Override public void windowClosed(IWorkbenchWindow window) { } @Override public void windowOpened(IWorkbenchWindow window) { createPageListener(window); } }; IWorkbench wb = PlatformUI.getWorkbench(); wb.addWindowListener(listener); } private void createPageListener(IWorkbenchWindow window) { IPageListener listener = new IPageListener() { @Override public void pageActivated(IWorkbenchPage page) { } @Override public void pageClosed(IWorkbenchPage page) { } @Override public void pageOpened(IWorkbenchPage page) { createPartListener(page); } }; window.addPageListener(listener); IWorkbenchPage workbenchPages[] = window.getPages(); for (int idx = 0; idx < workbenchPages.length; idx++) { createPartListener(workbenchPages[idx]); } } private void createPartListener(IWorkbenchPage page) { IPartListener2 listener = new IPartListener2() { @Override public void partActivated(IWorkbenchPartReference partRef) { } @Override public void partBroughtToTop(IWorkbenchPartReference partRef) { } @Override public void partClosed(IWorkbenchPartReference partRef) { } @Override public void partDeactivated(IWorkbenchPartReference partRef) { } @Override public void partOpened(IWorkbenchPartReference partRef) { } @Override public void partHidden(IWorkbenchPartReference partRef) { final IWorkbenchPart part = partRef.getPart(false); if (part instanceof InterceptView || part instanceof InterceptQueueView) { synchronized (alertBlinkTimer) { lastViewChange = new Date(); viewCnt--; } } } @Override public void partVisible(IWorkbenchPartReference partRef) { final IWorkbenchPart part = partRef.getPart(false); if (part instanceof InterceptView || part instanceof InterceptQueueView) { synchronized (alertBlinkTimer) { lastViewChange = new Date(); viewCnt++; // we don't cancel the alert here in case there are other callbacks still to be invoked } } } @Override public void partInputChanged(IWorkbenchPartReference partRef) { } }; page.addPartListener(listener); } public void setProxyRunning(int port) { runningMessage = "Proxy running on port " + port; setLabelImageAndText(proxyRunning, runningMessage); } public void setProxyPending(int size) { setInterceptionQueueSize(size); } public void setProxyStopped() { stopAlert(); setLabelImageAndText(proxyStopped, "Proxy is not running"); } /** * Determine how many InterceptView and InterceptQueueView views are visible to the user. */ private int countViewsVisible() { int cnt = 0; IWorkbench wb = PlatformUI.getWorkbench(); if (wb != null) { IWorkbenchWindow windows[] = wb.getWorkbenchWindows(); for (int windowIdx = 0; windowIdx < windows.length; windowIdx++) { IWorkbenchPage pages[] = windows[windowIdx].getPages(); for (int pageIdx = 0; pageIdx < pages.length; pageIdx++) { IViewReference references[] = pages[pageIdx].getViewReferences(); for (int referenceIdx = 0; referenceIdx < references.length; referenceIdx++) { IViewPart part = references[referenceIdx].getView(false); if (part instanceof InterceptView || part instanceof InterceptQueueView) { if (pages[pageIdx].isPartVisible(part) == true) { cnt++; } } } } } } return cnt; } private void setInterceptionQueueSize(final int size) { label.getDisplay().asyncExec(new Runnable() { @Override public void run() { synchronized(alertBlinkTimer) { queueCnt = size; if (size > 0) { if (!alertEnabled) { // views are counted each time due to a problem with Eclipse. when the application is opened // out of focus, IPartListener2.partVisible callbacks are invoked when focus is gained, // invalidating the initial view count. for now we have to count every time here. viewCnt = countViewsVisible(); if (viewCnt == 0) { startAlert(); } } final String msg = "("+ size + ") messages intercepted"; setLabelImageAndTextFromUiThread(proxyAlert, msg); } else { stopAlert(); setLabelImageAndTextFromUiThread(proxyRunning, runningMessage); } } } }); } /** Must be invoked in a synchronized block */ private void startAlert() { if(alertBlinkTask == null) { alertBlinkTask = createAlertTask(); alertBlinkTimer.scheduleAtFixedRate(alertBlinkTask, BLINK_INTERVAL, BLINK_INTERVAL); alertEnabled = true; } } /** Must be invoked in a synchronized block */ private void stopAlert() { alertEnabled = false; if(alertBlinkTask != null) { alertBlinkTask.cancel(); alertBlinkTask = null; } } private TimerTask createAlertTask() { return new TimerTask() { private boolean state = true; @Override public void run() { synchronized(alertBlinkTimer) { if (viewCnt != 0) { Date currentTime = new Date(); if (currentTime.getTime() - lastViewChange.getTime() >= BLINK_INTERVAL) { stopAlert(); if (!state) { setLabelImage(proxyAlert); } return; } } } state = !state; if(state) { setLabelImage(proxyAlert); } else { setLabelImage(proxyStopped); } } }; } private void setLabelImage(Image image) { setLabelImageAndText(image, null); } private void setLabelImageAndText(final Image image, final String text) { if(label == null || label.isDisposed()) { return; } label.getDisplay().asyncExec(new Runnable() { @Override public void run() { setLabelImageAndTextFromUiThread(image, text); } }); } private void setLabelImageAndTextFromUiThread(final Image image, final String text) { if(image != null) { label.setImage(image); } if(text != null) { label.setText(text); } label.pack(true); label.getParent().layout(); } private void handleAlertClick() { IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); try { window.getActivePage().showView(InterceptView.VIEW_ID); } catch (Exception e) { logger.log(Level.WARNING, "Failed to open interception view", e); } } }