org.projectforge.web.wicket.AbstractListForm.java Source code

Java tutorial

Introduction

Here is the source code for org.projectforge.web.wicket.AbstractListForm.java

Source

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2013 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition 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; version 3 of the License.
//
// This community edition 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 org.projectforge.web.wicket;

import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.HiddenField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.projectforge.common.DateHolder;
import org.projectforge.common.DatePrecision;
import org.projectforge.common.NumberHelper;
import org.projectforge.core.BaseDao;
import org.projectforge.core.BaseSearchFilter;
import org.projectforge.user.PFUserContext;
import org.projectforge.user.PFUserDO;
import org.projectforge.user.UserGroupCache;
import org.projectforge.web.user.UserSelectPanel;
import org.projectforge.web.wicket.bootstrap.GridBuilder;
import org.projectforge.web.wicket.bootstrap.GridSize;
import org.projectforge.web.wicket.components.DateTimePanel;
import org.projectforge.web.wicket.components.DateTimePanelSettings;
import org.projectforge.web.wicket.components.LabelValueChoiceRenderer;
import org.projectforge.web.wicket.components.SingleButtonPanel;
import org.projectforge.web.wicket.flowlayout.CheckBoxPanel;
import org.projectforge.web.wicket.flowlayout.DivPanel;
import org.projectforge.web.wicket.flowlayout.DivType;
import org.projectforge.web.wicket.flowlayout.FieldSetIconPosition;
import org.projectforge.web.wicket.flowlayout.FieldsetPanel;
import org.projectforge.web.wicket.flowlayout.HiddenInputPanel;
import org.projectforge.web.wicket.flowlayout.HtmlCommentPanel;
import org.projectforge.web.wicket.flowlayout.IconPanel;
import org.projectforge.web.wicket.flowlayout.IconType;
import org.projectforge.web.wicket.flowlayout.InputPanel;
import org.projectforge.web.wicket.flowlayout.MyComponentsRepeater;

public abstract class AbstractListForm<F extends BaseSearchFilter, P extends AbstractListPage<?, ?, ?>>
        extends AbstractSecuredForm<F, P> {
    private static final long serialVersionUID = 1304394324524767035L;

    public static final String I18N_ONLY_DELETED = "onlyDeleted";

    public static final String I18N_ONLY_DELETED_TOOLTIP = "onlyDeleted.tooltip";

    protected F searchFilter;

    protected abstract F newSearchFilterInstance();

    protected Integer pageSize;

    protected GridBuilder gridBuilder;

    private DivPanel extendedFilter;

    private Label modifiedSearchExpressionLabel;

    private String modificationSince;

    protected DateTimePanel startDateTimePanel;

    protected DateTimePanel stopDateTimePanel;

    @SpringBean(name = "userGroupCache")
    protected UserGroupCache userGroupCache;

    private FieldsetPanel optionsFieldsetPanel;

    /**
     * List to create action buttons in the desired order before creating the RepeatingView.
     */
    protected MyComponentsRepeater<Component> actionButtons;

    private SingleButtonPanel cancelButtonPanel;

    protected SingleButtonPanel resetButtonPanel;

    protected SingleButtonPanel searchButtonPanel;

    private SingleButtonPanel nextButtonPanel;

    protected FieldsetPanel pageSizeFieldsetPanel;

    public static final int[] PAGE_SIZES = new int[] { 3, 5, 10, 25, 50, 100, 200, 500, 1000 };

    public static DropDownChoice<Integer> getPageSizeDropDownChoice(final String id, final Locale locale,
            final IModel<Integer> model, final int minValue, final int maxValue) {
        final LabelValueChoiceRenderer<Integer> pageSizeChoiceRenderer = new LabelValueChoiceRenderer<Integer>();
        final NumberFormat nf = NumberFormat.getInstance(locale);
        for (final int size : PAGE_SIZES) {
            if (size >= minValue && size <= maxValue) {
                pageSizeChoiceRenderer.addValue(size, nf.format(size));
            }
        }
        final DropDownChoice<Integer> pageSizeChoice = new DropDownChoice<Integer>(id, model,
                pageSizeChoiceRenderer.getValues(), pageSizeChoiceRenderer);
        pageSizeChoice.setNullValid(false);
        return pageSizeChoice;
    }

    public AbstractListForm(final P parentPage) {
        super(parentPage);
        getSearchFilter();
    }

    @SuppressWarnings("serial")
    @Override
    protected void init() {
        super.init();
        final FeedbackPanel feedbackPanel = new FeedbackPanel("feedback");
        feedbackPanel.setOutputMarkupId(true);
        add(feedbackPanel);

        gridBuilder = newGridBuilder(this, "filter");
        if (isFilterVisible() == true) {
            {
                // Fieldset search filter
                final FieldsetPanel fs = gridBuilder.newFieldset(getString("searchFilter"));
                if (parentPage.getBaseDao().isHistorizable() == true) {
                    IconPanel icon = new IconPanel(fs.newIconChildId(), IconType.PLUS_SIGN,
                            getString("filter.extendedSearch")).setOnClick("javascript:showExtendedFilter();");
                    icon.setMarkupId("showExtendedFilter");
                    fs.add(icon, FieldSetIconPosition.BOTTOM_LEFT);
                    icon = new IconPanel(fs.newIconChildId(), IconType.MINUS_SIGN)
                            .setOnClick("javascript:hideExtendedFilter();");
                    icon.setMarkupId("hideExtendedFilter");
                    fs.add(icon, FieldSetIconPosition.BOTTOM_LEFT);
                }
                final TextField<?> searchTextField = createSearchTextField();
                fs.add(searchTextField);
                fs.setLabelFor(searchTextField);
                final Model<String> modifiedSearchExpressionModel = new Model<String>() {
                    @Override
                    public String getObject() {
                        return getModifiedSearchExpressionLabel(AbstractListForm.this,
                                searchFilter.getSearchString());
                    }
                };
                final DivPanel div = new DivPanel(fs.newChildId()) {
                    /**
                     * @see org.projectforge.web.wicket.flowlayout.DivPanel#isVisible()
                     */
                    @Override
                    public boolean isVisible() {
                        return StringUtils.isNotBlank(searchFilter.getSearchString()) == true;
                    }
                };
                div.add(AttributeModifier.append("class", "modifiedSearchExpressionLabel"));
                fs.add(div);
                modifiedSearchExpressionLabel = new Label(div.newChildId(), modifiedSearchExpressionModel);
                modifiedSearchExpressionLabel.setEscapeModelStrings(false);
                div.add(modifiedSearchExpressionLabel);

                fs.addHelpIcon(getString("tooltip.lucene.link"), FieldSetIconPosition.TOP_RIGHT)
                        .setOnClickLocation(getRequestCycle(), WebConstants.DOC_LINK_HANDBUCH_LUCENE, true);
                final String helpKeyboardImageTooltip = getHelpKeyboardImageTooltip();
                if (helpKeyboardImageTooltip != null) {
                    fs.addKeyboardHelpIcon(helpKeyboardImageTooltip);
                }
            }
        }
        if (parentPage.getBaseDao().isHistorizable() == true && isFilterVisible() == true) {
            addExtendedFilter();
        }
        if (showOptionsPanel() == true) {
            gridBuilder.newSplitPanel(GridSize.COL66);
            optionsFieldsetPanel = gridBuilder.newFieldset(getOptionsLabel()).supressLabelForWarning();
            final DivPanel optionsCheckBoxesPanel = new DivPanel(optionsFieldsetPanel.newChildId(),
                    DivType.CHECKBOX);
            onOptionsPanelCreate(optionsFieldsetPanel, optionsCheckBoxesPanel);
            if (showHistorySearchAndDeleteCheckbox() == true) {
                optionsCheckBoxesPanel.add(createAutoRefreshCheckBoxPanel(optionsCheckBoxesPanel.newChildId(),
                        new PropertyModel<Boolean>(getSearchFilter(), "deleted"), getString(I18N_ONLY_DELETED),
                        getString(I18N_ONLY_DELETED_TOOLTIP)).setWarning());
                optionsCheckBoxesPanel.add(createAutoRefreshCheckBoxPanel(optionsCheckBoxesPanel.newChildId(),
                        new PropertyModel<Boolean>(getSearchFilter(), "searchHistory"),
                        getString("search.searchHistory"), getString("search.searchHistory.additional.tooltip")));
            }
            if (optionsCheckBoxesPanel.hasChilds() == true) {
                optionsFieldsetPanel.add(optionsCheckBoxesPanel);
            }
            gridBuilder.newSplitPanel(GridSize.COL33);
        } else {
            gridBuilder.newGridPanel();
        }
        // DropDownChoice page size
        pageSizeFieldsetPanel = gridBuilder.newFieldset(getString("label.pageSize"));
        pageSizeFieldsetPanel.add(getPageSizeDropDownChoice(pageSizeFieldsetPanel.getDropDownChoiceId(),
                getLocale(), new PropertyModel<Integer>(this, "pageSize"), 25, 1000));

        final WebMarkupContainer buttonCell = new WebMarkupContainer("buttonCell");
        add(buttonCell);
        actionButtons = new MyComponentsRepeater<Component>("actionButtons");
        buttonCell.add(actionButtons.getRepeatingView());

        final Button cancelButton = new Button("button", new Model<String>("cancel")) {
            @Override
            public final void onSubmit() {
                getParentPage().onCancelSubmit();
            }
        };
        cancelButton.setDefaultFormProcessing(false);
        cancelButtonPanel = new SingleButtonPanel(getNewActionButtonChildId(), cancelButton, getString("cancel"),
                SingleButtonPanel.CANCEL);
        addActionButton(cancelButtonPanel);

        final Button resetButton = new Button("button", new Model<String>("reset")) {
            @Override
            public final void onSubmit() {
                getParentPage().onResetSubmit();
            }
        };
        resetButton.setDefaultFormProcessing(false);
        resetButtonPanel = new SingleButtonPanel(getNewActionButtonChildId(), resetButton, getString("reset"),
                SingleButtonPanel.RESET);
        addActionButton(resetButtonPanel);

        final Button nextButton = new Button("button", new Model<String>("next")) {
            @Override
            public final void onSubmit() {
                getParentPage().onNextSubmit();
            }
        };
        nextButtonPanel = new SingleButtonPanel(getNewActionButtonChildId(), nextButton, getString("next"),
                SingleButtonPanel.DEFAULT_SUBMIT);
        addActionButton(nextButtonPanel);

        final Button searchButton = new Button("button", new Model<String>("search")) {
            @Override
            public final void onSubmit() {
                getParentPage().onSearchSubmit();
            }
        };
        searchButtonPanel = new SingleButtonPanel(getNewActionButtonChildId(), searchButton, getString("search"),
                SingleButtonPanel.DEFAULT_SUBMIT);
        addActionButton(searchButtonPanel);

        setComponentsVisibility();
    }

    protected String getOptionsLabel() {
        return getString("label.options");
    }

    @SuppressWarnings("serial")
    private void addExtendedFilter() {
        gridBuilder.newSplitPanel(GridSize.COL66);
        extendedFilter = gridBuilder.getRowPanel();
        extendedFilter.setMarkupId("extendedFilter");
        if (searchFilter.isUseModificationFilter() == false) {
            extendedFilter.add(AttributeModifier.append("style", "display: none;"));
        }
        {
            final FieldsetPanel fieldset = gridBuilder.newFieldset(getString("search.periodOfModification"));
            fieldset.add(new HiddenInputPanel(fieldset.newChildId(),
                    new HiddenField<Boolean>(InputPanel.WICKET_ID,
                            new PropertyModel<Boolean>(searchFilter, "useModificationFilter")))
                                    .setHtmlId("useModificationFilter"));

            startDateTimePanel = new DateTimePanel(fieldset.newChildId(),
                    new PropertyModel<Date>(searchFilter, "startTimeOfLastModification"),
                    (DateTimePanelSettings) DateTimePanelSettings.get()
                            .withSelectProperty("startDateOfLastModification").withSelectPeriodMode(true),
                    DatePrecision.MINUTE);
            fieldset.add(startDateTimePanel);
            fieldset.setLabelFor(startDateTimePanel);
            stopDateTimePanel = new DateTimePanel(fieldset.newChildId(),
                    new PropertyModel<Date>(searchFilter, "stopTimeOfLastModification"),
                    (DateTimePanelSettings) DateTimePanelSettings.get()
                            .withSelectProperty("stopDateOfLastModification").withSelectPeriodMode(true),
                    DatePrecision.MINUTE);
            stopDateTimePanel.setRequired(false);
            fieldset.add(stopDateTimePanel);
            final HtmlCommentPanel comment = new HtmlCommentPanel(fieldset.newChildId(), new DatesAsUTCModel() {
                @Override
                public Date getStartTime() {
                    return searchFilter.getStartTimeOfModification();
                }

                @Override
                public Date getStopTime() {
                    return searchFilter.getStopTimeOfModification();
                }
            });
            fieldset.add(comment);
            // DropDownChoice for convenient selection of time periods.
            final LabelValueChoiceRenderer<String> timePeriodChoiceRenderer = new LabelValueChoiceRenderer<String>();
            timePeriodChoiceRenderer.addValue("lastMinute", getString("search.lastMinute"));
            timePeriodChoiceRenderer.addValue("lastMinutes:10",
                    PFUserContext.getLocalizedMessage("search.lastMinutes", 10));
            timePeriodChoiceRenderer.addValue("lastMinutes:30",
                    PFUserContext.getLocalizedMessage("search.lastMinutes", 30));
            timePeriodChoiceRenderer.addValue("lastHour", getString("search.lastHour"));
            timePeriodChoiceRenderer.addValue("lastHours:4",
                    PFUserContext.getLocalizedMessage("search.lastHours", 4));
            timePeriodChoiceRenderer.addValue("today", getString("search.today"));
            timePeriodChoiceRenderer.addValue("sinceYesterday", getString("search.sinceYesterday"));
            timePeriodChoiceRenderer.addValue("lastDays:3",
                    PFUserContext.getLocalizedMessage("search.lastDays", 3));
            timePeriodChoiceRenderer.addValue("lastDays:7",
                    PFUserContext.getLocalizedMessage("search.lastDays", 7));
            timePeriodChoiceRenderer.addValue("lastDays:14",
                    PFUserContext.getLocalizedMessage("search.lastDays", 14));
            timePeriodChoiceRenderer.addValue("lastDays:30",
                    PFUserContext.getLocalizedMessage("search.lastDays", 30));
            timePeriodChoiceRenderer.addValue("lastDays:60",
                    PFUserContext.getLocalizedMessage("search.lastDays", 60));
            timePeriodChoiceRenderer.addValue("lastDays:90",
                    PFUserContext.getLocalizedMessage("search.lastDays", 90));
            final DropDownChoice<String> modificationSinceChoice = new DropDownChoice<String>(
                    fieldset.getDropDownChoiceId(), new PropertyModel<String>(this, "modificationSince"),
                    timePeriodChoiceRenderer.getValues(), timePeriodChoiceRenderer);
            modificationSinceChoice.setNullValid(true);
            modificationSinceChoice.setRequired(false);
            fieldset.add(modificationSinceChoice, true);
        }

        {
            gridBuilder.newSplitPanel(GridSize.COL33);
            final FieldsetPanel fs = gridBuilder.newFieldset(getString("modifiedBy"), getString("user"));

            final UserSelectPanel userSelectPanel = new UserSelectPanel(fs.newChildId(), new Model<PFUserDO>() {
                @Override
                public PFUserDO getObject() {
                    return userGroupCache.getUser(searchFilter.getModifiedByUserId());
                }

                @Override
                public void setObject(final PFUserDO object) {
                    if (object == null) {
                        searchFilter.setModifiedByUserId(null);
                    } else {
                        searchFilter.setModifiedByUserId(object.getId());
                    }
                }
            }, parentPage, "modifiedByUserId");
            fs.add(userSelectPanel);
            userSelectPanel.setDefaultFormProcessing(false);
            userSelectPanel.init().withAutoSubmit(true);
        }
        gridBuilder.setCurrentLevel(0); // Go back to main row panel.
    }

    /**
     * Here you can add elements to the given FieldsetPanel or optionsPanel. The optionsPanel is not yet added to the FieldsetPanel.
     * @param optionsFieldsetPanel
     * @param optionsPanel
     */
    protected void onOptionsPanelCreate(final FieldsetPanel optionsFieldsetPanel,
            final DivPanel optionsCheckBoxesPanel) {

    }

    /**
     * Creates a simple TextField and sets the focus on it. Overwrite this method if you want to add for example an auto completion text field
     * (ajax). Please don't forget to call addSearchFieldTooltip() in your method!
     */
    protected TextField<?> createSearchTextField() {
        final TextField<String> searchField = new TextField<String>(InputPanel.WICKET_ID,
                new PropertyModel<String>(getSearchFilter(), "searchString"));
        createSearchFieldTooltip(searchField);
        searchField.add(WicketUtils.setFocus());
        return searchField;
    }

    protected void createSearchFieldTooltip(final Component field) {
        WicketUtils.addTooltip(field, getString("search.string.info.title"), getParentPage().getSearchToolTip());
    }

    public void addActionButton(final Component entry) {
        this.actionButtons.add(entry);
    }

    public void prependActionButton(final Component entry) {
        this.actionButtons.add(0, entry);
    }

    public String getNewActionButtonChildId() {
        return this.actionButtons.newChildId();
    }

    @Override
    public void onBeforeRender() {
        super.onBeforeRender();
        actionButtons.render();
    }

    protected void setComponentsVisibility() {
        if (parentPage.isMassUpdateMode() == true) {
            cancelButtonPanel.setVisible(true);
            searchButtonPanel.setVisible(false);
            resetButtonPanel.setVisible(false);
            nextButtonPanel.setVisible(true);
            setDefaultButton(nextButtonPanel.getButton());
        } else {
            if (parentPage.isSelectMode() == false) {
                // Show cancel button only in select mode.
                cancelButtonPanel.setVisible(false);
            }
            searchButtonPanel.setVisible(true);
            resetButtonPanel.setVisible(true);
            nextButtonPanel.setVisible(false);
            setDefaultButton(searchButtonPanel.getButton());
        }
    }

    /**
     * onchange, onclick or submit without button.
     * @see org.apache.wicket.markup.html.form.Form#onSubmit()
     */
    @Override
    protected void onSubmit() {
        super.onSubmit();
        if (modificationSince != null) {
            final int pos = modificationSince.indexOf(':');
            final Integer number;
            if (pos >= 0) {
                number = NumberHelper.parseInteger(modificationSince.substring(pos + 1));
            } else {
                number = null;
            }
            final DateHolder dateHolder = new DateHolder(DatePrecision.MINUTE);
            if ("lastMinute".equals(modificationSince) == true) {
                dateHolder.add(Calendar.MINUTE, -1);
            } else if (modificationSince.startsWith("lastMinutes:") == true) {
                dateHolder.add(Calendar.MINUTE, -number);
            } else if ("lastHour".equals(modificationSince) == true) {
                dateHolder.add(Calendar.HOUR, -1);
            } else if (modificationSince.startsWith("lastHours:") == true) {
                dateHolder.add(Calendar.HOUR, -number);
            } else if ("today".equals(modificationSince) == true) {
                dateHolder.setBeginOfDay();
            } else if ("sinceYesterday".equals(modificationSince) == true) {
                dateHolder.add(Calendar.DAY_OF_YEAR, -1);
                dateHolder.setBeginOfDay();
            } else if (modificationSince.startsWith("lastDays") == true) {
                dateHolder.add(Calendar.DAY_OF_YEAR, -number);
                dateHolder.setBeginOfDay();
            }
            searchFilter.setStartTimeOfModification(dateHolder.getDate());
            startDateTimePanel.markModelAsChanged();
            searchFilter.setStopTimeOfModification(null);
            stopDateTimePanel.markModelAsChanged();
            modificationSince = null;
        }
        getParentPage().onSearchSubmit();
    }

    @SuppressWarnings("unchecked")
    public F getSearchFilter() {
        if (this.searchFilter != null) {
            return this.searchFilter;
        } else {
            if (getParentPage().isStoreFilter() == true) {
                final Object filter = getParentPage().getUserPrefEntry(this.getClass().getName() + ":Filter");
                if (filter != null) {
                    if (filter.getClass().equals(newSearchFilterInstance().getClass()) == true) {
                        try {
                            this.searchFilter = (F) filter;
                        } catch (final ClassCastException ex) {
                            // No output needed, info message follows:
                        }
                        if (this.searchFilter == null) {
                            // Probably a new software release results in an incompability of old and new filter format.
                            getLogger().info("Could not restore filter from user prefs: (old) filter type "
                                    + filter.getClass().getName() + " is not assignable to (new) filter type "
                                    + newSearchFilterInstance().getClass().getName()
                                    + " (OK, probably new software release).");
                        }
                    }
                }
            }
        }
        if (this.searchFilter == null) {
            this.searchFilter = newSearchFilterInstance();
            this.searchFilter.reset();
            if (getParentPage().isStoreFilter() == true) {
                getParentPage().putUserPrefEntry(this.getClass().getName() + ":Filter", this.searchFilter, true);
            }
        }
        return this.searchFilter;
    }

    public CheckBoxPanel createAutoRefreshCheckBoxPanel(final String id, final IModel<Boolean> model,
            final String label) {
        return createAutoRefreshCheckBoxPanel(id, model, label, null);
    }

    @SuppressWarnings("serial")
    protected CheckBoxPanel createAutoRefreshCheckBoxPanel(final String id, final IModel<Boolean> model,
            final String label, final String tooltip) {
        final CheckBoxPanel checkBoxPanel = new CheckBoxPanel(id, model, label) {
            @Override
            protected boolean wantOnSelectionChangedNotifications() {
                return true;
            };

            @Override
            protected void onSelectionChanged(final Boolean newSelection) {
                parentPage.refresh();
            };

        };
        if (tooltip != null) {
            checkBoxPanel.setTooltip(tooltip);
        }
        return checkBoxPanel;
    }

    /**
     * The page size of display tag (result table).
     */
    public Integer getPageSize() {
        if (pageSize == null) {
            pageSize = (Integer) getParentPage().getUserPrefEntry(this.getClass().getName() + ":pageSize");
        }
        if (pageSize == null) {
            pageSize = 50;
        }
        return pageSize;
    }

    /**
     * For convenience combo box with quick select of often used time periods.
     */
    public String getModificationSince() {
        return modificationSince;
    }

    public void setModificationSince(final String modificationSince) {
        this.modificationSince = modificationSince;
    }

    public void setPageSize(final Integer pageSize) {
        this.pageSize = pageSize;
        if (getParentPage().isStoreFilter() == true) {
            getParentPage().putUserPrefEntry(this.getClass().getName() + ":pageSize", this.pageSize, true);
        }
    }

    /**
     * For displaying the modified search string for lucene, e. g. "modified searchstring: micromata*"
     * @param component Needed for {@link Component#getString(String)}.
     * @param searchString
     * @return
     */
    public static String getModifiedSearchExpressionLabel(final Component component, final String searchString) {
        return component.getString("search.lucene.expression") + " "
                + StringEscapeUtils.escapeHtml(BaseDao.modifySearchString(searchString));
    }

    /**
     * Any given de-serialized filter will be set from parent page.
     * @param filter
     */
    @SuppressWarnings("unchecked")
    void setFilter(final Object filter) {
        searchFilter = (F) filter;
    }

    void copySearchFieldsFrom(final BaseSearchFilter baseFilter) {
        searchFilter.copyBaseSearchFieldsFrom(baseFilter);
    }

    /**
     * Used by search cell to define visibility.
     * @return True if not overload.
     */
    protected boolean isFilterVisible() {
        return true;
    }

    /**
     * Used by search cell to define visibility of search input string and extended search filter.
     * @return True if not overload.
     */
    protected boolean isSearchFilterVisible() {
        return true;
    }

    /**
     * If the derived class returns a text, the keyboard image right to the search field will be shown with the returned string as tool-tip. <br/>
     * If the derived class uses the store-recent-search-terms-functionality then a generic tool-tip about this functionality is used.<br/>
     * Otherwise the image is invisible (default).
     */
    protected String getHelpKeyboardImageTooltip() {
        if (parentPage.isRecentSearchTermsStorage() == true) {
            return getString("tooltip.autocomplete.recentSearchTerms");
        } else {
            return null;
        }
    }

    /** This class uses the logger of the extended class. */
    protected abstract Logger getLogger();

    protected boolean showOptionsPanel() {
        return parentPage.getBaseDao().isHistorizable();
    }

    protected boolean showHistorySearchAndDeleteCheckbox() {
        return parentPage.getBaseDao().isHistorizable();
    }
}