package org.rr.jeborker.gui; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Collections; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JDialog; import javax.swing.JFrame; import org.apache.commons.io.Charsets; import org.rr.commons.io.LineReader; import org.rr.commons.log.LoggerFactory; import org.rr.commons.mufs.IResourceHandler; import org.rr.commons.xml.XMLUtils; import org.rr.jeborker.app.preferences.APreferenceStore; import org.rr.jeborker.app.preferences.PreferenceStoreFactory; import org.rr.jeborker.gui.action.ActionFactory; import org.rr.jeborker.metadata.IMetadataReader; import org.rr.jeborker.metadata.IMetadataWriter; import org.rr.jeborker.metadata.MetadataHandlerFactory; public class PlainMetadataEditorController { private IMetadataReader reader; private PlainMetadataEditorView xmlMetadataView = null; private IResourceHandler resourceHandler; private static int locationOffset = 0; private int[] rowsToRefresh; private PlainMetadataEditorController(IResourceHandler resourceHandler, int[] rowsToRefresh) { this.rowsToRefresh = rowsToRefresh; this.reader = MetadataHandlerFactory.getReader(resourceHandler); this.resourceHandler = resourceHandler; } public static PlainMetadataEditorController getInstance(final IResourceHandler resourceHandler, int[] rowsToRefresh) { PlainMetadataEditorController controller = new PlainMetadataEditorController(resourceHandler, rowsToRefresh); return controller; } public void showXMLMetadataDialog() { PlainMetadataEditorView view = getView(); view.editor.setContentType(reader.getPlainMetadataMime()); String plainMetadata = reader.getPlainMetadata(); view.editor.setText(plainMetadata); if(reader.getPlainMetadataMime().contains("/xml")) { toggleFolds(plainMetadata); } view.setVisible(true); } /** * Close the folds which have large data values. * @param xml The xml which is shown with the view. */ private void toggleFolds(String xml) { if(xml == null || xml.length() == 0) { return; } LineReader lineReader = new LineReader(new ByteArrayInputStream(xml.getBytes())); StringBuilder buffer = new StringBuilder(); try { int lastOpenTag = 0; int cDataCount = 0; for(int i=0;;i++) { buffer.setLength(0); if(lineReader.readLine(buffer) > 0) { if(buffer.length() > 0) { if(buffer.charAt(buffer.length()-1) == '>') { lastOpenTag = i; cDataCount = 0; } else { cDataCount++; } if(lastOpenTag > 0 && cDataCount > 10) { //after a minimum of 10 data lines we close the fold. PlainMetadataEditorView view = getView(); view.xmlFoldingMargin.toggleFold(lastOpenTag); lastOpenTag = -1; } } } else { break; } } } catch (IOException e) { LoggerFactory.logWarning(this, "could not toggle large folds.", e); } } private PlainMetadataEditorView getView() { if(xmlMetadataView==null) { JFrame mainWindow = MainController.getController().getMainWindow(); try { xmlMetadataView = new PlainMetadataEditorView(mainWindow, reader.getPlainMetadataMime()); } catch (IOException e) { LoggerFactory.logWarning(this, "could not create XML editor component for " + reader.getEbookResource(), e); } this.initialize(); } return xmlMetadataView; } private void initialize() { JFrame mainWindow = MainController.getController().getMainWindow(); locationOffset = locationOffset + 10; xmlMetadataView.setLocation(mainWindow.getLocation().x + locationOffset, mainWindow.getLocation().y + locationOffset); xmlMetadataView.setSize(800, 600); xmlMetadataView.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); restorePropeties(); initListeners(); //do not enable the save button if no writer support is provided. boolean hasWriterSupport = MetadataHandlerFactory.hasWriterSupport(Collections.singletonList(resourceHandler)); xmlMetadataView.btnSave.setEnabled(hasWriterSupport); } private void initListeners() { xmlMetadataView.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { storeProperties(); locationOffset -= 10; } }); xmlMetadataView.btnAbort.setAction(new AbstractAction() { private static final long serialVersionUID = -2551783359830548125L; { putValue(Action.NAME, Bundle.getString("PlainMetadataEditorView.abort")); } @Override public void actionPerformed(ActionEvent e) { close(); } }); xmlMetadataView.btnSave.setAction(new AbstractAction() { private static final long serialVersionUID = -2551783359830548125L; { putValue(Action.NAME, Bundle.getString("PlainMetadataEditorView.save")); } @Override public void actionPerformed(ActionEvent e) { final IMetadataWriter writer = MetadataHandlerFactory.getWriter(resourceHandler); final String metadataContent = xmlMetadataView.editor.getText(); writer.storePlainMetadata(metadataContent.getBytes(Charsets.UTF_8)); close(); ActionFactory.getAction(ActionFactory.COMMON_ACTION_TYPES.REFRESH_ENTRY_ACTION, resourceHandler.toString()).invokeAction(); MainController.getController().refreshTableRows(rowsToRefresh, true); } }); xmlMetadataView.btnFormat.setAction(new AbstractAction() { private static final long serialVersionUID = -2551783359830548125L; { putValue(Action.NAME, Bundle.getString("PlainMetadataEditorView.format")); } @Override public void actionPerformed(ActionEvent e) { final String metadataContent = xmlMetadataView.editor.getText(); final String formattedMetadataContent = XMLUtils.formatXML(metadataContent, 4, 160); //make it invisible because the editor should not show the cover data //in an opened fold because this can is very slow and possibly can hang the app //for a while. xmlMetadataView.editor.setVisible(false); try { xmlMetadataView.editor.setText(formattedMetadataContent); toggleFolds(formattedMetadataContent); } catch(Exception ex) { LoggerFactory.logWarning(this, "could not format metadata.", ex); } finally { xmlMetadataView.editor.setVisible(true); } } }); } public void close() { storeProperties(); locationOffset -= 10; xmlMetadataView.setVisible(false); xmlMetadataView.dispose(); if(this.reader != null) { this.reader = null; } } private void storeProperties() { final APreferenceStore preferenceStore = PreferenceStoreFactory.getPreferenceStore(PreferenceStoreFactory.DB_STORE); preferenceStore.addGenericEntryAsNumber("metadataDialogSizeWidth", getView().getSize().width); preferenceStore.addGenericEntryAsNumber("metadataDialogSizeHeight", getView().getSize().height); preferenceStore.addGenericEntryAsNumber("metadataDialogLocationX", getView().getLocation().x - locationOffset); preferenceStore.addGenericEntryAsNumber("metadataDialogLocationY", getView().getLocation().y - locationOffset); } private void restorePropeties() { final APreferenceStore preferenceStore = PreferenceStoreFactory.getPreferenceStore(PreferenceStoreFactory.DB_STORE); //restore the window size from the preferences. Number metadataDialogSizeWidth = preferenceStore.getGenericEntryAsNumber("metadataDialogSizeWidth"); Number metadataDialogSizeHeight = preferenceStore.getGenericEntryAsNumber("metadataDialogSizeHeight"); if(metadataDialogSizeWidth!=null && metadataDialogSizeHeight!=null) { getView().setSize(metadataDialogSizeWidth.intValue(), metadataDialogSizeHeight.intValue()); } //restore window location Point entryAsScreenLocation = preferenceStore.getGenericEntryAsScreenLocation("metadataDialogLocationX", "metadataDialogLocationY"); if(entryAsScreenLocation != null) { getView().setLocation(entryAsScreenLocation); } } }