/* CropDBCropList.java - Created: March 14, 2007 * Copyright (C) 2007, 2008 Clayton Carter * * This file is part of the project "Crop Planning Software". For more * information: * website: https://github.com/claytonrcarter/cropplanning * email: cropplanning@gmail.com * * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */ package CPS.Core.CropPlans; import CPS.Data.*; import CPS.UI.Modules.CPSMasterView; import CPS.Module.*; import CPS.UI.Modules.CPSAdvancedTableFormat; import CPS.UI.Modules.CPSMasterDetailModule; import CPS.UI.Swing.CPSComplexFilterDialog; import CPS.UI.Swing.CPSErrorDialog; import CPS.UI.Swing.CPSTable.CPSComboBoxCellEditor; import CPS.UI.Swing.autocomplete.*; import ca.odell.glazedlists.FunctionList; import ca.odell.glazedlists.TextFilterator; import ca.odell.glazedlists.calculation.Calculation; import ca.odell.glazedlists.calculation.Calculations; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.matchers.SearchEngineTextMatcherEditor; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Set; import javax.swing.*; // package access class CropPlanList extends CPSMasterView implements ActionListener, ItemListener, PropertyChangeListener { protected static final String KEY_FILTER = "LAST_FILTER"; private JLabel lblPlanName; // private JComboBox cmbxPlanList; private PlanManager planMan; private JComboBox cmbxCropList, cmbxFieldList; private JButton btnChangePlans; private JComboBox cmbxLimit; protected static final String LIMIT_ALL = "all"; protected static final String LIMIT_ALL_UNP = "all: not planted"; protected static final String LIMIT_THIS_WEEK = "all: this week"; protected static final String LIMIT_NEXT_WEEK = "all: next week"; protected static final String LIMIT_THIS_NEXT = "all: this and next week"; protected static final String LIMIT_DS = "just DS"; protected static final String LIMIT_DS_UNP = "just DS: not planted"; protected static final String LIMIT_TP = "just TP"; protected static final String LIMIT_TP_NOT_S = "just TP: not seeded"; protected static final String LIMIT_TP_NOT_P = "just TP: seeded, not planted"; protected static final String LIMIT_CUSTOM = "custom..."; // TODO, this is a terrible name private CPSComplexFilterDialog dlgFilter; private CPSComplexPlantingFilter plantingFilter; private List<String> listOfValidCrops, listOfFields; private Calculation<Integer> summaryPlantings = null; private Calculation<Integer> summaryPlants = null; private Calculation<Integer> summaryRowFt = null; private Calculation<Float> summaryBeds = null, summaryFlats = null; public CropPlanList( CPSMasterDetailModule mdm ) { super(mdm); plantingFilter = new CPSComplexPlantingFilter(); addFilter( plantingFilter ); dlgFilter = new CPSComplexFilterDialog( getAboveListPanel() ); planMan = new PlanManager( getAboveListPanel() ); } @Override public void setDataSource( CPSDataModel dm ) { super.setDataSource(dm); planMan.setDataModel(dm); String plan = getPrefs().get( KEY_DISPLAYED_TABLE, "" ); if ( !plan.equals( "" ) ) { CPSModule.debug( "CPList", "Found last displayed table:" + plan ); planMan.selectPlan( plan ); } cmbxLimit.setSelectedItem( getPrefs().get( KEY_FILTER, LIMIT_ALL ) ); dataUpdated(); updateListOfCrops(); updateListOfFieldNames(); // setup for the summary string FunctionList<CPSRecord, Float> bedsList = new FunctionList<CPSRecord, Float>( masterListFiltered, new FunctionList.Function<CPSRecord, Float>() { public Float evaluate( CPSRecord p ) { return ( (CPSPlanting) p ).getBedsToPlant(); }} ); FunctionList<CPSRecord, Integer> plantsList = new FunctionList<CPSRecord, Integer>( masterListFiltered, new FunctionList.Function<CPSRecord, Integer>() { public Integer evaluate( CPSRecord p ) { return ( (CPSPlanting) p ).getPlantsNeeded(); }} ); FunctionList<CPSRecord, Float> flatsList = new FunctionList<CPSRecord, Float>( masterListFiltered, new FunctionList.Function<CPSRecord, Float>() { public Float evaluate( CPSRecord p ) { return ( (CPSPlanting) p ).getFlatsNeeded(); }} ); FunctionList<CPSRecord, Integer> rftList = new FunctionList<CPSRecord, Integer>( masterListFiltered, new FunctionList.Function<CPSRecord, Integer>() { public Integer evaluate( CPSRecord p ) { return ( (CPSPlanting) p ).getRowFtToPlant(); }} ); summaryPlantings = Calculations.count( masterListFiltered ); summaryBeds = Calculations.sumFloats( bedsList ); summaryPlants = Calculations.sumIntegers( plantsList ); summaryFlats = Calculations.sumFloats( flatsList ); summaryRowFt = Calculations.sumIntegers( rftList ); // this propertyChangeListener will notice when this calculation is updated // and will fire the property change to update the stat string summaryRowFt.addPropertyChangeListener( this ); updateStatisticsLabel(); } @Override protected int saveState() { String selectedFilter = (String) cmbxLimit.getSelectedItem(); if ( ! selectedFilter.equals( LIMIT_CUSTOM )) getPrefs().put( KEY_FILTER, selectedFilter ); return super.saveState(); } @Override protected int getTypeOfDisplayedRecord() { return CPSDataModelConstants.RECORD_TYPE_PLANTING; } protected CPSPlanting getDetailsForIDs( List<Integer> ids ) { return getDataSource().getCommonInfoForPlantings( getDisplayedTableName(), ids ); } /** * Populates the crop name combobox with the list of valid crop names from CropDB. */ protected void updateListOfCrops() { if ( ! isDataAvailable() ) return; listOfValidCrops = getDataSource().getCropNameList(); Collections.sort( listOfValidCrops, String.CASE_INSENSITIVE_ORDER); cmbxCropList.removeAllItems(); cmbxCropList.addItem(""); for ( String s : listOfValidCrops ) cmbxCropList.addItem(s); } protected void updateListOfFieldNames() { if ( ! isDataAvailable() ) return; listOfFields = getDataSource().getFieldNameList( getDisplayedTableName() ); Collections.sort( listOfFields, String.CASE_INSENSITIVE_ORDER); cmbxFieldList.removeAllItems(); cmbxFieldList.addItem(""); for ( String s : listOfFields ) cmbxFieldList.addItem(s); } protected List getMasterListData() { if ( ! isDataAvailable() || ! getDataSource().cropPlanExists( getDisplayedTableName() )) return new ArrayList<CPSPlanting>(); String selectedPlan = getDisplayedTableName(); if ( selectedPlan != null ) return getDataSource().getCropPlan( selectedPlan ); else // TODO error checking fall through to following call when invalid plan is selected return new ArrayList(); } @Override protected CPSAdvancedTableFormat getTableFormat() { return new CropPlanTableFormat(); } @Override protected TextFilterator getTextFilterator() { return new CropPlanFilterator(); } protected Set<SearchEngineTextMatcherEditor.Field<CPSRecord>> getFilterFields() { Set<SearchEngineTextMatcherEditor.Field<CPSRecord>> s = new HashSet<SearchEngineTextMatcherEditor.Field<CPSRecord>>(); s.add( new SearchEngineTextMatcherEditor.Field<CPSRecord>( "crop", new TextFilterator<CPSRecord>() { public void getFilterStrings( List<String> list, CPSRecord e ) { list.add( ((CPSPlanting) e).getCropName() ); } })); s.add( new SearchEngineTextMatcherEditor.Field<CPSRecord>( "variety", new TextFilterator<CPSRecord>() { public void getFilterStrings( List<String> list, CPSRecord e ) { list.add( ((CPSPlanting) e).getVarietyName()); } })); s.add( new SearchEngineTextMatcherEditor.Field<CPSRecord>( "location", new TextFilterator<CPSRecord>() { public void getFilterStrings( List<String> list, CPSRecord e ) { list.add( ((CPSPlanting) e).getLocation()); } })); s.add( new SearchEngineTextMatcherEditor.Field<CPSRecord>( "groups", new TextFilterator<CPSRecord>() { public void getFilterStrings( List<String> list, CPSRecord e ) { list.add( ((CPSPlanting) e).getGroups()); } })); s.add( new SearchEngineTextMatcherEditor.Field<CPSRecord>( "keyword", new TextFilterator<CPSRecord>() { public void getFilterStrings( List<String> list, CPSRecord e ) { list.add( ((CPSPlanting) e).getKeywords()); } })); return s; } protected String getDisplayedTableName() { return planMan.getSelectedPlanName(); } @Override protected void buildAboveListPanel() { initAboveListPanel(); lblPlanName = new JLabel( "Plan Name: " ); btnChangePlans = new JButton( "Change Plans" ); btnChangePlans.setActionCommand( "ChangePlans" ); btnChangePlans.setMargin( new Insets( 1, 5, 1, 5 ) ); btnChangePlans.addActionListener( this ); jplAboveList.add( lblPlanName ); jplAboveList.add( btnChangePlans ); // false ==> do not initialize panel super.buildAboveListPanel(false); } @Override protected void buildListPanel() { super.buildListPanel(); cmbxCropList = new JComboBox(); cmbxCropList.addActionListener( new CropBoxInTableActionListener() ); AutoCompleteDecorator.decorate( cmbxCropList ); cmbxFieldList = new JComboBox(); cmbxFieldList.setEditable(true); AutoCompleteDecorator.decorate(cmbxFieldList); } @Override protected JPanel buildFilterComponent() { initFilterPanel(); // btnLimit = new JButton( "Limit View" ); // btnLimit.setMargin( new Insets( 1, 1, 1, 1 )); // btnLimit.addActionListener(this); cmbxLimit = new JComboBox(); cmbxLimit.setMaximumRowCount(15); cmbxLimit.addItem( LIMIT_ALL ); cmbxLimit.addItem( LIMIT_ALL_UNP ); // cmbxLimit.addItem( LIMIT_THIS_WEEK ); // cmbxLimit.addItem( LIMIT_NEXT_WEEK ); // cmbxLimit.addItem( LIMIT_THIS_NEXT ); cmbxLimit.addItem( LIMIT_DS ); cmbxLimit.addItem( LIMIT_DS_UNP ); cmbxLimit.addItem( LIMIT_TP ); cmbxLimit.addItem( LIMIT_TP_NOT_S ); cmbxLimit.addItem( LIMIT_TP_NOT_P ); cmbxLimit.addItem( LIMIT_CUSTOM ); cmbxLimit.setMaximumSize( cmbxLimit.getPreferredSize() ); cmbxLimit.addItemListener(this); cmbxLimit.addActionListener(this); jplFilter.add( new JLabel( "Show" )); // jplFilter.add( btnLimit ); jplFilter.add( cmbxLimit ); jplFilter.add( new JLabel( "matching")); return super.buildFilterComponent( false ); } public class CropPlanBoxActionListener implements ActionListener { public void actionPerformed( ActionEvent actionEvent ) { String action = actionEvent.getActionCommand(); System.out.println( "DEBUG Action performed in CropPlanList: " + action ); if ( action.equalsIgnoreCase( "comboBoxEdited" ) ) { // What do we do here? create a new plan? } else if ( action.equalsIgnoreCase( "comboBoxChanged" ) ) { updateMasterList(); } } } public class CropBoxInTableActionListener implements ActionListener { public void actionPerformed( ActionEvent actionEvent ) { String action = actionEvent.getActionCommand(); CPSModule.debug( "CropBoxInTable", "Action registered: " + action ); if ( action.equalsIgnoreCase( "comboBoxEdited" ) ) { // What do we do here? create a new crop? } else if ( action.equalsIgnoreCase( "comboBoxChanged" ) ) { String s = (String) cmbxCropList.getSelectedItem(); // update mesh this planting w/ the crop selected, but for now just ... // CPSModule.debug( "CropBoxInTable", "Crop name: selected " + s ); if ( listOfValidCrops.contains( s ) ) { CPSCrop crop = getDataSource().getCropInfo( (String) cmbxCropList.getSelectedItem() ); // CPSModule.debug( "CropBoxInTable", "Retrieved info for crop " + crop.getCropName() ); } else { // TODO handle selection of non-existing crop CPSModule.debug( "CropBoxInTable", "ERROR: Selected non existent crop, what to do? Create? For now, nothing." ); } } } } @Override public CPSPlanting getBlankRecord() { return new CPSPlanting(); } @Override protected void updateRecordInDB( CPSRecord r ) { if ( r instanceof CPSPlanting ) getDataSource().updatePlanting( getDisplayedTableName(), (CPSPlanting) r ); } @Override protected CPSPlanting createNewRecord( CPSRecord r ) { if ( getDisplayedTableName().equals("") ) { new CPSErrorDialog( this.getMainPanel(), "Cannot Create Entry", "<center>We can only create new planting entries<br>" + "after you have selected a crop plan.<br>" + "<b>No record created." ).setVisible( true ); return null; } CPSPlanting p = getDataSource().createPlanting( getDisplayedTableName(), (CPSPlanting) r ); return (CPSPlanting) super.createNewRecord(p); } @Override public void deleteRecord( int id ) { super.deleteRecord(id); getDataSource().deletePlanting( getDisplayedTableName(), id ); } @Override protected List<String> getDisplayableColumnList() { return getDataSource().getPlantingDisplayablePropertyNames(); } @Override protected List<Integer> getDefaultDisplayableColumnList() { return getDataSource().getPlantingDefaultProperties(); } protected List<String[]> getColumnPrettyNameMap() { return getDataSource().getPlantingPrettyNames(); } @Override protected String getTableStatisticsString() { if ( ! isDataAvailable() || getDisplayedTableName() == null ) return ""; String s = ""; if ( summaryPlantings != null && summaryPlantings.getValue() > 0 ) { s += "Plantings:" + summaryPlantings.getValue(); String t = "" + CPSCalculations.precision3( summaryBeds.getValue() + .001f ); if ( ! t.equals("") ) s += "/Beds:" + t; t = summaryPlants.getValue().toString(); if ( ! t.equals( "" ) ) s += "/Plants:" + t; t = summaryRowFt.getValue().toString(); if ( ! t.equals("") ) { if ( CPSGlobalSettings.useMetric() ) s += "/RowMeters:" + t; else s += "/RowFeet:" + t; } t = summaryFlats.getValue().toString(); if ( ! t.equals("") ) s += "/Flats:" + t; // t = p.getTotalYieldString(); // if ( ! t.equals("") ) // s += "/Yield:" + t; } return s; } protected void updateSelectedPlanLabel() { String plan = getDisplayedTableName(); if ( plan == null ) plan = "No Plan Selected!"; lblPlanName.setText( "Plan Name: " + plan + " " ); } @Override public void dataUpdated() { super.dataUpdated(); updateSelectedPlanLabel(); if ( getDisplayedTableName() == null ) { setStatus( CPSMasterView.STATUS_NO_PLAN_SELECTED ); enableRecordButtons(false); } else { updateListOfFieldNames(); enableRecordButtons(true); } } protected void updateAutocompletionComponents() { updateListOfCrops(); updateListOfFieldNames(); } public void itemStateChanged( ItemEvent arg0 ) { Object source = arg0.getSource(); if ( source == cmbxLimit ) { CPSModule.debug( "CropPlanList", "view limit combobox changed to: " + cmbxLimit.getSelectedItem() ); String s = (String) cmbxLimit.getSelectedItem(); if ( s.equalsIgnoreCase( LIMIT_ALL_UNP )) plantingFilter.setAsAllNotPlantedFilter(); else if ( s.equalsIgnoreCase( LIMIT_THIS_WEEK )) plantingFilter.setAsThisWeekFilter(); else if ( s.equalsIgnoreCase( LIMIT_NEXT_WEEK )) plantingFilter.setAsNextWeekFilter(); else if ( s.equalsIgnoreCase( LIMIT_THIS_NEXT )) plantingFilter.setAsThisAndNextWeekFilter(); else if ( s.equalsIgnoreCase( LIMIT_DS )) plantingFilter.setAsDirectSeededFilter(); else if ( s.equalsIgnoreCase( LIMIT_DS_UNP )) plantingFilter.setAsDSNotPlantedFilter(); else if ( s.equalsIgnoreCase( LIMIT_TP )) plantingFilter.setAsTransplatedFilter(); else if ( s.equalsIgnoreCase( LIMIT_TP_NOT_S )) plantingFilter.setAsTPNotSeededFilter(); else if ( s.equalsIgnoreCase( LIMIT_TP_NOT_P )) plantingFilter.setAsTPSeededNotPlantedFilter(); else { plantingFilter.reset(); if ( s.equalsIgnoreCase( LIMIT_ALL )) plantingFilter.changed(); } updateFilter(); } } @Override public void actionPerformed(ActionEvent actionEvent) { String action = actionEvent.getActionCommand(); if ( action.equalsIgnoreCase( btnChangePlans.getActionCommand() ) ) { if ( ! isDataAvailable() ) { CPSModule.debug( "CropPlanList", "ERROR: cannot create new plan, no data available" ); return; } String s = planMan.getSelectedPlanName(); planMan.setVisible(true); dataUpdated(); } else if ( actionEvent.getSource() == cmbxLimit && ((String) cmbxLimit.getSelectedItem()).equalsIgnoreCase( LIMIT_CUSTOM ) ) { dlgFilter.setVisible( true ); // dialog is modal, wait for it to return if ( dlgFilter.getFilter().isViewLimited() ) { dlgFilter.updateFilter( plantingFilter ); } else cmbxLimit.setSelectedItem( LIMIT_ALL ); } else super.actionPerformed( actionEvent ); } // for addPropertyChangeListener public void propertyChange( PropertyChangeEvent evt ) { updateStatisticsLabel(); } }