File: ExampleInstantMessenger.java

package info (click to toggle)
java-gnome 4.1.3-10
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid
  • size: 9,840 kB
  • sloc: java: 27,002; ansic: 4,517; perl: 1,651; python: 1,187; makefile: 136
file content (394 lines) | stat: -rw-r--r-- 12,521 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
/*
 * java-gnome, a UI library for writing GTK and GNOME programs from Java!
 *
 * Copyright © 2008-2010 Operational Dynamics Consulting, Pty Ltd and Others
 *
 * The code in this file, and the program it is a part of, is made available
 * to you by its authors as open source software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License version
 * 2 ("GPL") as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details.
 *
 * You should have received a copy of the GPL along with this program. If not,
 * see http://www.gnu.org/licenses/. The authors of this program may be
 * contacted through http://java-gnome.sourceforge.net/.
 */
package textview;

/*
 * The smiley image used in this program is from the Tango Icon Theme, whose
 * authors make available under the Creative Commons Attribution Share-Alike
 * licence version 2.5. See http://tango-project.org/ for more details.
 */

import java.io.FileNotFoundException;

import org.gnome.gdk.Event;
import org.gnome.gdk.EventKey;
import org.gnome.gdk.Keyval;
import org.gnome.gdk.Pixbuf;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.IconSize;
import org.gnome.gtk.ScrolledWindow;
import org.gnome.gtk.Stock;
import org.gnome.gtk.TextBuffer;
import org.gnome.gtk.TextIter;
import org.gnome.gtk.TextTag;
import org.gnome.gtk.TextView;
import org.gnome.gtk.VBox;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.pango.Style;
import org.gnome.pango.Weight;

import static org.freedesktop.bindings.Time.formatTime;
import static org.gnome.gtk.PolicyType.ALWAYS;
import static org.gnome.gtk.PolicyType.NEVER;
import static org.gnome.gtk.ShadowType.IN;
import static org.gnome.gtk.WrapMode.NONE;
import static org.gnome.gtk.WrapMode.WORD;

/**
 * An example of rendering multi-line text in an application.
 * 
 * There comes a point when it is difficult to demonstrate complex
 * functionality in a trivial example, so so illustrate usage of the powerful
 * TextView/TextBuffer APIs, we so we have opted to do something a bit more
 * involved.
 * 
 * This program creates the conversation window you might see in a typical
 * graphical instant messenger. It has a TextView displaying the conversation
 * above, and uses various formatting to differentiate between incoming
 * messages and outgoing ones. There is a second TextView at the bottom where
 * the user can write the messages and "send" them. Finally, a tiny worker
 * thread is kicked off to simulate incoming conversation.
 * 
 * Enjoy!
 * 
 * @author Andrew Cowie
 * @author Stefan Prelle
 * @author Serkan Kaba
 */
public class ExampleInstantMessenger
{
    private final TextBuffer buffer;

    private final TextView incoming;

    private final TextView outgoing;

    private final Pixbuf smiley;

    private final TextTag grey, italics, blue;

    private ExampleInstantMessenger() {
        final Window window;
        final VBox top;
        final ScrolledWindow scroll1, scroll2;
        final Thread other;
        Pixbuf tmp;

        /*
         * Start with the usual establishment of a Window to contain the
         * example.
         */

        window = new Window();
        window.setTitle("Instant Messaging");
        window.setDefaultSize(300, 200);

        top = new VBox(false, 3);
        window.add(top);

        window.connect(new Window.DeleteEvent() {
            public boolean onDeleteEvent(Widget source, Event event) {
                Gtk.mainQuit();
                return false;
            }
        });

        /*
         * Create a TextView which will display incoming text messages (and
         * also echo messages as they are sent). It is set up to be read only
         * and to not have a cursor, thereby conveying the impression that it
         * is just a display (a cursor especially would suggest that the text
         * there can be changed).
         */

        buffer = new TextBuffer();

        incoming = new TextView(buffer);
        incoming.setEditable(false);
        incoming.setCursorVisible(false);
        incoming.setPaddingBelowParagraph(10);

        /*
         * We want word wrapping, otherwise messages wider that the screen
         * width will be truncated. We also need to set up vertical scrolling
         * so that as the conversation continues it won't be inaccessible off
         * the bottom of the screen.
         */

        incoming.setWrapMode(WORD);

        scroll1 = new ScrolledWindow();
        scroll1.setPolicy(NEVER, ALWAYS);
        scroll1.setShadowType(IN);
        scroll1.add(incoming);

        top.packStart(scroll1, true, true, 0);

        /*
         * Create the place for the user to enter messages they want to send.
         * 
         * The interesting part here is that when the user presses Enter in
         * the TextView it "sends" a message and appends it to the log in the
         * incoming TextView.
         */

        outgoing = new TextView();
        outgoing.setSizeRequest(0, 20);
        outgoing.setAcceptsTab(false);
        outgoing.setWrapMode(NONE);

        scroll2 = new ScrolledWindow();
        scroll2.setPolicy(NEVER, NEVER);
        scroll2.setShadowType(IN);
        scroll2.add(outgoing);

        top.packStart(scroll2, false, false, 0);

        outgoing.connect(new Widget.KeyPressEvent() {
            public boolean onKeyPressEvent(Widget source, EventKey event) {
                if (event.getKeyval() == Keyval.Return) {
                    final TextBuffer buffer;
                    final String str;

                    buffer = outgoing.getBuffer();
                    str = buffer.getText();

                    /*
                     * Append the text in the TextView to the TextBuffer
                     * backing the incoming display.
                     */

                    appendMessage(str, true);

                    /*
                     * But now clear the TextView so that we can enter another
                     * message.
                     */

                    buffer.setText("");

                    /*
                     * And don't process the keystroke further.
                     */
                    return true;
                }
                return false;
            }
        });

        /*
         * Add English spellchecking to input TextView.
         */
        outgoing.attachSpell("en");

        /*
         * TextTags are how you apply formatting to content. We'll create a
         * few for later use in the display.
         */

        grey = new TextTag();
        grey.setForeground("#777777");

        italics = new TextTag();
        italics.setStyle(Style.ITALIC);
        italics.setForeground("darkgreen");

        blue = new TextTag();
        blue.setWeight(Weight.BOLD);
        blue.setForeground("blue");

        /*
         * Almost there. Quickly load an image that we'll use later to replace
         * text smileys with. Since people frequently screw up relative paths
         * when running things, we'll go to some trouble to load the broken
         * image icon instead if we can't find our smiley.
         */

        try {
            tmp = new Pixbuf("doc/examples/textview/face-smile.png");
        } catch (FileNotFoundException fnfe) {
            System.err.println("Warning: smiley image " + fnfe.getMessage());
            tmp = Gtk.renderIcon(window, Stock.MISSING_IMAGE, IconSize.BUTTON);
        }
        smiley = tmp;

        /*
         * Put the Window and all its children on screen.
         */

        window.showAll();

        /*
         * Make sure the user's text Entry has the keyboard focus. For a
         * number of reasons, this won't work until late in the game after
         * everything else is packed. If you try it earlier something else
         * will end up with focus despite this call having been made.
         */

        outgoing.grabFocus();

        /*
         * And start the "conversation" :)
         */

        other = new IncomingConversation();
        other.start();
    }

    /**
     * Append a received (or sent) message to the incoming display.
     */
    /*
     * For fun, we translate the smile emoticon into an image, giving us an
     * opportunity to demonstrate adding non-text elements to a TextBuffer.
     */
    private void appendMessage(String msg, boolean outbound) {
        final TextIter end;
        final long now;
        final String timestamp;
        final TextTag colour;
        int i;
        int prev;

        /*
         * Get a TextIter pointing at the end of the existing text.
         */

        end = buffer.getIterEnd();

        /*
         * Start with a paragraph separator and a timestamp. We colour the
         * timestamp a lighter colour so as to not distract from the text.
         */

        buffer.insert(end, "\n");

        now = System.currentTimeMillis() / 1000;
        timestamp = formatTime("%H:%M:%S\t", now);
        buffer.insert(end, timestamp, grey);

        /*
         * Loop over what we're going to add, replacing text smileys with
         * graphical ones. As for the text we write, if the user sent it we'll
         * make it blue but if incoming we'll leave it black.
         */

        if (outbound) {
            colour = blue;
        } else {
            colour = null;
        }

        prev = 0;

        while ((i = msg.indexOf(":)", prev)) != -1) {
            buffer.insert(end, msg.substring(prev, i), colour);
            buffer.insert(end, smiley);

            i += 2;
            prev = i;
        }
        buffer.insert(end, msg.substring(prev), colour);

        /*
         * We would be done, except that we need to scroll the TextView to
         * show the message just appended. Otherwise the display will stay
         * scrolled to top despite the fact that more message traffic is
         * coming in. An instant messenger shows the recently arrived traffic
         * on screen, letting older messages go off the top.
         */

        incoming.scrollTo(end);
    }

    /**
     * When a conversation starts we want to indicate who it is with.
     */
    private void startConversationWith(String who) {
        final TextIter pointer;

        /*
         * Place a message at the top of the display indicating who is writing
         * in.
         */

        pointer = buffer.getIterStart();
        buffer.insert(pointer, "Starting conversation with " + who, italics);
    }

    /*
     * We end with the program's main() method where we initialize GTK, call
     * the constructor to build the GUI and then start the main event loop.
     */

    public static void main(String[] args) {
        Gtk.init(args);

        new ExampleInstantMessenger();

        Gtk.main();
    }

    /*
     * And that's it! The remainder of this file is just some contrived
     * infrastructure to simulate a conversation. Run this example and you'll
     * see it all in action!
     */

    class IncomingConversation extends Thread
    {
        private final String[] messages;

        private IncomingConversation() {
            /*
             * Mark this thread as a daemon thread, else the main thread
             * terminating after Gtk.main() returns will not end the program.
             */

            this.setDaemon(true);

            /*
             * Some silly messages to use to fake incoming conversation.
             */

            messages = new String[] {
                "Hello there!",
                "Will you be my friend? :)",
                "I live in Kenya. " + "It's nice here because it is so warm.",
                "Someday, though, I want to see snow. " + "Perhaps I will go climb Kilimanjaro.",
                "Did you see the marathon on the last day of the Olympics? "
                        + "What a run by Samuel Wanjiru! " + "We are all so proud."
            };

            startConversationWith("Catherine Ojiambo");
        }

        public void run() {
            for (int i = 0; i < messages.length; i++) {
                try {
                    sleep((int) (1000 + i * 2000 * Math.random()));
                } catch (InterruptedException ie) {
                }

                appendMessage(messages[i], false);
            }
        }
    }
}