Dépôt officiel du code source de l'ERP OpenConcerto
Rev 151 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.sql.view;
import static javax.swing.JOptionPane.DEFAULT_OPTION;
import static javax.swing.JOptionPane.QUESTION_MESSAGE;
import org.openconcerto.openoffice.ContentType;
import org.openconcerto.openoffice.OOUtils;
import org.openconcerto.openoffice.XMLFormatVersion;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.sqlobject.SQLRequestComboBox;
import org.openconcerto.sql.users.rights.TableAllRights;
import org.openconcerto.sql.users.rights.UserRights;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.sql.view.list.IListe;
import org.openconcerto.sql.view.list.IListeAction;
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
import org.openconcerto.sql.view.list.ITableModel;
import org.openconcerto.sql.view.list.RowAction;
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction;
import org.openconcerto.sql.view.list.action.SQLRowValuesAction;
import org.openconcerto.sql.view.search.SearchListComponent;
import org.openconcerto.ui.ContinuousButtonModel;
import org.openconcerto.ui.FrameUtil;
import org.openconcerto.ui.SwingThreadUtils;
import org.openconcerto.ui.component.JRadioButtons.JStringRadioButtons;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Tuple2.List2;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.change.ListChangeIndex;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.prefs.Preferences;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
/**
* Un panel affichant une liste et des boutons pour la manipuler.
*
* @author ILM Informatique 11 juin 2004
*/
abstract public class IListPanel extends JPanel implements ActionListener {
/**
* System property to control the clone button. The value can be either <code>true</code>, in
* which case the button will always appear, or it can be a list of table names.
*/
public static final String CAN_CLONE = "org.openconcerto.sql.canCloneInList";
static private final String EXPORT_DIR_KEY = "exportDir";
protected static final String FALLBACK_KEY = "FALLBACK_ACTION";
private static final String FILE_STRUCT_VERSION = "20161018";
static public final File getConfigFile(final SQLElement elem, final Class<? extends Container> c) {
return getConfigFile(elem, c, null);
}
static public final File getConfigFile(final SQLElement elem, final Class<? extends Container> c, final String variant) {
final String suffix = StringUtils.isEmpty(variant, true) ? "" : "-" + variant;
return getConfigFile(IListFrame.getConfDir(elem), c, elem.getCode() + suffix);
}
static public final File getConfigFile(final Class<? extends Container> c, String code) {
return getConfigFile(IListFrame.getConfDir(null), c, code);
}
static public final File getConfigFile(final File rootDir, final Class<? extends Container> c, String code) {
if (rootDir == null)
return null;
final File structFile = new File(rootDir, "jtableState-" + FILE_STRUCT_VERSION);
return new File(structFile, c.getSimpleName() + File.separator + IListFrame.getConfigFileName(code));
}
public static enum FrameMode {
NO_FRAME, READ_ONLY_FRAME, READ_WRITE_FRAME;
}
public static final String TWO_X_SUFFIX = "_2x";
private static final String FLECHE_HAUT_PNG = "fleche_haut.png";
private static final String FLECHE_BAS_PNG = "fleche_bas.png";
private static final Icon UP_ARROW = getIcon(FLECHE_HAUT_PNG, false);
private static final Icon UP_ARROW_2X = getIcon(FLECHE_HAUT_PNG, true);
private static final Icon DOWN_ARROW = getIcon(FLECHE_BAS_PNG, false);
private static final Icon DOWN_ARROW_2X = getIcon(FLECHE_BAS_PNG, true);
static final String getResourceName(final String rsrcName, final boolean twoX) {
if (!twoX)
return rsrcName;
final String extension = FileUtils.getExtension(rsrcName, true);
if (extension == null)
return rsrcName + TWO_X_SUFFIX;
else
return FileUtils.removeSuffix(rsrcName, extension) + TWO_X_SUFFIX + extension;
}
static final Icon getIcon(final String fileName, final boolean twoX) {
return getIcon(IListPanel.class, fileName, twoX);
}
public static final Icon getIcon(final Class<?> clazz, final String fileName, final boolean twoX) {
return new ImageIcon(clazz.getResource(getResourceName(fileName, twoX)));
}
private final IListe liste;
protected final SQLElement element;
protected final BtnTooltipMnger btnMngr;
protected JButton buttonActualiser;
// partagé par ListModifyFrame
protected JButton buttonModifier;
protected JButton buttonEffacer;
protected JButton buttonAjouter;
protected JButton buttonClone;
protected JButton saveBtn;
private JButton buttonPlus;
private JButton buttonMoins;
protected final JPanel searchPanel = new JPanel(new GridBagLayout());
private static final JButton createBtn(Icon i) {
final JButton res = new JButton(i);
res.setMargin(new Insets(1, 1, 1, 1));
res.setModel(new ContinuousButtonModel(300));
res.setBorder(BorderFactory.createEmptyBorder());
res.setOpaque(false);
res.setFocusPainted(true);
res.setContentAreaFilled(false);
return res;
}
private final RowAction displayRowAction;
protected EditFrame createFrame;
private boolean selectRowOnAdd = true;
// NO_FRAME since at creation the action isn't added to our IListe
private FrameMode showFrameOnDoubleClick = FrameMode.NO_FRAME;
private boolean deaf = Boolean.getBoolean("org.openconcerto.sql.listPanel.deafEditPanel");
protected SearchListComponent searchComponent;
public IListPanel(SQLElement elem) {
this(elem, null);
}
public IListPanel(SQLElement elem, IListe list) {
this(elem, list, null);
}
/**
* Create a new instance. Often several panels for the same element are needed, in this case
* <code>variant</code> should be used to identify them, this allows each panel to have its own
* {@link IListe#getConfigFile() state}.
*
* @param elem the element that will be displayed.
* @param list the list to use, if <code>null</code> <code>elem</code> will configure a new one.
* @param variant this parameter should identify each panel on the same element, can be
* <code>null</code>.
*/
public IListPanel(SQLElement elem, IListe list, String variant) {
this.element = elem;
this.btnMngr = new BtnTooltipMnger();
// if the same conf is needed for subclasses, use IListPanel.class
final File config = getConfigFile(this.getElement(), this.getClass(), variant);
if (list == null) {
list = new IListe(this.getElement().getTableSource(), config);
} else {
if (list.getSource().getPrimaryTable() != elem.getTable())
throw new IllegalArgumentException("Different tables : " + elem.getTable() + " != " + list.getSource().getPrimaryTable());
if (list.getConfigFile() == null)
list.setConfigFile(config);
}
this.liste = list;
final Map<SQLRowValuesAction, IListeAction> actionsMap = new IdentityHashMap<>();
final IClosure<ListChangeIndex<SQLRowValuesAction>> l = new IClosure<ListChangeIndex<SQLRowValuesAction>>() {
@Override
public void executeChecked(ListChangeIndex<SQLRowValuesAction> input) {
SwingThreadUtils.invoke(() -> {
for (final SQLRowValuesAction a : input.getItemsRemoved()) {
final IListeAction la = actionsMap.remove(a);
if (la != null)
getListe().removeIListeAction(la);
}
actionsMap.putAll(getListe().addRowValuesActions(getElement().getRowValuesActions()));
});
}
};
// remove listener if non displayable since getElement() never dies
this.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0)
if (isDisplayable()) {
assert actionsMap.isEmpty();
actionsMap.putAll(getListe().addRowValuesActions(getElement().getRowValuesActions()));
getElement().addRowActionsListener(l);
} else {
getElement().removeRowActionsListener(l);
getListe().removeIListeActions(actionsMap.values());
actionsMap.clear();
}
}
});
this.displayRowAction = new PredicateRowAction(new AbstractAction(TM.tr("display")) {
private EditFrame listeningFrame = null;
private final EditFrame createFrame(final boolean listening) {
final EditFrame res = new EditFrame(getElement(), IListPanel.this.showFrameOnDoubleClick == FrameMode.READ_ONLY_FRAME ? EditPanel.READONLY : EditPanel.MODIFICATION);
if (listening)
getListe().addIListener(res);
res.selectionId(getListe().getSelectedId());
return res;
}
@Override
public void actionPerformed(ActionEvent e) {
final EditFrame frame;
if ((e.getModifiers() & ActionEvent.ALT_MASK) != 0) {
if (this.listeningFrame == null)
this.listeningFrame = createFrame(true);
frame = this.listeningFrame;
} else {
frame = createFrame(false);
}
FrameUtil.show(frame);
}
}, false).setPredicate(IListeEvent.getSingleSelectionPredicate());
this.init();
}
protected final void init() {
this.uiInit();
// on écoute pour mettre à jour les boutons effacer et modifier
this.liste.addIListener(new IListener() {
@Override
public void selectionId(int id, int field) {
IListPanel.this.listSelectionChanged(id);
}
});
this.liste.addSelectionDataListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
listSelectionDataChanged();
}
});
this.liste.addModelListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
IListPanel.this.searchComponent.reset((ITableModel) evt.getNewValue());
}
});
// don't need to display in a separate frame, if it's already displayed in this panel
final FrameMode m;
if (this.modifyIsImmediate())
m = FrameMode.NO_FRAME;
else if (Boolean.getBoolean("org.openconcerto.sql.listPanel.rwOnDoubleClick"))
m = FrameMode.READ_WRITE_FRAME;
else
m = FrameMode.READ_ONLY_FRAME;
this.setShowFrameOnDoubleClickMode(m);
// selectID() alone won't init us if NONEXISTANT_ID is already the selected id
this.btnMngr.updateBtns();
this.getListe().selectID(SQLRow.NONEXISTANT_ID);
}
final private void uiInit() {
this.createUI();
Container container = this;
container.setLayout(new GridBagLayout());
final GridBagConstraints c = createConstraints();
c.weighty = 1;
container.add(this.liste, c);
c.gridy++;
c.weighty = 0;
c.insets = new Insets(4, 1, 2, 1);
container.add(this.getMiddlePanel(), c);
this.addComponents(container, c);
this.setOpaque(false);
}
/**
* The constraints used to add the list and its panel. The {@link #getListe() list} will be
* added at the coordinates returned, and the {@link #getMiddleCompsLayout() panel} will be
* added underneath (i.e. gridy++).
*
* @return the constraints.
*/
protected GridBagConstraints createConstraints() {
final GridBagConstraints c = new GridBagConstraints();
c.weightx = 1;
c.fill = GridBagConstraints.BOTH;
c.gridx = 0;
c.gridy = 0;
return c;
}
protected void createUI() {
// int sca
final boolean twoX = this.getFont().getSize() > 16;
final int buttonSize = twoX ? 36 : 20;
this.setOpaque(false);
this.buttonActualiser = new JButton(getIcon("reload.png", twoX));
this.buttonActualiser.setBorderPainted(false);
this.buttonActualiser.setFocusPainted(false);
this.buttonActualiser.setOpaque(false);
this.buttonActualiser.setContentAreaFilled(false);
this.buttonActualiser.setMinimumSize(new Dimension(buttonSize, buttonSize));
this.buttonActualiser.setPreferredSize(new Dimension(buttonSize, buttonSize));
this.buttonActualiser.setMaximumSize(new Dimension(buttonSize, buttonSize));
this.searchComponent = new SearchListComponent(this.liste.getModel());
this.searchComponent.setFormats(this.liste.getSearchFormats());
this.buttonModifier = new JButton(TM.tr("modify"));
this.buttonModifier.setOpaque(false);
this.btnMngr.addBtn(this.buttonModifier, "noRightToModify", TableAllRights.MODIFY_ROW_TABLE, true, true);
this.buttonEffacer = new JButton(TM.tr("remove"));
this.buttonEffacer.setOpaque(false);
this.btnMngr.addBtn(this.buttonEffacer, "noRightToDel", TableAllRights.DELETE_ROW_TABLE, true);
this.buttonAjouter = new JButton(TM.tr("add"));
this.buttonAjouter.setOpaque(false);
this.btnMngr.addBtn(this.buttonAjouter, "noRightToAdd", TableAllRights.ADD_ROW_TABLE, false, false);
this.buttonClone = new JButton(TM.tr("duplicate"));
this.buttonClone.setOpaque(false);
this.btnMngr.addBtn(this.buttonClone, "noRightToClone", TableAllRights.ADD_ROW_TABLE, true, false);
this.btnMngr.setOKToolTip(this.buttonClone, TM.tr("listPanel.cloneToolTip"));
this.saveBtn = new JButton(getIcon("save.png", twoX));
this.saveBtn.setFocusPainted(false);
this.saveBtn.setOpaque(false);
this.saveBtn.setContentAreaFilled(false);
this.saveBtn.setBorderPainted(false);
this.saveBtn.setMinimumSize(new Dimension(buttonSize, buttonSize));
this.saveBtn.setPreferredSize(new Dimension(buttonSize, buttonSize));
this.saveBtn.setMaximumSize(new Dimension(buttonSize, buttonSize));
this.buttonMoins = createBtn(twoX ? UP_ARROW_2X : UP_ARROW);
this.buttonPlus = createBtn(twoX ? DOWN_ARROW_2X : DOWN_ARROW);
// needSelection = false since we handle it with the transformer
this.btnMngr.addBtn(this.buttonMoins, "noRightToReorder", TableAllRights.MODIFY_ROW_TABLE, false);
this.btnMngr.addBtn(this.buttonPlus, "noRightToReorder", TableAllRights.MODIFY_ROW_TABLE, false);
final ITransformer<JButton, String> transf = new ITransformer<JButton, String>() {
@Override
public String transformChecked(JButton input) {
final boolean ok = getListe().hasSelection() && !getListe().isSorted() && getListe().getSource().getReq().isTableOrder();
// keep them enabled when armed otherwise they will be disabled when used
// since they refresh the list which in turn does a clearSelection()
return input.getModel().isArmed() || ok ? null : TM.tr("listPanel.noSelectionOrSort");
}
};
this.btnMngr.setAdditional(this.buttonMoins, transf);
this.btnMngr.setAdditional(this.buttonPlus, transf);
this.searchPanel.setOpaque(false);
// ne pas permettre de changer l'ordre quand on trie
this.getListe().addSortListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
updateOrderButtons();
}
});
if (Boolean.getBoolean("org.openconcerto.listpanel.simpleui")) {
this.setAdjustVisible(false);
this.setSearchFullMode(false);
} else {
this.searchPanel.setBorder(BorderFactory.createEtchedBorder());
}
final GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(0, 2, 0, 2);
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0;
final JLabel label = new JLabel(TM.tr("search"));
label.setOpaque(false);
this.searchPanel.add(label, c);
c.gridx++;
c.weightx = 1;
this.searchComponent.setOpaque(false);
this.searchPanel.setOpaque(false);
this.searchPanel.add(this.searchComponent, c);
}
protected abstract boolean modifyIsImmediate();
/**
* Permet aux sous classes d'ajouter d'autres composants.
*
* @param container le conteneur dans lequel ajouter.
* @param c les contraintes actuelles.
*/
protected void addComponents(Container container, GridBagConstraints c) {
// par défaut ne fait rien
}
protected Object[] getMiddleCompsLayout() {
final JComponent[] comps = { this.buttonPlus, this.buttonMoins, this.buttonActualiser, canSave() ? this.saveBtn : null, this.searchPanel, this.buttonAjouter,
canClone() ? this.buttonClone : null, this.buttonModifier, this.buttonEffacer };
// le champ de recherche prend toute la largeur disponible
return new Object[] { comps, this.searchPanel };
}
private boolean canClone() {
final String prop = System.getProperty(CAN_CLONE, "");
return Boolean.parseBoolean(prop) || SQLRow.toList(prop).contains(getElement().getTable().getName());
}
private boolean canSave() {
// TODO use default right from UserRightsManager (see issue #79)
final String prop = System.getProperty("org.openconcerto.sql.canSaveInList", "true");
return Boolean.parseBoolean(prop) || TableAllRights.currentUserHasRight(TableAllRights.SAVE_ROW_TABLE, getElement().getTable());
}
final private JPanel getMiddlePanel() {
JPanel container = new JPanel();
container.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(0, 2, 0, 2);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
c.anchor = GridBagConstraints.CENTER;
Object[] middleCompsL = this.getMiddleCompsLayout();
JComponent[] comps = (JComponent[]) middleCompsL[0];
JComponent largest = (JComponent) middleCompsL[1];
for (int i = 0; i < comps.length; i++) {
final JComponent component = comps[i];
if (component != null) {
c.weightx = (component == largest) ? 1 : 0;
container.add(component, c);
if (component instanceof JButton)
((JButton) component).addActionListener(this);
c.gridx++;
}
}
container.setOpaque(false);
return container;
}
/**
* Recherche s dans ce panneau.
*
* @param s la chaine à rechercher.
*/
public void search(String s) {
this.searchComponent.setSearchString(s);
}
/**
* Recherche <code>s</code> dans ce panneau et s'assure que <code>r</code> soit exécuté dans la
* thread Swing une fois la recherche effectuée.
*
* @param s la chaine à rechercher.
* @param r le runnable à exécuter.
*/
public synchronized void search(final String s, final Runnable r) {
this.search(s);
// attendre les modifs en cours (update de la base + recherche que l'on vient d'effectuer)
this.getListe().getModel().invokeLater(r);
}
public final void actionPerformed(ActionEvent e) {
this.handleAction((JButton) e.getSource(), e);
}
private final int askSerious(Object msg, String title) {
return JOptionPane.showConfirmDialog(this, msg, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
}
private final JPanel createClonePanel(final int selectedLines, final boolean rec, final SQLRequestComboBox combo) {
final String msg = TM.getInstance().trM("listPanel.cloneRows", "rowCount", selectedLines, "rec", rec);
final JPanel p = new JPanel(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LINE_START;
c.insets = new Insets(2, 2, 2, 2);
p.add(new JLabel(msg), c);
if (combo != null) {
c.gridx = 0;
c.gridy++;
p.add(new JLabel(TM.tr("clone.newPlace")), c);
c.gridy++;
combo.uiInit(getElement().getParentElement().getComboRequest());
combo.setPreferredSize(new Dimension(600, combo.getPreferredSize().height));
p.add(combo, c);
}
return p;
}
protected void handleAction(JButton source, ActionEvent evt) {
if (source == this.buttonMoins) {
this.getListe().deplacerDe(-getInc(evt));
} else if (source == this.buttonPlus) {
this.getListe().deplacerDe(getInc(evt));
} else if (source == this.buttonActualiser) {
this.liste.update();
} else if (source == this.buttonAjouter) {
final boolean deaf = isDeaf();
// toujours remplir la createFrame avec la ligne sélectionnée
// car la frame écoute la sélection mais pas les modif, et se reset qd on la ferme
// donc si on clic ajouter, on ferme, on modif la ligne, on clic ajouter
// on doit reremplir l'EditFrame
final int selectedId = this.getListe().getSelectedId();
if (!deaf && this.selectRowOnAdd && selectedId >= 0) {
this.getCreateFrame().selectionId(selectedId);
}
FrameUtil.show(this.getCreateFrame());
} else if (source == this.buttonClone) {
final List<Integer> selectedIDs = this.getListe().getSelection().getSelectedIDs();
final boolean rec = (evt.getModifiers() & ActionEvent.CTRL_MASK) != 0;
// on Ubuntu ALT-Click is used to move windows
final boolean showParent = (evt.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
final SQLRequestComboBox combo = showParent && getElement().getParentElement() != null ? new SQLRequestComboBox() : null;
if (askSerious(createClonePanel(selectedIDs.size(), rec, combo), TM.tr("duplication")) == JOptionPane.YES_OPTION) {
final SQLRow parent = combo == null ? null : combo.getSelectedRow();
for (final int id : selectedIDs) {
final SQLRow row = this.getElement().getTable().getRow(id);
try {
if (rec)
this.getElement().copyRecursive(row, parent);
else
this.getElement().copy(row, parent);
} catch (SQLException e) {
ExceptionHandler.handle(this, TM.tr("listPanel.duplicationError", row), e);
}
}
}
} else if (source == this.buttonEffacer) {
this.getElement().askArchive(this, this.getListe().getSelection().getSelectedIDs());
} else if (source == this.saveBtn) {
try {
final String allRows = TM.tr("listPanel.wholeList");
final String selectedRows = TM.tr("listPanel.selection");
final JStringRadioButtons radios = new JStringRadioButtons(false, Arrays.asList(allRows, selectedRows));
// we rarely mean to save one row or less
radios.setValue(this.getListe().getSelection().getSelectedIDs().size() <= 1 ? allRows : selectedRows);
final JPanel p = new JPanel(new BorderLayout());
p.add(new JLabel(TM.tr("export")), BorderLayout.PAGE_START);
p.add(radios, BorderLayout.LINE_START);
final Object[] options = { TM.tr("open"), TM.tr("save") + "...", TM.tr("cancel") };
final int answer = JOptionPane.showOptionDialog(this, p, TM.tr("listPanel.export"), DEFAULT_OPTION, QUESTION_MESSAGE, null, options, options[0]);
if (answer == 0 || answer == 1) {
final boolean tmp = answer == 0;
final XMLFormatVersion version = XMLFormatVersion.getDefault();
final String prefix = this.element.getPluralName().replace('/', '-');
final String suffix = "." + ContentType.SPREADSHEET.getVersioned(version.getXMLVersion()).getExtension();
final File file;
if (tmp) {
file = File.createTempFile(prefix, suffix);
} else {
final FileDialog fd = new FileDialog(SwingThreadUtils.getAncestorOrSelf(Frame.class, this), TM.tr("listPanel.save"), FileDialog.SAVE);
final Preferences prefs = Preferences.userNodeForPackage(this.getClass());
fd.setDirectory(prefs.get(EXPORT_DIR_KEY, Configuration.getInstance().getWD().getAbsolutePath()));
fd.setFile(prefix + suffix);
fd.setVisible(true);
if (fd.getFile() != null) {
file = new File(fd.getDirectory(), fd.getFile());
prefs.put(EXPORT_DIR_KEY, fd.getDirectory());
} else {
file = null;
}
}
if (file != null) {
final File exportedFile = this.liste.exporter(file, radios.getValue().equals(selectedRows), version);
if (tmp) {
exportedFile.setWritable(false, false);
exportedFile.deleteOnExit();
}
OOUtils.open(exportedFile);
}
}
} catch (Exception e) {
ExceptionHandler.handle(this, TM.tr("saveError"), e);
}
} else {
throw new IllegalStateException("button '" + source.getText() + "' not implemented");
}
}
/**
* Whether the create panel listen to the selection of its list.
*
* @return <code>true</code> if the create panel should be empty.
*/
public final boolean isDeaf() {
return this.deaf;
}
public final void setDeaf(final boolean b) {
this.deaf = b;
}
protected final EditFrame getCreateFrame() {
if (this.createFrame == null) {
this.createFrame = new EditFrame(this.element, EditPanel.CREATION);
this.createFrame.getPanel().setIListe(this.getListe());
if (!isDeaf()) {
// la frame d'ajout se remplit suivant la sélection de cette frame
this.liste.addIListener(this.createFrame);
}
}
return this.createFrame;
}
// notre liste a changé de sélection
protected void listSelectionChanged(int id) {
}
// selection or selection content changed
protected void listSelectionDataChanged() {
// even if the same row is selected, its content can change (e.g. get locked)
this.btnMngr.updateBtns();
}
// handle enabled and tooltip properties of our buttons
protected final class BtnTooltipMnger {
private final Map<JButton, Tuple2<String, String>> code;
private final Set<JButton> needSelection, needRWSelection;
// btn -> tooltip
private final Map<JButton, ITransformer<JButton, String>> additional;
private final Map<JButton, String> okTooltip;
// if the normal action is denied, offer an alternate one
private final Map<JButton, List2<String>> alternateLabels;
public BtnTooltipMnger() {
super();
this.needSelection = new HashSet<JButton>();
this.needRWSelection = new HashSet<JButton>();
this.code = new HashMap<JButton, Tuple2<String, String>>();
this.additional = new HashMap<JButton, ITransformer<JButton, String>>();
this.okTooltip = new HashMap<JButton, String>();
this.alternateLabels = new HashMap<JButton, List2<String>>(4);
}
public void addBtn(final JButton btn, String desc, String rightCode, final boolean needSelection) {
this.addBtn(btn, desc, rightCode, needSelection, true);
}
public void addBtn(final JButton btn, String desc, String rightCode, final boolean needSelection, final boolean needRWSelection) {
// otherwise have to remove from other attributes
if (this.code.containsKey(btn))
throw new IllegalStateException("already in");
this.code.put(btn, Tuple2.create(desc, rightCode));
if (needSelection)
this.needSelection.add(btn);
if (needRWSelection && getElement().getTable().contains(SQLComponent.READ_ONLY_FIELD))
this.needRWSelection.add(btn);
}
public void setAdditional(final JButton btn, ITransformer<JButton, String> additional) {
if (additional != null)
this.additional.put(btn, additional);
else
this.additional.remove(btn);
}
public void setFallback(final JButton btn, final String normalLabel, final String altLabel) {
if (normalLabel == null || altLabel == null)
this.alternateLabels.remove(btn);
else
this.alternateLabels.put(btn, new List2<String>(normalLabel, altLabel));
}
public void setOKToolTip(final JButton btn, String tooltip) {
if (tooltip != null)
this.okTooltip.put(btn, tooltip);
else
this.okTooltip.remove(btn);
}
void updateBtn(final JButton btn) {
if (!this.code.containsKey(btn))
throw new IllegalArgumentException();
this.updateBtns(Collections.singleton(btn));
}
void updateBtns() {
this.updateBtns(this.code.keySet());
}
private void updateBtns(final Set<JButton> btns) {
final boolean hasSelection = getListe().getSelectedId() >= SQLRow.MIN_VALID_ID;
final UserRights rights = UserRightsManager.getCurrentUserRights();
for (final JButton btn : btns) {
final Tuple2<String, String> t = this.code.get(btn);
final boolean ok;
final String tooltip;
boolean fallBack = false;
if (!TableAllRights.hasRight(rights, t.get1(), getElement().getTable())) {
ok = false;
tooltip = TM.tr(t.get0());
fallBack = true;
} else if (this.needRWSelection.contains(btn) && isRO()) {
ok = false;
tooltip = TM.tr("editPanel.readOnlySelection");
fallBack = true;
} else if (this.needSelection.contains(btn) && !hasSelection) {
ok = false;
tooltip = TM.tr("noSelection");
} else if (this.additional.containsKey(btn)) {
tooltip = this.additional.get(btn).transformChecked(btn);
ok = tooltip == null;
} else {
ok = true;
tooltip = this.okTooltip.get(btn);
}
boolean finalOK = ok;
final List2<String> fallbackLabels = this.alternateLabels.get(btn);
if (fallbackLabels != null) {
final String label;
// the main action is denied (e.g. modify) but an alternate one is allowed (e.g.
// display)
if (fallBack) {
assert !ok;
finalOK = true;
label = fallbackLabels.get1();
} else {
label = fallbackLabels.get0();
}
btn.setText(TM.tr(label));
btn.putClientProperty(FALLBACK_KEY, fallBack);
}
btn.setToolTipText(tooltip);
btn.setEnabled(finalOK);
}
}
private boolean isRO() {
final SQLRowAccessor r = getListe().getSelectedRowAccessor();
return r != null && SQLComponent.isReadOnly(r);
}
}
protected final void updateOrderButtons() {
this.btnMngr.updateBtn(this.buttonMoins);
this.btnMngr.updateBtn(this.buttonPlus);
}
public final IListe getListe() {
return this.liste;
}
public final SQLElement getElement() {
return this.element;
}
/**
* The SQLComponent inside this panel, if any.
*
* @return our child component, or <code>null</code>.
*/
protected SQLComponent getSQLComponent() {
return null;
}
public abstract SQLComponent getModifComp();
public final SQLComponent getAddComp() {
return this.getCreateFrame().getSQLComponent();
}
public void grabFocus() {
this.liste.grabFocus();
}
// compute the increment from the event
private int getInc(ActionEvent evt) {
// move only 1 by 1 for the first 3
final int times = (int) evt.getWhen() - 3;
return times < 2 ? 1 : (int) Math.pow(times, 2);
}
public void setUpAndDownVisible(boolean b) {
this.buttonPlus.setVisible(b);
this.buttonMoins.setVisible(b);
// also disable move by drag and drop
this.getListe().getJTable().setDragEnabled(b);
}
@Deprecated
public void setShowReadOnlyFrameOnDoubleClick(boolean showReadOnlyFrameOnDoubleClick) {
this.setShowFrameOnDoubleClickMode(showReadOnlyFrameOnDoubleClick ? FrameMode.READ_ONLY_FRAME : FrameMode.NO_FRAME);
}
public void setShowFrameOnDoubleClickMode(FrameMode m) {
if (this.showFrameOnDoubleClick != m) {
this.showFrameOnDoubleClick = m;
if (this.showFrameOnDoubleClick != FrameMode.NO_FRAME)
this.liste.setDefaultRowAction(this.displayRowAction);
else
this.liste.removeIListeAction(this.displayRowAction);
}
}
public void setAddVisible(boolean b) {
this.buttonAjouter.setVisible(b);
}
public void setAdjustVisible(boolean b) {
this.liste.setAdjustVisible(b);
}
public void setReloadVisible(boolean b) {
this.buttonActualiser.setVisible(b);
}
public void setSaveVisible(boolean b) {
this.saveBtn.setVisible(b);
}
public void setSearchVisible(boolean b) {
this.searchPanel.setVisible(b);
}
public void setDeleteVisible(boolean b) {
this.buttonEffacer.setVisible(b);
}
public void setModifyVisible(boolean b) {
this.buttonModifier.setVisible(b);
}
public void setCloneVisible(boolean b) {
this.buttonClone.setVisible(b);
}
public void setReadWriteButtonsVisible(final boolean b) {
this.setUpAndDownVisible(b);
this.setAddVisible(b);
this.setDeleteVisible(b);
this.setModifyVisible(b);
this.setCloneVisible(b);
}
public void setSearchFullMode(boolean b) {
if (b)
this.searchPanel.setBorder(BorderFactory.createEtchedBorder());
else
this.searchPanel.setBorder(BorderFactory.createEmptyBorder());
this.searchComponent.setSearchFullMode(b);
}
/**
* Récupérer les valeurs de la row sélectionnée lors de l'ajout
*
* @param b
*/
public void setSelectRowOnAdd(boolean b) {
this.selectRowOnAdd = b;
}
public JButton getButtonAdd() {
return buttonAjouter;
}
public JButton getButtonModify() {
return buttonModifier;
}
public JButton getButtonDelete() {
return buttonEffacer;
}
}