- add sources.
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / input / SelectPopupDialog.java
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.
4
5 package org.chromium.content.browser.input;
6
7 import org.chromium.content.browser.ContentViewCore;
8
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;
20
21 import org.chromium.content.R;
22
23 /**
24  * Handles the popup dialog for the <select> HTML tag support.
25  */
26 public class SelectPopupDialog {
27     // The currently showing popup dialog, null if none is showing.
28     private static SelectPopupDialog sShownDialog;
29
30     private static final int[] SELECT_DIALOG_ATTRS = {
31         R.attr.select_dialog_multichoice,
32         R.attr.select_dialog_singlechoice
33     };
34
35     // The dialog hosting the popup list view.
36     private AlertDialog mListBoxPopup = null;
37
38     private ContentViewCore mContentViewCore;
39
40     /**
41      * Subclass ArrayAdapter so we can disable OPTION_GROUP items.
42      */
43     private class SelectPopupArrayAdapter extends ArrayAdapter<String> {
44         /**
45          * Possible values for mItemEnabled.
46          * Keep in sync with the value passed from content_view_core_impl.cc
47          */
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;
51
52         // Indicates the POPUP_ITEM_TYPE of each item.
53         private int[] mItemEnabled;
54
55         // True if all items are POPUP_ITEM_TYPE_ENABLED.
56         private boolean mAreAllItemsEnabled;
57
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;
65                     break;
66                 }
67             }
68         }
69
70         @Override
71         public View getView(int position, View convertView, ViewGroup parent) {
72             if (position < 0 || position >= getCount()) {
73                 return null;
74             }
75
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
79             // one.
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);
86                 } else {
87                     // Draw the disabled element in a disabled state.
88                     convertView.setEnabled(false);
89                 }
90             }
91             return convertView;
92         }
93
94         @Override
95         public boolean areAllItemsEnabled() {
96             return mAreAllItemsEnabled;
97         }
98
99         @Override
100         public boolean isEnabled(int position) {
101             if (position < 0 || position >= getCount()) {
102                 return false;
103             }
104             return mItemEnabled[position] == POPUP_ITEM_TYPE_ENABLED;
105         }
106     }
107
108     private int getSelectDialogLayout(boolean isMultiChoice) {
109         int resource_id;
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();
114         return resource_id;
115     }
116
117     private SelectPopupDialog(ContentViewCore contentViewCore, String[] labels, int[] enabled,
118             boolean multiple, int[] selected) {
119         mContentViewCore = contentViewCore;
120
121         final ListView listView = new ListView(mContentViewCore.getContext());
122         listView.setCacheColorHint(0);
123         AlertDialog.Builder b = new AlertDialog.Builder(mContentViewCore.getContext())
124                 .setView(listView)
125                 .setCancelable(true)
126                 .setInverseBackgroundForced(true);
127
128         if (multiple) {
129             b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
130                 @Override
131                 public void onClick(DialogInterface dialog, int which) {
132                     mContentViewCore.selectPopupMenuItems(getSelectedIndices(listView));
133                 }});
134             b.setNegativeButton(android.R.string.cancel,
135                     new DialogInterface.OnClickListener() {
136                 @Override
137                 public void onClick(DialogInterface dialog, int which) {
138                     mContentViewCore.selectPopupMenuItems(null);
139             }});
140         }
141         mListBoxPopup = b.create();
142         final SelectPopupArrayAdapter adapter = new SelectPopupArrayAdapter(labels, enabled,
143                 multiple);
144         listView.setAdapter(adapter);
145         listView.setFocusableInTouchMode(true);
146
147         if (multiple) {
148             listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
149             for (int i = 0; i < selected.length; ++i) {
150                 listView.setItemChecked(selected[i], true);
151             }
152         } else {
153             listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
154             listView.setOnItemClickListener(new OnItemClickListener() {
155                 @Override
156                 public void onItemClick(AdapterView<?> parent, View v,
157                         int position, long id) {
158                     mContentViewCore.selectPopupMenuItems(getSelectedIndices(listView));
159                     mListBoxPopup.dismiss();
160                 }
161             });
162             if (selected.length > 0) {
163                 listView.setSelection(selected[0]);
164                 listView.setItemChecked(selected[0], true);
165             }
166         }
167         mListBoxPopup.setOnCancelListener(new DialogInterface.OnCancelListener() {
168             @Override
169             public void onCancel(DialogInterface dialog) {
170                 mContentViewCore.selectPopupMenuItems(null);
171             }
172         });
173         mListBoxPopup.setOnDismissListener(new DialogInterface.OnDismissListener() {
174             @Override
175             public void onDismiss(DialogInterface dialog) {
176                 mListBoxPopup = null;
177                 sShownDialog = null;
178             }
179         });
180     }
181
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)) {
187                 selectedCount++;
188             }
189         }
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);
194             }
195         }
196         return indices;
197     }
198
199     /**
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.
206      */
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.
212         hide(null);
213         sShownDialog = new SelectPopupDialog(contentViewCore, items, enabled, multiple,
214                 selectedIndices);
215         sShownDialog.mListBoxPopup.show();
216     }
217
218     /**
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.
221      * @param contentView
222      */
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();
228         }
229     }
230
231     // The methods below are used by tests.
232     public static SelectPopupDialog getCurrent() {
233         return sShownDialog;
234     }
235 }