All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tentackle.swing.rdc.PdoFieldPanel Maven / Gradle / Ivy

There is a newer version: 8.3.0.1
Show newest version
/**
 * Tentackle - http://www.tentackle.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


package org.tentackle.swing.rdc;


import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.text.MessageFormat;
import org.tentackle.bind.Binding;
import org.tentackle.bind.BindingException;
import org.tentackle.log.Logger;
import org.tentackle.log.LoggerFactory;
import org.tentackle.misc.ShortLongText;
import org.tentackle.pdo.DomainContext;
import org.tentackle.pdo.Pdo;
import org.tentackle.pdo.PdoRuntimeException;
import org.tentackle.pdo.PersistentDomainObject;
import org.tentackle.reflect.ReflectionHelper;
import org.tentackle.swing.FormComponent;
import org.tentackle.swing.FormError;
import org.tentackle.swing.FormFieldComponentPanel;
import org.tentackle.swing.FormUtilities;
import org.tentackle.swing.StringFormField;
import org.tentackle.swing.plaf.PlafUtilities;



/**
 * A panel containing a FormField representing the key to select the data object
 * and buttons for editing/viewing/searching.
 *
 * @param  the pdo type
 * @see PdoLinkPanel
 */
@SuppressWarnings("serial")
public class PdoFieldPanel> extends FormFieldComponentPanel implements DropTargetListener {

  /**
   * logger for this class.
   */
  private static final Logger LOGGER = LoggerFactory.getLogger(PdoFieldPanel.class);

  private PdoSearch              pdoSearch;                  // search plugin
  protected long                    linkedId;                   // the original Id of the object
  protected T                       linkedObject;               // the linked Object, null = none
  private DataFlavor                dndFlavor;                  // DnD Flavor
  private DropTarget                dropTarget;                 // droptarget
  private boolean                   checkExistsEnabled = true;  // true if show error popup if object does not exist
  private boolean                   searchRunning;              // true if search is currently running



  /**
   * Creates an application database object field panel.
   * 

* Pressing {@code F2} in the key field will open a search dialog. * {@code F3} will edit the object. * Drag and drop is supported as well.
* By default, the editing component is a {@link StringFormField}. */ public PdoFieldPanel() { super(); initComponents(); setup(); } /** * Sets up the component, drop enabled and clears the object */ protected void setup() { setFormComponent(new StringFormField()); setDropEnabled(true); loadObject(); } /** * {@inheritDoc} *

* Overridden to set the names in subcomponents. */ @Override public void setName(String name) { super.setName(name); if (name != null) { ((Component) getFormComponent()).setName(name + "/key"); infoField.setName(name + "/info"); editButton.setName(name + "/edit"); searchButton.setName(name + "/search"); } else { ((Component) getFormComponent()).setName("key"); infoField.setName("info"); editButton.setName("edit"); searchButton.setName("search"); } } /** * Sets whether the object exists for given domain key.
* If there is no object for given key an error popup will be shown. * * @param checkExistsEnabled true to enable check, false to disable */ public void setCheckExistsEnabled(boolean checkExistsEnabled) { this.checkExistsEnabled = checkExistsEnabled; } /** * Gets the check exists feature. * * @return true if check is enabled (default) */ public boolean isCheckExistsEnabled() { return checkExistsEnabled; } @Override public void setFormComponent(FormComponent comp) { FormComponent oldComponent = getFormComponent(); if (oldComponent != null) { remove((Component)oldComponent); } super.setFormComponent(comp); setName(getName()); ((Component)comp).addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_F3) { if (searchButton.isEnabled()) { searchButton.doClick(); } } else if (e.getKeyCode() == KeyEvent.VK_F2) { if (editButton.isEnabled() && editButton.isVisible()) { runEdit(); } } else if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) { // any valid input deletes the longtext infoField.clearText(); } } }); GridBagConstraints gridBagConstraints = new GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 1); add((Component)comp, gridBagConstraints); } /** * {@inheritDoc} *

* Overridden due to binding.
* Notice that obj may be null. In such a case the domain context will * be retrieved from the bindingProperty DomainContext.class from the formcontainer. */ @Override @SuppressWarnings("unchecked") public void setFormValue(Object obj) { Binding binding = getBinding(); if (binding != null) { try { // check if getter bound and returns an PersistentDomainObject Class clazz = binding.getMember().getType(); if (PersistentDomainObject.class.isAssignableFrom(clazz)) { if (obj instanceof PersistentDomainObject) { // obj is set and valid setLink((Class) clazz, ((T) obj).getDomainContext(), ((T) obj).getId()); } else { setLink((Class) clazz, binding.getBinder().getBindingProperty(DomainContext.class), 0); } return; } } catch (Exception ex) { throw new BindingException("could not determine type for " + binding, ex); } } // else default: just text super.setFormValue(obj); } /** * {@inheritDoc} *

* Overridden due to binding.
*/ @Override @SuppressWarnings("unchecked") public Object getFormValue() { Binding binding = getBinding(); if (binding != null) { // check if setter bound and accepts an PersistentDomainObject as a single argument Class clazz; try { clazz = (Class) binding.getMember().getType(); } catch (Exception ex) { throw new BindingException("could not determine type for " + binding, ex); } if (PersistentDomainObject.class.isAssignableFrom(clazz)) { try { searchRunning = true; T pdo = findByKey(clazz, binding.getBinder().getBindingProperty(DomainContext.class), super.getFormValue()); // key to the object, usually key is a String FormUtilities.getInstance().doValidate(this); return checkObject(pdo, clazz); } catch (Exception ex) { throw new BindingException("cannot find object by domain key", ex); } finally { searchRunning = false; } } } else if (pdoSearch != null) { Class clazz = pdoSearch.getPdoClass(); DomainContext context = pdoSearch.getDomainContext(); if (clazz != null && context != null) { try { searchRunning = true; return checkObject(findByKey(clazz, context, super.getFormValue()), clazz); } catch (Exception ex) { throw new PdoRuntimeException("cannot find object by domain key", ex); } finally { searchRunning = false; } } } // else default: just text return super.getFormValue(); } /** * Checks the returned object against null and creates the error popup if enabled. * * @param object the returned object * @param clazz the PDO-class * @return the object */ @SuppressWarnings("unchecked") protected Object checkObject(Object object, Class clazz) { if (object == null && checkExistsEnabled) { String text = getFormComponent().getText(); if (text != null && !text.isEmpty()) { if (getFormComponent().wasTransferFocusByEnter()) { try { setErrorMessage(MessageFormat.format(RdcSwingRdcBundle.getString("NO SUCH {0} FOUND"), Pdo.create(clazz).getPlural())); } catch (Exception ex) { setErrorMessage(RdcSwingRdcBundle.getString("NO SUCH DATA")); } setErrorOffset(0); } } // else: clear (returned null will clear the field) } return object; } /** * Sets the infofield of this linkpanel to be a drop zone. *

* The default is true. * * @param dropEnabled true if this is a drop zone, false if not */ public void setDropEnabled(boolean dropEnabled) { // make infoField a drop-target if (dropEnabled) { dropTarget = new DropTarget (infoField, this); dropTarget.setDefaultActions(DnDConstants.ACTION_COPY_OR_MOVE); } else { dropTarget = null; } updateInfoFieldDropAndColor(); } /** * Returns whether the infofield of this linkpanel is a dropzone. * * @return true if this is a drop zone, false if not */ public boolean isDropEnabled() { return dropTarget != null; } /** * Finds an PersistentDomainObject of given class by the key entered in the formfield component. *

* The default implementation just invokes {@link PersistentDomainObject#findByUniqueDomainKey(java.lang.Object)}. * * @param clazz the object class * @param contextDb the domain context * @param key the domain key * @return the object, null if not found */ @SuppressWarnings("unchecked") public T findByKey(Class clazz, DomainContext contextDb, Object key) { return key == null ? null : Pdo.create(clazz, contextDb).findByUniqueDomainKey(key); } /** * Sets the link. * * @param pdoSearch is the PdoSearch to be used * @param linkedId the original, i.e. current Id of the linked object */ @SuppressWarnings("unchecked") public void setLink(PdoSearch pdoSearch, long linkedId) { this.pdoSearch = pdoSearch; this.linkedId = 0; linkedObject = null; if (linkedId != 0 && pdoSearch != null) { try { linkedObject = pdoSearch.createPdo().selectCached(linkedId); if (linkedObject != null) { this.linkedId = linkedObject.getId(); } } catch (Exception ex) { // treated as "object not found" LOGGER.logStacktrace(ex); } } loadObject(); } /** * sets the link object (if plugin matches) * * @param pdo the data object */ @SuppressWarnings("unchecked") public void setLink(T pdo) { if (pdo != null && pdoSearch != null && pdoSearch.getPdoClass().isAssignableFrom(pdo.getEffectiveClass())) { setLink(pdoSearch, pdo.getId()); } else { setLink(pdoSearch, 0); } } /** * Sets the link with default plugin. * * @param clazz the class of the linked object, e.g. Konto.class * @param context is the db-connection with context * @param linkedId the original, i.e. current Id of the linked object * @param keepPlugin is true if keep plugin if already initialized */ public void setLink(Class clazz, DomainContext context, long linkedId, boolean keepPlugin) { try { if (context != null && clazz != null) { if (keepPlugin && pdoSearch != null) { setLink(pdoSearch, linkedId); } else { setLink (Rdc.createGuiProvider(Pdo.create(clazz, context)).createPdoSearch(), linkedId); } return; } } catch (Exception ex) {} // treated as "clear" // else clear link setLink (null, 0); } /** * Sets the link with default plugin. * * @param clazz the class of the linked object, e.g. Konto.class * @param context is the db-connection with context * @param linkedId the original, i.e. current Id of the linked object */ public void setLink(Class clazz, DomainContext context, long linkedId) { setLink(clazz, context, linkedId, false); } /** * Gets the object Id of the link. * * @return the object id, 0 = no object linked */ public long getLinkId() { if (isFireRunning() && getBinding() == null) { // we're in fireValueEntered and no binding T obj = getLink(); linkedId = obj == null ? 0 : obj.getId(); } return linkedId; } /** * Gets the object. * * @return the data object, null = no object linked */ @SuppressWarnings("unchecked") public T getLink() { if (isFireRunning() && getBinding() == null) { // we're in fireValueEntered and no binding Object obj = getFormValue(); if (obj instanceof PersistentDomainObject) { setLink((T) obj); } else { linkedId = 0; linkedObject = null; } } return linkedObject; } /** * Gets the search plugin. * * @return the search plugin */ public PdoSearch getSearchPlugin() { return pdoSearch; } /** * Sets columns of the info field. * * @param col the columns */ public void setInfoColumns(int col) { infoField.setColumns(col); } /** * Get columns of info field * * @return the columns */ public int getInfoColumns() { return infoField.getColumns(); } /** * Updates both the code- and the info field. * Override this if the default does not match your objects behaviour! * * @param linkedObject the data object */ public void updateCodeAndInfoField(T linkedObject) { if (linkedObject instanceof ShortLongText) { getFormComponent().setText(((ShortLongText)linkedObject).getShortText()); infoField.setText(((ShortLongText)linkedObject).getLongText()); } else { getFormComponent().setText(linkedObject.toString()); infoField.setText(Rdc.createGuiProvider(linkedObject).getTreeText()); } getFormComponent().clearValueShownModified(); } /** * Sets the infofield's visibility. * Sometimes useful if getTreeText() is not appropriate for non-ShortLongText objects. * * @param visible true if info field is visible (default) */ public void setInfoFieldVisible(boolean visible) { infoField.setVisible(visible); } /** * Gets the infofield's visibility. * * @return true if visible */ public boolean isInfoFieldVisible() { return infoField.isVisible(); } @Override public void setChangeable(boolean changeable) { super.setChangeable(changeable); loadObject(); // load again } @Override public void setCellEditorUsage(boolean flag) { super.setCellEditorUsage(flag); /** * disable focus lost on field when used as a celleditor */ editButton.setFocusable(!flag); searchButton.setFocusable(!flag); } /** * Loads the object */ protected void loadObject() { if (linkedObject == null) { infoField.clearText(); getFormComponent().clearText(); linkedId = 0; editButton.setEnabled(false); } else { linkedId = linkedObject.getId(); updateCodeAndInfoField(linkedObject); editButton.setEnabled(isChangeable()); } searchButton.setEnabled(isChangeable()); updateInfoFieldDropAndColor(); } /** * Updates the background color of the infofield. */ protected void updateInfoFieldDropAndColor() { if (dropTarget != null) { if (isChangeable() && linkedId == 0 && pdoSearch != null) { // createPdo accepted data flavour dndFlavor = new DataFlavor(pdoSearch.getPdoClass(), ReflectionHelper.getClassBaseName(pdoSearch.getPdoClass())); dropTarget.setActive(true); // allow drop here infoField.setBackground(PlafUtilities.getInstance().getDropFieldActiveColor()); } else { dropTarget.setActive(false); // no plugin or object already set: no drop-target infoField.setBackground(PlafUtilities.getInstance().getDropFieldInactiveColor()); } } else { infoField.setBackground(PlafUtilities.getInstance().getTextFieldInactiveBackgroundColor()); } } /** * Creates the search dialog.

* Invoked from {@link #runSearch()}. * @return the dialog */ public PdoSearchDialog createSearchDialog() { return Rdc.createPdoSearchDialog(this, pdoSearch, (o) -> pdoSearch.getPdoClass().isAssignableFrom(o.getClass()), true, true); } /** * Runs the search */ public void runSearch() { if (pdoSearch != null && !searchRunning) { pdoSearch.resetSearchCriteria(); try { searchRunning = true; @SuppressWarnings("unchecked") T obj = (T) createSearchDialog().showDialog(); if (obj != null) { setLink(obj); if (!isCellEditorUsage()) { fireValueEntered(); searchButton.transferFocus(); } } } catch (Exception ex) { FormError.showException(RdcSwingRdcBundle.getString("SEARCH FAILED"), ex); } finally { searchRunning = false; } } } /** * Edits the object */ @SuppressWarnings("unchecked") public void runEdit() { // modal dialog if (linkedObject != null) { if (PdoEditDialogPool.getInstance().editModal(linkedObject) != null) { // object was updated, display new text loadObject(); if (!isCellEditorUsage() && isAutoUpdate()) { fireValueEntered(); // could be changed somehow } } } } /** * Sets the visibility of the edit button.
* Some apps don't want the user to edit the object. * The default is visible. * * @param visible true if editbutton is visible */ public void setEditButtonVisible(boolean visible) { editButton.setVisible(visible); } /** * Gets the visibility of the edit button. * * @return true if editbutton is visible */ public boolean isEditButtonVisible() { return editButton.isVisible(); } /** * Sets the visibility of the search button.
* The default is visible. * * @param visible true if searchbutton is visible */ public void setSearchButtonVisible(boolean visible) { searchButton.setVisible(visible); } /** * Gets the visibility of the search button. * * @return true if searchbutton is visible */ public boolean isSearchButtonVisible() { return searchButton.isVisible(); } /** * Returns the editButton. * @return The editButton */ public org.tentackle.swing.FormButton getEditButton() { return editButton; } /** * Returns the searchButton. * @return The searchButton */ public org.tentackle.swing.FormButton getSearchButton() { return searchButton; } // --------------- implements DropTargetListener ---------------------------- @Override public void dragEnter (DropTargetDragEvent event) { if (!isDragAcceptable(event)) { event.rejectDrag(); } else { event.acceptDrag(DnDConstants.ACTION_COPY); } } @Override public void dragExit (DropTargetEvent event) { } @Override public void dragOver (DropTargetDragEvent event) { if (!isDragAcceptable(event)) { event.rejectDrag(); } // see comment above! } @Override public void dropActionChanged (DropTargetDragEvent event) { } @Override public void drop (DropTargetDropEvent event) { if (isDropAcceptable(event)) { event.acceptDrop(DnDConstants.ACTION_COPY); Transferable trans = event.getTransferable(); try { Object transferData = trans.getTransferData(dndFlavor); if (transferData instanceof PdoTransferData) { @SuppressWarnings("unchecked") T object = pdoSearch.createPdo().selectCached(((PdoTransferData) transferData).getId()); setLink(object); if (isAutoUpdate()) { fireValueEntered(); } } } catch (Exception e) { FormError.showException(RdcSwingRdcBundle.getString("DROP ERROR:"), e); } event.dropComplete(true); } else { event.rejectDrop(); } } private boolean isDragAcceptable(DropTargetDragEvent event) { return ((event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0 && event.isDataFlavorSupported(dndFlavor)); } private boolean isDropAcceptable(DropTargetDropEvent event) { return ((event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0 && event.isDataFlavorSupported(dndFlavor)); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // //GEN-BEGIN:initComponents private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; editButton = new org.tentackle.swing.FormButton(); infoField = new org.tentackle.swing.StringFormField(); searchButton = new org.tentackle.swing.FormButton(); setToolTipText(""); setLayout(new java.awt.GridBagLayout()); editButton.setIcon(org.tentackle.swing.plaf.PlafUtilities.getInstance().getIcon("edit")); editButton.setToolTipText(RdcSwingRdcBundle.getString("EDIT")); // NOI18N editButton.setName("edit"); // NOI18N editButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { editButtonActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; add(editButton, gridBagConstraints); infoField.setEditable(false); infoField.setName("info"); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 0; gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0); add(infoField, gridBagConstraints); searchButton.setIcon(org.tentackle.swing.plaf.PlafUtilities.getInstance().getIcon("search")); searchButton.setToolTipText(RdcSwingRdcBundle.getString("SEARCH")); // NOI18N searchButton.setName("search"); // NOI18N searchButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { searchButtonActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; add(searchButton, gridBagConstraints); }// //GEN-END:initComponents private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed runSearch(); }//GEN-LAST:event_searchButtonActionPerformed private void editButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editButtonActionPerformed runEdit(); }//GEN-LAST:event_editButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private org.tentackle.swing.FormButton editButton; private org.tentackle.swing.StringFormField infoField; private org.tentackle.swing.FormButton searchButton; // End of variables declaration//GEN-END:variables }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy