001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package javax.microedition.lcdui;
028:
029: import com.sun.midp.lcdui.PhoneDial;
030:
031: import com.sun.midp.lcdui.DynamicCharacterArray;
032: import com.sun.midp.lcdui.Text;
033: import com.sun.midp.lcdui.TextCursor;
034: import com.sun.midp.lcdui.TextInfo;
035: import com.sun.midp.lcdui.TextPolicy;
036: import com.sun.midp.log.Logging;
037: import com.sun.midp.log.LogChannels;
038:
039: import com.sun.midp.chameleon.CGraphicsUtil;
040: import com.sun.midp.chameleon.input.*;
041: import com.sun.midp.chameleon.skins.*;
042: import com.sun.midp.chameleon.skins.resources.*;
043: import com.sun.midp.chameleon.layers.ScrollBarLayer;
044:
045: /**
046: * This is the look &s; feel implementation for TextBox.
047: */
048: class TextBoxLFImpl extends TextFieldLFImpl implements TextFieldLF {
049:
050: /**
051: * Contains line-break information for a blob of text
052: */
053: protected TextInfo myInfo;
054:
055: /**
056: * A flag indicating the scroll indicator has been initialized
057: * for this textbox. This happens only once when the textbox
058: * first paints its contents.
059: */
060: protected boolean scrollInitialized;
061:
062: /**
063: * Creates TextFieldLF for the passed in TextField.
064: * @param tf The TextField associated with this TextFieldLF
065: */
066: TextBoxLFImpl(TextField tf) {
067: super (tf);
068:
069: if (myInfo == null) {
070: myInfo = new TextInfo(4); // IMPL NOTE: add initial size to skin
071: }
072: drawsTraversalIndicator = false;
073: }
074:
075: // *****************************************************
076: // Public methods defined in interfaces
077: // *****************************************************
078:
079: /**
080: * Notifies L&F of a content change in the corresponding TextBox.
081: */
082: public void lSetChars() {
083: cursor.index = tf.buffer.length(); // cursor at the end
084: cursor.option = Text.PAINT_USE_CURSOR_INDEX;
085:
086: myInfo.scrollY = myInfo.isModified = true;
087: updateTextInfo();
088: }
089:
090: /**
091: * Update text info if required
092: *
093: */
094: private void updateTextInfo() {
095: int w = contentBounds[WIDTH];
096: int h = contentBounds[HEIGHT];
097: // bounds are already initialized
098: if (w > 0 && h > 0) {
099: w -= 2 * TextFieldSkin.BOX_MARGIN + 2 * TextFieldSkin.PAD_H;
100: h -= ((2 * TextFieldSkin.BOX_MARGIN) + (inputModeIndicator
101: .getDisplayMode() != null ? Font.getDefaultFont()
102: .getHeight() : 0));
103: Text.updateTextInfo(tf.buffer.toString(),
104: ScreenSkin.FONT_INPUT_TEXT, w, h, 0, Text.NORMAL,
105: cursor, myInfo);
106: if (setVerticalScroll()) {
107: lRequestInvalidate(true, true);
108: } else {
109: lRequestPaint();
110: }
111: }
112: }
113:
114: /**
115: * Set new cursor position. Update text info if cursor position is changed
116: * @param pos new position
117: */
118: protected void setCaretPosition(int pos) {
119: int oldPos = cursor.index;
120: super .setCaretPosition(pos);
121: cursor.option = Text.PAINT_USE_CURSOR_INDEX;
122: myInfo.isModified = myInfo.scrollY |= (oldPos != cursor.index);
123: updateTextInfo();
124: }
125:
126: /**
127: * Notifies L&s;F of a character insertion in the corresponding
128: * TextBox.
129: * @param data the source of the character data
130: * @param offset the beginning of the region of characters copied
131: * @param length the number of characters copied
132: * @param position the position at which insertion occurred
133: */
134: public void lInsert(char data[], int offset, int length,
135: int position) {
136: if (data != null) {
137: if (editable) {
138: if (position <= cursor.index) {
139: cursor.index += length;
140: cursor.option = Text.PAINT_USE_CURSOR_INDEX;
141: }
142: }
143: myInfo.isModified = myInfo.scrollY = true;
144: updateTextInfo();
145: }
146: }
147:
148: /**
149: * Notifies L&amsp;F of character deletion in the corresponding
150: * TextField.
151: * @param offset the beginning of the deleted region
152: * @param length the number of characters deleted
153: *
154: * @exception IllegalArgumentException if the resulting contents
155: * would be illegal for the current
156: * @exception StringIndexOutOfBoundsException if <code>offset</code>
157: * and <code>length</code> do not
158: * specify a valid range within the contents of the <code>TextField</code>
159: */
160: public void lDelete(int offset, int length) {
161: if (editable) {
162: if (cursor.index >= offset) {
163: int diff = cursor.index - offset;
164: cursor.index -= (diff < length) ? diff : length;
165: cursor.option = Text.PAINT_USE_CURSOR_INDEX;
166: }
167: }
168: myInfo.isModified = myInfo.scrollY = true;
169: updateTextInfo();
170: }
171:
172: /**
173: * Notifies L&s;F of a maximum size change in the corresponding
174: * TextBox.
175: * @param maxSize - the new maximum size
176: */
177: public void lSetMaxSize(int maxSize) {
178: if (editable) {
179: if (cursor.index >= maxSize) {
180: cursor.index = maxSize;
181: cursor.option = Text.PAINT_USE_CURSOR_INDEX;
182: }
183: }
184: myInfo.isModified = myInfo.scrollY = true;
185: updateTextInfo();
186: }
187:
188: /**
189: * Notifies L&s;F that constraints have to be changed.
190: */
191: public void lSetConstraints() {
192: setConstraintsCommon(false);
193:
194: setVerticalScroll();
195:
196: // reset cursor position if needed
197: if (editable && myInfo != null) {
198: int pos = cursor.y / ScreenSkin.FONT_INPUT_TEXT.getHeight();
199: int newPos = pos;
200: if (pos <= myInfo.topVis) {
201: newPos = myInfo.topVis + 1;
202: } else if (pos > myInfo.topVis + myInfo.visLines) {
203: newPos = myInfo.topVis + myInfo.visLines;
204: }
205: if (newPos != pos) {
206: cursor.y = newPos
207: * ScreenSkin.FONT_INPUT_TEXT.getHeight();
208: cursor.option = Text.PAINT_GET_CURSOR_INDEX;
209: myInfo.isModified = myInfo.scrollY = true;
210: updateTextInfo();
211: }
212: }
213:
214: lRequestPaint();
215: }
216:
217: /**
218: * Paint the text, linewrapping when necessary
219: *
220: * @param g the Graphics to use to paint with. If g is null then
221: * only the first four arguments are used and nothing is
222: * painted. Use this to return just the displayed string
223: * @param dca the text to paint
224: * @param opChar if opChar > 0 then an optional character to paint.
225: * @param constraints text constraints
226: * @param font the font to use to paint the text
227: * @param fgColor foreground color
228: * @param w the available width for the text
229: * @param h the available height for the text
230: * @param offset the first line pixel offset
231: * @param options any of Text.[NORMAL | INVERT | HYPERLINK | TRUNCATE]
232: * @param cursor text cursor object to use to draw vertical bar
233: * @param info TextInfo structure to use for paint
234: */
235: public void paint(Graphics g, DynamicCharacterArray dca,
236: char opChar, int constraints, Font font, int fgColor,
237: int w, int h, int offset, int options, TextCursor cursor,
238: TextInfo info) {
239: if (opChar != 0) {
240: cursor = new TextCursor(cursor);
241: info.isModified = true;
242: }
243:
244: String str = getDisplayString(dca, opChar, constraints, cursor,
245: true);
246: info.isModified |= !bufferedTheSameAsDisplayed(tf.constraints);
247:
248: Text.updateTextInfo(str, font, w, h, offset, options, cursor,
249: info);
250:
251: Text.paintText(info, g, str, font, fgColor, 0xffffff - fgColor,
252: w, h, offset, options, cursor);
253:
254: // just correct cursor index if the charracter has
255: // been already committed
256: if (str != null && str.length() > 0) {
257: getBufferString(new DynamicCharacterArray(str),
258: constraints, cursor, true);
259: }
260:
261: // We'll double check our anchor point in case the Form
262: // has scrolled and we need to update our InputModeLayer's
263: // location on the screen
264: if (hasFocus) {
265: moveInputModeIndicator();
266: }
267:
268: // has to be moved to correct place. It's incorrect to change
269: // the layer's dirty bounds in paint context
270: showPTPopup((int) 0, cursor, w, h);
271: }
272:
273: /**
274: * Sets the content size in the passed in array.
275: * Content is calculated based on the availableWidth.
276: * size[WIDTH] and size[HEIGHT] should be set by this method.
277: * @param size The array that holds Item content size and location
278: * in Item internal bounds coordinate system.
279: * @param availableWidth The width available for this Item
280: */
281: void lGetContentSize(int size[], int availableWidth) {
282: int oldWidth = size[WIDTH];
283: int oldHeight = size[HEIGHT];
284: try {
285: // We size to the maximum allowed, minus the padding
286: // defined in the skin.
287: size[WIDTH] = ((DisplayableLFImpl) tf.owner.getLF())
288: .getDisplayableWidth()
289: - 2 * TextFieldSkin.BOX_MARGIN;
290:
291: // Note: tf.owner is the original TextBox for this LFImpl
292: size[HEIGHT] = ((DisplayableLFImpl) tf.owner.getLF())
293: .getDisplayableHeight()
294: - 2 * TextFieldSkin.BOX_MARGIN;
295: } catch (Throwable t) {
296: // NOTE: the above call to getCurrent() will size the textbox
297: // appropriately if there is a title, ticker, etc. Calling
298: // this method depends on the textbox being current however.
299: size[WIDTH] = 100;
300: size[HEIGHT] = 100;
301: // IMPL NOTE: Log this as an error
302: }
303: if (oldHeight != size[HEIGHT] || oldWidth != size[WIDTH]) {
304: myInfo.scrollY = myInfo.isModified = true;
305: updateTextInfo();
306: }
307: }
308:
309: /**
310: * Paints the content area of this TextField.
311: * Graphics is translated to contents origin.
312: * @param g The graphics where Item content should be painted
313: * @param width The width available for the Item's content
314: * @param height The height available for the Item's content
315: */
316: void lPaintContent(Graphics g, int width, int height) {
317: g.translate(TextFieldSkin.BOX_MARGIN, TextFieldSkin.BOX_MARGIN);
318: width -= (2 * TextFieldSkin.BOX_MARGIN);
319: height -= ((2 * TextFieldSkin.BOX_MARGIN) + (inputModeIndicator
320: .getDisplayMode() != null ? Font.getDefaultFont()
321: .getHeight() : 0));
322:
323: if (editable) {
324: if (TextFieldSkin.IMAGE_BG != null) {
325: CGraphicsUtil.draw9pcsBackground(g, 0, 0, width,
326: height, TextFieldSkin.IMAGE_BG);
327: } else {
328: CGraphicsUtil.drawDropShadowBox(g, 0, 0, width, height,
329: TextFieldSkin.COLOR_BORDER,
330: TextFieldSkin.COLOR_BORDER_SHD,
331: TextFieldSkin.COLOR_BG);
332: }
333: } else {
334: if (TextFieldSkin.IMAGE_BG_UE != null) {
335: CGraphicsUtil.draw9pcsBackground(g, 0, 0, width,
336: height, TextFieldSkin.IMAGE_BG_UE);
337: } else {
338: CGraphicsUtil.drawDropShadowBox(g, 0, 0, width, height,
339: TextFieldSkin.COLOR_BORDER_UE,
340: TextFieldSkin.COLOR_BORDER_SHD_UE,
341: TextFieldSkin.COLOR_BG_UE);
342: }
343: }
344:
345: // We need to translate by 1 more pixel horizontally
346: // to reserve space for cursor in the empty textfield
347: g.translate(TextFieldSkin.PAD_H + 1, TextFieldSkin.PAD_V);
348:
349: paint(g, tf.buffer, inputSession.getPendingChar(),
350: tf.constraints, ScreenSkin.FONT_INPUT_TEXT,
351: (editable ? TextFieldSkin.COLOR_FG
352: : TextFieldSkin.COLOR_FG_UE), width
353: - (2 * (TextFieldSkin.PAD_H)), height, 0,
354: Text.NORMAL, cursor, myInfo);
355:
356: if (!scrollInitialized) {
357: setVerticalScroll();
358: scrollInitialized = true;
359: }
360:
361: g.translate(-(TextFieldSkin.PAD_H + 1), -(TextFieldSkin.PAD_V));
362:
363: if (usePreferredX) {
364: cursor.preferredX = cursor.x
365: + (myInfo.lineStart[myInfo.cursorLine] == cursor.index ? ScreenSkin.FONT_INPUT_TEXT
366: .charWidth(tf.buffer.charAt(cursor.index))
367: : 0);
368: }
369:
370: g.translate(-TextFieldSkin.BOX_MARGIN,
371: -TextFieldSkin.BOX_MARGIN);
372: }
373:
374: /**
375: * Get character index at the pointer position
376: *
377: * @param x pointer x coordinate
378: * @param y pointer y coordinate
379: * @return the character index
380: */
381: protected int getIndexAt(int x, int y) {
382: x -= contentBounds[X] + TextFieldSkin.BOX_MARGIN
383: + TextFieldSkin.PAD_H;
384: y -= contentBounds[Y] + TextFieldSkin.BOX_MARGIN
385: + TextFieldSkin.PAD_V;
386: int id = -1;
387: // the pointer is inside of the content
388: if (x >= 0 && y >= 0) {
389:
390: int numLines = myInfo.topVis + y
391: / ScreenSkin.FONT_INPUT_TEXT.getHeight();
392: id = tf.buffer.length();
393:
394: // the cursor has to be moved to the symbol the pointer is clicked at
395: // if pointer is out of text just move the cursor at the last text position
396: // if pointer is out of line just move the cursor at the last line position
397: if (numLines < myInfo.numLines) {
398: char[] data = tf.buffer.toCharArray();
399: int i = 1;
400: int startId = myInfo.lineStart[numLines];
401: for (; i <= myInfo.lineEnd[numLines] - startId; i++) {
402: if (x <= ScreenSkin.FONT_INPUT_TEXT.charsWidth(
403: data, startId, i)) {
404: break;
405: }
406: }
407: id = startId + i - 1;
408: }
409: }
410: return id;
411: }
412:
413: /**
414: * Used internally to set the vertical scroll position
415: */
416: boolean setVerticalScroll() {
417: ScreenLFImpl lf = null;
418: if (tf != null && tf.owner != null
419: && (lf = (ScreenLFImpl) tf.owner.getLF()) != null
420: && myInfo != null) {
421: return lf.setVerticalScroll(myInfo.getScrollPosition(),
422: myInfo.getScrollProportion());
423: }
424: return false;
425: }
426:
427: /**
428: * Scroll content inside of the form.
429: * @param scrollType scrollType. Scroll type can be one of the following
430: * @see ScrollBarLayer.SCROLL_NONE
431: * @see ScrollBarLayer.SCROLL_PAGEUP
432: * @see ScrollBarLayer.SCROLL_PAGEDOWN
433: * @see ScrollBarLayer.SCROLL_LINEUP
434: * @see ScrollBarLayer.SCROLL_LINEDOWN or
435: * @see ScrollBarLayer.SCROLL_THUMBTRACK
436: * @param thumbPosition
437: */
438: void uCallScrollContent(int scrollType, int thumbPosition) {
439: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
440: Logging.report(Logging.INFORMATION, LogChannels.LC_HIGHUI,
441: "TextBoxLFImpl.uCallScrollContent scrollType="
442: + scrollType + " thumbPosition="
443: + thumbPosition);
444: }
445:
446: switch (scrollType) {
447: case ScrollBarLayer.SCROLL_PAGEUP:
448: uScrollViewport(Canvas.UP);
449: break;
450: case ScrollBarLayer.SCROLL_PAGEDOWN:
451: uScrollViewport(Canvas.DOWN);
452: break;
453: case ScrollBarLayer.SCROLL_LINEUP:
454: uScrollByLine(Canvas.UP);
455: break;
456: case ScrollBarLayer.SCROLL_LINEDOWN:
457: uScrollByLine(Canvas.DOWN);
458: break;
459: case ScrollBarLayer.SCROLL_THUMBTRACK:
460: uScrollAt(thumbPosition);
461: break;
462: default:
463: break;
464: }
465: }
466:
467: /**
468: * Perform a page flip in the given direction. This method will
469: * attempt to scroll the view to show as much of the next page
470: * as possible. It uses the locations and bounds of the items on
471: * the page to best determine a new location - taking into account
472: * items which may lie on page boundaries as well as items which
473: * may span several pages.
474: *
475: * @param dir the direction of the flip, either DOWN or UP
476: */
477: protected void uScrollViewport(int dir) {
478: int lines = myInfo
479: .scrollByPage(dir == Canvas.UP ? TextInfo.BACK
480: : TextInfo.FORWARD);
481: if (lines != 0) {
482: if (editable) {
483: cursor.y += ScreenSkin.FONT_INPUT_TEXT.getHeight()
484: * lines;
485: cursor.option = Text.PAINT_GET_CURSOR_INDEX;
486: }
487: updateTextInfo();
488: }
489: }
490:
491: /**
492: * Perform a line scrolling in the given direction. This method will
493: * attempt to scroll the view to show next/previous line.
494: *
495: * @param dir the direction of the flip, either DOWN or UP
496: */
497: protected void uScrollByLine(int dir) {
498: int oldTopVis = myInfo.topVis;
499: if (myInfo.scroll(dir == Canvas.UP ? TextInfo.BACK
500: : TextInfo.FORWARD)) {
501: if (editable) {
502: cursor.y += (myInfo.topVis - oldTopVis)
503: * ScreenSkin.FONT_INPUT_TEXT.getHeight();
504: cursor.option = Text.PAINT_GET_CURSOR_INDEX;
505: }
506: updateTextInfo();
507: }
508: }
509:
510: /**
511: * Perform a scrolling at the given position.
512: * @param context position
513: */
514: protected void uScrollAt(int position) {
515: int oldTopVis = myInfo.topVis;
516: myInfo.topVis = ((myInfo.height - myInfo.visLines
517: * ScreenSkin.FONT_INPUT_TEXT.getHeight())
518: * position / 100)
519: / ScreenSkin.FONT_INPUT_TEXT.getHeight();
520:
521: if (myInfo.topVis < 0) {
522: myInfo.topVis = 0;
523: } else if (myInfo.topVis - myInfo.visLines > myInfo.numLines) {
524: myInfo.topVis = myInfo.numLines - myInfo.visLines;
525: }
526:
527: if (myInfo.topVis != oldTopVis) {
528: if (editable) {
529: cursor.y += (myInfo.topVis - oldTopVis)
530: * ScreenSkin.FONT_INPUT_TEXT.getHeight();
531: cursor.option = Text.PAINT_GET_CURSOR_INDEX;
532: }
533: myInfo.isModified = myInfo.scrollY = true;
534: updateTextInfo();
535: }
536: }
537:
538: /**
539: * Move the text cursor in the given direction
540: *
541: * @param dir direction to move
542: * @return true if the cursor was moved, false otherwise
543: */
544: boolean moveCursor(int dir) {
545:
546: boolean keyUsed = false;
547:
548: switch (dir) {
549:
550: case Canvas.LEFT:
551: if (editable) {
552: keyClicked(dir);
553: if (cursor.index > 0) {
554: cursor.index--;
555: cursor.option = Text.PAINT_USE_CURSOR_INDEX;
556: myInfo.isModified = myInfo.scrollX = keyUsed = true;
557: }
558: } else {
559: keyUsed = myInfo.scroll(TextInfo.BACK);
560: }
561: break;
562:
563: case Canvas.RIGHT:
564: if (editable) {
565: keyClicked(dir);
566: if (cursor.index < tf.buffer.length()) {
567: cursor.index++;
568: cursor.option = Text.PAINT_USE_CURSOR_INDEX;
569: myInfo.isModified = myInfo.scrollX = keyUsed = true;
570: }
571: } else {
572: keyUsed = myInfo.scroll(TextInfo.FORWARD);
573: }
574: break;
575:
576: case Canvas.UP:
577: if (editable) {
578: keyClicked(dir);
579: cursor.y -= ScreenSkin.FONT_INPUT_TEXT.getHeight();
580: if (cursor.y > 0) {
581: cursor.option = Text.PAINT_GET_CURSOR_INDEX;
582: myInfo.isModified = myInfo.scrollY = keyUsed = true;
583: } else {
584: cursor.y += ScreenSkin.FONT_INPUT_TEXT.getHeight();
585: }
586: } else {
587: keyUsed = myInfo.scroll(TextInfo.BACK);
588: }
589: break;
590:
591: case Canvas.DOWN:
592: if (editable) {
593: keyClicked(dir);
594: cursor.y += ScreenSkin.FONT_INPUT_TEXT.getHeight();
595: if (cursor.y <= myInfo.height) {
596: cursor.option = Text.PAINT_GET_CURSOR_INDEX;
597: myInfo.isModified = myInfo.scrollY = keyUsed = true;
598: } else {
599: cursor.y -= ScreenSkin.FONT_INPUT_TEXT.getHeight();
600: }
601: } else {
602: keyUsed = myInfo.scroll(TextInfo.FORWARD);
603: }
604: break;
605: default:
606: // no-op
607: break;
608: }
609:
610: updateTextInfo();
611:
612: return keyUsed;
613: }
614:
615: /**
616: * Called by the system to notify this Item it is being shown
617: *
618: * <p>The default implementation of this method updates
619: * the 'visible' state
620: */
621: void lCallShowNotify() {
622: super .lCallShowNotify();
623: this .scrollInitialized = false;
624: }
625:
626: /**
627: * This is a utility function to calculate the anchor point
628: * for the InputModeIndicator layer. Override TextFieldLFImpl
629: * version for effeciency.
630: * @return anchor (x, y, w, h)
631: */
632: protected int[] getInputModeAnchor() {
633: ScreenLFImpl sLF = (ScreenLFImpl) tf.owner.getLF();
634:
635: int space = TextFieldSkin.BOX_MARGIN
636: + Font.getDefaultFont().getHeight();
637:
638: return new int[] {
639: sLF.viewport[WIDTH]
640: - TextFieldSkin.BOX_MARGIN
641: - 4
642: + getCurrentDisplay().getWindow()
643: .getBodyAnchorX(),
644: getCurrentDisplay().getWindow().getBodyAnchorY(),
645: sLF.viewport[HEIGHT] - space - 4, space };
646: }
647:
648: }
|