/**
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.livingstories.client.ui;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.livingstories.client.ContentItemType;
import com.google.livingstories.client.BaseContentItem;
import com.google.livingstories.client.ContentRpcService;
import com.google.livingstories.client.ContentRpcServiceAsync;
import com.google.livingstories.client.util.Constants;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Widget that loads a list of content items based on an living story id, and has a built-in
* filtering mechanism.
* The underlying storage for the content items is a LinkedHashMap. To get incrementally-added items
* to appear at the top of the list, rather than at the bottom, the order in which items are
* stored in the LinkedHashMap is actually opposite to the display order.
*/
public class ContentItemListBox extends Composite {
/**
* Create a remote service proxy to talk to the server-side content persisting service.
*/
private final ContentRpcServiceAsync contentService = GWT.create(ContentRpcService.class);
private ItemList<BaseContentItem> itemList;
protected EnumDropdown<ContentItemType> filter;
private Map<Long, BaseContentItem> loadedContentItemsMap =
new LinkedHashMap<Long, BaseContentItem>();
public ContentItemListBox(final boolean multiSelect) {
filter = EnumDropdown.newInstance(ContentItemType.class, "All");
filter.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
refresh();
}
});
HorizontalPanel filterPanel = new HorizontalPanel();
filterPanel.add(new Label("Filter:"));
filterPanel.add(filter);
itemList = new ItemList<BaseContentItem>(multiSelect) {
@Override
public void loadItems() {
if (!loadedContentItemsMap.isEmpty()) {
// loads the items in reverse order from how they're stored.
List<BaseContentItem> contentItems =
new ArrayList<BaseContentItem>(loadedContentItemsMap.values());
Collections.reverse(contentItems);
for (BaseContentItem contentItem : contentItems) {
if (testContentItem(contentItem)) {
String content = contentItem.getDisplayString();
if (content.length() > Constants.CONTENT_SNIPPET_LENGTH) {
content = content.substring(0, Constants.CONTENT_SNIPPET_LENGTH).concat("...");
}
addItem(content, String.valueOf(contentItem.getId()));
}
}
}
}
};
VerticalPanel contentPanel = new VerticalPanel();
contentPanel.add(filterPanel);
contentPanel.add(itemList);
initWidget(contentPanel);
}
/**
* Tests whether a content item should be included in the displayed list, based on the filter
* setting.
*/
protected boolean testContentItem(BaseContentItem contentItem) {
ContentItemType type = filter.getSelectedConstant();
return type == null || contentItem.getContentItemType().equals(type) || isSelected(contentItem);
}
public void loadItemsForLivingStory(Long livingStoryId) {
itemList.setSelectedIndex(-1);
loadedContentItemsMap.clear();
contentService.getContentItemsForLivingStory(livingStoryId, false,
new AsyncCallback<List<BaseContentItem>>() {
@Override
public void onFailure(Throwable caught) {
itemList.clear();
itemList.addItem("Callback failed, please try again");
}
@Override
public void onSuccess(List<BaseContentItem> result) {
// Put result on loadedAContentItemsMap in reverse order. Can't use useful
// Google Collections stuff for it, so:
for (int i = result.size() - 1; i >= 0; i--) {
BaseContentItem contentItem = result.get(i);
loadedContentItemsMap.put(contentItem.getId(), contentItem);
}
refresh();
}
});
}
public void setVisibleItemCount(int count) {
itemList.setVisibleItemCount(count);
}
public void addSelectionChangeHandler(ChangeHandler handler) {
itemList.addChangeHandler(handler);
}
public void addFilterChangeHandler(ChangeHandler handler) {
filter.addChangeHandler(handler);
}
public void clear() {
itemList.clear();
}
public void refresh() {
itemList.refresh();
}
public Long getSelectedContentItemId() {
return itemList.hasSelection() ? Long.valueOf(itemList.getSelectedItemValue()) : null;
}
public BaseContentItem getSelectedContentItem() {
return itemList.hasSelection() ? loadedContentItemsMap.get(getSelectedContentItemId()) : null;
}
public List<String> getSelectedItems() {
return itemList.getSelectedItems();
}
public List<String> getSelectedValues() {
return itemList.getSelectedItemValues();
}
public void setSelectedContentItemIds(Set<Long> selectedContentItemIds) {
for (int i = 0; i < itemList.getItemCount(); i++) {
itemList.setItemSelected(i,
selectedContentItemIds.contains(Long.valueOf(itemList.getValue(i))));
}
}
public List<BaseContentItem> getSelectedContentitems() {
List<BaseContentItem> result = new ArrayList<BaseContentItem>();
for (String contentItemId : itemList.getSelectedItemValues()) {
result.add(loadedContentItemsMap.get(Long.valueOf(contentItemId)));
}
return result;
}
public Map<Long, BaseContentItem> getLoadedContentItemsMap() {
return loadedContentItemsMap;
}
public void addOrUpdateContentItem(BaseContentItem contentItem) {
boolean isAdd = !loadedContentItemsMap.containsKey(contentItem.getId());
loadedContentItemsMap.put(contentItem.getId(), contentItem);
// Change the filter if necessary so that the added/updated content item
// is visible and selectable.
if (filter.getSelectedConstant() != null
&& !contentItem.getContentItemType().equals(filter.getSelectedConstant())) {
filter.selectConstant(null);
}
itemList.refresh();
itemList.selectItemWithValue(String.valueOf(contentItem.getId()));
if (isAdd) {
itemList.fireEvent(new ChangeEvent(){});
}
}
public void addContentItems(List<BaseContentItem> contentItems) {
// Add to loadedContentItemsMap in reverse order to how the items were specified.
for (int i = contentItems.size() - 1; i >= 0; i--) {
BaseContentItem contentItem = contentItems.get(i);
loadedContentItemsMap.put(contentItem.getId(), contentItem);
}
itemList.refresh();
}
public void removeContentItem(long contentItemId) {
itemList.removeItemWithValue(String.valueOf(contentItemId));
loadedContentItemsMap.remove(contentItemId);
}
private boolean isSelected(BaseContentItem contentItem) {
if (itemList.isMultipleSelect()) {
for (String value : itemList.getSelectedItemValues()) {
if (Long.valueOf(value).equals(contentItem.getId())) {
return true;
}
}
return false;
} else {
return itemList.getSelectedItemValue() != null
&& Long.valueOf(itemList.getSelectedItemValue()).equals(contentItem.getId());
}
}
}