/*
* jMemorize - Learning made easy (and fun) - A Leitner flashcards tool
* Copyright(C) 2004-2008 Riad Djemili and contributors
*
* 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package jmemorize.gui.swing;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.net.URL;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JTextPane;
import javax.swing.TransferHandler;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import jmemorize.core.Card;
import jmemorize.core.Category;
import jmemorize.core.FormattedText;
import jmemorize.core.Main;
import jmemorize.gui.swing.panels.CardSidePanel;
import jmemorize.gui.swing.widgets.CardTable;
import jmemorize.gui.swing.widgets.CategoryTree;
/**
* Organizes datatransfers between the card table and the category tree.
*
* @author djemili
*/
public class GeneralTransferHandler extends TransferHandler
{
public class CardsTransferable implements Transferable
{
private List<Card> m_cards;
public CardsTransferable(List<Card> cards)
{
m_cards = cards;
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException
{
if (!isDataFlavorSupported(flavor))
throw new UnsupportedFlavorException(flavor);
if (CARDS_FLAVOR.equals(flavor))
return m_cards;
StringBuffer buffer = new StringBuffer();
for (Card card : m_cards)
{
buffer.append(card.getFrontSide().getText().getUnformatted());
buffer.append(" - ");
buffer.append(card.getBackSide().getText().getUnformatted());
buffer.append('\n');
}
return buffer.toString();
}
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[] {CARDS_FLAVOR, DataFlavor.stringFlavor};
}
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return CARDS_FLAVOR.equals(flavor) ||
DataFlavor.stringFlavor.equals(flavor);
}
}
public class CategoryTransferable implements Transferable
{
private Category m_category;
public CategoryTransferable(Category category)
{
m_category = category;
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException
{
if (!isDataFlavorSupported(flavor))
throw new UnsupportedFlavorException(flavor);
if (CATEGORY_FLAVOR.equals(flavor))
return m_category;
return m_category.getName();
}
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[] {CATEGORY_FLAVOR, DataFlavor.stringFlavor};
}
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return CATEGORY_FLAVOR.equals(flavor) ||
DataFlavor.stringFlavor.equals(flavor);
}
}
/**
* Represents a formatted text and it source document. We need the source
* for CUT-operations where we need to remove the formatted section from
* the original document.
*/
public class FormattedTextSection
{
private FormattedText m_text;
private Document m_document;
private int m_start;
private int m_end;
public FormattedTextSection(StyledDocument doc, int start, int end)
{
m_document = doc;
m_start = start;
m_end = end;
m_text = FormattedText.formatted(doc, start, end);;
}
public FormattedText getText()
{
return m_text;
}
public Document getDocument()
{
return m_document;
}
public int getStart()
{
return m_start;
}
public int getEnd()
{
return m_end;
}
}
public class FormattedTextTransferable implements Transferable
{
private FormattedTextSection m_formattedText;
public FormattedTextTransferable(StyledDocument doc, int start, int end)
{
m_formattedText = new FormattedTextSection(doc, start, end);
}
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException
{
if (!isDataFlavorSupported(flavor))
throw new UnsupportedFlavorException(flavor);
if (FORMATTED_TEXT_FLAVOR.equals(flavor))
return m_formattedText;
return m_formattedText.getText().getUnformatted();
}
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[] {FORMATTED_TEXT_FLAVOR,
DataFlavor.stringFlavor};
}
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return FORMATTED_TEXT_FLAVOR.equals(flavor) ||
DataFlavor.stringFlavor.equals(flavor);
}
}
public final static DataFlavor CARDS_FLAVOR =
new DataFlavor(Card.class, "Card"); //$NON-NLS-1$
public final static DataFlavor CATEGORY_FLAVOR =
new DataFlavor(Category.class, "Category"); //$NON-NLS-1$
public final static DataFlavor FORMATTED_TEXT_FLAVOR =
new DataFlavor(FormattedTextSection.class, "FormattedText"); //$NON-NLS-1$
private CardSidePanel m_cardSidePanel;
public GeneralTransferHandler()
{
}
public GeneralTransferHandler(CardSidePanel cardSidePanel)
{
m_cardSidePanel = cardSidePanel;
}
/*
* @see javax.swing.TransferHandler
*/
public int getSourceActions(JComponent c)
{
return COPY_OR_MOVE;
}
/*
* @see javax.swing.TransferHandler
*/
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors)
{
if (comp instanceof CategoryTree)
{
for (int i = 0; i < transferFlavors.length; i++)
{
if (transferFlavors[i] == CARDS_FLAVOR ||
transferFlavors[i] == CATEGORY_FLAVOR)
{
return true;
}
}
}
if (comp instanceof JTextPane)
{
for (int i = 0; i < transferFlavors.length; i++)
{
if (transferFlavors[i] == FORMATTED_TEXT_FLAVOR ||
transferFlavors[i] == DataFlavor.stringFlavor)
{
return true;
}
}
}
return false;
}
/*
* @see javax.swing.TransferHandler
*/
@SuppressWarnings("unchecked")
public boolean importData(JComponent comp, Transferable t)
{
try
{
Category targetCategory;
if (comp instanceof CategoryTree)
{
CategoryTree tree = (CategoryTree)comp;
targetCategory = tree.getSelectedCategory();
}
else if (comp instanceof CardTable)
{
CardTable table = (CardTable)comp;
targetCategory = table.getView().getCategory();
}
else if (comp instanceof JTextPane)
{
JTextPane textPane = (JTextPane)comp;
if (t.isDataFlavorSupported(FORMATTED_TEXT_FLAVOR))
{
int start = textPane.getSelectionStart();
FormattedTextSection fText = (FormattedTextSection)t.getTransferData(
FORMATTED_TEXT_FLAVOR);
fText.getText().insertIntoDocument(textPane.getStyledDocument(), start);
}
else if (t.isDataFlavorSupported(DataFlavor.imageFlavor))
{
if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
{
String link = (String)t.getTransferData(DataFlavor.stringFlavor);
link = link.substring(0, link.indexOf('\n'));
String lower = link.toLowerCase();
if (lower.startsWith("http://"))
{
if (lower.endsWith(".jpg") ||
lower.endsWith(".gif") ||
lower.endsWith(".png") ||
lower.endsWith(".jpeg") ||
lower.endsWith(".bmp"))
{
URL url = new URL(link);
ImageIcon icon = new ImageIcon(url);
icon.setDescription(link);
m_cardSidePanel.addImage(icon);
m_cardSidePanel.getTextPane().requestFocus();
return true;
}
}
}
}
else if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
{
int start = textPane.getSelectionStart();
String text = (String)t.getTransferData(DataFlavor.stringFlavor);
textPane.getDocument().insertString(start, text, null);
}
return true;
}
else
{
return false;
}
if (t.isDataFlavorSupported(CARDS_FLAVOR))
{
List<Card> cards = (List<Card>)t.getTransferData(CARDS_FLAVOR);
for (Card card : cards)
{
targetCategory.addCard((Card)card.clone(), card.getLevel());
}
return true;
}
else if (t.isDataFlavorSupported(CATEGORY_FLAVOR))
{
Category category = (Category)t.getTransferData(CATEGORY_FLAVOR);
if (!category.contains(targetCategory))
{
targetCategory.addCategoryChild(copyCategories(category));
return true;
}
else
{
return false;
}
}
}
catch (Exception e)
{
Main.logThrowable("Error importing data from clipboard", e);
}
return false;
}
/*
* @see javax.swing.TransferHandler#createTransferable(javax.swing.JComponent)
*/
protected Transferable createTransferable(JComponent c)
{
if (c instanceof CardTable)
{
CardTable table = (CardTable)c;
return new CardsTransferable(table.getSelectedCards());
}
else if (c instanceof CategoryTree)
{
CategoryTree tree = (CategoryTree)c;
Category category = tree.getSelectedCategory();
// dont allow operations with root category
return category.getParent() != null ? new CategoryTransferable(category) : null;
}
else if (c instanceof JTextPane)
{
JTextPane textPane = (JTextPane)c;
int start = textPane.getSelectionStart();
int end = textPane.getSelectionEnd();
if (end - start <= 0)
return null;
StyledDocument doc = (StyledDocument)textPane.getDocument();
return new FormattedTextTransferable(doc, start, end);
}
return null;
}
/*
* @see javax.swing.TransferHandler#exportDone
*/
@SuppressWarnings("unchecked")
protected void exportDone(JComponent source, Transferable data, int action)
{
if (action != MOVE)
return;
try
{
if (data.isDataFlavorSupported(CARDS_FLAVOR))
{
CardTable table = (CardTable)source;
Category category = table.getView().getCategory();
List<Card> cards = (List<Card>)data.getTransferData(CARDS_FLAVOR);
for (Card card : cards)
{
category.removeCard(card);
}
}
else if (data.isDataFlavorSupported(CATEGORY_FLAVOR))
{
Category category = (Category)data.getTransferData(CATEGORY_FLAVOR);
category.remove();
}
else if (data.isDataFlavorSupported(FORMATTED_TEXT_FLAVOR))
{
FormattedTextSection formattedText =
(FormattedTextSection)data.getTransferData(FORMATTED_TEXT_FLAVOR);
int start = formattedText.getStart();
int end = formattedText.getEnd();
formattedText.getDocument().remove(start, end-start);
}
}
catch (Exception e)
{
Main.logThrowable("Error exporting data to clipboard", e);
}
}
private Category copyCategories(Category original) throws CloneNotSupportedException
{
Category copy = new Category(original.getName());
// first copy categories..
for (Category category : original.getChildCategories())
{
copy.addCategoryChild(copyCategories(category));
}
// ..then copy cards
for (int i = 0; i < original.getNumberOfDecks(); i++)
{
for (Card card : original.getLocalCards(i))
{
copy.addCard((Card)card.clone(), i);
}
}
return copy;
}
}