Skip to main content

Implementing Copy and Paste for the Java ME TextBox

January 18, 2007

{cs.r.title}






One of the most convenient and frequently used features of
desktop-based text processing applications is copy (or
cut) and paste. This is a feature that would be
especially useful in "http://java.sun.com/javame/index.jsp">Java ME applications
because text entry can be quite tedious on a mobile phone. The text-handling classes in ME do not provide direct support for copy and
paste, but it is not very difficult to build up such a capability.
In this article we look at one approach to implementing this
feature.

I assume that the reader is familiar with ME and, in order to
try out the examples described here, that she has the "http://java.sun.com/products/sjwtoolkit/">J2ME Wireless Toolkit
2.2
installed on her computer.

The Basic Technique

The copy action has two components:

Similarly, paste needs to do the following:

  • Get what is saved on the clipboard.
  • Insert it at the desired position.

Additionally, of course, supporting activities like highlighting
the selected text and undoing a paste are also needed. These
functionalities are required in the case of a Java ME
implementation too. Before we look at how we are going to achieve
our goal, however, let us examine how we perform the basic tasks on
a desktop.

In order to select a part of the text, the mouse is clicked on
the left of the first character to be selected and then the mouse
is dragged (i.e., moved while the left button is held down) up to
the right of the last character for selection. While the dragging
is being done, the part of the text being selected gets
highlighted. The selection can also be done backwards; that is,
from the right of the last character to the left of the first. Once
selection is done, we activate the copy command (from a menu, for
example, or by typing Ctrl-C). Similarly, for pasting, we position
the caret at the point of insertion and choose the paste
command.

Now we can look at the mobile approach. A Java ME device cannot
be expected to have a mouse. So we have to move the caret by using
arrow keys. As in the case of a desktop, we position the caret at
one end of the text to be selected. However, no dragging action is
possible which means we have to use a command to start the
selection process. Then we have to move the caret to the other end
of the text we want to select and, again, use a command to signal
to the application that the selection process is over. Now the
application has to show us what has been selected and this can be
done by showing the selected portion on a screen. Once we signal,
through an appropriate command, that the selection is right, the
selected text can be saved in a record store.

Pasting is even simpler. All we need to do is position the caret
where we want the saved text to be pasted and select the Paste
command. This will cause the contents of the record store to be
retrieved and inserted at the right place.

The Problem

Implementing the simple activities described above does not
raise any theoretical problem. In practice though, things are
different. Generally speaking, the textbox from which something has
to be copied will have a number of associated commands. When we
want to start copying, therefore, we'll have to pop up a menu, and
therein lies our problem. To see actually what happens on some
devices let's run a simple MIDlet--TryCursor--on
WTK.

This MIDlet has just one screen with an Exit command and
three others labeled One, Two and Three. The last
three commands display an alert that shows the command selected
and the caret position as read by the getCaretPosition
method of the TextBox class, which is executed in the
commandAction method. So let us key in some text and
position the caret at any place within the text, as shown in as
shown in Figure 1.

<br "Caret positioned at index 14" />
Figure 1. Caret positioned at index 14

If we now select the Menu option, a pop-up menu will appear. We
can see this in Figure 2. Note that the caret is still at index
14.

Menu pops up
Figure 2. Menu pops up

Now select one of the three commands on the menu. We would expect
the alert to tell us where the caret had been positioned. But, as
Figure 3 shows, that doesn't happen; the caret position sensed
corresponds to the end of the text.

<br "Caret position read as end of text string" />
Figure 3. Caret position read as end of text string

Try this out with the caret at various positions and you'll see
that no matter where you position the caret, the
getCaretPosition method always returns the index of
the last position of the string! So what do we do?

The Solution

The QwertyDevice on the WTK offers us a clue. When we run
TryCursor on QwertyDevice, the command One is
shown on the screen while the other two can be called up by
clicking the Menu button. If One is selected, then the
application behaves the way we expect it to, and the alert shows
the correct caret position. But if we click on Menu and select one
of the other commands the problem recurs. What does that tell us?
When a pop-up menu hides a part (or all) of the screen, then the
screen has to be redrawn afterwards. It appears that, during the
process of redrawing the screen, the position of the caret (14 in
the above example) is not taken into account; the screen is redrawn
with the caret at the end of the text and then the position is
read. So as long as we can work without having to pop up a menu
we're safe.

The implication here is that the commands required for copy and
paste actions must always be on the screen. Then we won't need a
pop-up menu for copy/paste operations. In general, of course, this will
be difficult to achieve, as our screen will require a number of
other commands and we cannot always guarantee that the command
required next for copy (or paste) operation will be on the screen.
While this may be achieved by constantly removing a used command
and adding the one that will required next (so that we always have
a small number of active commands), in a generalized scenario this
will mean a lot of juggling with commands and will add to the
burden of the programmer writing the application. Even with all
that effort, we may not succeed in eliminating a pop-up when we want
to.

We therefore propose to create a dedicated set of screens that
will be invoked only for copying and pasting. For example, the copy
command will display a screen with the text we have entered and
a maximum of two commands so that the commands are available
onscreen and a pop-up menu will not be required. A similar thing will
happen for pasting. This approach has three benefits:

  • The classes can be programmed, tested, and debugged, creating reusable code--something like a CopyNPaste API.
  • As the classes are dedicated ones, they will guarantee that the
    required commands will always be onscreen.
  • The application programmer will need to write only a small
    amount of glue code and invoke the canned classes as
    needed.

The Classes

Our "API" is composed of the following classes:

  • ClipboardCompatible: This is an abstract class
    that must be subclassed by all text-handling classes that use the
    Copy and Paste functions.
  • Scratchpad: This class manages all the
    activities required for copy and paste operations. It has a private
    constructor and cannot be instantiated with the new
    statement. There are two static methods that can be used to obtain
    instances of Copyboard (the dedicated screen for copying) and
    Pasteboard (the dedicated screen for pasting).
  • ClipboardCommandProcessor: This class handles
    all commands related to selection, copying, saving, retrieving, and
    pasting.
  • ClipboardHandler: This class handles
    interactions with the clipboard. That is, it saves selected text
    and retrieves whatever is saved on the clipboard.

In addition to the above, we need the Clipboard
MIDlet to initialize the clipboard. The Clipboard MIDlet is very
simple; its only function is to create a record store named
clipboard:


clipboard = RecordStore.openRecordStore("clipboard", true, 
            RecordStore.AUTHMODE_ANY, true);

MIDP 2.0 permits sharing of record stores among MIDlets
belonging to different suites, provided such universal access is
authorized at the time of creation of a record store. As shown above,
the clipboard record store is created in this manner by
specifying RecordStore.AUTHMODE_ANY in the parameter
list of the openRecordStore method. This record store
can now be opened from any MIDlet, provided its full name is used.
We shall see how to do this in the next section.

Once the record store has been created, we can test our copy and
paste application. To do that, we create two MIDlet suites:
ClipDemo1 and ClipDemo2. Actually, the two
are identical except for the names of the respective MIDlet
classes. This difference has been introduced to underline the fact
that the clipboard can be used from any MIDlet.

In both the demos, the MIDlets (SourceScreen and
SourceScreen2) are just entry points. They instantiate
the ClipboardDemo class and display it. The four
classes mentioned above are present in both the demos and the
ClipboardDemo class in each case extends the
ClipboardCompatible class, which is really a customized
TextBox. The assumption here is that the text to be
copied from or pasted into will be entered into a subclass of
TextBox. ClipboardCompatible contains all
the items essential for implementing our copy-and-paste function
such as the commands and their listeners. It also declares, creates,
and uses two instances of the Scratchpad class--Copyboard, to be used for copying, and
Pasteboard, to be used for pasting. There are two
abstract methods whose implementations are left unspecified to meet
any special requirement of the text-handling application. Finally,
it has a concrete method, clearClipboard, for deleting
the contents of clipboard.

The ClipboardDemo class is the one that does the
actual text handling and wants to incorporate the copy-and-paste
functionality. We can see that all this class needs to do is take
care of its own functional requirements. As for copy-and-paste
actions, it only has to implement the two abstract methods. Since
ClipboardCompatible implements
CommandListener, its subclass
(ClipboardDemo) doesn't have to explicitly do that
again. In the commandAction method,
ClipboardDemo handles only the command it has added
and invokes the same method in its superclass for Copy and
Paste commands.

The real actions for copy and paste functions take place within
the Scratchpad class working in conjunction with the
ClipboardCommandProcessor--which handles all
commands associated with the scratchpad instances--and the
ClipboardHandler that provides the interface to the
clipboard. Copyboard and Pasteboard
provide the necessary sequencing of commands making sure that there
are never more than two commands on the user interface.







Testing the Action

Let us now see how the copy and paste functions work. For
copying text, we need to select the Copy command on
ClipboardDemo (see Figure 4).

<br "ClipboardDemo screen with text and pop-up menu" />
Figure 4. ClipboardDemo screen with text and pop-up
menu

This displays the Copyboard, which has the text that
was on ClipboardDemo. This can be seen in Figure 5.
Note that the command that we'll need next--"Start
selection"--is available onscreen.

<br "Copyboard with the contents of ClipboardDemo" />
Figure 5. Copyboard with the contents of
ClipboardDemo

Next we need to move the caret to the left of the first
character of the part to be copied and select the "Start
selection" command, as shown in Figure 6.

<br "Start selecting text to be copied" />
Figure 6. Start selecting text to be copied

We can see the result in Figure 7. This command is processed by
ClipboardCommandProcessor and we see an alert that
tells us where the start position is.

<br "Alert showing where selection has been started" />
Figure 7. Alert showing where selection has been
started

The alert is displayed for two seconds, after which
Copyboard is brought to the foreground. At this time,
the "Start selection" command is removed from the screen and
the Copy command takes its place. This is illustrated in
Figure 8.

<br "Copyboard after starting selection" />
Figure 8. Copyboard after starting
selection

Then we have to position the caret at the right of the last
character to be copied and select the Copy command. Again
ClipboardCommandProcessor takes over and shows the
string that has been selected. But before that, it checks to see if the
selection was done backwards and adjusts the copying action
accordingly:


if(endposition&lt;startposition)
{
    //selection has been made backwards
    //so simply interchange start and end positions
    int temp = endposition;
    endposition = startposition;
    startposition = temp;
}
                
//make the selection 
displaystring = text.substring(startposition, endposition);

//show the selected string
selection.setText(displaystring);
CopyForm.setTitle("Selected text");
CopyForm.addCommand(CMD_COPY);
display.setCurrent(CopyForm);

Figure 9 shows the screen for displaying the selected text.

<br "Screen showing the selected text" />
Figure 9. Screen showing the selected text

Once we approve the text selection through the OK
command, the saveCopiedInfo method of
ClipboardHandler class is invoked to save the copied
text into clipboard. Of course, we have to the option to
cancel the copying process at each step.

Similarly, we initiate pasting by selecting the Paste
command on ClipboardDemo. This brings up the
Pasteboard with the contents of
ClipboardDemo. Again we position the caret where we
want the saved text to be pasted and select Paste (Figure
10).

<br "Caret positioned for pasting" />
Figure 10. Caret positioned for pasting

We get an alert to inform us that the pasting operation has been
done and then we see the result as in Figure 11. At this point, we
can either accept or reject the pasting action.

Pasteboard after pasting
Figure 11. Pasteboard after pasting

The one thing that we have not talked about is how the demo
MIDlets get to access the common clipboard. The trick lies in
knowing how to address this common record store. We have seen
earlier how clipboard was created with universal access
authorization by the Clipboard MIDlet. While setting
up the Clipboard project on WTK, I had entered the
name of a fictitious vendor in the required field of project
settings. Record store names are internally constructed from the
name of the MIDlet suite that create them, the name of the vendor of
the MIDlet, and the name given to the record store by the MIDlet. So
the full name of clipboard is a combination of "clipboard"
(record store name), "V. Endor" (vendor name), and "Clipboard" (with
a capital "C"--the name of the creator). Any other MIDlet can access
clipboard if all these parameters are specified. This can be
seen in the openRecord method of
ClipboardHandler:


private void openRecord() throws Exception
{
    clipboard = RecordStore.openRecordStore("clipboard",
 "V. Endor", "Clipboard");
    //clipboard = RecordStore.openRecordStore("newclipboard",
 true);
}

Note that the significance of the commented-out line of code is
explained in the
concluding section of this article.

There is one feature that is definitely desirable, if not
essential: clearing clipboard. In desktop applications, this
usually happens automatically when we close the source.
Applications on mobile phones, however, run one at a time, which
means the source must always be closed before the destination can
be opened. So automatic deletion is not a good option here. On the
other hand, the user may not like to leave the copied matter on the
clipboard if the content is confidential. That is why we display a
reminder alert when the Exit command is selected. If the
user doesn't want to clear the clipboard, the application is closed. If,
on the other hand, she decides to delete whatever had been saved,
the clearClipboard method of
ClipboardCompatible is called. This call ripples
through to ClipboardHandler, which saves an empty
string thus deleting whatever was on clipboard. The
application is then closed.

With Clipboard, ClipDemo1, and
ClipDemo2 installed on a phone, you have to run
Clipboard first to create clipboard. Thereafter, you
can exchange text between the two demo applications by copying from
one and pasting into the other.

One Final Issue

A close look at Scratchpad and
ClipboardCommandProcessor will reveal that as a
command gets removed and another is added to the instances of
Scratchpad, there is always another screen that is
sandwiched between the two actions. For example, when we select the
starting position on Copyboard, the "Start
selection" command is removed, the Copy command is added,
and an alert is shown for two seconds, after which the
Copyboard is displayed once more. While these
intervening displays keep the user informed of what is happening,
they do serve another purpose as well.

When a command is added to a Displayable that is
"actually visible on the display, and this call affects the set of
visible commands, the implementation should update the display as
soon as it is feasible to do so." The quote is from "http://java.sun.com/javame/reference/apis/jsr118/">ME
documentation
. In some devices the visual updating does not
occur, while in some of them the added command does not become
functional until the screen is moved to background and then made
visible again. Thus the alerts and other screens that keep popping
up during copying and pasting ensure smooth functioning of newly
added commands by temporarily hiding Copyboard and
Pasteboard.

Conclusion

We have seen how we can incorporate copy and paste functions
into an ME TextBox. Along the way, we have also seen
that ME implementations can vary subtly but significantly from one
device to another and considerable effort is required to write,
test, and debug applications that work on a wide range of devices.
The application described here has been tested on the WTK22 devices
(with the modification mentioned below), and also on a Nokia 6101
and on a Samsung 309. On WTK, the universal access mechanism for
record stores does not seem to work. So if you want to test the
basic performance on WTK, you'll need to comment out the first line
of the openRecord method of
ClipboardHandler shown above and uncomment the second.
However, in that case, each MIDlet will have its own clipboard and
copying from one to the other will not be possible. On the Nokia
and Samsung phones the application works just as expected.

Along with Copy and Paste, there is another function that is used
widely--Cut. This third function is actually a combination
of Copy and Delete. I have not shown its implementation here
because I believe it'll be an enjoyable experience for you to
enhance this application by incorporating this missing
function.

Resources

width="1" height="1" border="0" alt=" " />
Biswajit Sarkar is an electrical engineer with specialization in Programmable Industrial Automation. Biswajit is the author of "LWUIT 1.1 for Java ME Developers" published by PACKT Publishing.
Related Topics >> Mobility   |