/*FreeMind - A Program for creating and viewing Mindmaps
*Copyright (C) 2000-2012 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitri Polivaev and others.
*
*See COPYING for Details
*
*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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package accessories.plugins;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JDialog;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;
import freemind.controller.MapModuleManager.MapModuleChangeObserver;
import freemind.controller.MenuItemSelectedListener;
import freemind.controller.StructuredMenuHolder;
import freemind.controller.actions.generated.instance.LogFileViewerConfigurationStorage;
import freemind.extensions.HookRegistration;
import freemind.main.FreeMind;
import freemind.main.LogFileLogHandler;
import freemind.main.LogFileLogHandler.LogReceiver;
import freemind.main.Tools;
import freemind.modes.MindMap;
import freemind.modes.Mode;
import freemind.modes.ModeController;
import freemind.modes.mindmapmode.MindMapController;
import freemind.modes.mindmapmode.actions.xml.ActionHandler;
import freemind.modes.mindmapmode.actions.xml.PrintActionHandler;
import freemind.modes.mindmapmode.hooks.MindMapHookAdapter;
import freemind.view.MapModule;
public class LogFileViewer extends MindMapHookAdapter implements
MapModuleChangeObserver, LogReceiver {
public static class Registration implements HookRegistration {
/**
* Maps MindMapController --> PrintActionHandler Here, a static map is
* used, as the HookRegistration are registered each time a map is
* changed. Thus, a normal member isn't possible here.
*/
private static HashMap mPrintActionHandler = new HashMap();
private final MindMapController modeController;
public Registration(ModeController controller, MindMap map) {
modeController = (MindMapController) controller;
}
public void register() {
}
public void deRegister() {
}
public void togglePrintAction() {
if (!mPrintActionHandler.containsKey(modeController)) {
PrintActionHandler printActionHandler = new freemind.modes.mindmapmode.actions.xml.PrintActionHandler(
modeController);
modeController.getActionFactory().registerHandler(
printActionHandler);
mPrintActionHandler.put(modeController, printActionHandler);
} else {
modeController.getActionFactory()
.deregisterHandler(
(ActionHandler) mPrintActionHandler
.get(modeController));
}
}
public boolean isPrintActionActive() {
return mPrintActionHandler.containsKey(modeController);
}
}
private static final String WINDOW_PREFERENCE_STORAGE_PROPERTY = LogFileViewer.class
.getName();
private MindMapController mMyMindMapController;
private JDialog mLogFileViewer;
private CloseAction mCloseAction;
private JTextArea mTextArea;
protected static java.util.logging.Logger logger = null;
private JMenuBar mMenuBar;
private UpdateTextAreaThread mUpdateTextAreaThread;
private SimpleFormatter mSimpleFormatter;
private final class CloseAction extends AbstractAction {
public CloseAction() {
super(getResourceString("LogFileViewer_close"));
}
public void actionPerformed(ActionEvent arg0) {
disposeDialog();
}
}
private final class PrintOperationAction extends AbstractAction implements
MenuItemSelectedListener {
public PrintOperationAction() {
super(getResourceString("LogFileViewer.PrintOperationAction"));
}
/*
* (non-Javadoc)
*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent
* )
*/
public void actionPerformed(ActionEvent pE) {
getRegistration().togglePrintAction();
}
/*
* (non-Javadoc)
*
* @see
* freemind.controller.MenuItemSelectedListener#isSelected(javax.swing
* .JMenuItem, javax.swing.Action)
*/
public boolean isSelected(JMenuItem pCheckItem, Action pAction) {
return getRegistration().isPrintActionActive();
}
}
private final class SetLogLevelAction extends AbstractAction implements
MenuItemSelectedListener {
private final Level mLevel;
public SetLogLevelAction(Level pLevel) {
super(getResourceString("LogFileViewer.SetLogLevelAction_"
+ pLevel.getName()));
mLevel = pLevel;
}
/*
* (non-Javadoc)
*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent
* )
*/
public void actionPerformed(ActionEvent pE) {
getBaseHandler().setLevel(mLevel);
List loggerList = getMindMapController().getFrame().getLoggerList();
for (Iterator it = loggerList.iterator(); it.hasNext();) {
Logger otherLogger = (Logger) it.next();
otherLogger.setLevel(mLevel);
}
}
/*
* (non-Javadoc)
*
* @see
* freemind.controller.MenuItemSelectedListener#isSelected(javax.swing
* .JMenuItem, javax.swing.Action)
*/
public boolean isSelected(JMenuItem pCheckItem, Action pAction) {
return getBaseHandler().getLevel().equals(mLevel);
}
}
public Registration getRegistration() {
return (Registration) getPluginBaseClass();
}
/*
* (non-Javadoc)
*
* @see freemind.extensions.HookAdapter#startupMapHook()
*/
public void startupMapHook() {
super.startupMapHook();
if (logger == null) {
logger = freemind.main.Resources.getInstance().getLogger(
this.getClass().getName());
}
mMyMindMapController = super.getMindMapController();
mSimpleFormatter = new SimpleFormatter();
// retrieve content
final String pathname = getMindMapController().getFrame()
.getFreemindDirectory()
+ File.separator
+ FreeMind.LOG_FILE_NAME + ".0";
String logFileContents = Tools.getFile(new File(pathname));
// done.
getMindMapController().getController().getMapModuleManager()
.addListener(this);
mLogFileViewer = new JDialog(getController().getFrame().getJFrame(),
false);
mLogFileViewer.setTitle(getResourceString("LogFileViewer_title")
+ pathname);
mLogFileViewer
.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
mLogFileViewer.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
disposeDialog();
}
});
mCloseAction = new CloseAction();
// the action title is changed by the following method, thus we create
// another close action.
Tools.addEscapeActionToDialog(mLogFileViewer, new CloseAction());
/** Menu **/
StructuredMenuHolder menuHolder = new StructuredMenuHolder();
mMenuBar = new JMenuBar();
JMenu mainItem = new JMenu(
getResourceString("MapControllerPopupDialog.Actions"));
menuHolder.addMenu(mainItem, "main/actions/.");
Action printOperationAction = new PrintOperationAction();
addAccelerator(menuHolder.addAction(printOperationAction,
"main/actions/printOperationAction"),
"keystroke_accessories/plugins/LogFileViewer_printOperationAction");
JMenu loggerItem = new JMenu(
getResourceString("MapControllerPopupDialog.LogLevels"));
menuHolder.addMenu(loggerItem, "main/loglevel/.");
Level[] levels = new Level[] {Level.FINEST, Level.FINER, Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.OFF};
for (int i = 0; i < levels.length; i++) {
Level level = levels[i];
menuHolder.addAction(new SetLogLevelAction(level),
"main/loglevel/setLogLevel_"+level.getName());
}
menuHolder.updateMenus(mMenuBar, "main/");
mLogFileViewer.setJMenuBar(mMenuBar);
mLogFileViewer.setSize(400, 400);
mLogFileViewer.setLayout(new BorderLayout());
mTextArea = new JTextArea(logFileContents);
mTextArea.setEditable(false);
mTextArea.getCaret().setVisible(true);
// scroll at the end
mTextArea.setCaretPosition(logFileContents.length());
mLogFileViewer.add(new JScrollPane(mTextArea), BorderLayout.CENTER);
// restore preferences:
// Retrieve window size and column positions.
LogFileViewerConfigurationStorage storage = (LogFileViewerConfigurationStorage) getMindMapController()
.decorateDialog(mLogFileViewer,
WINDOW_PREFERENCE_STORAGE_PROPERTY);
if (storage != null) {
// retrieve_additional_data_here
}
mLogFileViewer.setVisible(true);
mUpdateTextAreaThread = new UpdateTextAreaThread();
mUpdateTextAreaThread.start();
LogFileLogHandler baseHandler = getBaseHandler();
if (baseHandler != null) {
baseHandler.setLogReceiver(this);
}
}
protected Logger getBaseLogger() {
return logger.getParent();
}
/**
* @TODO: This is a bit dirty here, better would be to ask the resources class
* for the static logger, but this would result in too much new interfaces.
*/
protected LogFileLogHandler getBaseHandler() {
for (int i = 0; i < logger.getHandlers().length; i++) {
Handler handler = logger.getHandlers()[i];
if (handler instanceof LogFileLogHandler) {
LogFileLogHandler logHandler = (LogFileLogHandler) handler;
return logHandler;
}
}
return null;
}
/**
* Overwritten, as this dialog is not modal, but after the plugin has
* terminated, the dialog is still present and needs the controller to store
* its values.
* */
public MindMapController getMindMapController() {
return mMyMindMapController;
}
/**
*
*/
public void disposeDialog() {
mUpdateTextAreaThread.commitSuicide();
mUpdateTextAreaThread = null;
LogFileLogHandler baseHandler = getBaseHandler();
if (baseHandler != null) {
baseHandler.setLogReceiver(null);
}
// store window positions:
LogFileViewerConfigurationStorage storage = new LogFileViewerConfigurationStorage();
// put_additional_data_here
getMindMapController().storeDialogPositions(mLogFileViewer, storage,
WINDOW_PREFERENCE_STORAGE_PROPERTY);
getMindMapController().getController().getMapModuleManager()
.removeListener(this);
mLogFileViewer.setVisible(false);
mLogFileViewer.dispose();
}
/*
* (non-Javadoc)
*
* @see freemind.controller.MapModuleManager.MapModuleChangeObserver#
* isMapModuleChangeAllowed(freemind.view.MapModule, freemind.modes.Mode,
* freemind.view.MapModule, freemind.modes.Mode)
*/
public boolean isMapModuleChangeAllowed(MapModule pOldMapModule,
Mode pOldMode, MapModule pNewMapModule, Mode pNewMode) {
return true;
}
/*
* (non-Javadoc)
*
* @see freemind.controller.MapModuleManager.MapModuleChangeObserver#
* beforeMapModuleChange(freemind.view.MapModule, freemind.modes.Mode,
* freemind.view.MapModule, freemind.modes.Mode)
*/
public void beforeMapModuleChange(MapModule pOldMapModule, Mode pOldMode,
MapModule pNewMapModule, Mode pNewMode) {
}
/*
* (non-Javadoc)
*
* @see
* freemind.controller.MapModuleManager.MapModuleChangeObserver#afterMapClose
* (freemind.view.MapModule, freemind.modes.Mode)
*/
public void afterMapClose(MapModule pOldMapModule, Mode pOldMode) {
disposeDialog();
}
/*
* (non-Javadoc)
*
* @see freemind.controller.MapModuleManager.MapModuleChangeObserver#
* afterMapModuleChange(freemind.view.MapModule, freemind.modes.Mode,
* freemind.view.MapModule, freemind.modes.Mode)
*/
public void afterMapModuleChange(MapModule pOldMapModule, Mode pOldMode,
MapModule pNewMapModule, Mode pNewMode) {
disposeDialog();
}
/*
* (non-Javadoc)
*
* @see freemind.controller.MapModuleManager.MapModuleChangeObserver#
* numberOfOpenMapInformation(int, int)
*/
public void numberOfOpenMapInformation(int pNumber, int pIndex) {
}
public CloseAction getCloseAction() {
return mCloseAction;
}
/*
* (non-Javadoc)
*
* @see
* accessories.plugins.LogFileLogHandler.LogReceiver#receiveLog(java.util
* .logging.LogRecord)
*/
public void receiveLog(final LogRecord record) {
String msg = mSimpleFormatter.format(record);
mUpdateTextAreaThread.addToInbox(msg);
}
private class UpdateTextAreaThread extends Thread {
Vector mInbox = new Vector();
private boolean mCommitSuicide = false;
private boolean mSuicided = false;
/* (non-Javadoc)
* @see java.lang.Thread#run()
*/
public void run() {
while(!mCommitSuicide) {
final Vector queue = new Vector();
synchronized (mInbox) {
if(!mInbox.isEmpty()) {
queue.addAll(mInbox);
mInbox.clear();
}
}
if(!queue.isEmpty()) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
StringBuffer buffer = new StringBuffer();
for (Iterator it = queue.iterator(); it
.hasNext();) {
String msg = (String) it.next();
buffer.append(msg);
// buffer.append('\n');
}
String msg = buffer.toString();
// is cursor at the end?
final int length = mTextArea.getDocument().getLength();
boolean atEnd = mTextArea.getCaretPosition() == length;
mTextArea.getDocument().insertString(length, msg, null);
if (atEnd) {
// if at end, scroll again to the end
mTextArea.setCaretPosition(mTextArea.getDocument()
.getLength());
}
} catch (Exception ex) {
// We don't want to log anything here...
}
}
});
}
sleepALittle();
}
mSuicided = true;
}
/**
*
*/
public void commitSuicide() {
mCommitSuicide = true;
int timeout = 100;
while(timeout-->0) {
if(mSuicided)
break;
sleepALittle();
}
}
protected void sleepALittle() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
freemind.main.Resources.getInstance().logException(e);
}
}
public void addToInbox(String msg) {
synchronized (mInbox) {
mInbox.add(msg);
}
}
}
}