bug-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Extension to XIM support (2)


From: Kazunobu Kuriyama
Subject: Extension to XIM support (2)
Date: Sun, 13 Jul 2003 22:32:06 +0900
User-agent: Mozilla/5.0 (X11; U; Linux i686; ja-JP; rv:1.0.0) Gecko/20020614

Hi all,

The attached are a modified version of the patches I previously sent.

At user level, the following is an only change:

The input method style specification which was done through the environmental
variable GNUSTEP_INPUT_METHOD_STYLE are now done through the user default
GSXIMInputMethodStyle.

You can use it in a usual manner:
   $ defaults write NSGlobalDomain GSXIMInputMethodStyle <val>
where <val> is RootWindow, OffTheSpot, or OverTheSpot.

Thanks to Alexander Malmberg who very recently added some new internal methods to NSTextView for the XIM support, the code is fairly improved in view of OO design,
without loss of any functionality the patches offer.

Because the modified implementation makes use of the new interface of NSTextView as mentioned, the patches are expected to work properly with -gui in the CVS as of
July 12 (around 18:00 GMT).

Any feedback is welcome.

- KK
2003-07-13  Kazunobu Kuriyama <kazunobu.kuriyama@nifty.com>

        * Header/x11/XGInputServer.h: Add the new category InputMethod.
        * Header/x11/XGServer.h: Add the new category InputMethod.
        * Source/x11/XIMInputServer.m:
        ([XIMInputServer (XIMPrivate) -ximStyleInit]): Cover the input
        method styles RootWindow, OffTheSpot, OverTheSpot, and OnTheSpot.
        ([XIMInputServer (XIMPrivate) -ximCreateIC:]): Implement OffTheSpot
        and OverTheSpot.  Implement the category InputMethod.
        * Source/x11/XGServer.m: Implement the category InputMethod.  Add
        overriding methods to NSTextView (NSView (InputMethod)).

--- XGInputServer.h.orig        2003-07-13 06:13:36.000000000 +0900
+++ XGInputServer.h     2003-07-13 20:10:49.000000000 +0900
@@ -59,6 +59,22 @@
                   name: (NSString *)name;
 - (void) ximFocusICWindow: (gswindow_device_t *)windev;
 - (void) ximCloseIC: (XIC)xic;
+
 @end
 
+// Public interface for the input methods
+@interface XIMInputServer (InputMethod)
+- (NSString *) inputMethodStyle;
+- (NSString *) fontSize: (int *)size;
+- (BOOL) clientWindowRect: (NSRect *)rect;
+
+- (BOOL) statusArea: (NSRect *)rect;
+- (BOOL) preeditArea: (NSRect *)rect;
+- (BOOL) preeditSpot: (NSPoint *)p;
+
+- (BOOL) setStatusArea: (NSRect *)rect;
+- (BOOL) setPreeditArea: (NSRect *)rect;
+- (BOOL) setPreeditSpot: (NSPoint *)p;
+@end // XIMInputServer (InputMethod)
+
 #endif
--- XGServer.h.orig     2003-07-13 06:13:36.000000000 +0900
+++ XGServer.h  2003-07-13 20:11:45.000000000 +0900
@@ -81,4 +81,19 @@
 - (NSRect) _XFrameToOSFrame: (NSRect)x for: (void*)window;
 @end
 
+// Public interface for the input methods.  
+@interface XGServer (InputMethod)
+- (NSString *) inputMethodStyle;
+- (NSString *) fontSize: (int *)size;
+- (BOOL) clientWindowRect: (NSRect *)rect;
+
+- (BOOL) statusArea: (NSRect *)rect;
+- (BOOL) preeditArea: (NSRect *)rect;
+- (BOOL) preeditSpot: (NSPoint *)p;
+
+- (BOOL) setStatusArea: (NSRect *)rect;
+- (BOOL) setPreeditArea: (NSRect *)rect;
+- (BOOL) setPreeditSpot: (NSPoint *)p;
+@end
+
 #endif /* _XGServer_h_INCLUDE */
--- XGServer.m.orig     2003-07-13 06:13:37.000000000 +0900
+++ XGServer.m  2003-07-13 21:35:16.000000000 +0900
@@ -550,3 +550,187 @@
 
 
 @end
+
+@implementation XGServer (InputMethod)
+- (NSString *) inputMethodStyle
+{
+  return inputServer ? [(XIMInputServer *)inputServer inputMethodStyle] : nil;
+}
+
+- (NSString *) fontSize: (int *)size
+{
+  return inputServer ? [(XIMInputServer *)inputServer fontSize: size] : nil;
+}
+
+- (BOOL) clientWindowRect: (NSRect *)rect
+{
+  return inputServer
+    ? [(XIMInputServer *)inputServer clientWindowRect: rect] : NO;
+}
+
+- (BOOL) statusArea: (NSRect *)rect
+{
+  return inputServer ? [(XIMInputServer *)inputServer statusArea: rect] : NO;
+}
+
+- (BOOL) preeditArea: (NSRect *)rect
+{
+  return inputServer ? [(XIMInputServer *)inputServer preeditArea: rect] : NO;
+}
+
+- (BOOL) preeditSpot: (NSPoint *)p
+{
+  return inputServer ? [(XIMInputServer *)inputServer preeditSpot: p] : NO;
+}
+
+- (BOOL) setStatusArea: (NSRect *)rect
+{
+  return inputServer
+    ? [(XIMInputServer *)inputServer setStatusArea: rect] : NO;
+}
+
+- (BOOL) setPreeditArea: (NSRect *)rect
+{
+  return inputServer
+    ? [(XIMInputServer *)inputServer setPreeditArea: rect] : NO;
+}
+
+- (BOOL) setPreeditSpot: (NSPoint *)p
+{
+  return inputServer
+    ? [(XIMInputServer *)inputServer setPreeditSpot: p] : NO;
+}
+
+@end // XGServer (InputMethod)
+
+
+//==== Additional code for NSTextView =========================================
+//
+//  WARNING  This section is not genuine part of the XGServer implementation.
+//  -------
+//
+//  The methods implemented in this section override some of the internal
+//  methods defined in NSTextView so that the class can support input methods
+//  (XIM) in cooperation with XGServer.
+//
+//  Note that the orverriding is done by defining the methods in a category,
+//  the name of which is not explicitly mentioned in NSTextView.h; the
+//  category is called 'InputMethod'.
+//
+
+#include <AppKit/NSClipView.h>
+#include <AppKit/NSTextView.h>
+
+@implementation NSTextView (InputMethod)
+
+- (void) _updateInputMethodState
+{
+  NSRect    frame;
+  int      font_size;
+  NSRect    status_area;
+  NSRect    preedit_area;
+  id       displayServer = (XGServer *)GSCurrentServer();
+
+  if (![displayServer respondsToSelector: @selector(inputMethodStyle)])
+    return;
+
+  if (![displayServer fontSize: &font_size])
+    return;
+
+  if ([[self superview] isKindOfClass: [NSClipView class]])
+    frame = [[self superview] frame];
+  else
+    frame = [self frame];
+
+  status_area.size.width  = 2 * font_size;
+  status_area.size.height = font_size + 2;
+  status_area.origin.x    = 0;
+  status_area.origin.y    = frame.size.height - status_area.size.height;
+
+  if ([[displayServer inputMethodStyle] isEqual: @"OverTheSpot"])
+    {
+      preedit_area.origin.x    = 0;
+      preedit_area.origin.y    = 0;
+      preedit_area.size.width  = frame.size.width;
+      preedit_area.size.height = status_area.size.height;
+
+      [displayServer setStatusArea: &status_area];
+      [displayServer setPreeditArea: &preedit_area];
+    }
+  else if ([[displayServer inputMethodStyle] isEqual: @"OffTheSpot"])
+    {
+      preedit_area.origin.x    = status_area.size.width + 2;
+      preedit_area.origin.y    = status_area.origin.y;
+      preedit_area.size.width  = frame.origin.x + frame.size.width
+       - preedit_area.origin.x;
+      preedit_area.size.height = status_area.size.height;
+
+      [displayServer setStatusArea: &status_area];
+      [displayServer setPreeditArea: &preedit_area];
+    }
+  else
+    {
+      // Do nothing for the RootWindow style.
+    }
+}
+
+- (void) _updateInputMethodWithInsertionPoint: (NSPoint)insertionPoint
+{
+  id displayServer = (XGServer *)GSCurrentServer();
+
+  if (![displayServer respondsToSelector: @selector(inputMethodStyle)])
+    return;
+
+  if ([[displayServer inputMethodStyle] isEqual: @"OverTheSpot"])
+    {
+      id       view;
+      NSRect   frame;
+      NSPoint  p;
+      NSRect   client_win_rect;
+      NSPoint  screenXY_of_frame;
+      double   x_offset;
+      double   y_offset;
+      int      font_size;
+      NSRect   doc_rect;
+      NSRect   doc_visible_rect;
+      BOOL     cond;
+      float    x = insertionPoint.x;
+      float    y = insertionPoint.y;
+
+      [displayServer clientWindowRect: &client_win_rect];
+      [displayServer fontSize: &font_size];
+
+      cond = [[self superview] isKindOfClass: [NSClipView class]];
+      if (cond)
+       view = [self superview];
+      else
+       view = self;
+
+      frame = [view frame];
+      screenXY_of_frame = [[view window] convertBaseToScreen: frame.origin];
+
+      // N.B. The window of NSTextView isn't necessarily the same as the input
+      // method's client window.
+      x_offset = screenXY_of_frame.x - client_win_rect.origin.x; 
+      y_offset = (client_win_rect.origin.y + client_win_rect.size.height)
+       - (screenXY_of_frame.y + frame.size.height) + font_size;
+
+      x += x_offset;
+      y += y_offset;
+      if (cond) // If 'view' is of NSClipView, then
+       {
+         // N.B. Remember, (x, y) are the values with respect to NSTextView.
+         // We need to know the corresponding insertion position with respect
+         // to NSClipView.
+         doc_rect = [(NSClipView *)view documentRect];
+         doc_visible_rect = [view documentVisibleRect];
+         y -= doc_visible_rect.origin.y - doc_rect.origin.y;
+       }
+
+      p = NSMakePoint(x, y);
+      [displayServer setPreeditSpot: &p];
+    }
+}
+
+@end // NSTextView
+//==== End: Additional Code for NSTextView ====================================
--- XIMInputServer.m.orig       2003-07-13 06:13:37.000000000 +0900
+++ XIMInputServer.m    2003-07-13 06:37:13.000000000 +0900
@@ -27,6 +27,7 @@
 
 #include "config.h"
 
+#include <Foundation/NSUserDefaults.h>
 #include <Foundation/NSData.h>
 #include <Foundation/NSDebug.h>
 #include <Foundation/NSException.h>
@@ -37,6 +38,11 @@
 #include "x11/XGInputServer.h"
 #include <X11/Xlocale.h>
 
+#define RootWindowStyle            (XIMPreeditNothing  | XIMStatusNothing)
+#define OffTheSpotStyle            (XIMPreeditArea     | XIMStatusArea)
+#define OverTheSpotStyle    (XIMPreeditPosition        | XIMStatusArea)
+#define OnTheSpotStyle     (XIMPreeditCallbacks| XIMStatusCallbacks)
+
 
 @interface XIMInputServer (XIMPrivate)
 - (BOOL) ximInit: (Display *)dpy;
@@ -240,31 +246,59 @@
 
 - (int) ximStyleInit
 {
-  /* FIXME: Right now we only support this style *but*
-     this is only temporary */
-  XIMStyle xim_supported_style=XIMPreeditNothing|XIMStatusNothing;
-  XIMStyles *styles;
-  char *failed_arg;
-  int i;
+  NSUserDefaults    *uds;
+  NSString         *request;
+  XIMStyle         xim_requested_style;
+  XIMStyles        *styles;
+  char             *failed_arg;
+  int              i;
+  
+  uds = [NSUserDefaults standardUserDefaults];
+  if ((request = [uds stringForKey: @"GSXIMInputMethodStyle"]) == nil)
+    {
+      xim_requested_style = RootWindowStyle;
+    }
+  else if ([request isEqual: @"RootWindow"])
+    {
+      xim_requested_style = RootWindowStyle;
+    }
+  else if ([request isEqual: @"OffTheSpot"])
+    {
+      xim_requested_style = OffTheSpotStyle;
+    }
+  else if ([request isEqual: @"OverTheSpot"])
+    {
+      xim_requested_style = OverTheSpotStyle;
+    }
+  else if ([request isEqual: @"OnTheSpot"])
+    {
+      xim_requested_style = OnTheSpotStyle;
+    }
+  else
+    {
+      NSLog(@"XIM: Unknown style '%s'.\n"
+           @"Fallback to RootWindow style.", [request cString]);
+      xim_requested_style = RootWindowStyle;
+    }
 
-  failed_arg = XGetIMValues(xim,XNQueryInputStyle,&styles,NULL);
-  if (failed_arg!=NULL)
+  failed_arg = XGetIMValues(xim, XNQueryInputStyle, &styles, NULL);
+  if (failed_arg != NULL)
     {
       NSDebugLLog(@"XIM", @"Can't getting the following IM value :%s",
                  failed_arg);
       return 0;
     } 
 
-  for (i=0;i<styles->count_styles;i++)
+  for (i = 0; i < styles->count_styles; i++)
     {
-      if (styles->supported_styles[i]==xim_supported_style)
+      if (styles->supported_styles[i] == xim_requested_style)
        {
-         xim_style=xim_supported_style;
+         xim_style = xim_requested_style;
          XFree(styles);
          return 1;
        }
     }
-
+  NSLog(@"XIM: '%s' is not supported", request);
   XFree(styles);
   return 0;
 }
@@ -315,10 +349,111 @@
 
 - (XIC) ximCreateIC: (Window)w
 {
-  XIC xic;
-  xic = XCreateIC(xim, XNClientWindow, w, XNInputStyle,
-                 xim_style, NULL);
-  if (xic==NULL)
+  XIC xic = NULL;
+
+  if (xim_style == RootWindowStyle)
+    {
+      xic = XCreateIC(xim,
+                     XNInputStyle, xim_style,
+                     XNClientWindow, w,
+                     NULL);
+    }
+  else if (xim_style == OffTheSpotStyle || xim_style == OverTheSpotStyle)
+    {
+      Display      *dpy = [XGServer currentXDisplay];
+      XFontSet     font_set;
+      char         **missing_charset;
+      int          num_missing_charset;
+      int          dummy = 0;
+      XVaNestedList preedit_args = NULL;
+      XVaNestedList status_args = NULL;        
+      XRectangle    status_area;
+      XRectangle    preedit_area;
+      XPoint       preedit_spot;
+      NSString     *ns_font_size;
+      int          font_size;
+      char         base_font_name[64];
+
+      //
+      // Create a FontSet
+      //
+
+      // N.B. Because some input methods fail to provide a default font set,
+      // we have to do it by ourselves.
+      ns_font_size = [self fontSize: &font_size];
+      sprintf(base_font_name, "*medium-r-normal--%s*", [ns_font_size cString]);
+      font_set = XCreateFontSet(dpy,
+                               base_font_name,
+                               &missing_charset,
+                               &num_missing_charset,
+                               NULL);
+      if (!font_set)
+       {
+         goto finish;
+       }
+      if (missing_charset)
+       {
+         int i;
+         NSLog(@"XIM: missing charset: ");
+         for (i = 0; i < num_missing_charset; ++i)
+           NSLog(@"%s", missing_charset[i]);
+         XFreeStringList(missing_charset);
+       }
+
+      //
+      // Create XIC.
+      //
+      // At least, XNFontSet and XNPreeditSpotLocation must be specified
+      // at initialization time.
+      //
+      status_area.width = font_size * 2;
+      status_area.height = font_size + 2;
+      status_area.x = 0;
+      status_area.y = 0;
+
+      status_args = XVaCreateNestedList(dummy,
+                                       XNArea, &status_area,
+                                       XNFontSet, font_set,
+                                       NULL);
+
+      preedit_area.width = 120;
+      preedit_area.height = status_area.height;
+      preedit_area.x = 0;
+      preedit_area.y = 0;
+
+      preedit_spot.x = 0;
+      preedit_spot.y = 0;
+
+      preedit_args = XVaCreateNestedList(dummy,
+                                        XNArea, &preedit_area,
+                                        XNSpotLocation, &preedit_spot,
+                                        XNFontSet, font_set,
+                                        NULL);
+
+      xic = XCreateIC(xim,
+                     XNInputStyle, xim_style,
+                     XNClientWindow, w,
+                     XNPreeditAttributes, preedit_args,
+                     XNStatusAttributes, status_args,
+                     NULL);
+
+      if (preedit_args) { XFree(preedit_args); preedit_args = NULL; }
+      if (status_args) { XFree(status_args); status_args = NULL; }
+      if (font_set) XFreeFontSet(dpy, font_set);
+    }
+  else if (xim_style == OnTheSpotStyle)
+    {
+      NSLog(@"XIM: GNUstep doesn't support 'OnTheSpot'.\n"
+           @"Fallback to RootWindow style.");
+      xim_style = RootWindowStyle;
+      xic = XCreateIC(xim,
+                     XNInputStyle, xim_style,
+                     XNClientWindow, w,
+                     NULL);
+    }
+
+finish:
+  if (xic == NULL)
     NSDebugLLog(@"XIM", @"Can't create the input context.\n");
 
   xics = realloc(xics, sizeof(XIC) * (num_xics + 1));
@@ -356,3 +491,218 @@
 }
 
 @end
+
+@implementation XIMInputServer (InputMethod)
+- (NSString *) inputMethodStyle
+{
+  if (num_xics > 0)
+    {
+      if (xim_style == RootWindowStyle)
+       return @"RootWindow";
+      else if (xim_style == OffTheSpotStyle)
+       return @"OffTheSpot";
+      else if (xim_style == OverTheSpotStyle)
+       return @"OverTheSpot";
+      else if (xim_style == OnTheSpotStyle)
+       return @"OnTheSpot";
+    }
+  return nil;
+}
+
+- (NSString *) fontSize: (int *)size
+{
+  NSString *str;
+
+  str = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSFontSize"];
+  if (!str)
+    str = @"12";
+  *size = (int)strtol([str cString], NULL, 10);
+  return str;
+}
+
+- (BOOL) clientWindowRect: (NSRect *)rect
+{
+  Window       win;
+  Window       dummy;
+  Display      *dpy;
+  int          abs_x, abs_y;
+  int          x, y;
+  unsigned int  w, h;
+  unsigned int  bw, d;
+
+  if (num_xics <= 0 || !rect) return NO;
+
+  *rect = NSMakeRect(0, 0, 0, 0);
+
+  if (XGetICValues(xics[num_xics - 1], XNClientWindow, &win, NULL))
+    return NO;
+  dpy = [XGServer currentXDisplay];
+  if (XTranslateCoordinates(dpy, win, DefaultRootWindow(dpy), 0, 0,
+                           &abs_x, &abs_y, &dummy) == 0)
+    return NO;
+  XGetGeometry(dpy, win, &dummy, &x, &y, &w, &h, &bw, &d);
+
+  // X Window Coordinates to GNUstep Coordinates
+  x = abs_x;
+  y = XDisplayHeight(dpy, 0) - (abs_y + h);
+
+  *rect =  NSMakeRect((float)x, (float)y, (float)w, (float)h);
+
+  return YES;
+}
+
+- (BOOL) statusArea: (NSRect *)rect
+{
+  if (num_xics > 0 && (xim_style & XIMStatusArea))
+    {
+      XRectangle    area;
+      int          dummy = 0;
+      XVaNestedList arglist = NULL;
+
+      if (!(arglist = XVaCreateNestedList(dummy, XNArea, &area, NULL)))
+       {
+         return NO;
+       }
+      XGetICValues(xics[num_xics - 1], XNStatusAttributes, arglist, NULL);
+      
+      rect->origin.x   = area.x;
+      rect->origin.y   = area.y;
+      rect->size.width = area.width;
+      rect->size.height        = area.height;
+
+      if (arglist) { XFree(arglist); arglist = NULL; }
+
+      return YES;
+    }
+  return NO;
+}
+
+- (BOOL) preeditArea: (NSRect *)rect
+{
+  if (num_xics > 0
+      && ((xim_style & XIMPreeditArea) || (xim_style & XIMPreeditPosition)))
+    {
+      XRectangle    area;
+      int          dummy = 0;
+      XVaNestedList arglist = NULL;
+
+      if (!(arglist = XVaCreateNestedList(dummy, XNArea, &area, NULL)))
+       {
+         return NO;
+       }
+      XGetICValues(xics[num_xics - 1], XNPreeditAttributes, arglist, NULL);
+
+      rect->origin.x   = area.x;
+      rect->origin.y   = area.y;
+      rect->size.width = area.width;
+      rect->size.height        = area.height;
+
+      if (arglist) { XFree(arglist); arglist = NULL; }
+
+      return YES;
+    }
+  return NO;
+}
+
+- (BOOL) preeditSpot: (NSPoint *)p
+{
+  if (num_xics > 0 && (xim_style & XIMPreeditPosition))
+    {
+      XPoint       spot;
+      int          dummy = 0;
+      XVaNestedList arglist = NULL;
+
+      if (!(arglist = XVaCreateNestedList(dummy, XNSpotLocation, &spot, NULL)))
+       {
+         return NO;
+       }
+      XGetICValues(xics[num_xics - 1], XNPreeditAttributes, arglist, NULL);
+
+      p->x = spot.x;
+      p->y = spot.y;
+
+      if (arglist) { XFree(arglist); arglist = NULL; }
+
+      return YES;
+    }
+  return NO;
+}
+
+- (BOOL) setStatusArea: (NSRect *)rect
+{
+  if (num_xics > 0 && (xim_style & XIMStatusArea))
+    {
+      XRectangle    area;
+      int          dummy = 0;
+      XVaNestedList arglist = NULL;
+
+      area.x       = rect->origin.x;
+      area.y       = rect->origin.y;
+      area.width    = rect->size.width;
+      area.height   = rect->size.height;
+
+      if (!(arglist = XVaCreateNestedList(dummy, XNArea, &area, NULL)))
+       {
+         return NO;
+       }
+      XSetICValues(xics[num_xics - 1], XNStatusAttributes, arglist, NULL);
+
+      if (arglist) { XFree(arglist); arglist = NULL; }
+
+      return YES;
+    }
+  return NO;
+}
+
+- (BOOL) setPreeditArea: (NSRect *)rect
+{
+  if (num_xics > 0
+      && ((xim_style & XIMPreeditArea) || (xim_style & XIMPreeditPosition)))
+    {
+      XRectangle    area;
+      int          dummy = 0;
+      XVaNestedList arglist = NULL;
+
+      area.x       = rect->origin.x;
+      area.y       = rect->origin.y;
+      area.width    = rect->size.width;
+      area.height   = rect->size.height;
+
+      if (!(arglist = XVaCreateNestedList(dummy, XNArea, &area, NULL)))
+       {
+         return NO;
+       }
+      XSetICValues(xics[num_xics - 1], XNPreeditAttributes, arglist, NULL);
+
+      if (arglist) { XFree(arglist); arglist = NULL; }
+
+      return YES;
+    }
+  return NO;
+}
+
+- (BOOL) setPreeditSpot: (NSPoint *)p
+{
+  if (num_xics > 0 && (xim_style & XIMPreeditPosition))
+    {
+      XPoint       spot;
+      int          dummy = 0;
+      XVaNestedList arglist = NULL;
+
+      spot.x = p->x;
+      spot.y = p->y;
+
+      if (!(arglist = XVaCreateNestedList(dummy, XNSpotLocation, &spot, NULL)))
+       {
+         return NO;
+       }
+      XSetICValues(xics[num_xics - 1], XNPreeditAttributes, arglist, NULL);
+
+      if (arglist) { XFree(arglist); arglist = NULL; }
+
+      return YES;
+    }
+  return NO;
+}
+
+@end // XIMInputServer (InputMethod)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]