001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.workplace.list;
029
030import org.opencms.i18n.CmsMessageContainer;
031import org.opencms.i18n.CmsMessages;
032import org.opencms.main.CmsIllegalArgumentException;
033import org.opencms.main.CmsIllegalStateException;
034import org.opencms.util.CmsStringUtil;
035import org.opencms.workplace.CmsWorkplace;
036import org.opencms.workplace.commons.CmsProgressThread;
037import org.opencms.workplace.tools.A_CmsHtmlIconButton;
038import org.opencms.workplace.tools.CmsHtmlIconButtonStyleEnum;
039
040import java.util.ArrayList;
041import java.util.Collection;
042import java.util.Collections;
043import java.util.Iterator;
044import java.util.List;
045import java.util.Locale;
046
047/**
048 * The main class of the html list widget.<p>
049 *
050 * @since 6.0.0
051 */
052public class CmsHtmlList {
053
054    /** Standard list button location. */
055    public static final String ICON_LEFT = "list/leftarrow.png";
056
057    /** Standard list button location. */
058    public static final String ICON_RIGHT = "list/rightarrow.png";
059
060    /** Constant for item separator char used for coding/encoding multiselection. */
061    public static final String ITEM_SEPARATOR = "|";
062
063    /** Constant name for error message if no item has been selected. */
064    public static final String NO_SELECTION_HELP_VAR = "noSelHelp";
065
066    /** Constant name for error message if number of selected items does not match. */
067    public static final String NO_SELECTION_MATCH_HELP_VAR = "noSelMatchHelp";
068
069    /** Current displayed page number. */
070    protected int m_currentPage;
071
072    /** Current sort order. */
073    protected CmsListOrderEnum m_currentSortOrder;
074
075    /** Filtered list of items or <code>null</code> if no filter is set and not sorted. */
076    protected List<CmsListItem> m_filteredItems;
077
078    /** Dhtml id. */
079    protected final String m_id;
080
081    /** If this flag is set the list will be surrounded by a box. */
082    protected boolean m_isBoxed = true;
083
084    /** Maximum number of items per page. */
085    protected int m_maxItemsPerPage = 20;
086
087    /** Metadata for building the list. */
088    protected CmsListMetadata m_metadata;
089
090    /** Display Name of the list. */
091    protected CmsMessageContainer m_name;
092
093    /** Really content of the list. */
094    protected List<CmsListItem> m_originalItems = new ArrayList<CmsListItem>();
095
096    /** printable flag. */
097    protected boolean m_printable;
098
099    /** Search filter text. */
100    protected String m_searchFilter = "";
101
102    /** Show the title of the list. */
103    protected boolean m_showTitle;
104
105    /** The filtered content size, only used if data self managed. */
106    protected int m_size;
107
108    /** Column name to be sorted. */
109    protected String m_sortedColumn;
110
111    /** The total size, only used is data self managed. */
112    protected int m_totalSize;
113
114    /** Items currently displayed. */
115    protected List<CmsListItem> m_visibleItems;
116
117    /** The related workplace dialog object. */
118    protected transient A_CmsListDialog m_wp;
119
120    /**
121     * Default Constructor.<p>
122     *
123     * @param id unique id of the list, is used as name for controls and js functions and vars
124     * @param name the display name
125     * @param metadata the list's metadata
126     */
127    public CmsHtmlList(String id, CmsMessageContainer name, CmsListMetadata metadata) {
128
129        m_id = id;
130        m_name = name;
131        m_metadata = metadata;
132        m_currentPage = 1;
133        m_showTitle = true;
134    }
135
136    /**
137     * Generates the list of html option elements for a html select control to select a page of a list.<p>
138     *
139     * @param nrPages the total number of pages
140     * @param itemsPage the maximum number of items per page
141     * @param nrItems the total number of items
142     * @param curPage the current page
143     * @param locale the locale
144     *
145     * @return html code
146     */
147    public static String htmlPageSelector(int nrPages, int itemsPage, int nrItems, int curPage, Locale locale) {
148
149        StringBuffer html = new StringBuffer(256);
150        for (int i = 0; i < nrPages; i++) {
151            int displayedFrom = (i * itemsPage) + 1;
152            int displayedTo = ((i + 1) * itemsPage) < nrItems ? (i + 1) * itemsPage : nrItems;
153            html.append("\t\t\t\t<option value='");
154            html.append(i + 1);
155            html.append("'");
156            html.append((i + 1) == curPage ? " selected" : "");
157            html.append(">");
158            html.append(
159                Messages.get().getBundle(locale).key(
160                    Messages.GUI_LIST_PAGE_ENTRY_3,
161                    Integer.valueOf(i + 1),
162                    Integer.valueOf(displayedFrom),
163                    Integer.valueOf(displayedTo)));
164            html.append("</option>\n");
165        }
166        return html.toString();
167    }
168
169    /**
170     * This method resets the content of the list (no the metadata).<p>
171     */
172    public void clear() {
173
174        if (m_originalItems != null) {
175            m_originalItems.clear();
176        }
177        m_filteredItems = null;
178        synchronized (this) {
179            if (m_visibleItems != null) {
180                m_visibleItems.clear();
181            }
182        }
183        setSearchFilter("");
184        m_sortedColumn = null;
185    }
186
187    /**
188     * Returns all list items in the list, may be not visible and sorted.<p>
189     *
190     * @return all list items
191     */
192    public List<CmsListItem> getAllContent() {
193
194        if (m_metadata.isSelfManaged()) {
195            if (m_filteredItems != null) {
196                return Collections.unmodifiableList(m_filteredItems);
197            } else {
198                return Collections.emptyList();
199            }
200        } else {
201            if (m_originalItems != null) {
202                return Collections.unmodifiableList(m_originalItems);
203            } else {
204                return Collections.emptyList();
205            }
206        }
207    }
208
209    /**
210     * Returns the filtered list of list items.<p>
211     *
212     * Equals to <code>{@link #getAllContent()}</code> if no filter is set.<p>
213     *
214     * @return the filtered list of list items
215     */
216    public List<CmsListItem> getContent() {
217
218        if (m_filteredItems == null) {
219            return getAllContent();
220        } else {
221            return Collections.unmodifiableList(m_filteredItems);
222        }
223    }
224
225    /**
226     * returns the number of the current page.<p>
227     *
228     * @return the number of the current page
229     */
230    public int getCurrentPage() {
231
232        return m_currentPage;
233    }
234
235    /**
236     * Returns all items of the current page.<p>
237     *
238     * @return all items of the current page, a list of {@link CmsListItem} objects
239     */
240    public List<CmsListItem> getCurrentPageItems() {
241
242        if (getSize() == 0) {
243            return Collections.emptyList();
244        }
245        if (m_metadata.isSelfManaged()) {
246            return getContent();
247        }
248        return Collections.unmodifiableList(getContent().subList(displayedFrom() - 1, displayedTo()));
249    }
250
251    /**
252     * Returns the current used sort order.<p>
253     *
254     * @return the current used sort order
255     */
256    public CmsListOrderEnum getCurrentSortOrder() {
257
258        return m_currentSortOrder;
259    }
260
261    /**
262     * Returns the id.<p>
263     *
264     * @return the id
265     */
266    public String getId() {
267
268        return m_id;
269    }
270
271    /**
272     * This method returns the item identified by the parameter id.<p>
273     *
274     * Only current visible item can be retrieved using this method.<p>
275     *
276     * @param id the id of the item to look for
277     *
278     * @return the requested item or <code>null</code> if not found
279     */
280    public CmsListItem getItem(String id) {
281
282        Iterator<CmsListItem> it = getAllContent().iterator();
283        while (it.hasNext()) {
284            CmsListItem item = it.next();
285            if (item.getId().equals(id)) {
286                return item;
287            }
288        }
289        return null;
290    }
291
292    /**
293     * Returns the maximum number of items per page.<p>
294     *
295     * @return the maximum number of items per page
296     */
297    public int getMaxItemsPerPage() {
298
299        return m_maxItemsPerPage;
300    }
301
302    /**
303     * Returns the metadata.<p>
304     *
305     * @return the metadata
306     */
307    public CmsListMetadata getMetadata() {
308
309        return m_metadata;
310    }
311
312    /**
313     * Returns the name of the list.<p>
314     *
315     * @return the list's name
316     */
317    public CmsMessageContainer getName() {
318
319        return m_name;
320    }
321
322    /**
323     * Returns the filtered number of pages.<p>
324     *
325     * Equals to <code>{@link #getTotalNumberOfPages()}</code> if no filter is set.<p>
326     *
327     * @return the filtered of pages
328     */
329    public int getNumberOfPages() {
330
331        return (int)Math.ceil((double)getSize() / getMaxItemsPerPage());
332    }
333
334    /**
335     * Returns the search filter.<p>
336     *
337     * @return the search filter
338     */
339    public String getSearchFilter() {
340
341        return m_searchFilter;
342    }
343
344    /**
345     * Return the filtered number of items.<p>
346     *
347     * Equals to <code>{@link #getTotalSize()}</code> if no filter is set.<p>
348     *
349     * @return the filtered number of items
350     */
351    public int getSize() {
352
353        if (m_metadata.isSelfManaged() && (m_size != 0)) {
354            return m_size;
355        }
356        return getContent().size();
357    }
358
359    /**
360     * Returns the sorted column's name.<p>
361     *
362     * @return the sorted column's name
363     */
364    public String getSortedColumn() {
365
366        return m_sortedColumn;
367    }
368
369    /**
370     * Returns a filled list state.<p>
371     *
372     * @return the state of the list
373     */
374    public CmsListState getState() {
375
376        return new CmsListState(this);
377    }
378
379    /**
380     * Returns the total number of pages.<p>
381     *
382     * @return the total number of pages
383     */
384    public int getTotalNumberOfPages() {
385
386        return (int)Math.ceil((double)getTotalSize() / getMaxItemsPerPage());
387    }
388
389    /**
390     * Return the total number of items.<p>
391     *
392     * @return the total number of items
393     */
394    public int getTotalSize() {
395
396        if (m_metadata.isSelfManaged() && (m_totalSize != 0)) {
397            return m_totalSize;
398        }
399        return getAllContent().size();
400    }
401
402    /**
403     * Returns the workplace dialog object.<p>
404     *
405     * @return the workplace dialog object
406     */
407    public A_CmsListDialog getWp() {
408
409        return m_wp;
410    }
411
412    /**
413     * Returns the isBoxed flag.<p>
414     *
415     * If this flag is set the list will be surrounded by a box.<p>
416     *
417     * @return the isBoxed flag
418     */
419    public boolean isBoxed() {
420
421        return m_isBoxed;
422    }
423
424    /**
425     * Returns the printable flag.<p>
426     *
427     * @return the printable flag
428     */
429    public boolean isPrintable() {
430
431        return m_printable;
432    }
433
434    /**
435     * Returns if the list title is shown.<p>
436     *
437     * @return true if the list title is shown, otherwise false
438     */
439    public boolean isShowTitle() {
440
441        return m_showTitle;
442    }
443
444    /**
445     * Generates the csv output for the list.<p>
446     *
447     * @return csv output
448     */
449    public String listCsv() {
450
451        StringBuffer csv = new StringBuffer(5120);
452        csv.append(m_metadata.csvHeader());
453        if (getContent().isEmpty()) {
454            csv.append(m_metadata.csvEmptyList());
455        } else {
456            Iterator<CmsListItem> itItems = getContent().iterator();
457            while (itItems.hasNext()) {
458                CmsListItem item = itItems.next();
459                csv.append(m_metadata.csvItem(item));
460            }
461        }
462        return getWp().resolveMacros(csv.toString());
463    }
464
465    /**
466     * Generates the html code for the list.<p>
467     *
468     * @return html code
469     */
470    public synchronized String listHtml() {
471
472        // check if progress should be set in the thread
473        CmsProgressThread thread = null;
474        int progressOffset = 0;
475        if (Thread.currentThread() instanceof CmsProgressThread) {
476            thread = (CmsProgressThread)Thread.currentThread();
477            progressOffset = thread.getProgress();
478        }
479
480        // this block has to be executed before calling htmlBegin()
481        if (isPrintable()) {
482            m_visibleItems = new ArrayList<CmsListItem>(getContent());
483        } else {
484            m_visibleItems = new ArrayList<CmsListItem>(getCurrentPageItems());
485        }
486
487        StringBuffer html = new StringBuffer(5120);
488        html.append(htmlBegin());
489        if (!isPrintable()) {
490            html.append(htmlTitle());
491            html.append(htmlToolBar());
492        } else {
493            html.append("<style type='text/css'>\n");
494            html.append("td.listdetailitem, \n");
495            html.append(".linkdisabled {\n");
496            html.append("\tcolor: black;\n");
497            html.append("}\n");
498            html.append(".list th {\n");
499            html.append("\tborder: 1px solid black;\n");
500            html.append("}\n");
501            html.append(".list {\n");
502            html.append("\tborder: 1px solid black;\n");
503            html.append("}\n");
504            html.append("</style>");
505        }
506        // avoiding the layout problem where, if some table cells contain a lot of text,
507        // the right side of the table is cut off and can not be reached using the scroll bar.
508        html.append("<!--[if IE 7]>");
509        html.append("<style type='text/css'>");
510        html.append("table.list * { word-wrap: break-word !important; white-space: normal !important; }");
511        html.append("table.list { table-layout: fixed; }");
512        html.append("</style>");
513        html.append("<![endif]-->");
514
515        html.append("<table width='100%' cellpadding='1' cellspacing='0' class='list'>\n");
516        html.append(m_metadata.htmlHeader(this));
517        if (m_visibleItems.isEmpty()) {
518            html.append(m_metadata.htmlEmptyTable());
519        } else {
520            Iterator<CmsListItem> itItems = m_visibleItems.iterator();
521            boolean odd = true;
522            int count = 0;
523            while (itItems.hasNext()) {
524
525                // set progress in thread
526                count++;
527                if (thread != null) {
528
529                    if (thread.isInterrupted()) {
530                        throw new CmsIllegalStateException(
531                            org.opencms.workplace.commons.Messages.get().container(
532                                org.opencms.workplace.commons.Messages.ERR_PROGRESS_INTERRUPTED_0));
533                    }
534                    thread.setProgress(((count * (100 - progressOffset)) / m_visibleItems.size()) + progressOffset);
535                    thread.setDescription(
536                        org.opencms.workplace.commons.Messages.get().getBundle(thread.getLocale()).key(
537                            org.opencms.workplace.commons.Messages.GUI_PROGRESS_PUBLISH_STEP4_2,
538                            Integer.valueOf(count),
539                            Integer.valueOf(m_visibleItems.size())));
540                }
541
542                CmsListItem item = itItems.next();
543                html.append(m_metadata.htmlItem(item, odd, isPrintable()));
544                odd = !odd;
545            }
546        }
547
548        html.append("</table>\n");
549        if (!isPrintable()) {
550            html.append(htmlPagingBar());
551        }
552        html.append(htmlEnd());
553        return getWp().resolveMacros(html.toString());
554    }
555
556    /**
557     * Generate the need js code for the list.<p>
558     *
559     * @return js code
560     */
561    public String listJs() {
562
563        StringBuffer js = new StringBuffer(1024);
564        CmsMessages messages = Messages.get().getBundle(getWp().getLocale());
565        js.append("<script  src='");
566        js.append(CmsWorkplace.getSkinUri());
567        js.append("jquery/unpacked/jquery.js'></script>\n");
568        js.append("<script  src='");
569        js.append(CmsWorkplace.getSkinUri());
570        js.append("jquery/unpacked/jquery.hint.js'></script>\n");
571        js.append("<script  src='");
572        js.append(CmsWorkplace.getSkinUri());
573        js.append("admin/javascript/list.js'></script>\n");
574        if (!m_metadata.getMultiActions().isEmpty()) {
575            js.append("<script >\n");
576            js.append("\tvar ");
577            js.append(NO_SELECTION_HELP_VAR);
578            js.append(" = '");
579            js.append(CmsStringUtil.escapeJavaScript(messages.key(Messages.GUI_LIST_ACTION_NO_SELECTION_0)));
580            js.append("';\n");
581            Iterator<CmsListMultiAction> it = m_metadata.getMultiActions().iterator();
582            while (it.hasNext()) {
583                CmsListMultiAction action = it.next();
584                if (action instanceof CmsListRadioMultiAction) {
585                    CmsListRadioMultiAction rAction = (CmsListRadioMultiAction)action;
586                    js.append("\tvar ");
587                    js.append(NO_SELECTION_MATCH_HELP_VAR);
588                    js.append(rAction.getId());
589                    js.append(" = '");
590                    js.append(
591                        CmsStringUtil.escapeJavaScript(
592                            messages.key(
593                                Messages.GUI_LIST_ACTION_NO_SELECTION_MATCH_1,
594                                Integer.valueOf(rAction.getSelections()))));
595                    js.append("';\n");
596                }
597            }
598            js.append("</script>\n");
599        }
600        return js.toString();
601    }
602
603    /**
604     * Returns a new list item for this list.<p>
605     *
606     * @param id the id of the item has to be unique
607     * @return a new list item
608     */
609    public CmsListItem newItem(String id) {
610
611        return new CmsListItem(getMetadata(), id);
612    }
613
614    /**
615     * Returns html code for printing the list.<p>
616     *
617     * @return html code
618     */
619    public String printableHtml() {
620
621        m_printable = true;
622        String html = listHtml();
623        m_printable = false;
624        return html;
625    }
626
627    /**
628     * Removes an item from the list.<p>
629     *
630     * Keeping care of all the state like sorted column, sorting order, displayed page and search filter.<p>
631     *
632     * Try to use it instead of <code>{@link A_CmsListDialog#refreshList()}</code>.<p>
633     *
634     * @param id the id of the item to remove
635     *
636     * @return the removed list item
637     */
638    public CmsListItem removeItem(String id) {
639
640        CmsListItem item = getItem(id);
641        if (item == null) {
642            return null;
643        }
644        CmsListState state = null;
645        if ((m_filteredItems != null) || (m_visibleItems != null)) {
646            state = getState();
647        }
648        m_originalItems.remove(item);
649        if (m_filteredItems != null) {
650            m_filteredItems.remove(item);
651        }
652        if (m_visibleItems != null) {
653            m_visibleItems.remove(item);
654        }
655        if (state != null) {
656            setState(state);
657        }
658        return item;
659    }
660
661    /**
662     * Sets the isBoxed flag.<p>
663     *
664     * If this flag is set, the list will be surrounded by a box.<p>
665     *
666     * @param isBoxed the isBoxed flag to set
667     */
668    public void setBoxed(boolean isBoxed) {
669
670        m_isBoxed = isBoxed;
671    }
672
673    /**
674     * Sets the list item to display in the list.<p>
675     *
676     * @param listItems a collection of {@link CmsListItem} objects
677     */
678    public void setContent(Collection<CmsListItem> listItems) {
679
680        if (m_metadata.isSelfManaged()) {
681            m_filteredItems = new ArrayList<CmsListItem>(listItems);
682            m_originalItems = null;
683        } else {
684            m_filteredItems = null;
685            m_originalItems = new ArrayList<CmsListItem>(listItems);
686        }
687    }
688
689    /**
690     * Sets the current page.<p>
691     *
692     * @param currentPage the current page to set
693     *
694     * @throws CmsIllegalArgumentException if the argument is invalid
695     */
696    public void setCurrentPage(int currentPage) throws CmsIllegalArgumentException {
697
698        if (getSize() != 0) {
699            if ((currentPage < 1) || (currentPage > getNumberOfPages())) {
700                throw new CmsIllegalArgumentException(
701                    Messages.get().container(Messages.ERR_LIST_INVALID_PAGE_1, Integer.valueOf(currentPage)));
702            }
703        }
704        m_currentPage = currentPage;
705    }
706
707    /**
708     * Sets the maximum number of items per page.<p>
709     *
710     * @param maxItemsPerPage the maximum number of items per page to set
711     */
712    public void setMaxItemsPerPage(int maxItemsPerPage) {
713
714        m_maxItemsPerPage = maxItemsPerPage;
715    }
716
717    /**
718     * Sets the name of the list.<p>
719     *
720     * @param name the name of the list
721     */
722    public void setName(CmsMessageContainer name) {
723
724        m_name = name;
725    }
726
727    /**
728     * Sets the search filter.<p>
729     *
730     * @param searchFilter the search filter to set
731     */
732    public void setSearchFilter(String searchFilter) {
733
734        if (!m_metadata.isSearchable()) {
735            return;
736        }
737        if (searchFilter == null) {
738            searchFilter = "";
739        }
740        m_searchFilter = searchFilter;
741        boolean showAll = CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_searchFilter);
742        getMetadata().getSearchAction().getShowAllAction().setVisible(showAll);
743        if (!m_metadata.isSelfManaged()) {
744            if (CmsStringUtil.isEmptyOrWhitespaceOnly(searchFilter)) {
745
746                // reset content if filter is empty
747                m_filteredItems = null;
748            } else {
749                m_filteredItems = getMetadata().getSearchAction().filter(getAllContent(), m_searchFilter);
750            }
751        }
752        String sCol = m_sortedColumn;
753        m_sortedColumn = "";
754        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(sCol)) {
755            CmsListOrderEnum order = getCurrentSortOrder();
756            setSortedColumn(sCol);
757            if (order == CmsListOrderEnum.ORDER_DESCENDING) {
758                setSortedColumn(sCol);
759            }
760        }
761        setCurrentPage(1);
762    }
763
764    /**
765     * Sets if the list title is shown.<p>
766     *
767     * @param showTitle true if the list title is shown, otherwise false
768     */
769    public void setShowTitle(boolean showTitle) {
770
771        m_showTitle = showTitle;
772    }
773
774    /**
775     * Sets the current filtered size, only used if data self managed.<p>
776     *
777     * @param size the size to set
778     */
779    public void setSize(int size) {
780
781        m_size = size;
782    }
783
784    /**
785     * Sets the sorted column.<p>
786     *
787     * @param sortedColumn the sorted column to set
788     *
789     * @throws CmsIllegalArgumentException if the <code>sortedColumn</code> argument is invalid
790     */
791    public void setSortedColumn(String sortedColumn) throws CmsIllegalArgumentException {
792
793        // check if the parameter is valid
794        if ((getMetadata().getColumnDefinition(sortedColumn) == null)
795            || !getMetadata().getColumnDefinition(sortedColumn).isSorteable()) {
796            return;
797        }
798        // reset view
799        setCurrentPage(1);
800        // only reverse order if the column to sort is already sorted
801        if (sortedColumn.equals(m_sortedColumn)) {
802            if (m_currentSortOrder == CmsListOrderEnum.ORDER_ASCENDING) {
803                m_currentSortOrder = CmsListOrderEnum.ORDER_DESCENDING;
804            } else {
805                m_currentSortOrder = CmsListOrderEnum.ORDER_ASCENDING;
806            }
807            if (!m_metadata.isSelfManaged()) {
808                if (m_filteredItems == null) {
809                    m_filteredItems = new ArrayList<CmsListItem>(getAllContent());
810                }
811                Collections.reverse(m_filteredItems);
812            }
813            return;
814        }
815        // sort new column
816        m_sortedColumn = sortedColumn;
817        m_currentSortOrder = CmsListOrderEnum.ORDER_ASCENDING;
818        if (!m_metadata.isSelfManaged()) {
819            if (m_filteredItems == null) {
820                m_filteredItems = new ArrayList<CmsListItem>(getAllContent());
821            }
822            I_CmsListItemComparator c = getMetadata().getColumnDefinition(sortedColumn).getListItemComparator();
823            Collections.sort(m_filteredItems, c.getComparator(sortedColumn, getWp().getLocale()));
824        }
825    }
826
827    /**
828     * Sets the list state.<p>
829     *
830     * This may involve sorting, filtering and paging.<p>
831     *
832     * @param listState the state to be set
833     */
834    public void setState(CmsListState listState) {
835
836        if (!m_metadata.isSelfManaged()) {
837            m_filteredItems = null;
838        }
839        synchronized (this) {
840            if (m_visibleItems != null) {
841                m_visibleItems.clear();
842            }
843        }
844        setSearchFilter(listState.getFilter());
845        setSortedColumn(listState.getColumn());
846        if (listState.getOrder() == CmsListOrderEnum.ORDER_DESCENDING) {
847            setSortedColumn(listState.getColumn());
848        }
849        if (listState.getPage() > 0) {
850            if (listState.getPage() <= getNumberOfPages()) {
851                setCurrentPage(listState.getPage());
852            } else {
853                setCurrentPage(1);
854            }
855        }
856    }
857
858    /**
859     * Sets the total Size, only used if data self managed.<p>
860     *
861     * @param totalSize the total Size to set
862     */
863    public void setTotalSize(int totalSize) {
864
865        m_totalSize = totalSize;
866    }
867
868    /**
869     * Sets the workplace dialog object.<p>
870     *
871     * @param wp the workplace dialog object to set
872     */
873    public void setWp(A_CmsListDialog wp) {
874
875        m_wp = wp;
876        m_metadata.setWp(wp);
877    }
878
879    /**
880     * Returns the number (from 1) of the first displayed item.<p>
881     *
882     * @return the number (from 1) of the first displayed item, or zero if the list is empty
883     */
884    protected int displayedFrom() {
885
886        if (getSize() != 0) {
887            if (isPrintable()) {
888                return 1;
889            } else {
890                return ((getCurrentPage() - 1) * getMaxItemsPerPage()) + 1;
891            }
892        }
893        return 0;
894    }
895
896    /**
897     * Returns the number (from 1) of the last displayed item.<p>
898     *
899     * @return the number (from 1) of the last displayed item, or zero if the list is empty
900     */
901    protected int displayedTo() {
902
903        if (getSize() != 0) {
904            if (!isPrintable()) {
905                if ((getCurrentPage() * getMaxItemsPerPage()) < getSize()) {
906                    return getCurrentPage() * getMaxItemsPerPage();
907                }
908            }
909        }
910        return getSize();
911    }
912
913    /**
914     * Generates the initial html code.<p>
915     *
916     * @return html code
917     */
918    protected String htmlBegin() {
919
920        StringBuffer html = new StringBuffer(512);
921        // help & confirmation text for actions if needed
922        if (!isPrintable() && (m_visibleItems != null) && !m_visibleItems.isEmpty()) {
923            Iterator<CmsListColumnDefinition> cols = getMetadata().getColumnDefinitions().iterator();
924            while (cols.hasNext()) {
925                CmsListColumnDefinition col = cols.next();
926                Iterator<I_CmsListDirectAction> actions = col.getDirectActions().iterator();
927                while (actions.hasNext()) {
928                    I_CmsListDirectAction action = actions.next();
929                    action.setItem(m_visibleItems.get(0));
930                    html.append(action.helpTextHtml());
931                    html.append(action.confirmationTextHtml());
932                }
933                Iterator<CmsListDefaultAction> defActions = col.getDefaultActions().iterator();
934                while (defActions.hasNext()) {
935                    I_CmsListDirectAction action = defActions.next();
936                    action.setItem(m_visibleItems.get(0));
937                    html.append(action.helpTextHtml());
938                    html.append(action.confirmationTextHtml());
939                }
940            }
941        }
942        // start list code
943        html.append("<div class='listArea'>\n");
944        if (isBoxed()) {
945            html.append(getWp().dialogBlock(CmsWorkplace.HTML_START, m_name.key(getWp().getLocale()), false));
946        }
947        html.append("\t\t<table width='100%' cellspacing='0' cellpadding='0' border='0'>\n");
948        html.append("\t\t\t<tr><td>\n");
949        return html.toString();
950    }
951
952    /**
953     * Generates the need html code for ending a list.<p>
954     *
955     * @return html code
956     */
957    protected String htmlEnd() {
958
959        StringBuffer html = new StringBuffer(512);
960        html.append("\t\t\t</td></tr>\n");
961        html.append("\t\t</table>\n");
962        if (isBoxed()) {
963            html.append(getWp().dialogBlock(CmsWorkplace.HTML_END, m_name.key(getWp().getLocale()), false));
964        }
965        html.append("</div>\n");
966        return html.toString();
967    }
968
969    /**
970     * Generates the needed html code for the paging bar.<p>
971     *
972     * @return html code
973     */
974    protected String htmlPagingBar() {
975
976        if (getNumberOfPages() < 2) {
977            return "";
978        }
979        StringBuffer html = new StringBuffer(1024);
980        CmsMessages messages = Messages.get().getBundle(getWp().getLocale());
981        html.append("<table width='100%' cellspacing='0' style='margin-top: 5px;'>\n");
982        html.append("\t<tr>\n");
983        html.append("\t\t<td class='main'>\n");
984        // prev button
985        String id = "listPrev";
986        String name = messages.key(Messages.GUI_LIST_PAGING_PREVIOUS_NAME_0);
987        String iconPath = ICON_LEFT;
988        boolean enabled = getCurrentPage() > 1;
989        String helpText = messages.key(Messages.GUI_LIST_PAGING_PREVIOUS_HELP_0);
990        if (!enabled) {
991            helpText = messages.key(Messages.GUI_LIST_PAGING_PREVIOUS_HELPDIS_0);
992        }
993        String onClic = "listSetPage('" + getId() + "', " + (getCurrentPage() - 1) + ")";
994        html.append(A_CmsHtmlIconButton.defaultButtonHtml(
995            CmsHtmlIconButtonStyleEnum.SMALL_ICON_TEXT,
996            id,
997            name,
998            helpText,
999            enabled,
1000            iconPath,
1001            null,
1002            onClic));
1003        html.append("\n");
1004        // next button
1005        id = "listNext";
1006        name = messages.key(Messages.GUI_LIST_PAGING_NEXT_NAME_0);
1007        iconPath = ICON_RIGHT;
1008        enabled = getCurrentPage() < getNumberOfPages();
1009        helpText = messages.key(Messages.GUI_LIST_PAGING_NEXT_HELP_0);
1010        if (!enabled) {
1011            helpText = messages.key(Messages.GUI_LIST_PAGING_NEXT_HELPDIS_0);
1012        }
1013        onClic = "listSetPage('" + getId() + "', " + (getCurrentPage() + 1) + ")";
1014        html.append(A_CmsHtmlIconButton.defaultButtonHtml(
1015            CmsHtmlIconButtonStyleEnum.SMALL_ICON_TEXT,
1016            id,
1017            name,
1018            helpText,
1019            enabled,
1020            iconPath,
1021            null,
1022            onClic));
1023        html.append("\n");
1024        // page selection list
1025        html.append("\t\t\t&nbsp;&nbsp;&nbsp;");
1026        html.append("\t\t\t<select name='listPageSet' id='id-page_set' onChange =\"listSetPage('");
1027        html.append(getId());
1028        html.append("', this.value);\" style='vertical-align: bottom;'>\n");
1029        html.append(
1030            htmlPageSelector(
1031                getNumberOfPages(),
1032                getMaxItemsPerPage(),
1033                getSize(),
1034                getCurrentPage(),
1035                getWp().getLocale()));
1036        html.append("\t\t\t</select>\n");
1037        html.append("\t\t\t&nbsp;&nbsp;&nbsp;");
1038        boolean isNotSearching = true;
1039        if (getMetadata().isSearchable()) {
1040            isNotSearching = CmsStringUtil.isEmptyOrWhitespaceOnly(m_searchFilter);
1041        }
1042        if (isNotSearching) {
1043            html.append(
1044                messages.key(
1045                    Messages.GUI_LIST_PAGING_TEXT_2,
1046                    new Object[] {m_name.key(getWp().getLocale()), Integer.valueOf(getTotalSize())}));
1047        } else {
1048            html.append(messages.key(
1049                Messages.GUI_LIST_PAGING_FILTER_TEXT_3,
1050                new Object[] {m_name.key(getWp().getLocale()), Integer.valueOf(getSize()), Integer.valueOf(getTotalSize())}));
1051        }
1052        html.append("\t\t</td>\n");
1053        html.append("\t</tr>\n");
1054        html.append("</table>\n");
1055        return html.toString();
1056    }
1057
1058    /**
1059     * Returns the html for the title of the list.<p>
1060     *
1061     * @return html code
1062     */
1063    protected String htmlTitle() {
1064
1065        boolean showTitle = isShowTitle();
1066        Iterator<I_CmsListAction> itIndepActions = getMetadata().getIndependentActions().iterator();
1067        while (!showTitle && itIndepActions.hasNext()) {
1068            I_CmsListAction indepAction = itIndepActions.next();
1069            showTitle = showTitle || indepAction.isVisible();
1070        }
1071        Iterator<CmsListItemDetails> itItemDetails = getMetadata().getItemDetailDefinitions().iterator();
1072        while (!showTitle && itItemDetails.hasNext()) {
1073            CmsListItemDetails itemDetail = itItemDetails.next();
1074            showTitle = showTitle || itemDetail.getAction().isVisible();
1075        }
1076        if (!showTitle) {
1077            // prevent empty table if there is nothing to display
1078            return "";
1079        }
1080        StringBuffer html = new StringBuffer(512);
1081        CmsMessages messages = Messages.get().getBundle(getWp().getLocale());
1082        html.append("<table width='100%' cellspacing='0'>");
1083        html.append("\t<tr>\n");
1084        if (isShowTitle()) {
1085            html.append("\t\t<td align='left'>\n");
1086            html.append("\t\t\t");
1087            boolean isNotSearching = true;
1088            if (getMetadata().isSearchable()) {
1089                isNotSearching = CmsStringUtil.isEmptyOrWhitespaceOnly(m_searchFilter);
1090            }
1091            if (getTotalNumberOfPages() > 1) {
1092                if (isNotSearching) {
1093                    html.append(messages.key(
1094                        Messages.GUI_LIST_TITLE_TEXT_4,
1095                        new Object[] {
1096                            m_name.key(getWp().getLocale()),
1097                            Integer.valueOf(displayedFrom()),
1098                            Integer.valueOf(displayedTo()),
1099                            Integer.valueOf(getTotalSize())}));
1100                } else {
1101                    html.append(messages.key(
1102                        Messages.GUI_LIST_TITLE_FILTERED_TEXT_5,
1103                        new Object[] {
1104                            m_name.key(getWp().getLocale()),
1105                            Integer.valueOf(displayedFrom()),
1106                            Integer.valueOf(displayedTo()),
1107                            Integer.valueOf(getSize()),
1108                            Integer.valueOf(getTotalSize())}));
1109                }
1110            } else {
1111                if (isNotSearching) {
1112                    html.append(messages.key(
1113                        Messages.GUI_LIST_SINGLE_TITLE_TEXT_2,
1114                        new Object[] {m_name.key(getWp().getLocale()), Integer.valueOf(getTotalSize())}));
1115                } else {
1116                    html.append(messages.key(
1117                        Messages.GUI_LIST_SINGLE_TITLE_FILTERED_TEXT_3,
1118                        new Object[] {
1119                            m_name.key(getWp().getLocale()),
1120                            Integer.valueOf(getSize()),
1121                            Integer.valueOf(getTotalSize())}));
1122                }
1123            }
1124            html.append("\n");
1125            html.append("\t\t</td>\n\t\t");
1126        }
1127        html.append(getMetadata().htmlActionBar());
1128        html.append("\n\t</tr>\n");
1129        html.append("</table>\n");
1130        return html.toString();
1131    }
1132
1133    /**
1134     * Returns the html code for the toolbar (search bar + multiactions bar).<p>
1135     *
1136     * @return html code
1137     */
1138    protected String htmlToolBar() {
1139
1140        boolean showToolBar = getMetadata().isSearchable();
1141        Iterator<CmsListMultiAction> itMultiActions = getMetadata().getMultiActions().iterator();
1142        while (!showToolBar && itMultiActions.hasNext()) {
1143            CmsListMultiAction multiAction = itMultiActions.next();
1144            showToolBar = showToolBar || multiAction.isVisible();
1145        }
1146        if (!showToolBar) {
1147            // prevent empty table if there is nothing to display
1148            return "";
1149        }
1150        StringBuffer html = new StringBuffer(512);
1151        html.append("<table width='100%' cellspacing='0' style='margin-bottom: 5px'>\n");
1152        html.append("\t<tr>\n");
1153        html.append(m_metadata.htmlSearchBar());
1154        html.append(m_metadata.htmlMultiActionBar());
1155        html.append("\t</tr>\n");
1156        html.append("</table>\n");
1157        return html.toString();
1158    }
1159
1160    /**
1161     * Sets the metadata for this list.<p>
1162     *
1163     * Should only be used by the <code>{@link A_CmsListDialog}</code> class
1164     * for temporally removing the metadata object while the list is saved in the
1165     * <code>{@link org.opencms.workplace.CmsWorkplaceSettings}</code>.<p>
1166     *
1167     * @param metadata the list metadata
1168     */
1169    protected void setMetadata(CmsListMetadata metadata) {
1170
1171        m_metadata = metadata;
1172    }
1173}