root/dwt/widgets/Display.d

Revision 293:810b16d6559b, 162.6 kB (checked in by Frank Benoit <benoit@tionex.de>, 4 months ago)

Fix: non ascii Inputs.

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.widgets.Display;
14
15 import dwt.DWT;
16 import dwt.DWTError;
17 import dwt.DWTException;
18 import dwt.graphics.Color;
19 import dwt.graphics.Cursor;
20 import dwt.graphics.Device;
21 import dwt.graphics.DeviceData;
22 import dwt.graphics.Font;
23 import dwt.graphics.GC;
24 import dwt.graphics.GCData;
25 import dwt.graphics.Image;
26 import dwt.graphics.ImageData;
27 import dwt.graphics.PaletteData;
28 import dwt.graphics.Point;
29 import dwt.graphics.RGB;
30 import dwt.graphics.Rectangle;
31 import dwt.graphics.Resource;
32 import dwt.internal.ImageList;
33 import dwt.internal.Library;
34 import dwt.internal.win32.OS;
35
36 import dwt.widgets.Control;
37 import dwt.widgets.Dialog;
38 import dwt.widgets.Tray;
39 import dwt.widgets.Event;
40 import dwt.widgets.EventTable;
41 import dwt.widgets.Menu;
42 import dwt.widgets.MenuItem;
43 import dwt.widgets.Synchronizer;
44 import dwt.widgets.Monitor;
45 import dwt.widgets.Shell;
46 import dwt.widgets.Listener;
47 import dwt.widgets.Widget;
48 import dwt.widgets.TrayItem;
49
50 import dwt.dwthelper.utils;
51 import dwt.dwthelper.Runnable;
52 import dwt.dwthelper.System;
53 import tango.core.Thread;
54 import tango.stdc.stringz;
55 import tango.util.Convert;
56 static import tango.text.convert.Utf;
57 static import tango.text.Text;
58 alias tango.text.Text.Text!(char) StringBuffer;
59
60 /**
61  * Instances of this class are responsible for managing the
62  * connection between DWT and the underlying operating
63  * system. Their most important function is to implement
64  * the DWT event loop in terms of the platform event model.
65  * They also provide various methods for accessing information
66  * about the operating system, and have overall control over
67  * the operating system resources which DWT allocates.
68  * <p>
69  * Applications which are built with DWT will <em>almost always</em>
70  * require only a single display. In particular, some platforms
71  * which DWT supports will not allow more than one <em>active</em>
72  * display. In other words, some platforms do not support
73  * creating a new display if one already exists that has not been
74  * sent the <code>dispose()</code> message.
75  * <p>
76  * In DWT, the thread which creates a <code>Display</code>
77  * instance is distinguished as the <em>user-interface thread</em>
78  * for that display.
79  * </p>
80  * The user-interface thread for a particular display has the
81  * following special attributes:
82  * <ul>
83  * <li>
84  * The event loop for that display must be run from the thread.
85  * </li>
86  * <li>
87  * Some DWT API methods (notably, most of the public methods in
88  * <code>Widget</code> and its subclasses), may only be called
89  * from the thread. (To support multi-threaded user-interface
90  * applications, class <code>Display</code> provides inter-thread
91  * communication methods which allow threads other than the
92  * user-interface thread to request that it perform operations
93  * on their behalf.)
94  * </li>
95  * <li>
96  * The thread is not allowed to construct other
97  * <code>Display</code>s until that display has been disposed.
98  * (Note that, this is in addition to the restriction mentioned
99  * above concerning platform support for multiple displays. Thus,
100  * the only way to have multiple simultaneously active displays,
101  * even on platforms which support it, is to have multiple threads.)
102  * </li>
103  * </ul>
104  * Enforcing these attributes allows DWT to be implemented directly
105  * on the underlying operating system's event model. This has
106  * numerous benefits including smaller footprint, better use of
107  * resources, safer memory management, clearer program logic,
108  * better performance, and fewer overall operating system threads
109  * required. The down side however, is that care must be taken
110  * (only) when constructing multi-threaded applications to use the
111  * inter-thread communication mechanisms which this class provides
112  * when required.
113  * </p><p>
114  * All DWT API methods which may only be called from the user-interface
115  * thread are distinguished in their documentation by indicating that
116  * they throw the "<code>ERROR_THREAD_INVALID_ACCESS</code>"
117  * DWT exception.
118  * </p>
119  * <dl>
120  * <dt><b>Styles:</b></dt>
121  * <dd>(none)</dd>
122  * <dt><b>Events:</b></dt>
123  * <dd>Close, Dispose, Settings</dd>
124  * </dl>
125  * <p>
126  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
127  * </p>
128  * @see #syncExec
129  * @see #asyncExec
130  * @see #wake
131  * @see #readAndDispatch
132  * @see #sleep
133  * @see Device#dispose
134  * @see <a href="http://www.eclipse.org/swt/snippets/#display">Display snippets</a>
135  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
136  */
137
138 public class Display : Device {
139
140     /**
141      * the handle to the OS message queue
142      * (Warning: This field is platform dependent)
143      * <p>
144      * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
145      * public API. It is marked public only so that it can be shared
146      * within the packages provided by DWT. It is not available on all
147      * platforms and should never be accessed from application code.
148      * </p>
149      */
150     public MSG* msg;
151
152     /* Windows and Events */
153     Event [] eventQueue;
154     //Callback windowCallback;
155     //int windowProc_;
156     int threadId;
157     TCHAR[] windowClass_, windowShadowClass;
158     static int WindowClassCount;
159     static const String WindowName = "SWT_Window"; //$NON-NLS-1$
160     static const String WindowShadowName = "SWT_WindowShadow"; //$NON-NLS-1$
161     EventTable eventTable, filterTable;
162
163     /* Widget Table */
164     int freeSlot;
165     int [] indexTable;
166     Control lastControl, lastGetControl;
167     HANDLE lastHwnd;
168     HWND lastGetHwnd;
169     Control [] controlTable;
170     static const int GROW_SIZE = 1024;
171     private static /+const+/ int SWT_OBJECT_INDEX;
172     static const bool USE_PROPERTY = !OS.IsWinCE;
173
174     private static bool static_this_completed = false;
175     private static void static_this() {
176         if( static_this_completed ){
177             return;
178         }
179         synchronized {
180             if( static_this_completed ){
181                 return;
182             }
183             static if (USE_PROPERTY) {
184                 SWT_OBJECT_INDEX = OS.GlobalAddAtom (StrToTCHARz("SWT_OBJECT_INDEX")); //$NON-NLS-1$
185             } else {
186                 SWT_OBJECT_INDEX = 0;
187             }
188             static_this_StartupInfo ();
189             static_this_DeviceFinder ();
190             static_this_completed = true;
191         }
192     }
193
194     /* Startup info */
195     private static STARTUPINFO* lpStartupInfo;
196     private static void static_this_StartupInfo (){
197         static if (!OS.IsWinCE) {
198             lpStartupInfo = new STARTUPINFO ();
199             lpStartupInfo.cb = STARTUPINFO.sizeof;
200             OS.GetStartupInfo (lpStartupInfo);
201         }
202     }
203     /* XP Themes */
204     HTHEME hButtonTheme_, hEditTheme_, hExplorerBarTheme_, hScrollBarTheme_, hTabTheme_;
205     static const wchar [] BUTTON = "BUTTON\0"w;
206     static const wchar [] EDIT = "EDIT\0"w;
207     static const wchar [] EXPLORER = "EXPLORER\0"w;
208     static const wchar [] EXPLORERBAR = "EXPLORERBAR\0"w;
209     static const wchar [] SCROLLBAR = "SCROLLBAR\0"w;
210     static const wchar [] LISTVIEW = "LISTVIEW\0"w;
211     static const wchar [] TAB = "TAB\0"w;
212     static const wchar [] TREEVIEW = "TREEVIEW\0"w;
213
214     /* Focus */
215     int focusEvent;
216     Control focusControl;
217
218     /* Menus */
219     Menu [] bars, popups;
220     MenuItem [] items;
221
222     /*
223     * The start value for WM_COMMAND id's.
224     * Windows reserves the values 0..100.
225     *
226     * The SmartPhone DWT resource file reserves
227     * the values 101..107.
228     */
229     static const int ID_START = 108;
230
231     /* Filter Hook */
232     //Callback msgFilterCallback;
233     //int msgFilterProc_,
234     HHOOK filterHook;
235     bool runDragDrop = true, dragCancelled = false;
236     MSG* hookMsg;
237
238     /* Idle Hook */
239     //Callback foregroundIdleCallback;
240     //int foregroundIdleProc_;
241     HHOOK idleHook;
242
243     /* Message Hook and Embedding */
244     bool ignoreNextKey;
245     //Callback getMsgCallback, embeddedCallback;
246     int getMsgProc_;
247     HHOOK msgHook;
248     HWND embeddedHwnd;
249     int embeddedProc_;
250     static const String AWT_WINDOW_CLASS = "SunAwtWindow";
251     static const short [] ACCENTS = [ cast(short) '~', '`', '\'', '^', '"'];
252
253     /* Sync/Async Widget Communication */
254     Synchronizer synchronizer;
255     bool runMessages = true, runMessagesInIdle = false, runMessagesInMessageProc = true;
256     static const String RUN_MESSAGES_IN_IDLE_KEY = "org.eclipse.swt.internal.win32.runMessagesInIdle"; //$NON-NLS-1$
257     static final String RUN_MESSAGES_IN_MESSAGE_PROC_KEY = "dwt.internal.win32.runMessagesInMessageProc"; //$NON-NLS-1$
258     Thread thread;
259
260     /* Display Shutdown */
261     Runnable [] disposeList;
262
263     /* System Tray */
264     Tray tray;
265     int nextTrayId;
266
267     /* Timers */
268     int /*long*/ [] timerIds;
269     Runnable [] timerList;
270     int /*long*/ nextTimerId = SETTINGS_ID + 1;
271     static const int SETTINGS_ID = 100;
272     static const int SETTINGS_DELAY = 2000;
273
274     /* Keyboard and Mouse */
275     RECT* clickRect;
276     int clickCount, lastTime, lastButton;
277     HWND lastClickHwnd;
278     int scrollRemainder;
279     int lastKey, lastAscii, lastMouse;
280     bool lastVirtual, lastNull, lastDead;
281     ubyte [256] keyboard;
282     bool accelKeyHit, mnemonicKeyHit;
283     bool lockActiveWindow, captureChanged, xMouse;
284
285     /* Tool Tips */
286     int nextToolTipId;
287
288     /* MDI */
289     bool ignoreRestoreFocus;
290     Control lastHittestControl;
291     int lastHittest;
292
293     /* Message Only Window */
294     HWND hwndMessage;
295
296     /* System Resources */
297     LOGFONT* lfSystemFont;
298     Font systemFont;
299     Image errorImage, infoImage, questionImage, warningIcon;
300     Cursor [] cursors;
301     Resource [] resources;
302     static const int RESOURCE_SIZE = 1 + 4 + DWT.CURSOR_HAND + 1;
303
304     /* ImageList Cache */
305     ImageList[] imageList, toolImageList, toolHotImageList, toolDisabledImageList;
306
307     /* Custom Colors for ChooseColor */
308     COLORREF* lpCustColors;
309
310     /* Sort Indicators */
311     Image upArrow, downArrow;
312
313     /* Table */
314     char [] tableBuffer;
315     NMHDR* hdr;
316     NMLVDISPINFO* plvfi;
317     HWND hwndParent;
318     int columnCount;
319     bool [] columnVisible;
320
321     /* Resize and move recursion */
322     int resizeCount;
323     static final int RESIZE_LIMIT = 4;
324
325     /* Display Data */
326     Object data;
327     String [] keys;
328     Object [] values;
329
330     /* Key Mappings */
331     static const int [] [] KeyTable = [
332
333         /* Keyboard and Mouse Masks */
334         [OS.VK_MENU,    DWT.ALT],
335         [OS.VK_SHIFT,   DWT.SHIFT],
336         [OS.VK_CONTROL, DWT.CONTROL],
337 //      [OS.VK_????,    DWT.COMMAND],
338
339         /* NOT CURRENTLY USED */
340 //      [OS.VK_LBUTTON, DWT.BUTTON1],
341 //      [OS.VK_MBUTTON, DWT.BUTTON3],
342 //      [OS.VK_RBUTTON, DWT.BUTTON2],
343
344         /* Non-Numeric Keypad Keys */
345         [OS.VK_UP,      DWT.ARROW_UP],
346         [OS.VK_DOWN,    DWT.ARROW_DOWN],
347         [OS.VK_LEFT,    DWT.ARROW_LEFT],
348         [OS.VK_RIGHT,   DWT.ARROW_RIGHT],
349         [OS.VK_PRIOR,   DWT.PAGE_UP],
350         [OS.VK_NEXT,    DWT.PAGE_DOWN],
351         [OS.VK_HOME,    DWT.HOME],
352         [OS.VK_END,     DWT.END],
353         [OS.VK_INSERT,  DWT.INSERT],
354
355         /* Virtual and Ascii Keys */
356         [OS.VK_BACK,    DWT.BS],
357         [OS.VK_RETURN,  DWT.CR],
358         [OS.VK_DELETE,  DWT.DEL],
359         [OS.VK_ESCAPE,  DWT.ESC],
360         [OS.VK_RETURN,  DWT.LF],
361         [OS.VK_TAB,     DWT.TAB],
362
363         /* Functions Keys */
364         [OS.VK_F1,  DWT.F1],
365         [OS.VK_F2,  DWT.F2],
366         [OS.VK_F3,  DWT.F3],
367         [OS.VK_F4,  DWT.F4],
368         [OS.VK_F5,  DWT.F5],
369         [OS.VK_F6,  DWT.F6],
370         [OS.VK_F7,  DWT.F7],
371         [OS.VK_F8,  DWT.F8],
372         [OS.VK_F9,  DWT.F9],
373         [OS.VK_F10, DWT.F10],
374         [OS.VK_F11, DWT.F11],
375         [OS.VK_F12, DWT.F12],
376         [OS.VK_F13, DWT.F13],
377         [OS.VK_F14, DWT.F14],
378         [OS.VK_F15, DWT.F15],
379
380         /* Numeric Keypad Keys */
381         [OS.VK_MULTIPLY,    DWT.KEYPAD_MULTIPLY],
382         [OS.VK_ADD,         DWT.KEYPAD_ADD],
383         [OS.VK_RETURN,      DWT.KEYPAD_CR],
384         [OS.VK_SUBTRACT,    DWT.KEYPAD_SUBTRACT],
385         [OS.VK_DECIMAL,     DWT.KEYPAD_DECIMAL],
386         [OS.VK_DIVIDE,      DWT.KEYPAD_DIVIDE],
387         [OS.VK_NUMPAD0,     DWT.KEYPAD_0],
388         [OS.VK_NUMPAD1,     DWT.KEYPAD_1],
389         [OS.VK_NUMPAD2,     DWT.KEYPAD_2],
390         [OS.VK_NUMPAD3,     DWT.KEYPAD_3],
391         [OS.VK_NUMPAD4,     DWT.KEYPAD_4],
392         [OS.VK_NUMPAD5,     DWT.KEYPAD_5],
393         [OS.VK_NUMPAD6,     DWT.KEYPAD_6],
394         [OS.VK_NUMPAD7,     DWT.KEYPAD_7],
395         [OS.VK_NUMPAD8,     DWT.KEYPAD_8],
396         [OS.VK_NUMPAD9,     DWT.KEYPAD_9],
397 //      [OS.VK_????,        DWT.KEYPAD_EQUAL],
398
399         /* Other keys */
400         [OS.VK_CAPITAL,     DWT.CAPS_LOCK],
401         [OS.VK_NUMLOCK,     DWT.NUM_LOCK],
402         [OS.VK_SCROLL,      DWT.SCROLL_LOCK],
403         [OS.VK_PAUSE,       DWT.PAUSE],
404         [OS.VK_CANCEL,      DWT.BREAK],
405         [OS.VK_SNAPSHOT,    DWT.PRINT_SCREEN],
406 //      [OS.VK_????,        DWT.HELP],
407
408     ];
409
410     /* Multiple Displays */
411     static Display Default;
412     static Display [] Displays;
413
414     /* Multiple Monitors */
415     dwt.widgets.Monitor.Monitor[] monitors = null;
416     int monitorCount = 0;
417
418     /* Modality */
419     Shell [] modalShells;
420     Dialog modalDialog;
421     static bool TrimEnabled = false;
422
423     /* Private DWT Window Messages */
424     static const int SWT_GETACCELCOUNT  = OS.WM_APP;
425     static const int SWT_GETACCEL       = OS.WM_APP + 1;
426     static const int SWT_KEYMSG         = OS.WM_APP + 2;
427     static const int SWT_DESTROY        = OS.WM_APP + 3;
428     static const int SWT_TRAYICONMSG    = OS.WM_APP + 4;
429     static const int SWT_NULL           = OS.WM_APP + 5;
430     static const int SWT_RUNASYNC       = OS.WM_APP + 6;
431     static int SWT_TASKBARCREATED;
432     static int SWT_RESTORECARET;
433     static int DI_GETDRAGIMAGE;
434
435     /* Workaround for Adobe Reader 7.0 */
436     int hitCount;
437
438     /* Package Name */
439     static const String PACKAGE_PREFIX = "org.eclipse.swt.widgets."; //$NON-NLS-1$
440     /*
441     * This code is intentionally commented.  In order
442     * to support CLDC, .class cannot be used because
443     * it does not compile on some Java compilers when
444     * they are targeted for CLDC.
445     */
446 //  static {
447 //      String name = Display.class.getName ();
448 //      int index = name.lastIndexOf ('.');
449 //      PACKAGE_PREFIX = name.substring (0, index + 1);
450 //  }
451
452     /*
453     * TEMPORARY CODE.  Install the runnable that
454     * gets the current display. This code will
455     * be removed in the future.
456     */
457     private static void static_this_DeviceFinder () {
458         DeviceFinder = new class() Runnable {
459             public void run () {
460                 Device device = getCurrent ();
461                 if (device is null) {
462                     device = getDefault ();
463                 }
464                 setDevice (device);
465             }
466         };
467     }
468
469 /*
470 * TEMPORARY CODE.
471 */
472 static void setDevice (Device device) {
473     static_this();
474     CurrentDevice = device;
475 }
476
477 /**
478  * Constructs a new instance of this class.
479  * <p>
480  * Note: The resulting display is marked as the <em>current</em>
481  * display. If this is the first display which has been
482  * constructed since the application started, it is also
483  * marked as the <em>default</em> display.
484  * </p>
485  *
486  * @exception DWTException <ul>
487  *    <li>ERROR_THREAD_INVALID_ACCESS - if called from a thread that already created an existing display</li>
488  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
489  * </ul>
490  *
491  * @see #getCurrent
492  * @see #getDefault
493  * @see Widget#checkSubclass
494  * @see Shell
495  */
496 public this () {
497     this (null);
498 }
499
500 /**
501  * Constructs a new instance of this class using the parameter.
502  *
503  * @param data the device data
504  */
505 public this (DeviceData data) {
506     static_this();
507     msg = new MSG();
508     hookMsg = new MSG();
509     super (data);
510     synchronizer = new Synchronizer (this);
511     cursors = new Cursor [DWT.CURSOR_HAND + 1];
512 }
513
514 Control _getFocusControl () {
515     return findControl (OS.GetFocus ());
516 }
517
518 void addBar (Menu menu) {
519     if (bars is null) bars = new Menu [4];
520     int length_ = bars.length;
521     for (int i=0; i<length_; i++) {
522         if (bars [i] is menu) return;
523     }
524     int index = 0;
525     while (index < length_) {
526         if (bars [index] is null) break;
527         index++;
528     }
529     if (index is length_) {
530         Menu [] newBars = new Menu [length_ + 4];
531         System.arraycopy (bars, 0, newBars, 0, length_);
532         bars = newBars;
533     }
534     bars [index] = menu;
535 }
536
537 void addControl (HANDLE handle, Control control) {
538     if (handle is null) return;
539     if (freeSlot is -1) {
540         int length_ = (freeSlot = indexTable.length) + GROW_SIZE;
541         int [] newIndexTable = new int [length_];
542         Control [] newControlTable = new Control [length_];
543         System.arraycopy (indexTable, 0, newIndexTable, 0, freeSlot);
544         System.arraycopy (controlTable, 0, newControlTable, 0, freeSlot);
545         for (int i=freeSlot; i<length_-1; i++) newIndexTable [i] = i + 1;
546         newIndexTable [length_ - 1] = -1;
547         indexTable = newIndexTable;
548         controlTable = newControlTable;
549     }
550     static if (USE_PROPERTY) {
551         OS.SetProp (handle, cast(wchar*)SWT_OBJECT_INDEX, cast(void*) freeSlot + 1);
552     } else {
553         OS.SetWindowLongPtr (handle, OS.GWLP_USERDATA, freeSlot + 1);
554     }
555     int oldSlot = freeSlot;
556     freeSlot = indexTable [oldSlot];
557     indexTable [oldSlot] = -2;
558     controlTable [oldSlot] = control;
559 }
560
561 /**
562  * Adds the listener to the collection of listeners who will
563  * be notified when an event of the given type occurs anywhere
564  * in a widget. The event type is one of the event constants
565  * defined in class <code>DWT</code>. When the event does occur,
566  * the listener is notified by sending it the <code>handleEvent()</code>
567  * message.
568  * <p>
569  * Setting the type of an event to <code>DWT.None</code> from
570  * within the <code>handleEvent()</code> method can be used to
571  * change the event type and stop subsequent Java listeners
572  * from running. Because event filters run before other listeners,
573  * event filters can both block other listeners and set arbitrary
574  * fields within an event. For this reason, event filters are both
575  * powerful and dangerous. They should generally be avoided for
576  * performance, debugging and code maintenance reasons.
577  * </p>
578  *
579  * @param eventType the type of event to listen for
580  * @param listener the listener which should be notified when the event occurs
581  *
582  * @exception IllegalArgumentException <ul>
583  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
584  * </ul>
585  * @exception DWTException <ul>
586  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
587  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
588  * </ul>
589  *
590  * @see Listener
591  * @see DWT
592  * @see #removeFilter
593  * @see #removeListener
594  *
595  * @since 3.0
596  */
597 public void addFilter (int eventType, Listener listener) {
598     checkDevice ();
599     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
600     if (filterTable is null) filterTable = new EventTable ();
601     filterTable.hook (eventType, listener);
602 }
603
604 /**
605  * Adds the listener to the collection of listeners who will
606  * be notified when an event of the given type occurs. The event
607  * type is one of the event constants defined in class <code>DWT</code>.
608  * When the event does occur in the display, the listener is notified by
609  * sending it the <code>handleEvent()</code> message.
610  *
611  * @param eventType the type of event to listen for
612  * @param listener the listener which should be notified when the event occurs
613  *
614  * @exception IllegalArgumentException <ul>
615  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
616  * </ul>
617  * @exception DWTException <ul>
618  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
619  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
620  * </ul>
621  *
622  * @see Listener
623  * @see DWT
624  * @see #removeListener
625  *
626  * @since 2.0
627  */
628 public void addListener (int eventType, Listener listener) {
629     checkDevice ();
630     if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
631     if (eventTable is null) eventTable = new EventTable ();
632     eventTable.hook (eventType, listener);
633 }
634
635 void addMenuItem (MenuItem item) {
636     if (items is null) items = new MenuItem [64];
637     for (int i=0; i<items.length; i++) {
638         if (items [i] is null) {
639             item.id = i + ID_START;
640             items [i] = item;
641             return;
642         }
643     }
644     item.id = items.length + ID_START;
645     MenuItem [] newItems = new MenuItem [items.length + 64];
646     newItems [items.length] = item;
647     System.arraycopy (items, 0, newItems, 0, items.length);
648     items = newItems;
649 }
650
651 void addPo