package edu.harvard.wcfia.yoshikoder.ui;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.MouseEvent;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import edu.harvard.wcfia.yoshikoder.Yoshikoder;
import edu.harvard.wcfia.yoshikoder.document.DocumentList;
import edu.harvard.wcfia.yoshikoder.document.DocumentListImpl;
import edu.harvard.wcfia.yoshikoder.document.LazyYKDocument;
import edu.harvard.wcfia.yoshikoder.document.YKDocument;
import edu.harvard.wcfia.yoshikoder.document.YKDocumentFactory;
import edu.harvard.wcfia.yoshikoder.document.tokenizer.Location;
import edu.harvard.wcfia.yoshikoder.document.tokenizer.TokenList;
import edu.harvard.wcfia.yoshikoder.document.tokenizer.TokenListImpl;
import edu.harvard.wcfia.yoshikoder.document.tokenizer.TokenizationCache;
import edu.harvard.wcfia.yoshikoder.document.tokenizer.TokenizationService;
import edu.harvard.wcfia.yoshikoder.ui.model.DocumentListModel;
import edu.harvard.wcfia.yoshikoder.util.CharsetWrapper;
import edu.harvard.wcfia.yoshikoder.util.DialogUtil;
import edu.harvard.wcfia.yoshikoder.util.FileUtil;
import edu.harvard.wcfia.yoshikoder.util.LocaleWrapper;
import edu.harvard.wcfia.yoshikoder.util.Messages;
import edu.harvard.wcfia.yoshikoder.util.TaskWorker;
public class DocumentPanel extends JPanel {
private static Logger log =
Logger.getLogger("edu.harvard.wcfia.yoshikoder.ui.DocumentPanel");
protected String lostText = Messages.getString("DocumentPanel.lostDocument");
protected TaskWorker tworker;
protected Highlighter h;
protected DefaultHighlighter.DefaultHighlightPainter myHighlightPainter;
protected DocumentList documentList;
protected JTextArea area;
protected DefaultListModel model;
protected JList docList;
protected Font displayFont;
protected Color currentColor;
protected JLabel currentDocument = new JLabel();
protected Yoshikoder yoshikoder;
protected ListSelectionListener listListener = new ListSelectionListener(){
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting())
return;
if (docList.getSelectedIndex() != -1){
updateView();
log.info("updating view in listListener");
} else {
area.setText("");
currentDocument.setText("");
}
}
};
public DocumentPanel(Yoshikoder yk, DocumentList dl){
super(new GridBagLayout());
yoshikoder = yk;
area = new JTextArea(10, 40);
area.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
area.setLineWrap(true);
area.setWrapStyleWord(true);
area.setEditable(false);
displayFont = area.getFont(); // just take the default
currentColor = Color.yellow;
docList = new JList(){
public String getToolTipText(MouseEvent evt) {
int index = locationToIndex(evt.getPoint());
if (index != -1){
DocumentState state = (DocumentState)model.getElementAt(index);
return state.getTooltip();
} else {
return null;
}
}
};
docList.addListSelectionListener(listListener);
setDocumentList(dl);
docList.setSelectionMode(DefaultListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
h = area.getHighlighter();
myHighlightPainter =
new DefaultHighlighter.DefaultHighlightPainter( currentColor );
// arrangement
//JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
//JLabel topLabel = new JLabel(Messages.getString("DocumentPanel.topLabel"));
//topLabel.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
currentDocument.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
c.fill = GridBagConstraints.HORIZONTAL;
c.gridy = 0;
c.gridx = 0;
c.weightx = 0.8;
c.weighty = 0;
c.insets = new Insets(0, 0, 0, 5);
add(currentDocument, c);
JLabel lab2 = new JLabel("Documents");
lab2.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
c.fill = GridBagConstraints.HORIZONTAL;
c.gridy = 0;
c.gridx = 1;
c.weightx = 0.2;
c.weighty = 0;
c.insets = new Insets(0, 0, 0, 0);
add(lab2, c);
JScrollPane pa = new JScrollPane(area);
c.fill = GridBagConstraints.BOTH;
c.gridy = 1;
c.gridx = 0;
c.weightx = 0.8;
c.weighty = 1;
c.insets = new Insets(0, 0, 0, 5);
add(pa, c);
JScrollPane sp = new JScrollPane(docList);
c.fill = GridBagConstraints.BOTH;
c.gridy = 1;
c.gridx = 1;
c.weightx = 0.2;
c.weighty = 1;
c.insets = new Insets(0, 0, 0, 0);
add(sp, c);
}
protected void setHighlightColor(Color col){
if (!col.equals(myHighlightPainter.getColor()))
myHighlightPainter =
new DefaultHighlighter.DefaultHighlightPainter(col);
}
// removed the threading which was screwing up multi-delete
protected void updateView(){
final DocumentState docstate = (DocumentState)docList.getSelectedValue();
if (docstate == null) return;
String txt;
Set colors = null;
Map colorToLocation = null;
try {
txt = docstate.getDocument().getText();
} catch (Exception ex){
txt = lostText;
}
colors = docstate.getHighlightColors();
//log.info("Finding colors: " + colors.toString());
colorToLocation = new HashMap();
for (Iterator iter = colors.iterator(); iter.hasNext();) {
Color col = (Color) iter.next();
//log.info("examining " + col.toString());
Set s = docstate.getHighlightLocations(col);
//log.info("Found a set of locations: " + s.toString());
colorToLocation.put(col, s);
}
area.setText(txt);
currentDocument.setText(docstate.getDocument().getTitle());
area.setCaretPosition(docstate.getCaretPosition());
Font pf = docstate.getDocument().getPreferredFont();
if (pf != null)
area.setFont(pf);
else if (!displayFont.equals(area.getFont())){
log.info("Setting area to font: " + displayFont.toString());
area.setFont(displayFont);
}
// add highlights
for (Iterator iter = colors.iterator(); iter.hasNext();) {
Color col = (Color) iter.next();
setHighlightColor(col);
Set s = (Set)colorToLocation.get(col);
for (Iterator iterator = s.iterator(); iterator.hasNext();) {
Location loc = (Location) iterator.next();
try {
h.addHighlight(loc.getStartPosition(),
loc.getEndPosition(), myHighlightPainter);
} catch (BadLocationException ble){
log.warning("highlighting error");
}
}
}
/*
tworker = new TaskWorker(this){
String txt;
Set colors;
Map colorToLocation;
protected void doWork() throws Exception {
txt = docstate.getDocument().getText();
colors = docstate.getHighlightColors();
//log.info("Finding colors: " + colors.toString());
colorToLocation = new HashMap();
for (Iterator iter = colors.iterator(); iter.hasNext();) {
Color col = (Color) iter.next();
//log.info("examining " + col.toString());
Set s = docstate.getHighlightLocations(col);
//log.info("Found a set of locations: " + s.toString());
colorToLocation.put(col, s);
}
}
protected void onError() {
txt = lostText;
}
protected void onSuccess() {
area.setText(txt);
area.setCaretPosition(docstate.getCaretPosition());
Font pf = docstate.getDocument().getPreferredFont();
if (pf != null)
area.setFont(pf);
else if (!displayFont.equals(area.getFont())){
log.info("Setting area to font: " + displayFont.toString());
area.setFont(displayFont);
}
// add highlights
for (Iterator iter = colors.iterator(); iter.hasNext();) {
Color col = (Color) iter.next();
setHighlightColor(col);
Set s = (Set)colorToLocation.get(col);
for (Iterator iterator = s.iterator(); iterator.hasNext();) {
Location loc = (Location) iterator.next();
try {
h.addHighlight(loc.getStartPosition(),
loc.getEndPosition(), myHighlightPainter);
} catch (BadLocationException ble){
log.warning("highlighting error");
}
}
}
}
};
tworker.start();
*/
}
public void setDocumentList(DocumentList dl){
documentList = dl;
/*
// tidy up lost project documents
Set lostdocs = new HashSet();
for (Iterator iter = documentList.iterator(); iter.hasNext();) {
YKDocument doc = (YKDocument) iter.next();
if (!doc.getLocation().exists())
lostdocs.add(doc);
}
for (Iterator iter = lostdocs.iterator(); iter.hasNext();) {
YKDocument lostdoc = (YKDocument) iter.next();
log.info("Deleting non-existent " + lostdoc.getTitle() + " from the documentlist");
documentList.remove(lostdoc);
}
*/
model = new DocumentListModel(documentList);
docList.setModel(model);
if (model.size()>0)
docList.setSelectedIndex(0);
}
public void addDocument(YKDocument doc){
DocumentState ds = new DocumentState(doc);
model.addElement(ds);
docList.setSelectedValue(ds, true);
}
public void removeDocument(YKDocument doc){
DocumentState ds = new DocumentState(doc);
int where = model.indexOf(ds);
if (where != -1){
model.removeElement(ds);
log.info("removed the document " + ds.getDocument().getTitle() + " at " + where + " from model");
log.info("selecting " + (where-1));
if ((where-1) >= 0){
log.info("selecting " + (where-1));
docList.setSelectedIndex(where-1);
} else if (where < model.size()){
log.info("selecting " + where);
docList.setSelectedIndex(where);
} else {
log.warning("That was the last document");
area.setText("");
currentDocument.setText("");
}
}
}
public void addHighlights(TokenList tl, Color col){
DocumentState ds = (DocumentState)docList.getSelectedValue();
if (ds == null) {
log.info("No document state selected");
return;
}
ds.addHighlights(tl, col);
updateView();
}
public void removeHighlights(){
DocumentState ds = (DocumentState)docList.getSelectedValue();
if (ds == null) {
log.info("No document state selected");
return;
}
ds.removeHighlights();
updateView();
}
public void setSelectedDocument(YKDocument doc){
DocumentState ds = new DocumentState(doc);
docList.setSelectedValue(ds, true);
// view is updated by selection listener
}
public YKDocument getSelectedDocument(){
DocumentState ds = (DocumentState)docList.getSelectedValue();
if (ds != null)
return ds.getDocument();
else
return null;
}
class DocumentEditPanel extends JPanel {
DocumentState state;
JComboBox localeBox = new JComboBox(FileUtil.getLocaleList().toArray(new LocaleWrapper[]{}));
JComboBox encodingBox = new JComboBox(FileUtil.getCharsetList().toArray(new CharsetWrapper[]{}));
JTextField titleField = new JTextField();
public Locale getSelectedLocale(){
return ((LocaleWrapper)localeBox.getSelectedItem()).locale;
}
public Charset getSelectedEncoding(){
return ((CharsetWrapper)encodingBox.getSelectedItem()).charset;
}
public String getNewTitle(){
return titleField.getText();
}
public void setDocumentState(DocumentState ds){
state = ds;
YKDocument doc = state.getDocument();
localeBox.setSelectedItem(new LocaleWrapper(doc.getLocale()));
encodingBox.setSelectedItem(new CharsetWrapper(Charset.forName(doc.getCharsetName())));
titleField.setText(doc.getTitle());
}
public DocumentState getDocumentState(){
return state;
}
public DocumentEditPanel(DocumentState s) {
super(new GridBagLayout());
state = s;
encodingBox.setSelectedItem(new CharsetWrapper(Charset.forName(s.getDocument().getCharsetName())));
//System.err.println(s.getDocument().getTitle() + " has locale " + s.getDocument().getLocale());
localeBox.setSelectedItem(new LocaleWrapper(s.getDocument().getLocale()));
titleField.setText(s.getDocument().getTitle());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.insets = new Insets(0, 5, 5, 5);
c.gridx = 0;
c.gridy = 0;
add(new JLabel("Title"), c);
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 1;
add(new JLabel("Encoding"), c);
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 2;
add(new JLabel("Locale"), c);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 0;
add(titleField, c);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 1;
add(encodingBox, c);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 2;
add(localeBox, c);
}
}
protected DocumentEditPanel dep = null;
// return whether there were any actual changes
public boolean editDocument(){
DocumentState ds = (DocumentState)docList.getSelectedValue();
if (dep == null)
dep = new DocumentEditPanel(ds);
else
dep.setDocumentState(ds);
int resp = JOptionPane.showConfirmDialog(yoshikoder, dep,
"Edit Document", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
if (resp != JOptionPane.OK_OPTION)
return false;
String newtitle = dep.getNewTitle();
boolean exists = false;
for (int ii = 0; ii < docList.getModel().getSize(); ii++) {
DocumentState d = (DocumentState) docList.getModel().getElementAt(ii);
if (d.getDocument() != dep.getDocumentState().getDocument()){ // *exact* reference equality please
if (d.getDocument().getTitle().equals(newtitle)){
exists = true;
break;
}
}
}
if (exists){
DialogUtil.yelp(yoshikoder, "A document with this title already exists", "Title exists");
return false;
} else {
boolean changed = false;
DocumentState s = dep.getDocumentState();
YKDocument doc = s.getDocument();
if (!doc.getCharsetName().equals(dep.getSelectedEncoding().name())){
doc.setCharsetName(dep.getSelectedEncoding().name());
changed = true;
}
if (changed){ // need to remove cached tokens
TokenizationCache cache = yoshikoder.getTokenizationCache();
cache.removeTokenList(doc);
if (doc instanceof LazyYKDocument)
((LazyYKDocument)doc).clearCachedText(); // force a re-read from file system
// does not do anything weird to the text area immediately, but probably should...
}
if (!doc.getLocale().equals(dep.getSelectedLocale())){
doc.setLocale(dep.getSelectedLocale());
changed = true;
}
if (!doc.getTitle().equals(newtitle)){
doc.setTitle(newtitle);
changed = true;
}
repaint();
return changed;
// TODO reorder the list
//model = new DocumentListModel(documentList);
// trigger updates?
//docList.setModel(model);
// TODO select the original document
//docList.setSelectedValue(s, true);
}
}
public YKDocument[] getSelectedDocuments(){
//Object[] o = docList.getSelectedValues();
//for (int i = 0; i < o.length; i++) {
// System.err.println("Is this a documentstate instance? " + (o[i] instanceof DocumentState));
//}
int[] indices = docList.getSelectedIndices();
YKDocument[] d = new YKDocument[indices.length];
if (indices.length > 0){
for (int ii = 0; ii < indices.length; ii++) {
DocumentState ds = (DocumentState)docList.getModel().getElementAt(indices[ii]);
d[ii] = ds.getDocument();
//System.err.println(d[ii].getTitle() + " has been extracted from some document state object");
}
}
return d;
}
public void setDisplayFont(Font f){
displayFont = f;
updateView();
}
public static void main(String[] args) throws Exception {
/*
DocumentList dl = new DocumentListImpl();
for (int ii=0; ii<4; ii++){
YKDocument doc =
YKDocumentFactory.createDummyDocument("title" + ii,
"text for document " + ii, "UTF-8");
dl.add(doc);
}
DocumentPanel pane = new DocumentPanel(dl);
YKDocument doc = pane.getSelectedDocument();
TokenList tl = TokenizationService.getTokenizationService().tokenize(doc);
TokenList first = new TokenListImpl();
first.add(tl.get(0));
pane.addHighlights(first, Color.yellow);
first.clear();
first.add(tl.get(1));
pane.addHighlights(first, Color.lightGray);
JOptionPane jo = new JOptionPane(pane);
JDialog dia = jo.createDialog((JFrame)null, "Documents");
dia.show();
System.exit(0);
*/
}
}