View Javadoc
1   /**
2    * This file Copyright (c) 2012-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.admincentral.shellapp.pulse.item.detail;
35  
36  import info.magnolia.i18nsystem.SimpleTranslator;
37  
38  import java.util.HashMap;
39  import java.util.Iterator;
40  import java.util.Map;
41  
42  import org.apache.commons.lang3.StringUtils;
43  
44  import com.vaadin.data.Property.ValueChangeListener;
45  import com.vaadin.event.LayoutEvents.LayoutClickEvent;
46  import com.vaadin.event.LayoutEvents.LayoutClickListener;
47  import com.vaadin.ui.CheckBox;
48  import com.vaadin.ui.Component;
49  import com.vaadin.ui.CssLayout;
50  import com.vaadin.ui.HorizontalLayout;
51  import com.vaadin.ui.Label;
52  
53  /**
54   * Generic item category navigation component in Pulse. An item can be e.g. a Task, a Message, etc.
55   */
56  public final class PulseItemCategoryNavigator extends CssLayout {
57  
58      private CheckBox groupByCheckBox;
59  
60      private Map<PulseItemCategory, ItemCategoryTab> itemCategoryTabs = new HashMap<PulseItemCategory, ItemCategoryTab>();
61  
62      private final SimpleTranslator i18n;
63  
64      private boolean isTopRow;
65  
66      private boolean showGroupBy;
67  
68      /**
69       * @deprecated since 5.4 use the plain constructor {@link #PulseItemCategoryNavigator(SimpleTranslator, boolean, boolean, PulseItemCategory...)} instead.
70       */
71      @Deprecated
72      public static PulseItemCategoryNavigator createTopRowNavigator(SimpleTranslator i18n, PulseItemCategory... categories) {
73          return new PulseItemCategoryNavigator(i18n, false, true, categories);
74      }
75  
76      /**
77       * @deprecated since 5.4 use the plain constructor {@link #PulseItemCategoryNavigator(SimpleTranslator, boolean, boolean, PulseItemCategory...)} instead.
78       */
79      @Deprecated
80      public static PulseItemCategoryNavigator createSubRowNavigator(SimpleTranslator i18n, PulseItemCategory... categories) {
81          return new PulseItemCategoryNavigator(i18n, true, false, categories);
82      }
83  
84      public PulseItemCategoryNavigator(SimpleTranslator i18n, boolean showGroupBy, boolean isTopRow, PulseItemCategory... categories) {
85          super();
86          this.i18n = i18n;
87          setStyleName("navigator");
88          this.isTopRow = isTopRow;
89          this.showGroupBy = showGroupBy;
90          construct(categories);
91      }
92  
93      private void construct(PulseItemCategory... categories) {
94          setSizeUndefined();
95  
96          for (final PulseItemCategory category : categories) {
97              ItemCategoryTab tab = new ItemCategoryTab(category);
98              if (category == PulseItemCategory.ALL_MESSAGES || category == PulseItemCategory.UNCLAIMED || category == PulseItemCategory.TASKS) {
99                  tab.setActive(true);
100             }
101             itemCategoryTabs.put(category, tab);
102             addComponent(tab);
103         }
104         if (isTopRow) {
105             addStyleName("top-row");
106         }
107 
108         initCheckbox(categories);
109     }
110 
111     private void initCheckbox(PulseItemCategory... categories) {
112         final String caption = i18n.translate("pulse.items.groupby");
113         groupByCheckBox = new CheckBox(StringUtils.abbreviate(caption, 15));
114         groupByCheckBox.addStyleName("navigator-grouping");
115         groupByCheckBox.setImmediate(true);
116         groupByCheckBox.setVisible(false);
117         // tooltip
118         groupByCheckBox.setDescription(caption);
119 
120         if (showGroupBy) {
121             addComponent(groupByCheckBox);
122             for (final PulseItemCategory category : categories) {
123                 if (category == PulseItemCategory.ALL_MESSAGES) {
124                     enableGroupBy(true);
125                 }
126             }
127         }
128     }
129 
130     public void addGroupingListener(ValueChangeListener listener) {
131         groupByCheckBox.addValueChangeListener(listener);
132     }
133 
134     public void enableGroupBy(boolean enable) {
135         groupByCheckBox.setVisible(enable);
136     }
137 
138     /**
139      * Category changed event.
140      */
141     public static class CategoryChangedEvent extends Component.Event {
142 
143         public static final java.lang.reflect.Method ITEM_CATEGORY_CHANGED;
144 
145         static {
146             try {
147                 ITEM_CATEGORY_CHANGED = ItemCategoryChangedListener.class.getDeclaredMethod("itemCategoryChanged", new Class[]{CategoryChangedEvent.class});
148             } catch (final java.lang.NoSuchMethodException e) {
149                 throw new java.lang.RuntimeException(e);
150             }
151         }
152 
153         private final PulseItemCategory category;
154 
155         public CategoryChangedEvent(Component source, PulseItemCategory category) {
156             super(source);
157             this.category = category;
158         }
159 
160         public PulseItemCategory getCategory() {
161             return category;
162         }
163     }
164 
165     /**
166      * ItemCategoryChangedListener.
167      */
168     public interface ItemCategoryChangedListener {
169 
170         public void itemCategoryChanged(final CategoryChangedEvent event);
171     }
172 
173     public void addCategoryChangeListener(final ItemCategoryChangedListener listener) {
174         addListener("category_changed", CategoryChangedEvent.class, listener, CategoryChangedEvent.ITEM_CATEGORY_CHANGED);
175     }
176 
177     private void fireCategoryChangedEvent(PulseItemCategory category) {
178         Iterator<Component> iterator = iterator();
179         while (iterator.hasNext()) {
180             Component component = iterator.next();
181             if (component instanceof ItemCategoryTab) {
182                 ItemCategoryTab button = (ItemCategoryTab) component;
183                 button.setActive(button.category == category);
184             }
185         }
186         fireEvent(new CategoryChangedEvent(this, category));
187     }
188 
189     /**
190      * Item category button.
191      */
192     public class ItemCategoryTab extends HorizontalLayout {
193 
194         private final PulseItemCategory category;
195         private final Label categoryLabel;
196         private final Label badge;
197 
198         public ItemCategoryTab(PulseItemCategory category) {
199             super();
200             this.category = category;
201             this.addStyleName("navigator-tab");
202             this.setSizeUndefined();
203 
204             categoryLabel = new Label(i18n.translate(category.getKey()));
205             categoryLabel.addStyleName("category");
206 
207             badge = new Label();
208             badge.addStyleName("badge");
209             if (category == PulseItemCategory.ONGOING) {
210                 badge.addStyleName("empty-circle-gray");
211             }
212             badge.setVisible(false);
213 
214             this.addComponent(categoryLabel);
215             this.addComponent(badge);
216             this.addLayoutClickListener(new LayoutClickListener() {
217 
218                 @Override
219                 public void layoutClick(LayoutClickEvent event) {
220                     fireCategoryChangedEvent(ItemCategoryTab.this.category);
221                 }
222             });
223         }
224 
225         public void setActive(boolean active) {
226             if (active) {
227                 addStyleName("active");
228             } else {
229                 removeStyleName("active");
230             }
231         }
232 
233         public void updateItemsCount(int count) {
234             if (count <= 0) {
235                 badge.setVisible(false);
236             } else {
237                 String countAsString = String.valueOf(count);
238                 if (count > 99) {
239                     countAsString = "99+";
240                 }
241                 badge.setValue(countAsString);
242                 badge.setVisible(true);
243             }
244         }
245 
246         public String getLabel() {
247             return categoryLabel.getValue();
248         }
249     }
250 
251     public void updateCategoryBadgeCount(PulseItemCategory category, int count) {
252         itemCategoryTabs.get(category).updateItemsCount(count);
253     }
254 
255     /**
256      * Sets the passed category as selected and un-select all the others.
257      */
258     public void setActive(PulseItemCategory category) {
259         for (ItemCategoryTab tab : itemCategoryTabs.values()) {
260             tab.setActive(false);
261         }
262         itemCategoryTabs.get(category).setActive(true);
263     }
264 }