OSDN Git Service

Ver.1.3.0.5:
[nicownn/NicoWnn.git] / src / com / hiroshica / android / input / nicownn2 / TextCandidatesViewManager.java
1 /*
2  * Copyright (C) 2008,2009  OMRON SOFTWARE Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.hiroshica.android.input.nicownn2;
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.lang.Math;
22
23 import android.content.Context;
24 import android.content.SharedPreferences;
25 import android.preference.PreferenceManager;
26 import android.content.res.Configuration;
27 import android.content.res.Resources;
28 import android.media.MediaPlayer;
29 import android.os.AsyncTask;
30 import android.os.SystemClock;
31 import android.os.Vibrator;
32 import android.text.TextUtils;
33 import android.text.TextPaint;
34 import android.text.SpannableString;
35 import android.text.Spanned;
36 import android.text.style.ImageSpan;
37 import android.text.style.DynamicDrawableSpan;
38 import android.util.Log;
39 import android.util.DisplayMetrics;
40 import android.view.Gravity;
41 import android.view.MotionEvent;
42 import android.view.View;
43 import android.view.ViewGroup;
44 import android.view.View.OnClickListener;
45 import android.view.View.OnLongClickListener;
46 import android.view.View.OnTouchListener;
47 import android.view.GestureDetector;
48 import android.view.LayoutInflater;
49 import android.widget.Button;
50 import android.widget.LinearLayout;
51 import android.widget.ScrollView;
52 import android.widget.HorizontalScrollView;
53 import android.widget.TextView;
54 import android.widget.EditText;
55 import android.widget.RelativeLayout;
56 import android.graphics.drawable.Drawable;
57
58 /**
59  * The default candidates view manager class using {@link EditText}.
60  *
61  * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
62  */
63 public class TextCandidatesViewManager implements CandidatesViewManager, GestureDetector.OnGestureListener {
64         /** Height of a line */
65         public static final int LINE_HEIGHT = 34;
66
67         public static final int LINE_NUM_1LINE    = 1;
68         public static final int LINE_NUM_2LINE    = 2;
69         public static final int LINE_NUM_MAX      = 2;
70
71         /** Number of lines to display (Portrait) */
72         //public static final int LINE_NUM_PORTRAIT       = 1;
73         /** Number of lines to display (Landscape) */
74         //public static final int LINE_NUM_LANDSCAPE      = 1;
75
76         /** Maximum lines */
77         private static final int DISPLAY_LINE_MAX_COUNT = 1000;
78         /** Width of the view */
79         private static final int CANDIDATE_MINIMUM_WIDTH = 48;
80         /** Height of the view */
81         //private static final int CANDIDATE_MINIMUM_HEIGHT = 42;
82         /**
83          * candidate-line height
84          */
85         private static final HashMap<String, Integer> candidateViewKeyHeight = new HashMap<String, Integer>() {
86                 /**
87                  * 
88                  */
89                 private static final long serialVersionUID = 1L;
90                 {
91                         put("candidateview_value_0", 0);
92                         put("candidateview_value_1", 1);
93                         put("candidateview_value_2", 2);
94                 }};
95         private static int mCandidateViewHeightIndex = 0;
96         public static int candidateViewDataTable[] = { 42, 51, 60 };
97
98         /** Maximum number of displaying candidates par one line (full view mode) */
99         private static final int FULL_VIEW_DIV = 5;
100
101         /** Body view of the candidates list */
102         private ViewGroup  mViewBody;
103         /** Scroller of {@code mViewBodyText} */
104         private ScrollView           mViewBodyVScroll;
105         private HorizontalScrollView mViewBodyHScroll;
106
107         private boolean mIsScroll;
108         /** Base of {@code mViewCandidateList1st}, {@code mViewCandidateList2nd} */
109         private ViewGroup mViewCandidateBase;
110         /** The view of the scaling up candidate */
111         private View mViewScaleUp;
112         /** Layout for the candidates list on normal view */
113         private LinearLayout  mViewCandidateList1st;
114         /** Layout for the candidates list on full view */
115         private RelativeLayout mViewCandidateList2nd;
116
117         /** {@link NicoWnn} instance using this manager */
118         private NicoWnn mWnn;
119         /** View type (VIEW_TYPE_NORMAL or VIEW_TYPE_FULL or VIEW_TYPE_CLOSE) */
120         private int mViewType;
121         /** Portrait display({@code true}) or landscape({@code false}) */
122         private boolean mPortrait;
123
124         /** Width of the view */
125         int mViewWidth;
126         /** Height of the view */
127         private int mViewHeight;
128         /** Whether hide the view if there is no candidates */
129         private boolean mAutoHideMode;
130         /** The converter to be get candidates from and notice the selected candidate to. */
131         public WnnEngine mConverter;
132         /** Vibrator for touch vibration */
133         private Vibrator mVibrator = null;
134         /** MediaPlayer for click sound */
135         private MediaPlayer mSound = null;
136
137         /** Number of candidates displaying */
138         int mWordCount;
139         int m1stWordCount;
140         int[] mWnnLineCount    = new int[LINE_NUM_MAX];
141         int[] mWnnLineCountMax = new int[LINE_NUM_MAX];
142         private int[] mWnnLineOffset   = new int[LINE_NUM_MAX];
143
144         /** portrait/landscape line */
145         private static final HashMap<String, Integer> lineModeTable = new HashMap<String, Integer>() {/**
146                  * 
147                  */
148                 private static final long serialVersionUID = 1L;
149
150         {
151                         put("1_1", 0);
152                         put("2_1", 1);
153                         put("1_2", 2);
154                         put("2_2", 3);
155                 }};
156         
157         private int mLineMode;
158         private int mPortraitLine;
159         private int mLandscapeLine;
160
161         private boolean mNoSpace;
162
163         /** get last id of topline */
164         private int mToplineLastId;
165
166         private int mTotalLastId;
167         /** List of candidates */
168         private ArrayList<WnnWord> mWnnWordArray;
169         private ArrayList<Integer> mWnnWordTextLength;
170         private ArrayList<Integer> mWnnWordOccupyCount;
171
172         /** Gesture detector */
173         private GestureDetector mGestureDetector;
174         /** The word pressed */
175         private WnnWord mWord;
176         /** Number of lines displayed */
177         private int mLineCount = 1;
178
179         /** {@code true} if the candidate delete state is selected */
180         private boolean mIsScaleUp = false;
181
182         /** {@code true} if the full screen mode is selected */
183         private boolean mIsFullView = false;
184
185         private boolean mIsCreateFullView = false;
186
187         private boolean mIsLockHScroll = false;
188
189         private int mTargetScrollWidth;
190
191         /** The event object for "touch" */
192         private MotionEvent mMotionEvent = null;
193
194         /** {@code true} if there are more candidates to display. */
195         private boolean mCanReadMore = false;
196         /** Width of {@code mReadMoreButton} */
197         private int mTextColor = 0;
198         /** Template object for each candidate and normal/full view change button */
199         private TextView mViewCandidateTemplate;
200         /** Number of candidates in full view */
201         private int mFullViewWordCount;
202         /** Number of candidates in the current line (in full view) */
203         private int mFullViewOccupyCount;
204         /** View of the previous candidate (in full view) */
205         private TextView mFullViewPrevView;
206         /** Id of the top line view (in full view) */
207         private int mFullViewPrevLineTopId;
208         /** Layout of the previous candidate (in full view) */
209         private RelativeLayout.LayoutParams mFullViewPrevParams;
210         /** Whether all candidates is displayed */
211         private boolean mCreateCandidateDone;
212         /** general infomation about a display */
213         private static final DisplayMetrics mMetrics = new DisplayMetrics();
214
215         /* asynctask */
216         private TextCandidateTask mCandidateTask = null;
217         private boolean   mIsActiveTask = false;
218         private boolean   mIsCancelTask = false;
219         
220         // docomo emoji hashmap
221         private static final HashMap<String, Integer> DOCOMO_EMOJI_TABLE = new HashMap<String, Integer>() {/**
222                  * 
223                  */
224                 private static final long serialVersionUID = 1L;
225
226         {
227                         put("\uE63E", R.drawable.docomo_1); put("\uE63F", R.drawable.docomo_2); put("\uE640", R.drawable.docomo_3); put("\uE641", R.drawable.docomo_4);
228                         put("\uE642", R.drawable.docomo_5); put("\uE643", R.drawable.docomo_6); put("\uE644", R.drawable.docomo_7); put("\uE645", R.drawable.docomo_8);
229                         put("\uE646", R.drawable.docomo_9); put("\uE647", R.drawable.docomo_10); put("\uE648", R.drawable.docomo_11); put("\uE649", R.drawable.docomo_12);
230                         put("\uE64A", R.drawable.docomo_13); put("\uE64B", R.drawable.docomo_14); put("\uE64C", R.drawable.docomo_15); put("\uE64D", R.drawable.docomo_16);
231                         put("\uE64E", R.drawable.docomo_17); put("\uE64F", R.drawable.docomo_18); put("\uE650", R.drawable.docomo_19); put("\uE651", R.drawable.docomo_20);
232                         put("\uE652", R.drawable.docomo_21); put("\uE653", R.drawable.docomo_22); put("\uE654", R.drawable.docomo_23); put("\uE655", R.drawable.docomo_24);
233                         put("\uE656", R.drawable.docomo_25); put("\uE657", R.drawable.docomo_26); put("\uE658", R.drawable.docomo_27); put("\uE659", R.drawable.docomo_28);
234                         put("\uE65A", R.drawable.docomo_29); put("\uE65B", R.drawable.docomo_30); put("\uE65C", R.drawable.docomo_31); put("\uE65D", R.drawable.docomo_32);
235                         put("\uE65E", R.drawable.docomo_33); put("\uE65F", R.drawable.docomo_34); put("\uE660", R.drawable.docomo_35); put("\uE661", R.drawable.docomo_36);
236                         put("\uE662", R.drawable.docomo_37); put("\uE663", R.drawable.docomo_38); put("\uE664", R.drawable.docomo_39); put("\uE665", R.drawable.docomo_40);
237                         put("\uE666", R.drawable.docomo_41); put("\uE667", R.drawable.docomo_42); put("\uE668", R.drawable.docomo_43); put("\uE669", R.drawable.docomo_44);
238                         put("\uE66A", R.drawable.docomo_45); put("\uE66B", R.drawable.docomo_46); put("\uE66C", R.drawable.docomo_47); put("\uE66D", R.drawable.docomo_48);
239                         put("\uE66E", R.drawable.docomo_49); put("\uE66F", R.drawable.docomo_50); put("\uE670", R.drawable.docomo_51); put("\uE671", R.drawable.docomo_52);
240                         put("\uE672", R.drawable.docomo_53); put("\uE673", R.drawable.docomo_54); put("\uE674", R.drawable.docomo_55); put("\uE675", R.drawable.docomo_56);
241                         put("\uE676", R.drawable.docomo_57); put("\uE677", R.drawable.docomo_58); put("\uE678", R.drawable.docomo_59); put("\uE679", R.drawable.docomo_60);
242                         put("\uE67A", R.drawable.docomo_61); put("\uE67B", R.drawable.docomo_62); put("\uE67C", R.drawable.docomo_63); put("\uE67D", R.drawable.docomo_64);
243                         put("\uE67E", R.drawable.docomo_65); put("\uE67F", R.drawable.docomo_66); put("\uE680", R.drawable.docomo_67); put("\uE681", R.drawable.docomo_68);
244                         put("\uE682", R.drawable.docomo_69); put("\uE683", R.drawable.docomo_70); put("\uE684", R.drawable.docomo_71); put("\uE685", R.drawable.docomo_72);
245                         put("\uE686", R.drawable.docomo_73); put("\uE687", R.drawable.docomo_74); put("\uE688", R.drawable.docomo_75); put("\uE689", R.drawable.docomo_76);
246                         put("\uE68A", R.drawable.docomo_77); put("\uE68B", R.drawable.docomo_78); put("\uE68C", R.drawable.docomo_79); put("\uE68D", R.drawable.docomo_80);
247                         put("\uE68E", R.drawable.docomo_81); put("\uE68F", R.drawable.docomo_82); put("\uE690", R.drawable.docomo_83); put("\uE691", R.drawable.docomo_84);
248                         put("\uE692", R.drawable.docomo_85); put("\uE693", R.drawable.docomo_86); put("\uE694", R.drawable.docomo_87); put("\uE695", R.drawable.docomo_88);
249                         put("\uE696", R.drawable.docomo_89); put("\uE697", R.drawable.docomo_90); put("\uE698", R.drawable.docomo_91); put("\uE699", R.drawable.docomo_92);
250                         put("\uE69A", R.drawable.docomo_93); put("\uE69B", R.drawable.docomo_94); put("\uE69C", R.drawable.docomo_95); put("\uE69D", R.drawable.docomo_96);
251                         put("\uE69E", R.drawable.docomo_97); put("\uE69F", R.drawable.docomo_98); put("\uE6A0", R.drawable.docomo_99); put("\uE6A1", R.drawable.docomo_100);
252                         put("\uE6A2", R.drawable.docomo_101); put("\uE6A3", R.drawable.docomo_102); put("\uE6A4", R.drawable.docomo_103); put("\uE6A5", R.drawable.docomo_104);
253                         put("\uE6CE", R.drawable.docomo_105); put("\uE6CF", R.drawable.docomo_106); put("\uE6D0", R.drawable.docomo_107); put("\uE6D1", R.drawable.docomo_108);
254                         put("\uE6D2", R.drawable.docomo_109); put("\uE6D3", R.drawable.docomo_110); put("\uE6D4", R.drawable.docomo_111); put("\uE6D5", R.drawable.docomo_112);
255                         put("\uE6D6", R.drawable.docomo_113); put("\uE6D7", R.drawable.docomo_114); put("\uE6D8", R.drawable.docomo_115); put("\uE6D9", R.drawable.docomo_116);
256                         put("\uE6DA", R.drawable.docomo_117); put("\uE6DB", R.drawable.docomo_118); put("\uE6DC", R.drawable.docomo_119); put("\uE6DD", R.drawable.docomo_120);
257                         put("\uE6DE", R.drawable.docomo_121); put("\uE6DF", R.drawable.docomo_122); put("\uE6E0", R.drawable.docomo_123); put("\uE6E1", R.drawable.docomo_124);
258                         put("\uE6E2", R.drawable.docomo_125); put("\uE6E3", R.drawable.docomo_126); put("\uE6E4", R.drawable.docomo_127); put("\uE6E5", R.drawable.docomo_128);
259                         put("\uE6E6", R.drawable.docomo_129); put("\uE6E7", R.drawable.docomo_130); put("\uE6E8", R.drawable.docomo_131); put("\uE6E9", R.drawable.docomo_132);
260                         put("\uE6EA", R.drawable.docomo_133); put("\uE6EB", R.drawable.docomo_134); put("\uE70B", R.drawable.docomo_135); put("\uE6EC", R.drawable.docomo_136);
261                         put("\uE6ED", R.drawable.docomo_137); put("\uE6EE", R.drawable.docomo_138); put("\uE6EF", R.drawable.docomo_139); put("\uE6F0", R.drawable.docomo_140);
262                         put("\uE6F1", R.drawable.docomo_141); put("\uE6F2", R.drawable.docomo_142); put("\uE6F3", R.drawable.docomo_143); put("\uE6F4", R.drawable.docomo_144);
263                         put("\uE6F5", R.drawable.docomo_145); put("\uE6F6", R.drawable.docomo_146); put("\uE6F7", R.drawable.docomo_147); put("\uE6F8", R.drawable.docomo_148);
264                         put("\uE6F9", R.drawable.docomo_149); put("\uE6FA", R.drawable.docomo_150); put("\uE6FB", R.drawable.docomo_151); put("\uE6FC", R.drawable.docomo_152);
265                         put("\uE6FD", R.drawable.docomo_153); put("\uE6FE", R.drawable.docomo_154); put("\uE6FF", R.drawable.docomo_155); put("\uE700", R.drawable.docomo_156);
266                         put("\uE701", R.drawable.docomo_157); put("\uE702", R.drawable.docomo_158); put("\uE703", R.drawable.docomo_159); put("\uE704", R.drawable.docomo_160);
267                         put("\uE705", R.drawable.docomo_161); put("\uE706", R.drawable.docomo_162); put("\uE707", R.drawable.docomo_163); put("\uE708", R.drawable.docomo_164);
268                         put("\uE709", R.drawable.docomo_165); put("\uE70A", R.drawable.docomo_166); put("\uE6AC", R.drawable.docomo_167); put("\uE6AD", R.drawable.docomo_168);
269                         put("\uE6AE", R.drawable.docomo_169); put("\uE6B1", R.drawable.docomo_170); put("\uE6B2", R.drawable.docomo_171); put("\uE6B3", R.drawable.docomo_172);
270                         put("\uE6B7", R.drawable.docomo_173); put("\uE6B8", R.drawable.docomo_174); put("\uE6B9", R.drawable.docomo_175); put("\uE6BA", R.drawable.docomo_176);
271                         put("\uE70C", R.drawable.docomo_ex1); put("\uE70D", R.drawable.docomo_ex2); put("\uE70E", R.drawable.docomo_ex3); put("\uE70F", R.drawable.docomo_ex4);
272                         put("\uE710", R.drawable.docomo_ex5); put("\uE711", R.drawable.docomo_ex6); put("\uE712", R.drawable.docomo_ex7); put("\uE713", R.drawable.docomo_ex8);
273                         put("\uE714", R.drawable.docomo_ex9); put("\uE715", R.drawable.docomo_ex10); put("\uE716", R.drawable.docomo_ex11); put("\uE717", R.drawable.docomo_ex12);
274                         put("\uE718", R.drawable.docomo_ex13); put("\uE719", R.drawable.docomo_ex14); put("\uE71A", R.drawable.docomo_ex15); put("\uE71B", R.drawable.docomo_ex16);
275                         put("\uE71C", R.drawable.docomo_ex17); put("\uE71D", R.drawable.docomo_ex18); put("\uE71E", R.drawable.docomo_ex19); put("\uE71F", R.drawable.docomo_ex20);
276                         put("\uE720", R.drawable.docomo_ex21); put("\uE721", R.drawable.docomo_ex22); put("\uE722", R.drawable.docomo_ex23); put("\uE723", R.drawable.docomo_ex24);
277                         put("\uE724", R.drawable.docomo_ex25); put("\uE725", R.drawable.docomo_ex26); put("\uE726", R.drawable.docomo_ex27); put("\uE727", R.drawable.docomo_ex28);
278                         put("\uE728", R.drawable.docomo_ex29); put("\uE729", R.drawable.docomo_ex30); put("\uE72A", R.drawable.docomo_ex31); put("\uE72B", R.drawable.docomo_ex32);
279                         put("\uE72C", R.drawable.docomo_ex33); put("\uE72D", R.drawable.docomo_ex34); put("\uE72E", R.drawable.docomo_ex35); put("\uE72F", R.drawable.docomo_ex36);
280                         put("\uE730", R.drawable.docomo_ex37); put("\uE731", R.drawable.docomo_ex38); put("\uE732", R.drawable.docomo_ex39); put("\uE733", R.drawable.docomo_ex40);
281                         put("\uE734", R.drawable.docomo_ex41); put("\uE735", R.drawable.docomo_ex42); put("\uE736", R.drawable.docomo_ex43); put("\uE737", R.drawable.docomo_ex44);
282                         put("\uE738", R.drawable.docomo_ex45); put("\uE739", R.drawable.docomo_ex46); put("\uE73A", R.drawable.docomo_ex47); put("\uE73B", R.drawable.docomo_ex48);
283                         put("\uE73C", R.drawable.docomo_ex49); put("\uE73D", R.drawable.docomo_ex50); put("\uE73E", R.drawable.docomo_ex51); put("\uE73F", R.drawable.docomo_ex52);
284                         put("\uE740", R.drawable.docomo_ex53); put("\uE741", R.drawable.docomo_ex54); put("\uE742", R.drawable.docomo_ex55); put("\uE743", R.drawable.docomo_ex56);
285                         put("\uE744", R.drawable.docomo_ex57); put("\uE745", R.drawable.docomo_ex58); put("\uE746", R.drawable.docomo_ex59); put("\uE747", R.drawable.docomo_ex60);
286                         put("\uE748", R.drawable.docomo_ex61); put("\uE749", R.drawable.docomo_ex62); put("\uE74A", R.drawable.docomo_ex63); put("\uE74B", R.drawable.docomo_ex64);
287                         put("\uE74C", R.drawable.docomo_ex65); put("\uE74D", R.drawable.docomo_ex66); put("\uE74E", R.drawable.docomo_ex67); put("\uE74F", R.drawable.docomo_ex68);
288                         put("\uE750", R.drawable.docomo_ex69); put("\uE751", R.drawable.docomo_ex70); put("\uE752", R.drawable.docomo_ex71); put("\uE753", R.drawable.docomo_ex72);
289                         put("\uE754", R.drawable.docomo_ex73); put("\uE755", R.drawable.docomo_ex74); put("\uE756", R.drawable.docomo_ex75); put("\uE757", R.drawable.docomo_ex76);
290                 }};
291
292         /** Event listener for touching a candidate */
293         private OnTouchListener mCandidateOnTouch = new OnTouchListener() {
294                 public boolean onTouch(View v, MotionEvent event) {
295                         if (mMotionEvent != null) {
296                                 return true;
297                         }
298
299                         if ((event.getAction() == MotionEvent.ACTION_UP)
300                             && (v instanceof TextView)) {
301                                 Drawable d = v.getBackground();
302                                 if (d != null) {
303                                         d.setState(new int[] {});
304                                 }
305                         }
306
307                         mMotionEvent = event;
308                         boolean ret = mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.CANDIDATE_VIEW_TOUCH));
309                         mMotionEvent = null;
310                         return ret;
311                 }
312         };
313     
314     
315         /** Event listener for clicking a candidate */
316         private OnClickListener mCandidateOnClick = new OnClickListener() {
317                 public void onClick(View v) {
318                         if (!v.isShown()) {
319                                 return;
320                         }
321                 
322                         if (v instanceof TextView) {
323                                 checkCandidateTask();
324                                 TextView text = (TextView)v;
325                                 int wordcount = text.getId() - 1;
326                                 WnnWord word = null;
327                                 word = mWnnWordArray.get(wordcount);
328                                 selectCandidate(word);
329                         }
330                 }
331         };
332
333         /** Event listener for long-clicking a candidate */
334         private OnLongClickListener mCandidateOnLongClick = new OnLongClickListener() {
335                 public boolean onLongClick(View v) {
336                         if (mViewScaleUp == null) {
337                                 return false;
338                         }
339
340                         if (!v.isShown()) {
341                                 return true;
342                         }
343
344                         Drawable d = v.getBackground();
345                         if (d != null) {
346                                 if(d.getState().length == 0){
347                                         return true;
348                                 }
349                         }
350             
351                         int wordcount = ((TextView)v).getId() - 1;
352                         mWord = mWnnWordArray.get(wordcount);
353                         setViewScaleUp(true, mWord);
354             
355                         return true;
356                 }
357         };
358
359
360         /**
361          * Constructor
362          */
363         public TextCandidatesViewManager() {
364                 this(-1);
365         }
366
367         /**
368          * Constructor
369          *
370          * @param displayLimit      The limit of display
371          */
372         public TextCandidatesViewManager(int displayLimit) {
373                 this.mWnnWordArray = new ArrayList<WnnWord>();
374                 this.mWnnWordTextLength = new ArrayList<Integer>();
375                 this.mWnnWordOccupyCount = new ArrayList<Integer>();
376                 this.mAutoHideMode = true;
377                 this.mIsActiveTask = false;
378                 mMetrics.setToDefaults();
379         }
380
381         /**
382          * Set auto-hide mode.
383          * @param hide      {@code true} if the view will hidden when no candidate exists;
384          *                  {@code false} if the view is always shown.
385          */
386         public void setAutoHide(boolean hide) {
387                 mAutoHideMode = hide;
388         }
389
390         /** @see CandidatesViewManager */
391         public View initView(NicoWnn parent, int width, int height) {
392                 mWnn = parent;
393                 mViewWidth = Math.round((float)width / mMetrics.density);
394                 mViewHeight = Math.round((float)height / mMetrics.density);
395                 mPortrait = 
396                         (parent.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE);
397
398                 mLineMode = 0;
399                 mPortraitLine  = 1;
400                 mLandscapeLine = 1;
401                 mNoSpace = false;
402
403                 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(parent);
404                 if (null != pref) {
405                         mLineMode = lineModeTable.get(pref.getString("nico_candidate_mode", "1_1"));
406                         if (0 == mLineMode) {
407                                 mPortraitLine  = 1;
408                                 mLandscapeLine = 1;
409                         }
410                         if (1 == mLineMode) {
411                                 mPortraitLine  = 2;
412                                 mLandscapeLine = 1;
413                         }
414                         if (2 == mLineMode) {
415                                 mPortraitLine  = 1;
416                                 mLandscapeLine = 2;
417                         }
418                         if (3 == mLineMode) {
419                                 mPortraitLine  = 2;
420                                 mLandscapeLine = 2;
421                         }
422                         mNoSpace = pref.getBoolean("nospace_candidate", false);
423                         // candidate line height
424                         mCandidateViewHeightIndex = candidateViewKeyHeight.get(pref.getString("candidateview_height_mode", "candidateview_value_0"));
425                 }
426
427                 Resources r = mWnn.getResources();
428
429                 LayoutInflater inflater = parent.getLayoutInflater();
430                 mViewBody = (ViewGroup)inflater.inflate(R.layout.candidates, null);
431
432                 mViewBodyVScroll = (ScrollView)mViewBody.findViewById(R.id.candview_scroll);
433                 mViewBodyVScroll.setOnTouchListener(mCandidateOnTouch);
434
435                 mViewBodyHScroll = (HorizontalScrollView)mViewBody.findViewById(R.id.candview_hscroll);
436                 mViewBodyHScroll.setOnTouchListener(mCandidateOnTouch);
437                 mIsLockHScroll = false;
438
439                 mViewCandidateBase = (ViewGroup)mViewBody.findViewById(R.id.candview_base);
440
441                 mViewCandidateList1st = (LinearLayout)mViewBody.findViewById(R.id.candidates_1st_view);
442                 mViewCandidateList1st.setOnTouchListener(mCandidateOnTouch);
443                 mViewCandidateList1st.setOnClickListener(mCandidateOnClick);
444
445                 mViewCandidateList2nd = (RelativeLayout)mViewBody.findViewById(R.id.candidates_2nd_view);
446                 mViewCandidateList2nd.setOnTouchListener(mCandidateOnTouch);
447                 mViewCandidateList2nd.setOnClickListener(mCandidateOnClick);
448
449                 // create first textView
450                 for (int iI = 0; iI < LINE_NUM_MAX; ++iI) {
451             LinearLayout lineView = new LinearLayout(mViewBodyVScroll.getContext());
452             lineView.setOrientation(LinearLayout.HORIZONTAL);
453             LinearLayout.LayoutParams layoutParams = 
454                                 new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
455                                               ViewGroup.LayoutParams.WRAP_CONTENT);
456             lineView.setLayoutParams(layoutParams);
457                         lineView.setGravity(Gravity.LEFT);
458
459                         lineView.setMinimumHeight(getCandidateMinimumHeight());
460             mViewCandidateList1st.addView(lineView);
461                 }
462
463                 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(getCandidateMinimumWidth(), getCandidateMinimumHeight());
464                 params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
465                 params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
466                 
467                 TextView textView = createCandidateView();
468                 //textView.setLayoutParams(params);
469                 mViewCandidateList2nd.addView(textView);
470                 mViewCandidateTemplate = textView;
471
472                 mTextColor = r.getColor(R.color.candidate_text);
473
474                 setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
475
476                 mGestureDetector = new GestureDetector(this);
477
478                 View scaleUp = (View)inflater.inflate(R.layout.candidate_scale_up, null);
479                 mViewScaleUp = scaleUp;
480
481                 /* select button */
482                 Button b = (Button)scaleUp.findViewById(R.id.candidate_select);
483                 b.setOnClickListener(new View.OnClickListener() {
484                         public void onClick(View v) {
485                                 selectCandidate(mWord);
486                         }
487                 });
488
489                 /* cancel button */
490                 b = (Button)scaleUp.findViewById(R.id.candidate_cancel);
491                 b.setOnClickListener(new View.OnClickListener() {
492                         public void onClick(View v) {
493                                 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
494                                 mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.UPDATE_CANDIDATE));
495                         }
496                 });
497
498                 return mViewBody;
499         }
500
501         /** @see CandidatesViewManager#getCurrentView */
502         public View getCurrentView() {
503                 return mViewBody;
504         }
505
506         /** @see CandidatesViewManager#setViewType */
507         public void setViewType(int type) {
508                 boolean readMore = setViewLayout(type);
509                 
510                 if (readMore) {
511                         if (false == mIsCreateFullView) {
512                                 mCanReadMore = false;
513                                 mFullViewWordCount = 0;
514                                 mFullViewOccupyCount = 0;
515                                 mFullViewPrevLineTopId = 0;
516                                 mCreateCandidateDone = false;
517                                 mIsScroll = false;
518
519                                 mLineCount     = 1;
520                                 mWordCount     = 0;
521                                 mToplineLastId = 0;
522                                 mTotalLastId   = 0;
523                                 mIsLockHScroll = false;
524
525                                 displayCandidates(this.mConverter, false, -1);
526                                 mIsCreateFullView = true;
527                         }
528                         else{
529                                 visibleFullCandidate();
530                         }
531                 } else { 
532                         if (type == CandidatesViewManager.VIEW_TYPE_NORMAL) {
533                                 mIsFullView = false;
534                                 /*
535                                   if (mDisplayEndOffset > 0) {
536                                   int maxLine = getMaxLine();
537                                   displayCandidates(this.mConverter, false, maxLine);
538                                   }
539                                 */
540                         } else {
541                                 mIsFullView = true;
542                                 if (mViewBody.isShown()) {
543                                         mWnn.setCandidatesViewShown(false);
544                                 }
545                         }
546                 }
547         }
548
549         /**
550          * Set the view layout
551          *
552          * @param type      View type
553          * @return          {@code true} if display is updated; {@code false} if otherwise
554          */
555         private boolean setViewLayout(int type) {
556                 mViewType = type;
557                 setViewScaleUp(false, null);
558
559                 switch (type) {
560                 case CandidatesViewManager.VIEW_TYPE_CLOSE:
561                         checkCandidateTask();
562                         mViewCandidateBase.setMinimumHeight(-1);
563                         //mIsLockHScroll = false;
564                         return false;
565
566                 case CandidatesViewManager.VIEW_TYPE_NORMAL:
567                         clearNormalCandidates();
568                         mViewBodyVScroll.scrollTo(0, 0);
569                         if (false == mIsLockHScroll) {
570                                 //mViewBodyHScroll.scrollTo(0, 0);
571                         }
572                         mViewCandidateList1st.setVisibility(View.VISIBLE);
573                         LinearLayout candidateList = mViewCandidateList1st;
574                         for (int iI = 0; iI < LINE_NUM_MAX; ++iI) {
575                                 LinearLayout lineView = (LinearLayout)candidateList.getChildAt(iI);
576                                 if (iI < getMaxLine()) {
577                                         lineView.setVisibility(View.VISIBLE);
578                                 }
579                                 else{
580                                         lineView.setVisibility(View.GONE);
581                                 }
582                         }
583                         mViewCandidateList2nd.setVisibility(View.GONE);
584                         mViewCandidateBase.setMinimumHeight(-1);
585                         int line = getMaxLine();
586                         int minheight = (getCandidateMinimumHeight() * line);
587                         if (false == mNoSpace) {
588                                 minheight += ((getCandidateMinimumHeight()/3) * 2);
589                         }
590                         mViewCandidateList1st.setMinimumHeight(minheight);
591                         return false;
592                         
593                 case CandidatesViewManager.VIEW_TYPE_FULL:
594                 default:
595                         mViewCandidateList1st.setVisibility(View.GONE);
596                         mViewCandidateList2nd.setVisibility(View.VISIBLE);
597                         mViewCandidateList2nd.setMinimumHeight(mViewHeight);
598                         mViewCandidateBase.setMinimumHeight(mViewHeight);
599                         return true;
600                 }
601         }
602
603         /** @see CandidatesViewManager#getViewType */
604         public int getViewType() {
605                 return mViewType;
606         }
607
608         /** set full view */
609         public void setFullView() {
610                 mIsFullView = true;
611                 mIsScroll   = false; // reset scroll flag
612                 mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.LIST_CANDIDATES_FULL));
613         }
614         
615         /** @see CandidatesViewManager#displayCandidates */
616         public void displayCandidates(WnnEngine converter) {
617                 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(mWnn);
618
619                 mLineMode = 0;
620                 mPortraitLine  = 1;
621                 mLandscapeLine = 1;
622                 mNoSpace = false;
623
624                 mLineMode = lineModeTable.get(pref.getString("nico_candidate_mode", "1_1"));
625                 if (0 == mLineMode) {
626                         mPortraitLine  = 1;
627                         mLandscapeLine = 1;
628                 }
629                 else if (1 == mLineMode) {
630                         mPortraitLine  = 2;
631                         mLandscapeLine = 1;
632                 }
633                 else if (2 == mLineMode) {
634                         mPortraitLine  = 1;
635                         mLandscapeLine = 2;
636                 }
637                 else if (3 == mLineMode) {
638                         mPortraitLine  = 2;
639                         mLandscapeLine = 2;
640                 }
641                 mNoSpace = pref.getBoolean("nospace_candidate", false);
642                 // candidate line height
643                 mCandidateViewHeightIndex = candidateViewKeyHeight.get(pref.getString("candidateview_height_mode", "candidateview_value_0"));
644
645                 mCanReadMore = false;
646                 mIsFullView = false;
647                 mFullViewWordCount = 0;
648                 mFullViewOccupyCount = 0;
649                 mFullViewPrevLineTopId = 0;
650                 mCreateCandidateDone = false;
651                 mIsCreateFullView = false;
652                 mIsScroll = false;
653
654                 clearCandidates();
655                 mConverter = converter;
656                 
657                 createWnnWordArray();
658
659                 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
660                 /* create normalview */
661                 display1stCandidates(mConverter, mViewWidth);
662                 mTargetScrollWidth = mViewWidth / 2;
663                 mViewBodyHScroll.scrollTo(0, 0);
664                 mIsLockHScroll = false;
665                 /* create background normalview */
666                 startCandidateTask();
667         }
668         /*
669          * 
670          */
671         private void startCandidateTask() {
672                 int viewWidth = mViewWidth * 1000;
673                 mCandidateTask = new TextCandidateTask(this);
674                 if (null != mCandidateTask) {
675                         mIsActiveTask = true;
676                         mIsCancelTask = false;
677                         mCandidateTask.execute(viewWidth);
678                 }
679         }
680         /*
681          * 
682          */
683         public void checkCandidateTask() {
684                 if (null != mCandidateTask) {
685             mCandidateTask.cancel(true);
686                         mCandidateTask = null;
687                 }
688         }
689         /*
690          * 
691          */
692         public void hideCandidateTask() {
693         int count = 100;
694                 if (null != mCandidateTask) {
695             mCandidateTask.cancel(true);
696             while(count > 0) {
697                 try {
698                     Thread.sleep(20);
699                 } catch (Exception e) {
700                     return;
701                 }
702                 //SystemClock.sleep(20);
703                 if (true == mIsCancelTask) {
704                         break;
705                 }
706                 count--;
707             }
708                         mCandidateTask = null;
709                 }
710         }
711         /*
712          * cancel callback 
713          */
714         public void cancelTask() {
715         mIsCancelTask = true;
716         }
717         /*
718          * 
719          */
720         synchronized private void createWnnWordArray() {
721         //private void createWnnWordArray() {
722                 WnnEngine converter = mConverter;
723                 if (null == converter) {
724                         return;
725                 }
726                 WnnWord result = null;
727                 int index = 0;
728                 int indentWidth = mViewWidth / FULL_VIEW_DIV;
729                 int maxindex = DISPLAY_LINE_MAX_COUNT * FULL_VIEW_DIV;
730                 int maxWidth = 0;
731                 do {
732                         result = converter.getNextCandidate();
733                         if (result == null) {
734                                 break;
735                         }
736                         int textLength  = measureText(result.candidate, 0, result.candidate.length());
737                         int occupyCount = Math.min((textLength + indentWidth) / indentWidth, FULL_VIEW_DIV);
738                         int textWidth   = indentWidth * occupyCount;
739                         
740                         mWnnWordArray.add(index, result);
741                         mWnnWordTextLength.add(index, textWidth);
742                         mWnnWordOccupyCount.add(index, occupyCount);
743                         maxWidth += textWidth;
744                         index++;
745                 } while (index < maxindex);
746
747                 /* split wnnword tables */
748                 int iI;
749                 int size      = mWnnWordArray.size();
750                 int maxline   = getMaxLine();
751                 int halfWidth = (maxWidth / maxline);
752                 int calcWidth = 0;
753                 int divsize   = 0;
754                 int offset    = 0;
755
756                 halfWidth = Math.max(halfWidth, maxWidth - halfWidth);
757                 for (iI = 0; iI < size; ++iI, ++divsize) {
758                         if (halfWidth <= calcWidth) {
759                                 break;
760                         }
761                         calcWidth += mWnnWordTextLength.get(iI);
762                 }
763                 for (iI = 0; iI < maxline; ++iI) {
764                         mWnnLineCount[iI]    = 0;
765                         mWnnLineOffset[iI]   = offset;
766                         mWnnLineCountMax[iI] = divsize;
767                         offset  += divsize;
768                         divsize  = mWnnWordArray.size() - divsize;
769                 }
770         }
771
772         /** @see CandidatesViewManager#getMaxLine */
773         int getMaxLine() {
774                 int maxLine = (mPortrait) ? mPortraitLine : mLandscapeLine;
775                 return maxLine;
776         }
777         /*
778          *
779          */
780         //synchronized private void display1stCandidates(WnnEngine converter, int width) {
781         private void display1stCandidates(WnnEngine converter, int width) {
782                 if (converter == null) {
783                         return;
784                 }
785                 /* Get candidates */
786                 WnnWord result = null;
787                 int maxline = getMaxLine();
788                 int wordcount = 0;
789                 int size      = 0;
790                 int offset    = 0;
791                 int calcwidth = 0;
792                 int calcid    = 0;
793                 int iI;
794                 mWordCount = 0;
795                 for (iI = 0; iI < maxline; ++iI) {
796                         wordcount = mWnnLineCount[iI];
797                         size      = mWnnLineCountMax[iI];
798                         offset    = mWnnLineOffset[iI];
799                         calcwidth = 0;
800
801                         LinearLayout candidateList = mViewCandidateList1st;
802             LinearLayout lineView = (LinearLayout)candidateList.getChildAt(iI);
803                         while (wordcount < size) {
804                                 calcid = wordcount + offset;
805                                 result = mWnnWordArray.get(calcid);
806                                 calcwidth += mWnnWordTextLength.get(calcid);
807                                 set1stCandidate(wordcount, calcid, result, mWnnWordTextLength.get(calcid), mWnnWordOccupyCount.get(calcid), lineView);
808                                 wordcount++;
809                                 if (calcwidth >= width) {
810                                         break;
811                                 }
812                         }
813                         mWordCount += wordcount;
814                         mWnnLineCount[iI] = wordcount;
815                 }
816                 m1stWordCount = mWordCount;
817
818                 if (mWordCount < 1) { /* no candidates */
819                         if (mAutoHideMode) {
820                                 mWnn.setCandidatesViewShown(false);
821                                 return;
822                         } else {
823                                 mCanReadMore = false;
824                                 mIsFullView = false;
825                                 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
826                         }
827                 }
828                 else{
829                         mCanReadMore = true;
830                 }
831                 if (!(mViewBody.isShown())) {
832                         mWnn.setCandidatesViewShown(true);
833                 }
834         }
835         /*
836          * 
837          */
838         public int calc1stCandidates(int line, int width) {
839                 if (false == mIsActiveTask) {
840                         return 0;
841                 }
842
843                 int wordcount = 0;
844                 int size      = 0;
845                 int offset    = 0;
846                 int calcid    = 0;
847                 int calcwidth = 0;
848                 wordcount = mWnnLineCount[line];
849                 size      = mWnnLineCountMax[line];
850                 offset    = mWnnLineOffset[line];
851                 while(calcwidth < width) {
852                         if (wordcount >= size) {
853                                 break;
854                         }
855                         calcid = wordcount + offset;
856                         WnnWord wnnword = null;
857                         try {
858                                 wnnword = mWnnWordArray.get(calcid);
859                         } catch (Exception e) {
860                                 mWnnLineCount[line] = mWnnLineCountMax[line];
861                                 return 0;
862                         }
863                         calcwidth += mWnnWordTextLength.get(calcid);
864                         wordcount++;
865                 }
866                 return calcwidth;
867         }
868         /*
869          * 
870          */
871         public boolean display1stCandidates(int line, int width) {
872                 if (false == mIsActiveTask) {
873                         return true;
874                 }
875                 boolean result = false;
876
877                 int wordcount = 0;
878                 int size      = 0;
879                 int offset    = 0;
880                 int calcid    = 0;
881                 int calcwidth = 0;
882                 wordcount = mWnnLineCount[line];
883                 size      = mWnnLineCountMax[line];
884                 offset    = mWnnLineOffset[line];
885                 LinearLayout candidateList = mViewCandidateList1st;
886                 LinearLayout lineView = (LinearLayout)candidateList.getChildAt(line);
887                 while(calcwidth < width) {
888                         if (wordcount >= size) {
889                                 result = true;
890                                 break;
891                         }
892                         calcid = wordcount + offset;
893                         WnnWord wnnword = null;
894                         try {
895                                 wnnword = mWnnWordArray.get(calcid);
896                         } catch (Exception e) {
897                                 mWnnLineCount[line] = mWnnLineCountMax[line];
898                                 return true;
899                         }
900                         calcwidth += mWnnWordTextLength.get(calcid);
901                         set1stCandidate(wordcount, calcid, wnnword, mWnnWordTextLength.get(calcid), mWnnWordOccupyCount.get(calcid), lineView);
902                         wordcount++;
903                 }
904                 mWnnLineCount[line] = wordcount;
905                 return result;
906         }
907         /*
908          *
909          */
910         public void invalidate1stView() {
911                 int maxLine  = getMaxLine();
912                 LinearLayout lineView = (LinearLayout)mViewCandidateList1st.getChildAt(0);
913                 int width = 0;
914                 for (int iI = 0; iI < maxLine; ++iI) {
915                         lineView = (LinearLayout)mViewCandidateList1st.getChildAt(iI);
916                         int getwidth = lineView.getWidth();
917                         if (width < getwidth) {
918                                 width = getwidth;
919                         }
920                 }
921                 int getx = mViewBodyHScroll.getScrollX();
922                 int gety = mViewBodyHScroll.getScrollY();
923                 mViewCandidateList1st.invalidate();
924                 mViewBodyHScroll.scrollTo(getx, gety);
925         }
926         /*
927          * 
928          */
929         public void display1stLastSetup() {
930                 m1stWordCount = mWordCount;
931                 mIsActiveTask = false;
932         }
933         /**
934          * Display the candidates.
935          * 
936          * @param converter  {@link WnnEngine} which holds candidates.
937          * @param dispFirst  Whether it is the first time displaying the candidates
938          * @param maxLine    The maximum number of displaying lines
939          */
940         //synchronized private void displayCandidates(WnnEngine converter, boolean dispFirst, int maxLine) {
941         private void displayCandidates(WnnEngine converter, boolean dispFirst, int maxLine) {
942                 if (converter == null) {
943                         return;
944                 }
945                 boolean isBreak = false;
946
947                 /* Get candidates */
948                 WnnWord result = null;
949                 int size = mWnnWordArray.size();
950                 while (mWordCount < size) {
951                         result = mWnnWordArray.get(mWordCount);
952                         setCandidate(result, mWnnWordTextLength.get(mWordCount), mWnnWordOccupyCount.get(mWordCount));
953                         if (dispFirst && (maxLine < mLineCount)) {
954                                 mCanReadMore = true;
955                                 isBreak = true;
956                                 break;
957                         }
958                 }
959
960                 if (!isBreak && !mCreateCandidateDone) {
961                         /* align left if necessary */
962                         createNextLine();
963                         mCreateCandidateDone = true;
964                 }
965         
966                 if (mWordCount < 1) { /* no candidates */
967                         if (mAutoHideMode) {
968                                 mWnn.setCandidatesViewShown(false);
969                                 return;
970                         } else {
971                                 mCanReadMore = false;
972                                 mIsFullView = false;
973                                 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
974                         }
975                 }
976                 if (!(mViewBody.isShown())) {
977                         mWnn.setCandidatesViewShown(true);
978                 }
979                 return;
980         }
981
982         /**
983          * 
984          */
985         private void set1stCandidate(int viewindex, int inid, WnnWord word, int textLength, int occupyCount, LinearLayout lineView) {
986                 TextView textView;
987                 int indentWidth = mViewWidth / FULL_VIEW_DIV;
988                 
989                 //int width = indentWidth * occupyCount;
990                 int width = textLength;
991                 int height = getCandidateMinimumHeight();
992                 boolean iscreate = false;
993                 textView = (TextView) lineView.getChildAt(viewindex);
994                 if (textView == null) {
995                         textView = createCandidateView();
996                         iscreate = true;
997                 }
998                 
999                 int textId = inid + 1;
1000                 textView.setWidth(width);
1001                 textView.setHeight(height);
1002                 textView.setText(word.candidate);
1003                 textView.setTextColor(mTextColor);
1004                 textView.setId(textId);
1005                 textView.setVisibility(View.VISIBLE);
1006                 textView.setPressed(false);
1007                 textView.setEllipsize(TextUtils.TruncateAt.END);
1008
1009                 textView.setOnClickListener(mCandidateOnClick);
1010                 textView.setOnLongClickListener(mCandidateOnLongClick);
1011                 textView.setBackgroundResource(R.drawable.cand_back);
1012
1013                 textView.setOnTouchListener(mCandidateOnTouch);
1014
1015                 checkImageSpan(textView, word);
1016                 if (true == iscreate) {
1017                         lineView.addView(textView);
1018                 }
1019         }
1020         /**
1021          * Add a candidate into the list.
1022          * @param word        A candidate word
1023          */
1024         private void setCandidate(WnnWord word, int textLength, int occupyCount) {
1025                 TextView textView;
1026                 int indentWidth = mViewWidth / FULL_VIEW_DIV;
1027
1028                 RelativeLayout layout = mViewCandidateList2nd;
1029
1030                 int width = indentWidth * occupyCount;
1031                 int height = getCandidateMinimumHeight();
1032                 RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
1033
1034                 if (mFullViewPrevLineTopId == 0) {
1035                         params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
1036                 } else {
1037                         params.addRule(RelativeLayout.BELOW, mFullViewPrevLineTopId);
1038                 }
1039
1040                 if (mFullViewOccupyCount == 0) {
1041                         params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
1042                 } else {
1043                         params.addRule(RelativeLayout.RIGHT_OF, (mWordCount));
1044                 }
1045
1046                 textView = (TextView) layout.getChildAt(mFullViewWordCount);
1047                 if (textView == null) {
1048                         textView = createCandidateView();
1049                         textView.setLayoutParams(params);
1050
1051                         mViewCandidateList2nd.addView(textView);
1052                 } else {
1053                         mViewCandidateList2nd.updateViewLayout(textView, params);
1054                 }
1055                 int textId = mWordCount+1;
1056
1057                 mFullViewOccupyCount += occupyCount;
1058                 mFullViewWordCount++;
1059                 mFullViewPrevView = textView;
1060                 mFullViewPrevParams = params;
1061
1062                 textView.setText(word.candidate);
1063                 textView.setTextColor(mTextColor);
1064                 textView.setId(textId);
1065                 textView.setVisibility(View.VISIBLE);
1066                 textView.setPressed(false);
1067                 textView.setEllipsize(TextUtils.TruncateAt.END);
1068
1069                 textView.setOnClickListener(mCandidateOnClick);
1070                 textView.setOnLongClickListener(mCandidateOnLongClick);
1071                 textView.setBackgroundResource(R.drawable.cand_back);
1072
1073                 textView.setOnTouchListener(mCandidateOnTouch);
1074
1075                 checkImageSpan(textView, word);
1076                 mWordCount++;
1077                 mTotalLastId = textId;
1078
1079                 if (FULL_VIEW_DIV < (mFullViewOccupyCount + occupyCount)) {
1080                         if (FULL_VIEW_DIV != mFullViewOccupyCount) {
1081                                 mFullViewPrevParams.width += (FULL_VIEW_DIV - mFullViewOccupyCount) * indentWidth;
1082                                 mViewCandidateList2nd.updateViewLayout(mFullViewPrevView, mFullViewPrevParams);
1083                         }
1084                         if (getMaxLine() >= mLineCount) {
1085                                 mToplineLastId = textId;
1086                         }
1087                         createNextLine();
1088                 }
1089         }
1090         /*
1091          * check Image Span
1092          */
1093         private void checkImageSpan(TextView textView, WnnWord word) {
1094                 ImageSpan span = null;
1095                 if (word.candidate.equals(" ")) {
1096                         span = new ImageSpan(mWnn, R.drawable.word_half_space, DynamicDrawableSpan.ALIGN_BASELINE);
1097                 } else if (word.candidate.equals("\u3000" /* full-width space */)) {
1098                         span = new ImageSpan(mWnn, R.drawable.word_full_space, DynamicDrawableSpan.ALIGN_BASELINE);
1099                 }
1100                 // check docomo emoji
1101                 Integer getres = getDocomoEmojiRes(word);
1102                 if (null != getres) {
1103                         span = new ImageSpan(mWnn, getres.intValue(), DynamicDrawableSpan.ALIGN_BASELINE);
1104                 }
1105                 if (span != null) {
1106                         SpannableString spannable = new SpannableString("   ");
1107                         spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
1108                         textView.setText(spannable);
1109                 }
1110         }
1111         /**
1112          * Create a view for a candidate.
1113          * @return the view
1114          */
1115         public TextView createCandidateView() {
1116                 TextView text = new TextView(mViewBodyVScroll.getContext());
1117                 text.setTextSize(20);
1118                 text.setBackgroundResource(R.drawable.cand_back);
1119                 text.setGravity(Gravity.CENTER);
1120                 text.setSingleLine();
1121                 text.setPadding(1, 1, 1, 1);
1122                 text.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
1123                                                                                                                    ViewGroup.LayoutParams.WRAP_CONTENT,
1124                                                                                                                    1.0f));
1125                 text.setMinHeight(getCandidateMinimumHeight());
1126                 text.setMinimumWidth(getCandidateMinimumWidth());
1127                 return text;
1128         }
1129
1130     /**
1131      * Clear the list of the normal candidate view.
1132      */
1133     private void clearNormalViewCandidate() {
1134         LinearLayout candidateList = mViewCandidateList1st;
1135         int lineNum = candidateList.getChildCount();
1136         for (int i = 0; i < lineNum; i++) {
1137             LinearLayout lineView = (LinearLayout)candidateList.getChildAt(i);
1138             int size = lineView.getChildCount();
1139             for (int j = 0; j < size; j++) {
1140                 View v = lineView.getChildAt(j);
1141                 v.setVisibility(View.GONE);
1142             }
1143         }
1144     }
1145
1146         /** @see CandidatesViewManager#clearCandidates */
1147         public void clearCandidates() {
1148                 int size = 0;
1149                 checkCandidateTask();
1150         clearNormalViewCandidate();
1151
1152                 RelativeLayout layout2nd = mViewCandidateList2nd;
1153                 size = layout2nd.getChildCount();
1154                 for (int i = 0; i < size; i++) {
1155                         View v = layout2nd.getChildAt(i);
1156                         v.setVisibility(View.GONE);
1157                 }
1158     
1159                 mLineCount = 1;
1160                 mWordCount = 0;
1161                 m1stWordCount = 0;
1162                 mToplineLastId = 0;
1163                 mTotalLastId   = 0;
1164                 mWnnWordArray.clear();
1165                 mWnnWordTextLength.clear();
1166                 mWnnWordOccupyCount.clear();
1167
1168                 mIsFullView = false;
1169                 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
1170                 if (mAutoHideMode) {
1171                         setViewLayout(CandidatesViewManager.VIEW_TYPE_CLOSE);
1172                 }
1173
1174                 if (mAutoHideMode && mViewBody.isShown()) {
1175                         mWnn.setCandidatesViewShown(false);
1176                 }
1177                 mCanReadMore = false;
1178         }
1179         /** clear normalCandidate */
1180         private void clearNormalCandidates() {
1181                 if (false == mIsCreateFullView) {
1182                         return;
1183                 }
1184                 if (mTotalLastId > mToplineLastId) {
1185                         mCanReadMore = true;
1186                         mIsScroll    = false;
1187                 }
1188         }
1189         /** view fullCandidate */
1190         private void visibleFullCandidate() {
1191                 if (false == mIsCreateFullView) {
1192                         return;
1193                 }
1194                 RelativeLayout layout = mViewCandidateList2nd;
1195                 int size = layout.getChildCount();
1196                 if (size > mTotalLastId) {
1197                         size = mTotalLastId;
1198                 }
1199                 for (int i = 0; i < size; i++) {
1200                         View v = layout.getChildAt(i);
1201                         v.setVisibility(View.VISIBLE);
1202                 }
1203                 if (mTotalLastId != 0) {
1204                         mCanReadMore = false;
1205                 }
1206         }
1207         /** @see CandidatesViewManager#setPreferences */
1208         public void setPreferences(SharedPreferences pref) {
1209                 try {
1210                         if (pref.getBoolean("key_vibration", false)) {
1211                                 mVibrator = (Vibrator)mWnn.getSystemService(Context.VIBRATOR_SERVICE);
1212                         } else {
1213                                 mVibrator = null;
1214                         }
1215                         if (pref.getBoolean("key_sound", false)) {
1216                                 mSound = MediaPlayer.create(mWnn, R.raw.type);
1217                         } else {
1218                                 mSound = null;
1219                         }
1220                 } catch (Exception ex) {
1221                         Log.d("iwnn", "NO VIBRATOR");
1222                 }
1223         }
1224     
1225         /**
1226          * Process {@code OpenWnnEvent.CANDIDATE_VIEW_TOUCH} event.
1227          * 
1228          * @return      {@code true} if event is processed; {@code false} if otherwise
1229          */
1230         public boolean onTouchSync() {
1231                 return mGestureDetector.onTouchEvent(mMotionEvent);
1232         }
1233
1234         /**
1235          * Select a candidate.
1236          * <br>
1237          * This method notices the selected word to {@link NicoWnn}.
1238          *
1239          * @param word  The selected word
1240          */
1241         private void selectCandidate(WnnWord word) {
1242                 setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
1243                 if (mVibrator != null) {
1244                         try { mVibrator.vibrate(30); } catch (Exception ex) { }
1245                 }
1246                 if (mSound != null) {
1247                         try { mSound.seekTo(0); mSound.start(); } catch (Exception ex) { }
1248                 }
1249                 mIsLockHScroll = true;
1250                 mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.SELECT_CANDIDATE, word));
1251         }
1252
1253         /** @see android.view.GestureDetector.OnGestureListener#onDown */
1254         public boolean onDown(MotionEvent arg0) {
1255                 return false;
1256         }
1257
1258         /** @see android.view.GestureDetector.OnGestureListener#onFling */
1259         public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
1260                 if (mIsScaleUp) {
1261                         return false;
1262                 }
1263                 boolean consumed = false;
1264                 
1265                 int gety = 0;
1266                 if (arg0 != null && arg1 != null) {
1267                         gety = (int)(arg1.getY() - arg0.getY());
1268                 }
1269                 else{
1270                         gety = (int)arg3;
1271                 }
1272
1273                 //if (arg3 < (float)-(getCandidateMinimumHeight() * 10)) {
1274                 if (gety < -(getCandidateMinimumHeight() * 2)) {
1275                         if ((mViewType == CandidatesViewManager.VIEW_TYPE_NORMAL) && mCanReadMore) {
1276                                 if (mVibrator != null) {
1277                                         try { mVibrator.vibrate(30); } catch (Exception ex) { }
1278                                 }
1279                                 mIsFullView = true;
1280                                 mIsScroll   = false; // reset scroll flag
1281                                 mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.LIST_CANDIDATES_FULL));
1282                                 consumed = true;
1283                         }
1284                 }
1285                 //else if (arg3 > (float)getCandidateMinimumHeight()) {
1286                 else if (gety > (getCandidateMinimumHeight() * 1)) {
1287                         if ((mViewBodyVScroll.getScrollY() == 0) && (true == mIsFullView)) {
1288                                 if (true == mIsScroll) {
1289                                         mIsScroll = false;
1290                                         return false;
1291                                 }
1292                                 if (mVibrator != null) {
1293                                         try { mVibrator.vibrate(30); } catch (Exception ex) { }
1294                                 }
1295                                 mIsFullView = false;
1296                                 mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.LIST_CANDIDATES_NORMAL));
1297                                 consumed = true;
1298                         }
1299                 }
1300                 return consumed;
1301         }
1302
1303         /** @see android.view.GestureDetector.OnGestureListener#onLongPress */
1304         public void onLongPress(MotionEvent arg0) {
1305                 return;
1306         }
1307
1308         /** @see android.view.GestureDetector.OnGestureListener#onScroll */
1309         public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
1310                 if (mViewBodyVScroll.getScrollY() != 0) {
1311                         mIsScroll = true;
1312                 }
1313                 return false;
1314         }
1315         /** @see android.view.GestureDetector.OnGestureListener#onShowPress */
1316         public void onShowPress(MotionEvent arg0) {
1317         }
1318
1319         /** @see android.view.GestureDetector.OnGestureListener#onSingleTapUp */
1320         public boolean onSingleTapUp(MotionEvent arg0) {
1321                 return false;
1322         }
1323     
1324         /**
1325          * Retrieve the width of string to draw.
1326          * 
1327          * @param text          The string
1328          * @param start         The start position (specified by the number of character)
1329          * @param end           The end position (specified by the number of character)
1330          * @return          The width of string to draw
1331          */ 
1332         public int measureText(CharSequence text, int start, int end) {
1333         TextPaint paint = mViewCandidateTemplate.getPaint();
1334         int getwidth = Math.max((int)paint.measureText(text, start, end), CANDIDATE_MINIMUM_WIDTH);
1335         //int getwidth = Math.max((int)(end - start) * 40, CANDIDATE_MINIMUM_WIDTH);
1336                 return getwidth;
1337         }
1338
1339         /**
1340          * Switch list/enlarge view mode.
1341          * @param up  {@code true}:enlarge, {@code false}:list
1342          * @param word  The candidate word to be enlarged.
1343          */
1344         private void setViewScaleUp(boolean up, WnnWord word) {
1345                 if (up == mIsScaleUp || (mViewScaleUp == null)) {
1346                         return;
1347                 }
1348                 if (up) {
1349                         setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
1350                         mViewCandidateList1st.setVisibility(View.GONE);
1351                         mViewCandidateList2nd.setVisibility(View.GONE);
1352                         mViewCandidateBase.setMinimumHeight(-1);
1353                         mViewCandidateBase.addView(mViewScaleUp);
1354                         TextView text = (TextView)mViewScaleUp.findViewById(R.id.candidate_scale_up_text);
1355                         text.setText(word.candidate);
1356                         checkImageSpan(text, word);
1357                         if (!mPortrait) {
1358                                 Resources r = mViewBodyVScroll.getContext().getResources();
1359                                 text.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_delete_word_size_landscape));
1360                         }
1361
1362                         mIsScaleUp = true;
1363                 } else {
1364                         mIsScaleUp = false;
1365                         mViewCandidateBase.removeView(mViewScaleUp);
1366                 }
1367         }
1368
1369         /**
1370          * Create a layout for the next line.
1371          */
1372         private void createNextLine() {
1373                 mFullViewOccupyCount = 0;
1374                 mFullViewPrevLineTopId = mFullViewPrevView.getId();
1375                 mLineCount++;
1376         }
1377
1378         /**
1379          * @return the minimum width of a candidate view.
1380          */
1381         public static int getCandidateMinimumWidth() {
1382         //return (int)(CANDIDATE_MINIMUM_WIDTH * mMetrics.density);
1383         return (int)(CANDIDATE_MINIMUM_WIDTH);
1384         }
1385
1386         /**
1387          * @return the minimum height of a candidate view.
1388          */
1389         public static int getCandidateMinimumHeight() {
1390                 //return (int)((float)candidateViewDataTable[mCandidateViewHeightIndex] * mMetrics.density);
1391                 return (int)((float)candidateViewDataTable[mCandidateViewHeightIndex]);
1392         }
1393
1394         /**
1395          * get docomo-emoji resource
1396          */
1397         private Integer getDocomoEmojiRes(WnnWord word) {
1398                 return DOCOMO_EMOJI_TABLE.get(word.candidate);
1399         }
1400
1401 }