Logo Search packages:      
Sourcecode: uim version File versions

convdisp.cpp

/*

  Copyright (c) 2003-2005 uim Project http://uim.freedesktop.org/

  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
  3. Neither the name of authors nor the names of its contributors
     may be used to endorse or promote products derived from this software
     without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  SUCH DAMAGE.
*/

// classes for preedit draw

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>
#include <stdlib.h>
#include <locale.h>
#include "xim.h"
#include "ximserver.h"
#include "convdisp.h"
#include "canddisp.h"
#include "xdispatch.h"
#include "util.h"

#define UNDERLINE_HEIGHT      2
// Temporal hack for flashplayer plugin's broken over-the-spot XIM style
#define FLASHPLAYER_WORKAROUND

//
// PeWin:     Base class for preedit window
// PeLineWin: Child class for root window style preedit window
// PeOvWin:   Child class for over the spot style preedit window

#ifdef FLASHPLAYER_WORKAROUND
static Window getTopWindow(Display *, Window);
#endif

const char *fontset_zhCN = "-sony-fixed-medium-r-normal--16-*-*-*-c-80-iso8859-1, -isas-fangsong ti-medium-r-normal--16-160-72-72-c-160-gb2312.1980-0";
const char *fontset_zhTW = "-sony-fixed-medium-r-normal--16-*-*-*-c-80-iso8859-1, -taipei-fixed-medium-r-normal--16-150-75-75-c-160-big5-0";
const char *fontset_ja = "-sony-fixed-medium-r-normal--16-*-*-*-c-80-iso8859-1, -jis-fixed-medium-r-normal--16-*-75-75-c-160-jisx0208.1983-0, -sony-fixed-medium-r-normal--16-*-*-*-c-80-jisx0201.1976-0";
const char *fontset_ko = "-sony-fixed-medium-r-normal--16-*-*-*-c-80-iso8859-1, -daewoo-gothic-medium-r-normal--16-120-100-100-c-160-ksc5601.1987-0";


static XFontSet
create_default_fontset(const char *im_lang, const char *encoding, const char *locale) {
    char *orig_locale;
    const char *name;
    XFontSet ret;

    orig_locale = strdup(setlocale(LC_CTYPE, NULL));

    if (strcmp(locale, orig_locale))
      setlocale(LC_CTYPE, locale);

    if (!strcmp(im_lang, "ja"))
      name = fontset_ja;
    else if (!strcmp(im_lang, "ko"))
      name = fontset_ko;
    else if (!strcmp(im_lang, "zh_CN"))
      name = fontset_zhCN;
    else if (!strcmp(im_lang, "zh_TW:zh_HK"))
      name = fontset_zhTW;
    else
      name = fontset_ja; // XXX fallback fontset

    ret = get_font_set(name, locale);

    if (strcmp(locale, orig_locale))
      setlocale(LC_CTYPE, orig_locale);
    free(orig_locale);

    return ret;
}

static XFontSet
choose_default_fontset(const char *im_lang, const char *encoding, const char *locale) {
    return create_default_fontset(im_lang, encoding, locale);
}

struct char_ent {
    uchar c;
    int stat;
    int width;
    int height;
    int x, y;
};

// Preedit Window
class PeWin : public WindowIf {
public:
    PeWin(Window pw, const char *im_lang, const char *encoding, const char *locale);
    virtual ~PeWin();

    virtual void release();
    
    virtual void destroy(Window w);
    virtual void expose(Window w);

    void draw_char(int x, int y, uchar ch, int stat);
    void set_back(unsigned long p);
    void set_fore(unsigned long p);
    void set_fontset(XFontSet f);
    
    virtual void set_size(int w, int h);
    void set_pos(int x, int y);

    void do_map();

    void clear();
    void draw();
    void unmap();
protected:
    Window mParentWin;
    Window mWin;
    Pixmap mPixmap;
    GC mGC, mClearGC;
    GC mHilitGC;
    unsigned int mFore, mBack;
    XFontSet mFontset;
    const char *mEncoding;
    int mWidth, mHeight;
    bool mIsMapped;
};

// one line preedit window for RootWindowStyle
class PeLineWin : public PeWin {
public:
    PeLineWin(Window w, const char *im_lang, const char *encoding, const char *locale);
    virtual ~PeLineWin();

    void draw_pe(pe_stat *p);

    void draw_segment(pe_ustring *s);

private:
    void calc_extent(pe_stat *p);

    int m_x;
};

// window for over the spot style
class PeOvWin : public PeWin {
public:
    PeOvWin(Window w, const char *im_lang, const char *encoding, const char *locale);
    
    void draw_ce(char_ent *ce, int len);
    virtual void set_size(int w, int h);
    virtual ~PeOvWin();
private:
    void draw_a_ce(char_ent *ce);
    Pixmap m_mask_pix;
    GC m_mask_pix_gc;
};

class ConvdispOv : public Convdisp {
public:
    ConvdispOv(InputContext *, icxatr *);
    virtual ~ConvdispOv();
    virtual void update_preedit();
    virtual void clear_preedit();
    virtual void update_icxatr();
    virtual void move_candwin();
    virtual void set_im_lang(const char *im_lang);
private:
    bool check_win();
    bool check_atr();
    void layoutCharEnt();
    void make_ce_array();
    void draw_preedit();
    void do_draw_preedit();
    int calc_ce_width(int b, int e);
#ifdef FLASHPLAYER_WORKAROUND
    int get_ce_font_height(char_ent *ce, int len);
    int revised_spot_y;
#endif

    void validate_area();

    char_ent *m_ce;
    int m_ce_len;
    PeOvWin *m_ov_win;
    XFontSet m_initial_fontset;
    char *m_initial_lang;
    bool m_lang_changed;
};

// Preedit window for RootWindowStyle
class ConvdispRw : public Convdisp {
public:
    ConvdispRw(InputContext *, icxatr *);
    virtual ~ConvdispRw();

    virtual void update_preedit();
    virtual void clear_preedit();
    virtual void update_icxatr();
    virtual void move_candwin();
private:
    PeLineWin *mPeWin;
};

class ConvdispOs : public Convdisp {
public:
    ConvdispOs(InputContext *, icxatr *, Connection *);
    virtual ~ConvdispOs();
    virtual void update_preedit();
    virtual void clear_preedit();
    virtual void update_icxatr();
    virtual void move_candwin();

private:
    void compose_preedit_array(TxPacket *);
    void compose_feedback_array(TxPacket *);
  
    Connection *mConn;
    int mImid, mIcid;
    int mPrevLen;
};

Convdisp *create_convdisp(int style, InputContext *k,
                    icxatr *a, Connection *c)
{
    switch (style) {
    case IS_ROOT_WINDOW:
      return new ConvdispRw(k, a);
    case IS_OVER_THE_SPOT:
      return new ConvdispOv(k, a);
    case IS_ON_THE_SPOT:
      return new ConvdispOs(k, a, c);
    default:
      break;
    }
    return 0;
}

//
// PeWin(PreEdit Window)
//
PeWin::PeWin(Window pw, const char *im_lang, const char *encoding, const char *locale)
{
    mParentWin = pw;
    int scr_num = DefaultScreen(XimServer::gDpy);
    // tentative
    mWidth = 1;
    mHeight = 1;
    mWin = XCreateSimpleWindow(XimServer::gDpy, mParentWin,
                         0, 0, mWidth, mHeight, 0,
                         BlackPixel(XimServer::gDpy, scr_num),
                         WhitePixel(XimServer::gDpy, scr_num));
    XSetWindowAttributes attr;
    attr.override_redirect = True;
    XChangeWindowAttributes(XimServer::gDpy, mWin, CWOverrideRedirect,
                      &attr);
    mPixmap = XCreatePixmap(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy),
                      mWidth, mHeight,
                      DefaultDepth(XimServer::gDpy, scr_num));
    
    mGC = XCreateGC(XimServer::gDpy, mPixmap, 0, 0);
    mClearGC = XCreateGC(XimServer::gDpy, mPixmap, 0, 0);
    mHilitGC = XCreateGC(XimServer::gDpy, mPixmap, 0, 0);
    
    XSetBackground(XimServer::gDpy, mGC, WhitePixel(XimServer::gDpy, scr_num));
    XSetForeground(XimServer::gDpy, mGC, BlackPixel(XimServer::gDpy, scr_num));
    XSetForeground(XimServer::gDpy, mClearGC, WhitePixel(XimServer::gDpy, scr_num));
    XSetBackground(XimServer::gDpy, mClearGC, BlackPixel(XimServer::gDpy, scr_num));

    add_window_watch(mWin, this, EXPOSE_MASK|STRUCTURE_NOTIFY_MASK);
    mIsMapped = false; //not mapped now
    
    mFontset = choose_default_fontset(im_lang, encoding, locale);
    mEncoding = encoding;
    
    XFlush(XimServer::gDpy);
}

PeWin::~PeWin()
{
    if (mWin)
      release();

    XFreePixmap(XimServer::gDpy, mPixmap);
    
    XFreeGC(XimServer::gDpy, mGC);
    XFreeGC(XimServer::gDpy, mClearGC);
    XFreeGC(XimServer::gDpy, mHilitGC);

    XFlush(XimServer::gDpy);
}

void PeWin::release()
{
    remove_window_watch(mWin);
    XDestroyWindow(XimServer::gDpy, mWin);
    mWin = 0;
}

void PeWin::destroy(Window w)
{
    if (mWin && mWin == w)
      mWin = 0;
}

void PeWin::expose(Window w)
{
    XCopyArea(XimServer::gDpy, mPixmap, mWin, mGC,
            0, 0, mWidth, mHeight, 0, 0);

    XFlush(XimServer::gDpy);
}

void PeWin::draw_char(int x, int y, uchar ch, int stat)
{
    GC gc = mGC;
    if (stat & PE_REVERSE)
      gc = mClearGC;

#if 0
    if (stat & PE_HILIGHT)
      gc = mHilitGC;
#endif
    char utf8[6];
    int len = utf8_wctomb((unsigned char *)utf8, ch);
    utf8[len] = '\0';

    if (!strcmp(mEncoding, "UTF-8"))
      XwcDrawImageString(XimServer::gDpy, mPixmap, mFontset,
                  gc, x, y, &ch, 1);
    else {
      char *native_str;
      
      native_str = utf8_to_native_str(utf8, mEncoding);
      if (!native_str)
          return;
      int len = strlen(native_str);
      XmbDrawImageString(XimServer::gDpy, mPixmap, mFontset,
                     gc, x, y, native_str, len);
      free(native_str);
    }
}

void PeWin::set_back(unsigned long p)
{
    mBack = p;
    XSetBackground(XimServer::gDpy, mGC, p);
    XSetForeground(XimServer::gDpy, mClearGC, p);
}

void PeWin::set_fore(unsigned long p)
{
    mFore = p;
    XSetForeground(XimServer::gDpy, mGC, p);
    XSetBackground(XimServer::gDpy, mClearGC, p);
}

void PeWin::set_fontset(XFontSet f)
{
    mFontset = f;
}

void PeWin::set_size(int w, int h)
{
    if (w == mWidth && h == mHeight)
      return;

    XResizeWindow(XimServer::gDpy, mWin, w, h);
    XFreePixmap(XimServer::gDpy, mPixmap);
    mPixmap = XCreatePixmap(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy), w, h,
                      DefaultDepth(XimServer::gDpy, DefaultScreen(XimServer::gDpy)));
    mWidth = w;
    mHeight = h;
    clear();
}

void PeWin::set_pos(int x, int y)
{
    XWindowChanges ch;
    ch.x = x;
    ch.y = y;
    XConfigureWindow(XimServer::gDpy, mWin, CWX|CWY, &ch);
}

void PeWin::do_map()
{
    if (!mIsMapped) {
      XFlush(XimServer::gDpy);
      XMapRaised(XimServer::gDpy, mWin);
      mIsMapped = true;
    }
}

void PeWin::clear()
{
    XFillRectangle(XimServer::gDpy, mPixmap, mClearGC,
               0, 0, mWidth, mHeight);
}

void PeWin::draw()
{
    if (!mIsMapped)
      do_map();
    else
      expose(mWin);
}

void PeWin::unmap()
{
    if (mIsMapped) {
      XUnmapWindow(XimServer::gDpy, mWin);
      XFlush(XimServer::gDpy);
      mIsMapped = false;
    }
}

//
// PeLineWin
//
PeLineWin::PeLineWin(Window w, const char *im_lang, const char *encoding, const char *locale) : PeWin(w, im_lang, encoding, locale)
{
    set_size(400, 28); // set window height wider than its font height 16
    clear();
}

PeLineWin::~PeLineWin()
{
}

void PeLineWin::draw_pe(pe_stat *p)
{
    clear();
    calc_extent(p);
    m_x = 0;
    std::list<pe_ustring>::iterator i;
    for (i = p->ustrings.begin(); i != p->ustrings.end(); i++) {
      draw_segment(&(*i));
    }
}

void PeLineWin::draw_segment(pe_ustring *s)
{
    uString::iterator i;
    for (i = s->s.begin(); i != s->s.end(); i++) {
      uchar ch = *i;
      draw_char(m_x, 20, ch, s->stat);
      if (s->stat & PE_UNDERLINE) {
          XDrawLine(XimServer::gDpy, mPixmap, mGC,
                      m_x, 20 + UNDERLINE_HEIGHT,
                      m_x + 16, 20 + UNDERLINE_HEIGHT);

      }
      m_x += 16; // XXX font width
    }
}

void PeLineWin::calc_extent(pe_stat *p)
{
    int c;
    c = p->get_char_count();
    set_size(400, 28); // XXX need to extent
}


//
// PeOvWin
//
PeOvWin::PeOvWin(Window w, const char *im_lang, const char *encoding, const char *locale) : PeWin(w, im_lang, encoding, locale)
{
    m_mask_pix = 0;
    m_mask_pix_gc = 0;
}

PeOvWin::~PeOvWin()
{
    if (m_mask_pix) {
      XFreePixmap(XimServer::gDpy, m_mask_pix);
      XFreeGC(XimServer::gDpy, m_mask_pix_gc);
    }
}

void PeOvWin::set_size(int w, int h)
{
    if (w == mWidth && h == mHeight)
      return;

    PeWin::set_size(w, h);
    if (m_mask_pix) {
      XFreePixmap(XimServer::gDpy, m_mask_pix);
      m_mask_pix = XCreatePixmap(XimServer::gDpy, mWin,
                           mWidth, mHeight, 1);
    }
}

void PeOvWin::draw_ce(char_ent *ce, int len)
{
    if (m_mask_pix == 0) {
      m_mask_pix = XCreatePixmap(XimServer::gDpy, mWin, mWidth, mHeight, 1);
      m_mask_pix_gc = XCreateGC(XimServer::gDpy, m_mask_pix, 0, 0);
    }


    clear();
    XSetForeground(XimServer::gDpy, m_mask_pix_gc,
               BlackPixel(XimServer::gDpy,
                        DefaultScreen(XimServer::gDpy)));
    XFillRectangle(XimServer::gDpy, m_mask_pix,
               m_mask_pix_gc, 0, 0, mWidth, mHeight);
    XSetForeground(XimServer::gDpy, m_mask_pix_gc,
               WhitePixel(XimServer::gDpy,
                        DefaultScreen(XimServer::gDpy)));
    int i;
    for (i = 0; i < len; i++) {
      draw_a_ce(&ce[i]);
    }
    XShapeCombineMask(XimServer::gDpy, mWin, ShapeBounding,
                  0, 0, m_mask_pix, ShapeSet);
    do_map();
}

void PeOvWin::draw_a_ce(char_ent *ce)
{
    draw_char(ce->x, ce->y, ce->c, ce->stat);
    XFillRectangle(XimServer::gDpy, m_mask_pix, m_mask_pix_gc,
               ce->x, ce->y - ce->height + 2,
               ce->width, ce->height + UNDERLINE_HEIGHT - 1);
    if (ce->stat & PE_UNDERLINE) {
      XDrawLine(XimServer::gDpy, mPixmap, mGC,
              ce->x, ce->y + UNDERLINE_HEIGHT,
              ce->x + ce->width, ce->y + UNDERLINE_HEIGHT);
    }
}

//
//
//
Convdisp::Convdisp(InputContext *k, icxatr *a)
{
    mKkContext = k;
    m_atr = a;
    mIMLang = get_im_lang_from_engine(k->get_engine_name());
    mEncoding = k->get_ic()->get_encoding();
    mLocaleName = k->get_locale_name();
}

Convdisp::~Convdisp()
{
}

void Convdisp::set_im_lang(const char *im_lang)
{
    mIMLang = im_lang;
}

void Convdisp::set_locale_name(const char *locale)
{
    mLocaleName = locale;
}

void Convdisp::set_pe(pe_stat *p)
{
    m_pe = p;
}

uString Convdisp::get_pe()
{
    uString s;
    std::list<pe_ustring>::iterator it;
    for (it = m_pe->ustrings.begin(); it != m_pe->ustrings.end(); it++) {
      append_ustring(&s, &(*it).s);
    }
    return s;
}

void Convdisp::set_focus()
{
    Canddisp *disp = canddisp_singleton();
    disp->show();
}

void Convdisp::unset_focus()
{
    Canddisp *disp = canddisp_singleton();
    disp->hide();
}


// Root window style
ConvdispRw::ConvdispRw(InputContext *k, icxatr *a) : Convdisp(k, a)
{
    mPeWin = NULL;
}

ConvdispRw::~ConvdispRw()
{
    if (mPeWin)
      delete mPeWin;
}

void ConvdispRw::update_preedit()
{
    if (!m_pe)
      return; 

    if (!m_pe->get_char_count()) {
      clear_preedit();
      return;
    }

    // preedit string exists
    if (!mPeWin)
      mPeWin = new PeLineWin(DefaultRootWindow(XimServer::gDpy), mIMLang, mEncoding, mLocaleName);
    
    if (m_atr->has_atr(ICA_ClientWindow)) {
      int x, y;
      Window win;
      XWindowAttributes xattr;

      XGetWindowAttributes(XimServer::gDpy, m_atr->client_window, &xattr);
      XTranslateCoordinates(XimServer::gDpy, m_atr->client_window, DefaultRootWindow(XimServer::gDpy), 0, 0, &x, &y, &win);
      mPeWin->set_pos(x, y + xattr.height);
    }

    mPeWin->do_map();
    mPeWin->draw_pe(m_pe);
    mPeWin->draw();

    move_candwin();
}

void ConvdispRw::clear_preedit()
{
    delete mPeWin;
    mPeWin = NULL;
}

void ConvdispRw::update_icxatr()
{
}

void ConvdispRw::move_candwin()
{
    if (m_atr->has_atr(ICA_ClientWindow)) {
      int x, y;
      Window win;
      XWindowAttributes xattr;

      XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
                        DefaultRootWindow(XimServer::gDpy),
                        0, 0, &x, &y, &win);

      Canddisp *disp = canddisp_singleton();

      XGetWindowAttributes(XimServer::gDpy, m_atr->client_window, &xattr);
      disp->move(x, y + xattr.height + 28); // lower-left side under the preedit window
    }
}

// Over the spot style
ConvdispOv::ConvdispOv(InputContext *k, icxatr *a) : Convdisp(k, a)
{
    m_ov_win = 0;
    m_initial_lang = strdup(mIMLang);
    m_initial_fontset = NULL;
    m_lang_changed = false;
}

ConvdispOv::~ConvdispOv()
{
    if (m_ov_win)
      delete m_ov_win;

    free(m_initial_lang);
}

void ConvdispOv::set_im_lang(const char *im_lang)
{
    mIMLang = im_lang;

    if (!strcmp(m_initial_lang, im_lang)) {
#if 0
      if (m_initial_fontset)
          m_atr->font_set = m_initial_fontset;
      else
          m_atr->font_set = choose_default_fontset(mLang);
#endif
    } else {
#if 0
      m_atr->font_set = choose_default_fontset(mLang);
#endif
      m_lang_changed = true;
    }
}


void ConvdispOv::update_preedit()
{
    if (!m_pe)
      return;

    if (!m_pe->get_char_count()) {
      clear_preedit();
      return;
    }

    draw_preedit();
    move_candwin();
}

void ConvdispOv::move_candwin()
{
    if (m_atr->has_atr(ICA_SpotLocation) &&
            mKkContext == InputContext::focusedContext()) {
      int x = -1, y = -1;
      Window win;

      if (m_atr->has_atr(ICA_ClientWindow)) {

          XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
                          DefaultRootWindow(XimServer::gDpy),
                          m_atr->spot_location.x,
                          m_atr->spot_location.y,
                          &x, &y, &win);
      }

      if (m_atr->has_atr(ICA_FocusWindow)) {

          XTranslateCoordinates(XimServer::gDpy, m_atr->focus_window,
                          DefaultRootWindow(XimServer::gDpy),
                          m_atr->spot_location.x,
                          m_atr->spot_location.y,
                          &x, &y, &win);
      }
#ifdef FLASHPLAYER_WORKAROUND
      if (revised_spot_y != -1) {
          XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
                          DefaultRootWindow(XimServer::gDpy),
                          m_atr->spot_location.x,
                          revised_spot_y,
                          &x, &y, &win);
      }
#endif
      if (x > -1 && y > -1) {
          Canddisp *disp = canddisp_singleton();
          disp->move(x, y + UNDERLINE_HEIGHT + 1);
      }
#ifdef FLASHPLAYER_WORKAROUND
    } else if (m_atr->has_atr(ICA_SpotLocation)) {
      int x = -1, y = -1;
      Window win;
      if (revised_spot_y != -1) {
          XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
                          DefaultRootWindow(XimServer::gDpy),
                          m_atr->spot_location.x,
                          revised_spot_y,
                          &x, &y, &win);
          Canddisp *disp = canddisp_singleton();
          disp->move(x, y + UNDERLINE_HEIGHT + 1);
      }
#endif
    }
}

void ConvdispOv::clear_preedit()
{
    delete m_ov_win;
    m_ov_win = NULL;
}

void ConvdispOv::validate_area()
{
    Window r, win;
    int x;
    unsigned int w, h, tmp;
    if (m_atr->has_atr(ICA_FocusWindow))
      win = m_atr->focus_window;
    else
      win = m_atr->client_window;

    XGetGeometry(XimServer::gDpy, win,
             &r, &x, &x, &w, &h, &tmp, &tmp);
    // Absurd... (cope with Qt from RedHat7.3, and maybe some other)
    m_atr->area.width = w;
    m_atr->area.height = h;
}

void ConvdispOv::update_icxatr()
{
    if (!m_ov_win)
      return;
    
    if (m_atr->is_changed(ICA_FocusWindow)) {
      // Some clients send FocusWindow later
      if (!check_win())
          return;
    }
    
    if (m_atr->is_changed(ICA_Area)) {
      if (m_atr->area.width == 0)
          validate_area();
      m_ov_win->set_size(m_atr->area.width, m_atr->area.height);
      m_atr->unset_change_mask(ICA_Area);
    }
  
    if (m_atr->is_changed(ICA_Foreground)) {
      m_ov_win->set_fore(m_atr->foreground_pixel);
      m_atr->unset_change_mask(ICA_Foreground);
    }
    if (m_atr->is_changed(ICA_Background)) {
      m_ov_win->set_back(m_atr->background_pixel);
      m_atr->unset_change_mask(ICA_Background);
    }
    if (m_atr->is_changed(ICA_FontSet)) {
      m_ov_win->set_fontset(m_atr->font_set);
      m_atr->unset_change_mask(ICA_FontSet);
    }
  
    update_preedit();
}

void ConvdispOv::draw_preedit()
{
    m_ce_len = m_pe->get_char_count();
    if (!check_win())
      return;

    m_ce = (char_ent *)malloc(sizeof(char_ent)*m_ce_len);
    make_ce_array();
    layoutCharEnt();
    do_draw_preedit();
    free(m_ce);
    m_ov_win->draw();
    XFlush(XimServer::gDpy);
}

void ConvdispOv::do_draw_preedit()
{
#ifdef FLASHPLAYER_WORKAROUND
    revised_spot_y = -1;
#endif

    if (m_atr->has_atr(ICA_Area)) {
#ifdef FLASHPLAYER_WORKAROUND
      // Workaround for brain damaged flash player plugin (at least
      // version 7.0 r25). --ekato
      //
      // Background: flashplayer plugin set same values for
      // m_atr->area.y and m_atr->spot_location.y.
      //
      // 1. If preedit area goes beyond the parent window (browser),
      // set the area at the upper side of client window.
      //
      // 2. If height for preedit is too narrow, make sure to set
      // preedit within the client window.

      if (m_atr->has_atr(ICA_SpotLocation) && (m_atr->area.y == m_atr->spot_location.y)) {
          XWindowAttributes clientattr, topattr;
          Window clientwin, topwin, win;
          int x, y;
          int font_height;
      
          topattr.height = 0;
          if (m_atr->has_atr(ICA_FocusWindow))
            clientwin = m_atr->focus_window;
          else
            clientwin = m_atr->client_window;

          XGetWindowAttributes(XimServer::gDpy, clientwin, &clientattr);
          topwin = getTopWindow(XimServer::gDpy, clientwin);
          if (topwin)
            XGetWindowAttributes(XimServer::gDpy, topwin, &topattr);
          XTranslateCoordinates(XimServer::gDpy, clientwin,
                      DefaultRootWindow(XimServer::gDpy),
                      m_atr->area.x,
                      m_atr->area.y,
                      &x, &y, &win);
          font_height = get_ce_font_height(m_ce, m_ce_len);

          if (topattr.height) {
            if (y > (topattr.height + topattr.y)) {
                // preedit area goes beyond the top window's geometry
                if ((y - m_atr->area.y) < topattr.y) {
                  // Set preedit at upper side of the top
                  // window.  But it may doesn't work because it
                  // lacks height for window manager's title bar
                  // and some toolbars of browser...
                  m_ov_win->set_pos(m_atr->area.x, topattr.y - (y - m_atr->area.y));
                  revised_spot_y = font_height + topattr.y - (y - m_atr->area.y);
                } else {
                  // Set preedit at upper side of the flash
                  // player's window.
                  m_ov_win->set_pos(m_atr->area.x, 0);
                  revised_spot_y = font_height;
                }
            } else {
                // preedit area is visible within the browser
                if ((clientattr.height - m_atr->area.y) < font_height + UNDERLINE_HEIGHT) {
                  // Make sure preedit+underline to be fit
                  // within the client window.
                  m_ov_win->set_pos(m_atr->area.x, clientattr.height - font_height - (UNDERLINE_HEIGHT + 1));
                  revised_spot_y = clientattr.height - (UNDERLINE_HEIGHT + 1);
                } else {
                  m_ov_win->set_pos(m_atr->area.x, m_atr->area.y);
                  revised_spot_y = font_height;
                }
            }
          }
      } else
#endif // FLASHPLAYER_WORKAROUND
          m_ov_win->set_pos(m_atr->area.x, m_atr->area.y);
    } else {
      m_ov_win->set_pos(0, 0);
    }
    m_ov_win->draw_ce(m_ce, m_ce_len);
}

bool ConvdispOv::check_win()
{
    if (!check_atr())
      return false; // not enough information to map preedit
  
    if (m_ov_win && !m_atr->is_changed(ICA_FocusWindow))
      return true; // no need to update window

    if (m_ov_win)
      delete m_ov_win;
  
    m_atr->unset_change_mask(ICA_FocusWindow);
    m_atr->unset_change_mask(ICA_Foreground);
    m_atr->unset_change_mask(ICA_Background);
    m_atr->unset_change_mask(ICA_FontSet);

    Window w;
    if (m_atr->has_atr(ICA_FocusWindow))
      w = m_atr->focus_window;
    else
      w = m_atr->client_window;

    m_ov_win = new PeOvWin(w, mIMLang, mEncoding, mLocaleName);
    m_ov_win->set_size(m_atr->area.width, m_atr->area.height);
    m_ov_win->set_fore(m_atr->foreground_pixel);
    m_ov_win->set_back(m_atr->background_pixel);
    m_ov_win->set_fontset(m_atr->font_set);
  
    return true;
}

bool ConvdispOv::check_atr()
{
    if (!m_atr->has_atr(ICA_FocusWindow) &&
      !m_atr->has_atr(ICA_ClientWindow))
      return false;

    if (!m_atr->has_atr(ICA_SpotLocation)) {
      // set at top-left corner unless SpotLocation is available
      m_atr->spot_location.x = 0;
      m_atr->spot_location.y = 0;
    }
    if (!m_atr->has_atr(ICA_Area) ||
      m_atr->area.width == 0) {
      validate_area();
      m_atr->area.x = 0;
      m_atr->area.y = 0;
    }
    if (!m_atr->has_atr(ICA_FontSet))
      m_atr->font_set = choose_default_fontset(mIMLang, mEncoding, mLocaleName);
    else {
      if (!m_initial_fontset && !m_lang_changed)
          m_initial_fontset = m_atr->font_set;
    }
    if (!m_atr->has_atr(ICA_LineSpace)) {
      m_atr->line_space = 16;
    }

    if (!m_atr->has_atr(ICA_Foreground))
      m_atr->foreground_pixel = BlackPixel(XimServer::gDpy,
                                   DefaultScreen(XimServer::gDpy));

    if (!m_atr->has_atr(ICA_Background))
      m_atr->background_pixel = WhitePixel(XimServer::gDpy,
                                   DefaultScreen(XimServer::gDpy));

    return true;
}

void ConvdispOv::make_ce_array()
{
    std::list<pe_ustring>::iterator i;
    uString::iterator j;
    int s;
    int c = 0;
    for (i = m_pe->ustrings.begin(); i != m_pe->ustrings.end(); i++) {
      s = (*i).stat;
      for (j = (*i).s.begin(); j != (*i).s.end(); j++) {
          m_ce[c].c = *j;
          m_ce[c].stat = s;
          c++;
      }
    }
}

// Dirty,,, 
void ConvdispOv::layoutCharEnt()
{
    int i;
    int x, y;

    x = m_atr->spot_location.x;
    y = m_atr->spot_location.y;

    for (i = 0; i < m_ce_len; i++) {
      uchar ch = m_ce[i].c;
      XRectangle ink, logical;

      if (!strcmp(mEncoding, "UTF-8"))
          XwcTextExtents(m_atr->font_set, &ch, 1, &ink, &logical);
      else {
          char utf8[6];
          int len = utf8_wctomb((unsigned char *)utf8, ch);
          utf8[len] = '\0';
          char *str;
          str = utf8_to_native_str(utf8, mEncoding);
          if (!str) {
            logical.width = 0;
            logical.height = (i > 0) ? m_ce[i - 1].height : 0;
          } else {
            len = strlen(str);
            XmbTextExtents(m_atr->font_set, str, len, &ink, &logical);
            free(str);
          }
      }

      m_ce[i].width = logical.width;
      m_ce[i].height = logical.height;
      int right_limit = m_atr->area.width;
      if (m_atr->has_atr(ICA_Area))
          right_limit += m_atr->area.x;

      if (m_ce[i].width + x >
          right_limit) {
          // goto next line
          x = m_atr->area.x;
          y += m_atr->line_space;
      }
      m_ce[i].x = x;
      m_ce[i].y = y;
      x += m_ce[i].width;
    }
    if (m_atr->has_atr(ICA_Area)) {
      for (i = 0; i < m_ce_len; i++) {
          m_ce[i].x -= m_atr->area.x;
          m_ce[i].y -= m_atr->area.y;
#ifdef FLASHPLAYER_WORKAROUND
          // workaround for brain damaged flash player plugin
          if (m_ce[i].y == 0)
            m_ce[i].y += m_ce[i].height;
#endif
      }
    }
}

int ConvdispOv::calc_ce_width(int b, int e)
{
    int i, w = 0;
    for (i = b; i < e; i++) {
      w += m_ce[i].width;
    }
    return w;
}

#ifdef FLASHPLAYER_WORKAROUND
int ConvdispOv::get_ce_font_height(char_ent *ce, int len)
{
    int i, h = 0;
    for (i = 0; i < len; i++) {
      if (ce[i].height > h)
          h = ce[i].height;
    }
    return h;
}
#endif

// On the spot style
ConvdispOs::ConvdispOs(InputContext *k, icxatr *a, Connection *c)
    : Convdisp(k, a)
{
    XimIC *ic = k->get_ic();
    mConn = c;
    mImid = ic->get_imid();
    mIcid = ic->get_icid();
    mPrevLen = 0;
}

ConvdispOs::~ConvdispOs()
{
}

void ConvdispOs::update_preedit()
{
    if (!m_pe)
      return;

    TxPacket *t;

    int len, caret_pos;
    len = m_pe->get_char_count();
    caret_pos = m_pe->caret_pos;

    if (mPrevLen == 0 && len == 0)
      return;

    if (mPrevLen == 0 && len) {
      t = createTxPacket(XIM_PREEDIT_START, 0);
      t->pushC16(mImid);
      t->pushC16(mIcid);
      mConn->push_passive_packet(t);
    }

    t = createTxPacket(XIM_PREEDIT_DRAW, 0);
    t->pushC16(mImid);
    t->pushC16(mIcid);
    t->pushC32(caret_pos);// caret
    t->pushC32(0); // chg_first
    t->pushC32(mPrevLen); // chg_length

    if (m_pe->ustrings.size())
      t->pushC32(0);
    else
      t->pushC32(3);
  
    compose_preedit_array(t);
    compose_feedback_array(t);
    mConn->push_passive_packet(t);

    if (mPrevLen && len == 0) {
      t = createTxPacket(XIM_PREEDIT_DONE, 0);
      t->pushC16(mImid);
      t->pushC16(mIcid);
      mConn->push_passive_packet(t);
    }
    mPrevLen = len;
    /*
      t = createTxPacket(XIM_PREEDIT_CARET,0);
      t->pushC16(mImid);
      t->pushC16(mIcid);
      t->pushC32(pos);
      t->pushC32(dir);
      t->pushC32(style);
      mConn->push_packet(t);
      */
    move_candwin();
}
      
void ConvdispOs::move_candwin()
{
    if (m_atr->has_atr(ICA_ClientWindow)) {
      int x, y;
      Window win;
      XWindowAttributes xattr;

      if (!m_atr->has_atr(ICA_SpotLocation)) {
          m_atr->spot_location.x = 0;
          m_atr->spot_location.y = 0;
      }

      XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
                        DefaultRootWindow(XimServer::gDpy),
                        m_atr->spot_location.x,
                        m_atr->spot_location.y,
                        &x, &y, &win);

      Canddisp *disp = canddisp_singleton();

      if (m_atr->has_atr(ICA_SpotLocation))
          disp->move(x, y + 36); // 36 pixel seems too long.
                           // Is there any way to get current preedit
                           // height?
      else {
          XGetWindowAttributes(XimServer::gDpy, m_atr->client_window,
                        &xattr);
          //disp->move(x + xattr.width + 2, y + 2); //upper-right side
          disp->move(x, y + xattr.height + 2); //lower-left side
      }
    }    
    
}

void ConvdispOs::clear_preedit()
{
    mPrevLen = 0;
}

void ConvdispOs::update_icxatr()
{
}

void ConvdispOs::compose_preedit_array(TxPacket *t)
{
    uString s;
    std::list<pe_ustring>::iterator it;
    for (it = m_pe->ustrings.begin(); it != m_pe->ustrings.end(); it++) {
      append_ustring(&s, &(*it).s);
    }

    char *c = mKkContext->getServer()->uStringToCtext(&s, mEncoding);
    int i, len = 0;
    if (c)
      len = strlen(c);
    t->pushC16(len); // LENGTH
    for (i = 0; i < len; i++) {
      t->pushC8(c[i]); // CTEXT
    }
    len = pad4(len + 2);
    for (i = 0; i < len; i++) {
      t->pushC8(0); // PADDING
    }
    if (c)
      free(c);
}

void ConvdispOs::compose_feedback_array(TxPacket *t)
{
    int i, len, stat, xstat;
    len = m_pe->get_char_count();
    t->pushC16(len * 4);
    t->pushC16(0);
    std::list<pe_ustring>::iterator it;
    for (it = m_pe->ustrings.begin(); it != m_pe->ustrings.end(); it++) {
      len = (*it).s.size();
      stat = (*it).stat;
      xstat = FB_None;
      if (stat & PE_REVERSE)
          xstat |= FB_Reverse;

      if (stat & PE_UNDERLINE)
          xstat |= FB_Underline;

      if (stat & PE_HILIGHT)
          xstat |= FB_Highlight;

      for (i = 0; i < len; i++) {
          t->pushC32(xstat);
      }
    }
}

#ifdef FLASHPLAYER_WORKAROUND
static Window getTopWindow(Display *d, Window w)
{
    Window root, parent, *children;
    unsigned int nchild;
    Status retval;

    for (;;) {
      retval = XQueryTree(d, w, &root, &parent, &children, &nchild);
      if (retval == 0)
          return 0;
      if (children)
          XFree(children);
      if (parent == root)
          break;
      w = parent;
    }
    return w;
};
#endif

/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 * End:
 */

Generated by  Doxygen 1.6.0   Back to index