Dépôt officiel du code source de l'ERP OpenConcerto
Rev 144 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.openoffice.generation.view;
import org.openconcerto.openoffice.ContentType;
import org.openconcerto.openoffice.Log;
import org.openconcerto.openoffice.ODSingleXMLDocument;
import org.openconcerto.openoffice.OOUtils;
import org.openconcerto.openoffice.generation.GenerationTask;
import org.openconcerto.openoffice.generation.ReportGeneration;
import org.openconcerto.openoffice.generation.TaskStatus;
import org.openconcerto.openoffice.generation.desc.ReportType;
import org.openconcerto.openoffice.generation.desc.ReportTypes;
import org.jopendocument.link.Component;
import org.jopendocument.link.OOConnexion;
import org.openconcerto.utils.EmailClient;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.model.ListComboBoxModel;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Future;
import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import org.jdom.JDOMException;
/**
* A panel to choose a ReportType, see the tasks of the generation, and optionnaly open the
* document. NOTE: you have to call {@link #enableGeneration(boolean)} before being able to
* generate, because it's set to false in the constructor.
*
* @author ILM Informatique 27 déc. 2003
* @param <R> type of generation
*/
public abstract class BaseGenerationRapport<R extends ReportGeneration<?>> extends JPanel {
static public enum FileAction {
DO_NOTHING("ne rien faire"), OPEN("ouvrir le fichier"), MAIL("envoyer par courriel");
private final String label;
private FileAction(final String label) {
this.label = label;
}
@Override
public String toString() {
return this.label;
}
}
private JComboBox<ReportType> typeRapportComboSelection;
private JButton genererButton;
private JComboBox<FileAction> fileActionCombo;
private JList<GenerationTask> tasksView;
private final JLabel status;
// le groupe dans lequel doivent être toutes les thread de la génération
private final ThreadGroup thg;
public BaseGenerationRapport() throws JDOMException, IOException {
this.thg = new ThreadGroup(this + " thread group");
this.status = new JLabel();
this.setStatus("Inactif");
}
protected Map<String, JComponent> getEntries() {
return new LinkedHashMap<String, JComponent>();
}
protected File getReportDir() {
return null;
}
protected final void uiInit() throws JDOMException, IOException {
setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.ipadx = 40;
c.insets = new Insets(2, 5, 0, 0);
c.gridx = 0;
c.gridy = 0;
c.gridheight = 1;
c.gridwidth = 1;
c.fill = GridBagConstraints.HORIZONTAL;
final Map<String, JComponent> allEntries = new LinkedHashMap<String, JComponent>();
this.typeRapportComboSelection = new JComboBox<>(new ListComboBoxModel<ReportType>(this.getTypes().getTypes()));
allEntries.put("Type de rapport", this.typeRapportComboSelection);
allEntries.putAll(this.getEntries());
for (final Map.Entry<String, JComponent> e : allEntries.entrySet()) {
c.gridx = 0;
c.gridy++;
this.add(new JLabel(e.getKey()), c);
c.gridx++;
c.weightx = 1;
this.add(e.getValue(), c);
c.weightx = 0;
}
final File reportDir = getReportDir();
if (reportDir != null) {
c.gridx = 1;
c.gridy++;
c.weightx = 1;
this.add(new JButton(new AbstractAction("Ouvrir le dossier des rapports") {
@Override
public void actionPerformed(final ActionEvent e) {
try {
Desktop.getDesktop().browse(reportDir.toURI());
} catch (final Exception e1) {
JOptionPane.showMessageDialog(BaseGenerationRapport.this, "Impossible d'ouvrir le dossier", "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
}), c);
c.weightx = 0;
}
c.gridx = 0;
c.gridy++;
this.genererButton = new JButton("Générer le rapport et");
this.add(this.genererButton, c);
this.genererButton.setEnabled(false);
this.genererButton.addActionListener(new GenerateAction());
c.gridx++;
this.fileActionCombo = new JComboBox<>(getAllowedActions());
this.fileActionCombo.setSelectedItem(FileAction.OPEN);
this.add(this.fileActionCombo, c);
this.fileActionCombo.setEnabled(false);
c.gridx = 0;
c.gridy++;
c.gridwidth = GridBagConstraints.REMAINDER;
this.add(this.status, c);
c.gridy++;
this.tasksView = new JList<>(new TasksModel());
this.tasksView.setCellRenderer(new GenerationTaskView());
c.fill = GridBagConstraints.BOTH;
c.weighty = 1;
final JScrollPane scroll = new JScrollPane(this.tasksView);
scroll.setPreferredSize(new Dimension(200, 400));
this.add(scroll, c);
}
/**
* Permet de modifier la liste des tâches.
*
* @author Sylvain CUAZ
*/
private static final class TasksModel extends AbstractListModel<GenerationTask> implements PropertyChangeListener {
private List<GenerationTask> tasks;
{
this.tasks = new ArrayList<GenerationTask>(15);
}
@Override
public int getSize() {
return this.tasks.size();
}
@Override
public GenerationTask getElementAt(final int index) {
return this.tasks.get(index);
}
void add(final GenerationTask task) {
this.tasks.add(task);
task.addPropertyChangeListener(this);
this.fireIntervalAdded(this, this.tasks.size(), this.tasks.size());
}
void clear() {
for (final GenerationTask t : this.tasks) {
t.removePropertyChangeListener(this);
}
this.fireIntervalRemoved(this, 0, this.tasks.size());
this.tasks.clear();
}
/*
* Une tache a changé.
*/
@Override
public void propertyChange(final PropertyChangeEvent evt) {
final int index = this.tasks.indexOf(evt.getSource());
this.fireContentsChanged(this, index, index);
}
}
/**
* Interrompt la génération.
*/
public final void interrupt() {
// pas de threads active, quand pas génération
this.thg.interrupt();
}
class GenerateAction implements ActionListener {
public GenerateAction() {
}
@Override
public void actionPerformed(final ActionEvent e) {
enableGeneration(false);
final FileAction sel = (FileAction) BaseGenerationRapport.this.fileActionCombo.getSelectedItem();
// "génération..."
new Thread(BaseGenerationRapport.this.thg, new Runnable() {
@Override
public void run() {
generate(sel);
// toujours le faire, même si interrompu
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
enableGeneration(true);
}
});
}
}).start();
}
}
protected final void enableGeneration(final boolean b) {
this.fileActionCombo.setEnabled(b);
this.genererButton.setEnabled(b);
}
// doit s'exécuter dans this.thg
protected final void generate(final FileAction sel) {
final ReportType type = (ReportType) this.typeRapportComboSelection.getSelectedItem();
final R rg = this.createGeneration(type);
rg.addTaskListener(new PropertyChangeListener() {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
final TaskStatus st = (TaskStatus) evt.getOldValue();
if (st.getState().equals(TaskStatus.State.NOT_STARTED))
addTask((GenerationTask) evt.getSource());
}
});
if (Thread.currentThread().isInterrupted())
return;
Map<String, ODSingleXMLDocument> report = null;
try {
report = this.generate(rg);
if (report != null) {
for (final Entry<String, ODSingleXMLDocument> e : report.entrySet()) {
try {
final ODSingleXMLDocument singleDoc = e.getValue();
final File reportFile = singleDoc.saveToPackageAs(this.getFile(rg, e.getKey()));
if (sel == FileAction.OPEN)
OOUtils.open(reportFile);
else if (sel == FileAction.MAIL) {
final File fileOutPDF = FileUtils.addSuffix(reportFile, ".pdf");
try {
final OOConnexion conn = OOConnexion.create();
if (conn == null)
throw new IllegalStateException("OpenOffice n'a pu être trouvé");
final Component doc = conn.loadDocument(reportFile, true);
final String filter = singleDoc.getPackage().getContentType().getType() == ContentType.SPREADSHEET ? "calc_pdf_Export" : "writer_pdf_Export";
final Future<File> pdf = doc.saveToPDF(fileOutPDF, filter);
doc.close();
conn.closeConnexion();
// can wait for the pdf since we're not in the EDT
EmailClient.getPreferred().compose(getEmailRecipient(rg), "Rapport", null, pdf.get());
} catch (final Exception exn) {
exn.printStackTrace();
ExceptionHandler.handle("Impossible de charger le document OpenOffice dans le logiciel de courriel", exn);
}
}
} catch (final FileNotFoundException exn) {
// TODO tester pb de droits
ExceptionHandler.handle(BaseGenerationRapport.this, "Le fichier est déjà ouvert, veuillez le refermer avant de générer.", exn);
} catch (final IOException exn) {
ExceptionHandler.handle(BaseGenerationRapport.this, "Impossible de sauver le rapport", exn);
}
}
}
} catch (final Throwable e) {
ExceptionHandler.handle(BaseGenerationRapport.this, "Impossible de générer le rapport", e);
}
}
private Map<String, ODSingleXMLDocument> generate(final org.openconcerto.openoffice.generation.ReportGeneration<?> rg) throws Throwable {
clearTasks();
setStatus("Génération en cours...");
if (Thread.currentThread().isInterrupted())
return null;
final long start = System.currentTimeMillis();
Map<String, ODSingleXMLDocument> report = null;
String s;
Throwable t = null;
try {
report = rg.generateMulti();
if (report == null) {
s = "Génération interrompue.";
} else {
s = "Rapport généré en " + (System.currentTimeMillis() - start) / 1000 + " secondes.";
}
} catch (final Throwable e) {
s = "Erreur de génération.";
t = e;
}
setStatus(s);
if (t == null)
return report;
else
throw t;
}
private final void addTask(final GenerationTask task) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
((TasksModel) BaseGenerationRapport.this.tasksView.getModel()).add(task);
}
});
}
private final void clearTasks() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
((TasksModel) BaseGenerationRapport.this.tasksView.getModel()).clear();
}
});
}
private final void setStatus(final String status) {
Log.get().info(this + " status: " + status);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
BaseGenerationRapport.this.status.setText(status);
}
});
}
@Override
public String toString() {
return "Generation panel";
}
abstract protected ReportTypes getTypes() throws JDOMException, IOException;
abstract protected R createGeneration(final ReportType type);
abstract protected File getFile(final R rg, String docID);
protected String getEmailRecipient(final R rg) {
return null;
}
protected FileAction[] getAllowedActions() {
return FileAction.values();
}
}