javauis/lcdui_qt/src/javax/microedition/lcdui/CustomItem.java
branchRCL_3
changeset 65 ae942d28ec0e
equal deleted inserted replaced
60:6c158198356e 65:ae942d28ec0e
       
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 package javax.microedition.lcdui;
       
    18 
       
    19 import javax.microedition.lcdui.EventDispatcher.LCDUIEvent;
       
    20 
       
    21 import org.eclipse.swt.graphics.Point;
       
    22 import org.eclipse.swt.graphics.Rectangle;
       
    23 import org.eclipse.swt.widgets.Control;
       
    24 
       
    25 /**
       
    26  * Implementation of LCDUI abstract <code>CustomItem</code> class.
       
    27  */
       
    28 public abstract class CustomItem extends Item
       
    29 {
       
    30     protected static final int NONE = 0;
       
    31     protected static final int TRAVERSE_HORIZONTAL = 1;
       
    32     protected static final int TRAVERSE_VERTICAL = 2;
       
    33 
       
    34     protected static final int KEY_PRESS = 4;
       
    35     protected static final int KEY_RELEASE = 8;
       
    36     protected static final int KEY_REPEAT = 0x10;
       
    37 
       
    38     protected static final int POINTER_PRESS = 0x20;
       
    39     protected static final int POINTER_RELEASE = 0x40;
       
    40     protected static final int POINTER_DRAG = 0x80;
       
    41 
       
    42     /**
       
    43      * If CustomItem is changed, reasons for Re-layouting.
       
    44      */
       
    45 	static final int UPDATE_REASON_REPAINT = UPDATE_ITEM_MAX << 1;
       
    46 
       
    47 
       
    48     private boolean cleanupNeeded;
       
    49     private int contentWidth;
       
    50     private int contentHeight;
       
    51 
       
    52     private boolean repaintPending;
       
    53     private int repaintX1;
       
    54     private int repaintY1;
       
    55     private int repaintX2;
       
    56     private int repaintY2;
       
    57     private Object repaintLock;
       
    58     private Object cleanupLock;
       
    59     private Object resizeLock;
       
    60 
       
    61     // Flag for passing info between UI thread
       
    62     // and Dispatcher thread
       
    63     private boolean widgetDisposed;
       
    64 
       
    65     private com.nokia.mj.impl.rt.support.Finalizer finalizer;
       
    66 
       
    67     // Graphics command buffer for this instance
       
    68     Buffer graphicsBuffer;
       
    69     Graphics customItemGraphics;
       
    70 
       
    71     CustomItemLayouter layouter;
       
    72 
       
    73 
       
    74 
       
    75     /**
       
    76      * Constructor.
       
    77      *
       
    78      * @param label Label of CustomItem.
       
    79      */
       
    80     protected CustomItem(String label)
       
    81     {
       
    82         finalizer = ((finalizer != null) ? finalizer
       
    83                      : new com.nokia.mj.impl.rt.support.Finalizer()
       
    84         {
       
    85             public void finalizeImpl()
       
    86             {
       
    87                 if(finalizer != null)
       
    88                 {
       
    89                     finalizer = null;
       
    90                     if(!ESWTUIThreadRunner.isDisposed())
       
    91                     {
       
    92                         dispose();
       
    93                     }
       
    94                 }
       
    95             }
       
    96         });
       
    97         repaintLock = new Object();
       
    98         cleanupLock = new Object();
       
    99         resizeLock = new Object();
       
   100         setLabel(label);
       
   101     }
       
   102 
       
   103     /**
       
   104      * Get the game action associated with the key code.
       
   105      *
       
   106      * @param keyCode key code
       
   107      * @return game action bound to the key
       
   108      */
       
   109     public int getGameAction(int keyCode)
       
   110     {
       
   111         return KeyTable.getGameAction(keyCode);
       
   112     }
       
   113 
       
   114     /**
       
   115      * Get the interaction modes available on this device.
       
   116      *
       
   117      * @return bitmask of available interaction modes.
       
   118      */
       
   119     protected final int getInteractionModes()
       
   120     {
       
   121         // TODO: check final interaction modes
       
   122         return TRAVERSE_HORIZONTAL | TRAVERSE_VERTICAL
       
   123                | KEY_PRESS | KEY_RELEASE
       
   124                | POINTER_PRESS | POINTER_DRAG | POINTER_RELEASE;
       
   125     }
       
   126 
       
   127     /**
       
   128      * Invalidates CustomItem.
       
   129      */
       
   130     protected final void invalidate()
       
   131     {
       
   132         updateParent(UPDATE_SIZE_CHANGED);
       
   133     }
       
   134 
       
   135     /**
       
   136      *  Requests repainting of CustomItem.
       
   137      */
       
   138     protected final void repaint()
       
   139     {
       
   140         repaint(0, 0, contentWidth, contentHeight);
       
   141     }
       
   142 
       
   143     /**
       
   144      * Requests repainting of the specified area in CustomItem.
       
   145      *
       
   146      * @param aX
       
   147      * @param aY
       
   148      * @param aWidth
       
   149      * @param aHeight
       
   150      */
       
   151     protected final void repaint(int aX, int aY, int aWidth, int aHeight)
       
   152     {
       
   153         Rectangle rect = new Rectangle(aX, aY, aWidth, aHeight);
       
   154         // From here it goes to updateItem()
       
   155         updateParent(UPDATE_REASON_REPAINT, rect);
       
   156     }
       
   157 
       
   158     /**
       
   159      * Callback method for traverse-in event.
       
   160      *
       
   161      * @param direction
       
   162      * @param viewportWidth
       
   163      * @param viewportHeight
       
   164      * @param visRect if the CustomItem supports internal traversal and keeps
       
   165      *            focus, visRect represents the visible rectangle (focused area)
       
   166      * @return true if internal traversal has occurred, false otherwise
       
   167      */
       
   168     protected boolean traverse(int direction, int viewportWidth,
       
   169                                int viewportHeight, int[] visRect)
       
   170     {
       
   171         return false;
       
   172     }
       
   173 
       
   174     /**
       
   175      * Callback method for traverse-out event.
       
   176      */
       
   177     protected void traverseOut()
       
   178     {
       
   179     }
       
   180 
       
   181     /**
       
   182      * Callback method for key pressed event.
       
   183      */
       
   184     protected void keyPressed(int aKeyCode)
       
   185     {
       
   186     }
       
   187 
       
   188     /**
       
   189      * Callback method for key released event.
       
   190      */
       
   191     protected void keyReleased(int aKeyCode)
       
   192     {
       
   193     }
       
   194 
       
   195     /**
       
   196      * Callback method for key repeated event.
       
   197      */
       
   198     protected void keyRepeated(int aKeyCode)
       
   199     {
       
   200     }
       
   201 
       
   202     /**
       
   203      * Callback method for pointer pressed event.
       
   204      */
       
   205     protected void pointerPressed(int aX, int aY)
       
   206     {
       
   207     }
       
   208 
       
   209     /**
       
   210      * Callback method for pointer released event.
       
   211      */
       
   212     protected void pointerReleased(int aX, int aY)
       
   213     {
       
   214     }
       
   215 
       
   216     /**
       
   217      * Callback method for pointer dragged event.
       
   218      */
       
   219     protected void pointerDragged(int aX, int aY)
       
   220     {
       
   221     }
       
   222 
       
   223     /**
       
   224      * Callback method when item gets shown.
       
   225      */
       
   226     protected void showNotify()
       
   227     {
       
   228     }
       
   229 
       
   230     /**
       
   231      * Callback method when item gets hidden.
       
   232      */
       
   233     protected void hideNotify()
       
   234     {
       
   235     }
       
   236 
       
   237     /**
       
   238      * Callback method for content area size change event.
       
   239      */
       
   240     protected void sizeChanged(int aWidth, int aHeight)
       
   241     {
       
   242     }
       
   243 
       
   244     /**
       
   245      * Abstract method.
       
   246      *
       
   247      * @param graphics
       
   248      * @param aWidth
       
   249      * @param aHeight
       
   250      */
       
   251     protected abstract void paint(Graphics graphics, int aWidth, int aHeight);
       
   252 
       
   253     /**
       
   254      * Should return the minimum width of the content area.
       
   255      */
       
   256     protected abstract int getMinContentWidth();
       
   257 
       
   258     /**
       
   259      * Should return the minimum height of the content area.
       
   260      */
       
   261     protected abstract int getMinContentHeight();
       
   262 
       
   263     /**
       
   264      * Should return the preferred width of the content area.
       
   265      */
       
   266     protected abstract int getPrefContentWidth(int aHeight);
       
   267 
       
   268     /**
       
   269      * Should return the preferred height of the content area.
       
   270      */
       
   271     protected abstract int getPrefContentHeight(int aWidth);
       
   272 
       
   273     /**
       
   274      * Calculates minimum size of this item.
       
   275      *
       
   276      * @return Minimum size.
       
   277      */
       
   278     Point calculateMinimumSize()
       
   279     {
       
   280         return CustomItemLayouter.calculateMinimumBounds(this);
       
   281     }
       
   282 
       
   283     /**
       
   284      * Calculates preferred size of this item.
       
   285      *
       
   286      * @return Preferred size.
       
   287      */
       
   288     Point calculatePreferredSize()
       
   289     {
       
   290         return CustomItemLayouter.calculatePreferredBounds(this);
       
   291     }
       
   292 
       
   293     /**
       
   294      * Set the size of the content area.
       
   295      *
       
   296      * @param newWidth
       
   297      * @param newHeight
       
   298      */
       
   299     void internalHandleSizeChanged(int newWidth, int newHeight)
       
   300     {
       
   301         if(contentWidth != newWidth || contentHeight != newHeight)
       
   302         {
       
   303             synchronized(resizeLock)
       
   304             {
       
   305                 contentWidth = newWidth;
       
   306                 contentHeight = newHeight;
       
   307             }
       
   308             EventDispatcher eventDispatcher = EventDispatcher.instance();
       
   309             LCDUIEvent event = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_SIZECHANGED, layouter.formLayouter.getForm());
       
   310             event.item = this;
       
   311             eventDispatcher.postEvent(event);
       
   312             synchronized(cleanupLock)
       
   313             {
       
   314                 cleanupNeeded = true;
       
   315             }
       
   316             repaint();
       
   317         }
       
   318     }
       
   319 
       
   320     /*
       
   321      * Note that if you call this and getContentHeight() from a non-UI thread
       
   322      * then it must be made sure size doesn't change between the calls.
       
   323      */
       
   324     int getContentWidth()
       
   325     {
       
   326         return contentWidth;
       
   327     }
       
   328 
       
   329     /*
       
   330      * Note that if you call this and getContentHeight() from a non-UI thread
       
   331      * then it must be made sure size doesn't change between the calls.
       
   332      */
       
   333     int getContentHeight()
       
   334     {
       
   335         return contentHeight;
       
   336     }
       
   337 
       
   338     boolean isFocusable()
       
   339     {
       
   340         return true;
       
   341     }
       
   342 
       
   343     /*
       
   344      * This gets called when Form contents are modified.
       
   345      */
       
   346     void setParent(Screen parent)
       
   347     {
       
   348         super.setParent(parent);
       
   349         if(parent == null)
       
   350         {
       
   351             // Item was removed from a Form
       
   352             layouter = null;
       
   353         }
       
   354         else
       
   355         {
       
   356             // Item was added to a Form
       
   357             layouter = ((CustomItemLayouter)((Form)parent).getFormLayouter().getItemLayouter(this));
       
   358         }
       
   359     }
       
   360 
       
   361     private boolean invalidate(int x, int y, int width, int height)
       
   362     {
       
   363         // Regularize bounds
       
   364         final int x1 = x;
       
   365         final int y1 = y;
       
   366         final int x2 = x + width;
       
   367         final int y2 = y + height;
       
   368 
       
   369         // Union the current and new damaged rects
       
   370         final boolean valid = ((repaintX2 - repaintX1) <= 0) && ((repaintY2 - repaintY1) <= 0);
       
   371         if(!valid)
       
   372         {
       
   373             repaintX1 = Math.min(repaintX1, x1);
       
   374             repaintY1 = Math.min(repaintY1, y1);
       
   375             repaintX2 = Math.max(repaintX2, x2);
       
   376             repaintY2 = Math.max(repaintY2, y2);
       
   377         }
       
   378         else
       
   379         {
       
   380             repaintX1 = x1;
       
   381             repaintY1 = y1;
       
   382             repaintX2 = x2;
       
   383             repaintY2 = y2;
       
   384         }
       
   385 
       
   386         // UI thread can change the size at any time. Must make sure it doesn't
       
   387         // change between reading the width and the height.
       
   388         final int w;
       
   389         final int h;
       
   390         synchronized(resizeLock)
       
   391         {
       
   392             w = contentWidth;
       
   393             h = contentHeight;
       
   394         }
       
   395 
       
   396         // Clip to bounds
       
   397         repaintX1 = repaintX1 > 0 ? repaintX1 : 0;
       
   398         repaintY1 = repaintY1 > 0 ? repaintY1 : 0;
       
   399         repaintX2 = repaintX2 < w ? repaintX2 : w;
       
   400         repaintY2 = repaintY2 < h ? repaintY2 : h;
       
   401 
       
   402         return valid;
       
   403     }
       
   404 
       
   405     /*
       
   406      * Note the control passed as a parameter can change. It must not be stored
       
   407      * to the CustomItem. Adding and removing Form item is blocked for the
       
   408      * duration of the call.
       
   409      */
       
   410     void updateItem(Rectangle rect, Control control)
       
   411     {
       
   412         // Paint callback event is posted without any invalid area info.
       
   413         // Invalid area info is kept in the member variables. Only one event
       
   414         // per Canvas is added to the queue. If there are more repaint() calls
       
   415         // before the already posted event has been served those are merged
       
   416         // to the invalid area in the member variables without posting a new
       
   417         // event.
       
   418         synchronized(repaintLock)
       
   419         {
       
   420             if(invalidate(rect.x, rect.y, rect.width, rect.height))
       
   421             {
       
   422                 // Note that repaintPending doesn't mean that there's a repaint
       
   423                 // event in the queue. It means that the invalid area is going
       
   424                 // to be repainted and there's no need to add a new event.
       
   425                 // It's possible that the repaint event has already been
       
   426                 // removed from the queue but repaintPending is still true. In
       
   427                 // that case it's currently being processed and we can still
       
   428                 // add to the invalid area.
       
   429                 if(!repaintPending)
       
   430                 {
       
   431                     EventDispatcher eventDispatcher = EventDispatcher.instance();
       
   432                     LCDUIEvent event = eventDispatcher.newEvent(
       
   433                                            LCDUIEvent.CUSTOMITEM_PAINT_MIDLET_REQUEST, layouter.formLayouter.getForm());
       
   434                     event.widget = control;
       
   435                     event.item = this;
       
   436                     eventDispatcher.postEvent(event);
       
   437                     repaintPending = true;
       
   438                 }
       
   439             }
       
   440         }
       
   441     }
       
   442 
       
   443     /*
       
   444      * Dispatcher thread thread calls. Operations changing Form content through
       
   445      * public API are blocked for the duration of the method call, however form
       
   446      * internal layout may modify items or even delete some. It has been checked
       
   447      * that the eSWT widget has not been disposed and that the CustomItem has not
       
   448      * been removed from the Form.
       
   449      */
       
   450     void doCallback(final LCDUIEvent event)
       
   451     {
       
   452         switch(event.type)
       
   453         {
       
   454         case LCDUIEvent.CUSTOMITEM_PAINT_MIDLET_REQUEST:
       
   455         case LCDUIEvent.CUSTOMITEM_PAINT_NATIVE_REQUEST:
       
   456             doPaintCallback(event);
       
   457             break;
       
   458         case LCDUIEvent.CUSTOMITEM_HIDENOTIFY:
       
   459             hideNotify();
       
   460             break;
       
   461         case LCDUIEvent.CUSTOMITEM_SHOWNOTIFY:
       
   462             showNotify();
       
   463             break;
       
   464         case LCDUIEvent.CUSTOMITEM_KEYPRESSED:
       
   465             keyPressed(event.keyCode);
       
   466             break;
       
   467         case LCDUIEvent.CUSTOMITEM_KEYREPEATED:
       
   468             keyRepeated(event.keyCode);
       
   469             break;
       
   470         case LCDUIEvent.CUSTOMITEM_KEYRELEASED:
       
   471             keyReleased(event.keyCode);
       
   472             break;
       
   473         case LCDUIEvent.CUSTOMITEM_POINTERDRAGGED:
       
   474             pointerDragged(event.x, event.y);
       
   475             break;
       
   476         case LCDUIEvent.CUSTOMITEM_POINTERPRESSED:
       
   477             pointerPressed(event.x, event.y);
       
   478             break;
       
   479         case LCDUIEvent.CUSTOMITEM_POINTERRELEASED:
       
   480             pointerReleased(event.x, event.y);
       
   481             break;
       
   482         case LCDUIEvent.CUSTOMITEM_SIZECHANGED:
       
   483             sizeChanged(event.width, event.height);
       
   484             break;
       
   485         default:
       
   486             super.doCallback(event);
       
   487         }
       
   488     }
       
   489 
       
   490     /*
       
   491      * Dispatcher thread calls.
       
   492      */
       
   493     void doPaintCallback(final LCDUIEvent event)
       
   494     {
       
   495         // Decide the area going to be painted by the callback.
       
   496         final int redrawNowX;
       
   497         final int redrawNowY;
       
   498         final int redrawNowW;
       
   499         final int redrawNowH;
       
   500         // Before this thread obtains the repaintLock any repaint() calls
       
   501         // will still be adding to the invalid area that is going to be
       
   502         // painted by this callback.
       
   503 
       
   504         synchronized(repaintLock)
       
   505         {
       
   506             if(event.type == LCDUIEvent.CUSTOMITEM_PAINT_NATIVE_REQUEST)
       
   507             {
       
   508                 // Merge with possibly existing repaint() requests
       
   509                 invalidate(event.x, event.y, event.width, event.height);
       
   510             }
       
   511             else
       
   512             {
       
   513                 // Need to add a new event to the queue in subsequent repaint()
       
   514                 // calls.
       
   515                 repaintPending = false;
       
   516             }
       
   517 
       
   518             // Store the current area to be painted
       
   519             redrawNowX = repaintX1;
       
   520             redrawNowY = repaintY1;
       
   521             redrawNowW = repaintX2-repaintX1;
       
   522             redrawNowH = repaintY2-repaintY1;
       
   523 
       
   524             // After releasing the lock the repaint() calls will start with
       
   525             // new invalid area.
       
   526             repaintX1 = repaintX2 = repaintY1 = repaintY2 = 0;
       
   527 
       
   528             // Don't do the callback if there's nothing to paint
       
   529             if(!((redrawNowW > 0) && (redrawNowH > 0)))
       
   530             {
       
   531                 return;
       
   532             }
       
   533         }
       
   534 
       
   535         // Instantiate GraphicsBuffer and Graphics if needed
       
   536         // or just update the bounds
       
   537         widgetDisposed = false;
       
   538         final CustomItem self = this;
       
   539         ESWTUIThreadRunner.safeSyncExec(new Runnable()
       
   540         {
       
   541             public void run()
       
   542             {
       
   543                 // Checking the widget state here ensures that
       
   544                 // it is not disposed during this method execution
       
   545                 // as we are in UI thread
       
   546                 if(event.widget.isDisposed())
       
   547                 {
       
   548                     widgetDisposed = true;
       
   549                     return;
       
   550                 }
       
   551                 if(customItemGraphics == null)
       
   552                 {
       
   553                     graphicsBuffer = Buffer.createInstance(self, (Control)event.widget);
       
   554                     customItemGraphics = graphicsBuffer.getGraphics();
       
   555                     customItemGraphics.setSyncStrategy(Graphics.SYNC_LEAVE_SURFACE_SESSION_OPEN);
       
   556                 }
       
   557                 else
       
   558                 {
       
   559                     graphicsBuffer.setControlBounds((Control)event.widget);
       
   560                 }
       
   561             }
       
   562         });
       
   563 
       
   564         // Quit if widget was already disposed
       
   565         if(widgetDisposed)
       
   566         {
       
   567             return;
       
   568         }
       
   569 
       
   570         // Clean the background if dirty, buffer the operations.
       
   571         synchronized(cleanupLock)
       
   572         {
       
   573             if(cleanupNeeded && layouter.noBackground)
       
   574             {
       
   575                 // Must be made sure that size doesn't change between reading
       
   576                 // the width and the height.
       
   577                 int contentWidth, contentHeight;
       
   578                 synchronized(resizeLock)
       
   579                 {
       
   580                     contentWidth = this.contentWidth;
       
   581                     contentHeight = this.contentHeight;
       
   582                 }
       
   583 
       
   584                 customItemGraphics.setClip(0, 0, contentWidth, contentHeight);
       
   585                 customItemGraphics.cleanBackground(new Rectangle(0, 0, contentWidth, contentHeight));
       
   586                 cleanupNeeded = false;
       
   587             }
       
   588         }
       
   589 
       
   590         // Clip must define the invalid area
       
   591         customItemGraphics.reset();
       
   592         customItemGraphics.setClip(redrawNowX, redrawNowY, redrawNowW, redrawNowH);
       
   593 
       
   594         // The callback
       
   595         paint(customItemGraphics, contentWidth, contentHeight);
       
   596 
       
   597         // Wait until the UI thread is available. Then in the UI thread
       
   598         // synchronously send a paint event.
       
   599         ESWTUIThreadRunner.safeSyncExec(new Runnable()
       
   600         {
       
   601             public void run()
       
   602             {
       
   603                 if(event.widget.isDisposed())
       
   604                 {
       
   605                     return;
       
   606                 }
       
   607                 graphicsBuffer.sync();
       
   608                 graphicsBuffer.blitToDisplay(null, event.widget);
       
   609             }
       
   610         });
       
   611     }
       
   612 
       
   613     /**
       
   614      * Disposes this instance
       
   615      */
       
   616     private void dispose()
       
   617     {
       
   618         ESWTUIThreadRunner.safeSyncExec(new Runnable()
       
   619         {
       
   620             public void run()
       
   621             {
       
   622                 if(graphicsBuffer != null)
       
   623                 {
       
   624                     graphicsBuffer.dispose();
       
   625                     graphicsBuffer = null;
       
   626                 }
       
   627             }
       
   628         });
       
   629     }
       
   630 }