1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.content.browser.input;
7 import org.chromium.content.browser.ContentViewCore;
9 import android.app.AlertDialog;
10 import android.content.DialogInterface;
11 import android.content.res.TypedArray;
12 import android.util.SparseBooleanArray;
13 import android.view.View;
14 import android.view.ViewGroup;
15 import android.widget.AdapterView;
16 import android.widget.AdapterView.OnItemClickListener;
17 import android.widget.ArrayAdapter;
18 import android.widget.CheckedTextView;
19 import android.widget.ListView;
21 import org.chromium.content.R;
24 * Handles the popup dialog for the <select> HTML tag support.
26 public class SelectPopupDialog {
27 // The currently showing popup dialog, null if none is showing.
28 private static SelectPopupDialog sShownDialog;
30 private static final int[] SELECT_DIALOG_ATTRS = {
31 R.attr.select_dialog_multichoice,
32 R.attr.select_dialog_singlechoice
35 // The dialog hosting the popup list view.
36 private AlertDialog mListBoxPopup = null;
38 private ContentViewCore mContentViewCore;
41 * Subclass ArrayAdapter so we can disable OPTION_GROUP items.
43 private class SelectPopupArrayAdapter extends ArrayAdapter<String> {
45 * Possible values for mItemEnabled.
46 * Keep in sync with the value passed from content_view_core_impl.cc
48 final static int POPUP_ITEM_TYPE_GROUP = 0;
49 final static int POPUP_ITEM_TYPE_DISABLED = 1;
50 final static int POPUP_ITEM_TYPE_ENABLED = 2;
52 // Indicates the POPUP_ITEM_TYPE of each item.
53 private int[] mItemEnabled;
55 // True if all items are POPUP_ITEM_TYPE_ENABLED.
56 private boolean mAreAllItemsEnabled;
58 public SelectPopupArrayAdapter(String[] labels, int[] enabled, boolean multiple) {
59 super(mContentViewCore.getContext(), getSelectDialogLayout(multiple), labels);
60 mItemEnabled = enabled;
61 mAreAllItemsEnabled = true;
62 for (int item : mItemEnabled) {
63 if (item != POPUP_ITEM_TYPE_ENABLED) {
64 mAreAllItemsEnabled = false;
71 public View getView(int position, View convertView, ViewGroup parent) {
72 if (position < 0 || position >= getCount()) {
76 // Always pass in null so that we will get a new CheckedTextView. Otherwise, an item
77 // which was previously used as an <optgroup> element (i.e. has no check), could get
78 // used as an <option> element, which needs a checkbox/radio, but it would not have
80 convertView = super.getView(position, null, parent);
81 if (mItemEnabled[position] != POPUP_ITEM_TYPE_ENABLED) {
82 if (mItemEnabled[position] == POPUP_ITEM_TYPE_GROUP) {
83 // Currently select_dialog_multichoice & select_dialog_multichoice use
84 // CheckedTextViews. If that changes, the class cast will no longer be valid.
85 ((CheckedTextView) convertView).setCheckMarkDrawable(null);
87 // Draw the disabled element in a disabled state.
88 convertView.setEnabled(false);
95 public boolean areAllItemsEnabled() {
96 return mAreAllItemsEnabled;
100 public boolean isEnabled(int position) {
101 if (position < 0 || position >= getCount()) {
104 return mItemEnabled[position] == POPUP_ITEM_TYPE_ENABLED;
108 private int getSelectDialogLayout(boolean isMultiChoice) {
110 TypedArray styledAttributes = mContentViewCore.getContext().obtainStyledAttributes(
111 R.style.SelectPopupDialog, SELECT_DIALOG_ATTRS);
112 resource_id = styledAttributes.getResourceId(isMultiChoice ? 0 : 1, 0);
113 styledAttributes.recycle();
117 private SelectPopupDialog(ContentViewCore contentViewCore, String[] labels, int[] enabled,
118 boolean multiple, int[] selected) {
119 mContentViewCore = contentViewCore;
121 final ListView listView = new ListView(mContentViewCore.getContext());
122 listView.setCacheColorHint(0);
123 AlertDialog.Builder b = new AlertDialog.Builder(mContentViewCore.getContext())
126 .setInverseBackgroundForced(true);
129 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
131 public void onClick(DialogInterface dialog, int which) {
132 mContentViewCore.selectPopupMenuItems(getSelectedIndices(listView));
134 b.setNegativeButton(android.R.string.cancel,
135 new DialogInterface.OnClickListener() {
137 public void onClick(DialogInterface dialog, int which) {
138 mContentViewCore.selectPopupMenuItems(null);
141 mListBoxPopup = b.create();
142 final SelectPopupArrayAdapter adapter = new SelectPopupArrayAdapter(labels, enabled,
144 listView.setAdapter(adapter);
145 listView.setFocusableInTouchMode(true);
148 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
149 for (int i = 0; i < selected.length; ++i) {
150 listView.setItemChecked(selected[i], true);
153 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
154 listView.setOnItemClickListener(new OnItemClickListener() {
156 public void onItemClick(AdapterView<?> parent, View v,
157 int position, long id) {
158 mContentViewCore.selectPopupMenuItems(getSelectedIndices(listView));
159 mListBoxPopup.dismiss();
162 if (selected.length > 0) {
163 listView.setSelection(selected[0]);
164 listView.setItemChecked(selected[0], true);
167 mListBoxPopup.setOnCancelListener(new DialogInterface.OnCancelListener() {
169 public void onCancel(DialogInterface dialog) {
170 mContentViewCore.selectPopupMenuItems(null);
173 mListBoxPopup.setOnDismissListener(new DialogInterface.OnDismissListener() {
175 public void onDismiss(DialogInterface dialog) {
176 mListBoxPopup = null;
182 private int[] getSelectedIndices(ListView listView) {
183 SparseBooleanArray sparseArray = listView.getCheckedItemPositions();
184 int selectedCount = 0;
185 for (int i = 0; i < sparseArray.size(); ++i) {
186 if (sparseArray.valueAt(i)) {
190 int[] indices = new int[selectedCount];
191 for (int i = 0, j = 0; i < sparseArray.size(); ++i) {
192 if (sparseArray.valueAt(i)) {
193 indices[j++] = sparseArray.keyAt(i);
200 * Shows the popup menu triggered by the passed ContentView.
201 * Hides any currently shown popup.
202 * @param items Items to show.
203 * @param enabled POPUP_ITEM_TYPEs for items.
204 * @param multiple Whether the popup menu should support multi-select.
205 * @param selectedIndices Indices of selected items.
207 public static void show(ContentViewCore contentViewCore, String[] items, int[] enabled,
208 boolean multiple, int[] selectedIndices) {
209 // Hide the popup currently showing if any. This could happen if the user pressed a select
210 // and pressed it again before the popup was shown. In that case, the previous popup is
211 // irrelevant and can be hidden.
213 sShownDialog = new SelectPopupDialog(contentViewCore, items, enabled, multiple,
215 sShownDialog.mListBoxPopup.show();
219 * Hides the showing popup menu if any it was triggered by the passed ContentView. If
220 * contentView is null, hides it regardless of which ContentView triggered it.
223 public static void hide(ContentViewCore contentView) {
224 if (sShownDialog != null &&
225 (contentView == null || sShownDialog.mContentViewCore == contentView)) {
226 if (contentView != null) contentView.selectPopupMenuItems(null);
227 sShownDialog.mListBoxPopup.dismiss();
231 // The methods below are used by tests.
232 public static SelectPopupDialog getCurrent() {