root/java/org/gnu/emacs/EmacsView.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. handleDirtyBitmap
  2. explicitlyDirtyBitmap
  3. getBitmap
  4. getCanvas
  5. prepareForLayout
  6. onMeasure
  7. onLayout
  8. damageRect
  9. swapBuffers
  10. onKeyDown
  11. onKeyMultiple
  12. onKeyUp
  13. onFocusChanged
  14. onGenericMotionEvent
  15. onTouchEvent
  16. moveChildToBack
  17. raise
  18. lower
  19. onCreateContextMenu
  20. popupMenu
  21. cancelPopupMenu
  22. onDetachedFromWindow
  23. onAttachedToWindow
  24. showOnScreenKeyboard
  25. hideOnScreenKeyboard
  26. onCreateInputConnection
  27. onCheckIsTextEditor
  28. isOpaque
  29. setICMode
  30. getICMode
  31. onGlobalLayout

     1 /* Communication module for Android terminals.  -*- c-file-style: "GNU" -*-
     2 
     3 Copyright (C) 2023 Free Software Foundation, Inc.
     4 
     5 This file is part of GNU Emacs.
     6 
     7 GNU Emacs is free software: you can redistribute it and/or modify
     8 it under the terms of the GNU General Public License as published by
     9 the Free Software Foundation, either version 3 of the License, or (at
    10 your option) any later version.
    11 
    12 GNU Emacs is distributed in the hope that it will be useful,
    13 but WITHOUT ANY WARRANTY; without even the implied warranty of
    14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15 GNU General Public License for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    19 
    20 package org.gnu.emacs;
    21 
    22 import android.content.Context;
    23 
    24 import android.text.InputType;
    25 
    26 import android.view.ContextMenu;
    27 import android.view.View;
    28 import android.view.KeyEvent;
    29 import android.view.MotionEvent;
    30 import android.view.ViewGroup;
    31 import android.view.ViewTreeObserver;
    32 
    33 import android.view.inputmethod.EditorInfo;
    34 import android.view.inputmethod.InputConnection;
    35 import android.view.inputmethod.InputMethodManager;
    36 
    37 import android.graphics.Bitmap;
    38 import android.graphics.Canvas;
    39 import android.graphics.Rect;
    40 import android.graphics.Region;
    41 import android.graphics.Paint;
    42 
    43 import android.os.Build;
    44 import android.util.Log;
    45 
    46 /* This is an Android view which has a back and front buffer.  When
    47    swapBuffers is called, the back buffer is swapped to the front
    48    buffer, and any damage is invalidated.  frontBitmap and backBitmap
    49    are modified and used both from the UI and the Emacs thread.  As a
    50    result, there is a lock held during all drawing operations.
    51 
    52    It is also a ViewGroup, as it also lays out children.  */
    53 
    54 public final class EmacsView extends ViewGroup
    55   implements ViewTreeObserver.OnGlobalLayoutListener
    56 {
    57   public static final String TAG = "EmacsView";
    58 
    59   /* The associated EmacsWindow.  */
    60   public EmacsWindow window;
    61 
    62   /* The buffer bitmap.  */
    63   public Bitmap bitmap;
    64 
    65   /* The associated canvases.  */
    66   public Canvas canvas;
    67 
    68   /* The damage region.  */
    69   public Region damageRegion;
    70 
    71   /* The associated surface view.  */
    72   private EmacsSurfaceView surfaceView;
    73 
    74   /* Whether or not a configure event must be sent for the next layout
    75      event regardless of what changed.  */
    76   public boolean mustReportLayout;
    77 
    78   /* Whether or not bitmaps must be recreated upon the next call to
    79      getBitmap.  */
    80   private boolean bitmapDirty;
    81 
    82   /* Whether or not a popup is active.  */
    83   private boolean popupActive;
    84 
    85   /* The current context menu.  */
    86   private EmacsContextMenu contextMenu;
    87 
    88   /* The last measured width and height.  */
    89   private int measuredWidth, measuredHeight;
    90 
    91   /* Object acting as a lock for those values.  */
    92   private Object dimensionsLock;
    93 
    94   /* The serial of the last clip rectangle change.  */
    95   private long lastClipSerial;
    96 
    97   /* The InputMethodManager for this view's context.  */
    98   public InputMethodManager imManager;
    99 
   100   /* Whether or not this view is attached to a window.  */
   101   public boolean isAttachedToWindow;
   102 
   103   /* Whether or not this view should have the on screen keyboard
   104      displayed whenever possible.  */
   105   public boolean isCurrentlyTextEditor;
   106 
   107   /* The associated input connection.  */
   108   private EmacsInputConnection inputConnection;
   109 
   110   /* The current IC mode.  See `android_reset_ic' for more
   111      details.  */
   112   private int icMode;
   113 
   114   /* The number of calls to `resetIC' to have taken place the last
   115      time an InputConnection was created.  */
   116   public long icSerial;
   117 
   118   /* The number of calls to `recetIC' that have taken place.  */
   119   public volatile long icGeneration;
   120 
   121   public
   122   EmacsView (EmacsWindow window)
   123   {
   124     super (EmacsService.SERVICE);
   125 
   126     Object tem;
   127     Context context;
   128 
   129     this.window = window;
   130     this.damageRegion = new Region ();
   131 
   132     setFocusable (true);
   133     setFocusableInTouchMode (true);
   134 
   135     /* Create the surface view.  */
   136     this.surfaceView = new EmacsSurfaceView (this);
   137     addView (this.surfaceView);
   138 
   139     /* Get rid of the default focus highlight.  */
   140     if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
   141       setDefaultFocusHighlightEnabled (false);
   142 
   143     /* Obtain the input method manager.  */
   144     context = getContext ();
   145     tem = context.getSystemService (Context.INPUT_METHOD_SERVICE);
   146     imManager = (InputMethodManager) tem;
   147 
   148     /* Add this view as its own global layout listener.  */
   149     getViewTreeObserver ().addOnGlobalLayoutListener (this);
   150 
   151     /* Create an object used as a lock.  */
   152     this.dimensionsLock = new Object ();
   153   }
   154 
   155   private void
   156   handleDirtyBitmap ()
   157   {
   158     Bitmap oldBitmap;
   159     int measuredWidth, measuredHeight;
   160 
   161     synchronized (dimensionsLock)
   162       {
   163         /* Load measuredWidth and measuredHeight.  */
   164         measuredWidth = this.measuredWidth;
   165         measuredHeight = this.measuredHeight;
   166       }
   167 
   168     if (measuredWidth == 0 || measuredHeight == 0)
   169       return;
   170 
   171     if (!isAttachedToWindow)
   172       return;
   173 
   174     /* If bitmap is the same width and height as the measured width
   175        and height, there is no need to do anything.  Avoid allocating
   176        the extra bitmap.  */
   177     if (bitmap != null
   178         && (bitmap.getWidth () == measuredWidth
   179             && bitmap.getHeight () == measuredHeight))
   180       {
   181         bitmapDirty = false;
   182         return;
   183       }
   184 
   185     /* Save the old bitmap.  */
   186     oldBitmap = bitmap;
   187 
   188     /* Recreate the back buffer bitmap.  */
   189     bitmap
   190       = Bitmap.createBitmap (measuredWidth,
   191                              measuredHeight,
   192                              Bitmap.Config.ARGB_8888);
   193     bitmap.eraseColor (window.background | 0xff000000);
   194 
   195     /* And canvases.  */
   196     canvas = new Canvas (bitmap);
   197     canvas.save ();
   198 
   199     /* Since the clip rectangles have been cleared, clear the clip
   200        rectangle ID.  */
   201     lastClipSerial = 0;
   202 
   203     /* Copy over the contents of the old bitmap.  */
   204     if (oldBitmap != null)
   205       canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
   206 
   207     bitmapDirty = false;
   208 
   209     /* Explicitly free the old bitmap's memory.  */
   210 
   211     if (oldBitmap != null)
   212       oldBitmap.recycle ();
   213 
   214     /* Some Android versions still don't free the bitmap until the
   215        next GC.  */
   216     Runtime.getRuntime ().gc ();
   217   }
   218 
   219   public synchronized void
   220   explicitlyDirtyBitmap ()
   221   {
   222     bitmapDirty = true;
   223   }
   224 
   225   public synchronized Bitmap
   226   getBitmap ()
   227   {
   228     if (bitmapDirty || bitmap == null)
   229       handleDirtyBitmap ();
   230 
   231     return bitmap;
   232   }
   233 
   234   public synchronized Canvas
   235   getCanvas (EmacsGC gc)
   236   {
   237     int i;
   238 
   239     if (bitmapDirty || bitmap == null)
   240       handleDirtyBitmap ();
   241 
   242     if (canvas == null)
   243       return null;
   244 
   245     /* Update clip rectangles if necessary.  */
   246     if (gc.clipRectID != lastClipSerial)
   247       {
   248         canvas.restore ();
   249         canvas.save ();
   250 
   251         if (gc.real_clip_rects != null)
   252           {
   253             for (i = 0; i < gc.real_clip_rects.length; ++i)
   254               canvas.clipRect (gc.real_clip_rects[i]);
   255           }
   256 
   257         lastClipSerial = gc.clipRectID;
   258       }
   259 
   260     return canvas;
   261   }
   262 
   263   public void
   264   prepareForLayout (int wantedWidth, int wantedHeight)
   265   {
   266     synchronized (dimensionsLock)
   267       {
   268         measuredWidth = wantedWidth;
   269         measuredHeight = wantedWidth;
   270       }
   271   }
   272 
   273   @Override
   274   protected void
   275   onMeasure (int widthMeasureSpec, int heightMeasureSpec)
   276   {
   277     Rect measurements;
   278     int width, height;
   279 
   280     /* Return the width and height of the window regardless of what
   281        the parent says.  */
   282     measurements = window.getGeometry ();
   283 
   284     width = measurements.width ();
   285     height = measurements.height ();
   286 
   287     /* Now apply any extra requirements in widthMeasureSpec and
   288        heightMeasureSpec.  */
   289 
   290     if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.EXACTLY)
   291       width = MeasureSpec.getSize (widthMeasureSpec);
   292     else if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.AT_MOST
   293              && width > MeasureSpec.getSize (widthMeasureSpec))
   294       width = MeasureSpec.getSize (widthMeasureSpec);
   295 
   296     if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.EXACTLY)
   297       height = MeasureSpec.getSize (heightMeasureSpec);
   298     else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
   299              && height > MeasureSpec.getSize (heightMeasureSpec))
   300       height = MeasureSpec.getSize (heightMeasureSpec);
   301 
   302     super.setMeasuredDimension (width, height);
   303   }
   304 
   305   /* Note that the monitor lock for the window must never be held from
   306      within the lock for the view, because the window also locks the
   307      other way around.  */
   308 
   309   @Override
   310   protected void
   311   onLayout (boolean changed, int left, int top, int right,
   312             int bottom)
   313   {
   314     int count, i, oldMeasuredWidth, oldMeasuredHeight;
   315     View child;
   316     Rect windowRect;
   317     boolean needExpose;
   318 
   319     count = getChildCount ();
   320     needExpose = false;
   321 
   322     synchronized (dimensionsLock)
   323       {
   324         /* Load measuredWidth and measuredHeight.  */
   325         oldMeasuredWidth = measuredWidth;
   326         oldMeasuredHeight = measuredHeight;
   327 
   328         /* Set measuredWidth and measuredHeight.  */
   329         measuredWidth = right - left;
   330         measuredHeight = bottom - top;
   331       }
   332 
   333     /* Dirty the back buffer if the layout change resulted in the view
   334        being resized.  */
   335 
   336     if (changed && (right - left != oldMeasuredWidth
   337                     || bottom - top != oldMeasuredHeight))
   338       {
   339         explicitlyDirtyBitmap ();
   340 
   341         /* Expose the window upon a change in the view's size.  */
   342 
   343         if (right - left > oldMeasuredWidth
   344             || bottom - top > oldMeasuredHeight)
   345           needExpose = true;
   346       }
   347 
   348     for (i = 0; i < count; ++i)
   349       {
   350         child = getChildAt (i);
   351 
   352         Log.d (TAG, "onLayout: " + child);
   353 
   354         if (child == surfaceView)
   355           child.layout (0, 0, right - left, bottom - top);
   356         else if (child.getVisibility () != GONE)
   357           {
   358             if (!(child instanceof EmacsView))
   359               continue;
   360 
   361             /* What to do: lay out the view precisely according to its
   362                window rect.  */
   363             windowRect = ((EmacsView) child).window.getGeometry ();
   364             child.layout (windowRect.left, windowRect.top,
   365                           windowRect.right, windowRect.bottom);
   366           }
   367       }
   368 
   369     /* Now report the layout change to the window.  */
   370 
   371     if (changed || mustReportLayout)
   372       {
   373         mustReportLayout = false;
   374         window.viewLayout (left, top, right, bottom);
   375       }
   376 
   377     if (needExpose)
   378       EmacsNative.sendExpose (this.window.handle, 0, 0,
   379                               right - left, bottom - top);
   380   }
   381 
   382   public void
   383   damageRect (Rect damageRect)
   384   {
   385     EmacsService.checkEmacsThread ();
   386     damageRegion.union (damageRect);
   387   }
   388 
   389   /* This method is called from both the UI thread and the Emacs
   390      thread.  */
   391 
   392   public void
   393   swapBuffers ()
   394   {
   395     Canvas canvas;
   396     Rect damageRect;
   397     Bitmap bitmap;
   398 
   399     /* Make sure this function is called only from the Emacs
   400        thread.  */
   401     EmacsService.checkEmacsThread ();
   402 
   403     damageRect = null;
   404 
   405     /* Now see if there is a damage region.  */
   406 
   407     if (damageRegion.isEmpty ())
   408       return;
   409 
   410     /* And extract and clear the damage region.  */
   411 
   412     damageRect = damageRegion.getBounds ();
   413     damageRegion.setEmpty ();
   414 
   415     bitmap = getBitmap ();
   416 
   417     /* Transfer the bitmap to the surface view, then invalidate
   418        it.  */
   419     surfaceView.setBitmap (bitmap, damageRect);
   420   }
   421 
   422   @Override
   423   public boolean
   424   onKeyDown (int keyCode, KeyEvent event)
   425   {
   426     if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
   427          || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
   428          || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
   429         && !EmacsNative.shouldForwardMultimediaButtons ())
   430       return false;
   431 
   432     window.onKeyDown (keyCode, event);
   433     return true;
   434   }
   435 
   436   @Override
   437   public boolean
   438   onKeyMultiple (int keyCode, int repeatCount, KeyEvent event)
   439   {
   440     if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
   441          || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
   442          || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
   443         && !EmacsNative.shouldForwardMultimediaButtons ())
   444       return false;
   445 
   446     window.onKeyDown (keyCode, event);
   447     return true;
   448   }
   449 
   450   @Override
   451   public boolean
   452   onKeyUp (int keyCode, KeyEvent event)
   453   {
   454     if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
   455          || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
   456          || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
   457         && !EmacsNative.shouldForwardMultimediaButtons ())
   458       return false;
   459 
   460     window.onKeyUp (keyCode, event);
   461     return true;
   462   }
   463 
   464   @Override
   465   public void
   466   onFocusChanged (boolean gainFocus, int direction,
   467                   Rect previouslyFocusedRect)
   468   {
   469     window.onFocusChanged (gainFocus);
   470     super.onFocusChanged (gainFocus, direction,
   471                           previouslyFocusedRect);
   472   }
   473 
   474   @Override
   475   public boolean
   476   onGenericMotionEvent (MotionEvent motion)
   477   {
   478     return window.onGenericMotionEvent (motion);
   479   }
   480 
   481   @Override
   482   public boolean
   483   onTouchEvent (MotionEvent motion)
   484   {
   485     return window.onTouchEvent (motion);
   486   }
   487 
   488   private void
   489   moveChildToBack (View child)
   490   {
   491     int index;
   492 
   493     index = indexOfChild (child);
   494 
   495     if (index > 0)
   496       {
   497         detachViewFromParent (index);
   498 
   499         /* The view at 0 is the surface view.  */
   500         attachViewToParent (child, 1,
   501                             child.getLayoutParams());
   502       }
   503   }
   504 
   505   /* The following two functions must not be called if the view has no
   506      parent, or is parented to an activity.  */
   507 
   508   public void
   509   raise ()
   510   {
   511     EmacsView parent;
   512 
   513     parent = (EmacsView) getParent ();
   514 
   515     Log.d (TAG, "raise: parent " + parent);
   516 
   517     if (parent.indexOfChild (this)
   518         == parent.getChildCount () - 1)
   519       return;
   520 
   521     parent.bringChildToFront (this);
   522   }
   523 
   524   public void
   525   lower ()
   526   {
   527     EmacsView parent;
   528 
   529     parent = (EmacsView) getParent ();
   530 
   531     Log.d (TAG, "lower: parent " + parent);
   532 
   533     if (parent.indexOfChild (this) == 1)
   534       return;
   535 
   536     parent.moveChildToBack (this);
   537   }
   538 
   539   @Override
   540   protected void
   541   onCreateContextMenu (ContextMenu menu)
   542   {
   543     if (contextMenu == null)
   544       return;
   545 
   546     contextMenu.expandTo (menu, this);
   547   }
   548 
   549   public boolean
   550   popupMenu (EmacsContextMenu menu, int xPosition,
   551              int yPosition, boolean force)
   552   {
   553     if (popupActive && !force)
   554       return false;
   555 
   556     contextMenu = menu;
   557     popupActive = true;
   558 
   559     Log.d (TAG, "popupMenu: " + menu + " @" + xPosition
   560            + ", " + yPosition + " " + force);
   561 
   562     /* Use showContextMenu (float, float) on N to get actual popup
   563        behavior.  */
   564     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
   565       return showContextMenu ((float) xPosition, (float) yPosition);
   566     else
   567       return showContextMenu ();
   568   }
   569 
   570   public void
   571   cancelPopupMenu ()
   572   {
   573     if (!popupActive)
   574       throw new IllegalStateException ("cancelPopupMenu called without"
   575                                        + " popupActive set");
   576 
   577     contextMenu = null;
   578     popupActive = false;
   579 
   580     /* It is not possible to know with 100% certainty which activity
   581        is currently displaying the context menu.  Loop through each
   582        activity and call `closeContextMenu' instead.  */
   583 
   584     for (EmacsWindowAttachmentManager.WindowConsumer consumer
   585            : EmacsWindowAttachmentManager.MANAGER.consumers)
   586       {
   587         if (consumer instanceof EmacsActivity)
   588           ((EmacsActivity) consumer).closeContextMenu ();
   589       }
   590   }
   591 
   592   @Override
   593   public synchronized void
   594   onDetachedFromWindow ()
   595   {
   596     isAttachedToWindow = false;
   597 
   598     /* Recycle the bitmap and call GC.  */
   599 
   600     if (bitmap != null)
   601       bitmap.recycle ();
   602 
   603     bitmap = null;
   604     canvas = null;
   605     surfaceView.setBitmap (null, null);
   606 
   607     /* Collect the bitmap storage; it could be large.  */
   608     Runtime.getRuntime ().gc ();
   609 
   610     super.onDetachedFromWindow ();
   611   }
   612 
   613   @Override
   614   public synchronized void
   615   onAttachedToWindow ()
   616   {
   617     isAttachedToWindow = true;
   618 
   619     /* Dirty the bitmap, as it was destroyed when onDetachedFromWindow
   620        was called.  */
   621     bitmapDirty = true;
   622 
   623     synchronized (dimensionsLock)
   624       {
   625         /* Now expose the view contents again.  */
   626         EmacsNative.sendExpose (this.window.handle, 0, 0,
   627                                 measuredWidth, measuredHeight);
   628       }
   629 
   630     super.onAttachedToWindow ();
   631   }
   632 
   633   public void
   634   showOnScreenKeyboard ()
   635   {
   636     /* Specifying no flags at all tells the system the user asked for
   637        the input method to be displayed.  */
   638 
   639     imManager.showSoftInput (this, 0);
   640     isCurrentlyTextEditor = true;
   641   }
   642 
   643   public void
   644   hideOnScreenKeyboard ()
   645   {
   646     imManager.hideSoftInputFromWindow (this.getWindowToken (),
   647                                        0);
   648     isCurrentlyTextEditor = false;
   649   }
   650 
   651   @Override
   652   public InputConnection
   653   onCreateInputConnection (EditorInfo info)
   654   {
   655     int mode;
   656     int[] selection;
   657 
   658     /* Figure out what kind of IME behavior Emacs wants.  */
   659     mode = getICMode ();
   660 
   661     /* Make sure the input method never displays a full screen input
   662        box that obscures Emacs.  */
   663     info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
   664     info.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
   665 
   666     /* Set a reasonable inputType.  */
   667     info.inputType = InputType.TYPE_CLASS_TEXT;
   668 
   669     /* If this fails or ANDROID_IC_MODE_NULL was requested, then don't
   670        initialize the input connection.  */
   671 
   672     if (mode == EmacsService.IC_MODE_NULL)
   673       {
   674         info.inputType = InputType.TYPE_NULL;
   675         return null;
   676       }
   677 
   678     /* Set icSerial.  If icSerial < icGeneration, the input connection
   679        has been reset, and future input should be ignored until a new
   680        connection is created.  */
   681 
   682     icSerial = icGeneration;
   683 
   684     /* Reset flags set by the previous input method.  */
   685 
   686     EmacsNative.clearInputFlags (window.handle);
   687 
   688     /* Obtain the current position of point and set it as the
   689        selection.  Don't do this under one specific situation: if
   690        `android_update_ic' is being called in the main thread, trying
   691        to synchronize with it can cause a dead lock in the IM manager.
   692        See icBeginSynchronous in EmacsService.java for more
   693        details.  */
   694 
   695     selection = EmacsService.viewGetSelection (window.handle);
   696 
   697     if (selection != null)
   698       Log.d (TAG, "onCreateInputConnection: current selection is: "
   699              + selection[0] + ", by " + selection[1]);
   700     else
   701       {
   702         Log.d (TAG, "onCreateInputConnection: current selection could"
   703                + " not be retrieved.");
   704 
   705         /* If the selection could not be obtained, return 0 by 0.
   706            However, ask for the selection position to be updated as
   707            soon as possible.  */
   708 
   709         selection = new int[] { 0, 0, };
   710         EmacsNative.requestSelectionUpdate (window.handle);
   711       }
   712 
   713     if (mode == EmacsService.IC_MODE_ACTION)
   714       info.imeOptions |= EditorInfo.IME_ACTION_DONE;
   715 
   716     /* Set the initial selection fields.  */
   717     info.initialSelStart = selection[0];
   718     info.initialSelEnd = selection[1];
   719 
   720     /* Create the input connection if necessary.  */
   721 
   722     if (inputConnection == null)
   723       inputConnection = new EmacsInputConnection (this);
   724     else
   725       /* Clear several pieces of state in the input connection.  */
   726       inputConnection.reset ();
   727 
   728     /* Return the input connection.  */
   729     return inputConnection;
   730   }
   731 
   732   @Override
   733   public synchronized boolean
   734   onCheckIsTextEditor ()
   735   {
   736     /* If value is true, then the system will display the on screen
   737        keyboard.  */
   738     return isCurrentlyTextEditor;
   739   }
   740 
   741   @Override
   742   public boolean
   743   isOpaque ()
   744   {
   745     /* Returning true here allows the system to not draw the contents
   746        of windows underneath this view, thereby improving
   747        performance.  */
   748     return true;
   749   }
   750 
   751   public synchronized void
   752   setICMode (int icMode)
   753   {
   754     this.icMode = icMode;
   755   }
   756 
   757   public synchronized int
   758   getICMode ()
   759   {
   760     return icMode;
   761   }
   762 
   763   @Override
   764   public void
   765   onGlobalLayout ()
   766   {
   767     int[] locations;
   768 
   769     /* Get the absolute offset of this view and specify its left and
   770        top position in subsequent ConfigureNotify events.  */
   771 
   772     locations = new int[2];
   773     getLocationInWindow (locations);
   774     window.notifyContentRectPosition (locations[0],
   775                                       locations[1]);
   776   }
   777 };

/* [<][>][^][v][top][bottom][index][help] */