1 /*
2 
3   Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 */
32 
33 // classes for preedit draw
34 
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38 
39 #include <X11/Xlib.h>
40 #include <X11/Xutil.h>
41 #include <X11/extensions/shape.h>
42 #if HAVE_XFT_UTF8_STRING
43 #include <X11/Xft/Xft.h>
44 #endif
45 #include <clocale>
46 #include <cstdlib>
47 #include "xim.h"
48 #include "ximserver.h"
49 #include "convdisp.h"
50 #include "canddisp.h"
51 #include "xdispatch.h"
52 #include "util.h"
53 
54 #include "uim/uim-scm.h"
55 
56 #define UNDERLINE_HEIGHT	2
57 #define DEFAULT_FONT_SIZE	16
58 // Temporal hack for flashplayer plugin's broken over-the-spot XIM style
59 #define FLASHPLAYER7_WORKAROUND
60 #define FLASHPLAYER9_WORKAROUND
61 
62 //
63 // PeWin:     Base class for preedit window
64 // PeLineWin: Child class for root window style preedit window
65 // PeOvWin:   Child class for over the spot style preedit window
66 
67 #ifdef FLASHPLAYER7_WORKAROUND
68 static Window getTopWindow(Display *, Window);
69 #endif
70 
71 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";
72 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";
73 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";
74 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";
75 
76 
77 #if HAVE_XFT_UTF8_STRING
78 XftFont *gXftFont;
79 char *gXftFontName;
80 char *gXftFontLocale;
81 
82 void
init_default_xftfont()83 init_default_xftfont() {
84     char *fontname = uim_scm_symbol_value_str("uim-xim-xft-font-name");
85     gXftFontName = fontname;
86 
87     gXftFont = XftFontOpen(XimServer::gDpy, DefaultScreen(XimServer::gDpy),
88 		    XFT_FAMILY, XftTypeString, fontname,
89 		    XFT_PIXEL_SIZE, XftTypeDouble, (double)DEFAULT_FONT_SIZE,
90 		    (char *)NULL);
91     gXftFontLocale = strdup(setlocale(LC_CTYPE, NULL));
92     // maybe not needed, but in case it return NULL...
93     if (!gXftFont) {
94 	gXftFont = XftFontOpen(XimServer::gDpy, DefaultScreen(XimServer::gDpy),
95 			XFT_FAMILY, XftTypeString, "Sans",
96 			XFT_PIXEL_SIZE, XftTypeDouble, (double)DEFAULT_FONT_SIZE,
97 			(char *)NULL);
98     }
99 }
100 
101 void
update_default_xftfont()102 update_default_xftfont() {
103     char *fontname;
104 
105     if (!uim_scm_symbol_value_bool("uim-xim-use-xft-font?"))
106       return;
107 
108     fontname = uim_scm_symbol_value_str("uim-xim-xft-font-name");
109 
110     if (fontname) {
111 	XftFont *xftfont = XftFontOpen(XimServer::gDpy,
112 			DefaultScreen(XimServer::gDpy),
113 			XFT_FAMILY, XftTypeString, fontname,
114 			XFT_PIXEL_SIZE, XftTypeDouble, (double)DEFAULT_FONT_SIZE,
115 			(char *)NULL);
116 	if (xftfont) {
117 	    if (gXftFont)
118 		XftFontClose(XimServer::gDpy, gXftFont);
119 	    free(gXftFontName);
120 	    free(gXftFontLocale);
121 	    gXftFont = xftfont;
122 	    gXftFontName = fontname;
123 	    gXftFontLocale = strdup(setlocale(LC_CTYPE, NULL));
124 	} else {
125 	    free(fontname);
126 	}
127     }
128 }
129 #endif
130 
131 static XFontSet
create_default_fontset(const char * im_lang,const char * locale)132 create_default_fontset(const char *im_lang, const char *locale) {
133     char *orig_locale;
134     const char *name;
135     XFontSet ret;
136 
137     orig_locale = strdup(setlocale(LC_CTYPE, NULL));
138 
139     if (strcmp(locale, orig_locale))
140 	setlocale(LC_CTYPE, locale);
141 
142     if (!strcmp(im_lang, "ja"))
143 	name = fontset_ja;
144     else if (!strcmp(im_lang, "ko"))
145 	name = fontset_ko;
146     else if (!strcmp(im_lang, "zh_CN"))
147 	name = fontset_zhCN;
148     else if (!strcmp(im_lang, "zh_TW:zh_HK"))
149 	name = fontset_zhTW;
150     else
151 	name = fontset_ja; // XXX fallback fontset
152 
153     ret = get_font_set(name, locale);
154 
155     if (strcmp(locale, orig_locale))
156 	setlocale(LC_CTYPE, orig_locale);
157     free(orig_locale);
158 
159     return ret;
160 }
161 
162 static XFontSet
choose_default_fontset(const char * im_lang,const char * locale)163 choose_default_fontset(const char *im_lang, const char *locale) {
164     return create_default_fontset(im_lang, locale);
165 }
166 
167 struct char_ent {
168     uchar c;
169     int stat;
170     int width;
171     int height;
172     int x, y;
173 };
174 
175 // Preedit Window
176 class PeWin : public WindowIf {
177 public:
178     PeWin(Window pw, const char *im_lang, const char *encoding, const char *locale, Convdisp *cd);
179     virtual ~PeWin();
180 
181     virtual void release();
182 
183     virtual void destroy(Window w);
184     virtual void expose(Window w);
185 
186     void draw_char(int x, int y, uchar ch, int stat);
187     void set_back(unsigned long p);
188     void set_fore(unsigned long p);
189 #if HAVE_XFT_UTF8_STRING
190     void set_xftfont(const char *xfld);
191 #endif
192     void set_fontset(XFontSet f);
193 
194     virtual void set_size(int w, int h);
195     void set_pos(int x, int y);
196 
197     void do_map();
198 
199     void clear();
200     void draw();
201     void unmap();
202 
203 #if HAVE_XFT_UTF8_STRING
204     XftFont *mXftFont;
205     int mXftFontSize;
206 #endif
207 protected:
208 #if HAVE_XFT_UTF8_STRING
209     int get_fontsize(const char *xfld);
210 #endif
211     Window mParentWin;
212     Window mWin;
213     Pixmap mPixmap;
214     GC mGC, mClearGC;
215     unsigned int mFore, mBack;
216 #if HAVE_XFT_UTF8_STRING
217     XftDraw *mXftDraw;
218     XftColor mXftColorFg;
219     XftColor mXftColorFgRev;
220 #endif
221     XFontSet mFontset;
222     const char *mEncoding;
223     int mWidth, mHeight;
224     bool mIsMapped;
225     Convdisp *mConvdisp;
226 };
227 
228 // one line preedit window for RootWindowStyle
229 class PeLineWin : public PeWin {
230 public:
231     PeLineWin(Window w, const char *im_lang, const char *encoding, const char *locale, Convdisp *cd);
232     virtual ~PeLineWin();
233 
234     void draw_pe(pe_stat *p);
235     int mCandWinXOff;
236 
237 private:
238     void calc_extent(pe_stat *p);
239     int calc_segment_extent(pe_ustring *s);
240     void draw_segment(pe_ustring *s);
241     void draw_cursor();
242     int get_char_width(uchar ch);
243 
244     int m_x;
245     int mCharPos;
246     int mCursorX;
247 };
248 
249 // window for over the spot style
250 class PeOvWin : public PeWin {
251 public:
252     PeOvWin(Window w, const char *im_lang, const char *encoding, const char *locale, Convdisp *cd);
253 
254     void draw_ce(char_ent *ce, int len);
255     virtual void set_size(int w, int h);
256     virtual ~PeOvWin();
257 private:
258     void draw_a_ce(char_ent *ce);
259     void draw_cursor(char_ent *ce);
260     Pixmap m_mask_pix;
261     GC m_mask_pix_gc;
262 };
263 
264 class ConvdispOv : public Convdisp {
265 public:
266     ConvdispOv(InputContext *, icxatr *);
267     virtual ~ConvdispOv();
268     virtual void update_preedit();
269     virtual void clear_preedit();
270     virtual void update_icxatr();
271     virtual void move_candwin();
272     virtual bool use_xft();
273 private:
274     bool check_win();
275     bool check_atr();
276     void layoutCharEnt();
277     void make_ce_array();
278     void draw_preedit();
279     void do_draw_preedit();
280 #ifdef FLASHPLAYER7_WORKAROUND
281     int get_ce_font_height(char_ent *ce, int len);
282     int revised_spot_y;
283 #endif
284 
285     void validate_area();
286 
287     char_ent *m_ce;
288     int m_ce_len;
289     int m_candwin_x_off;
290     int m_candwin_y_off;
291     PeOvWin *m_ov_win;
292 };
293 
294 // Preedit window for RootWindowStyle
295 class ConvdispRw : public Convdisp {
296 public:
297     ConvdispRw(InputContext *, icxatr *);
298     virtual ~ConvdispRw();
299 
300     virtual void update_preedit();
301     virtual void clear_preedit();
302     virtual void update_icxatr();
303     virtual void move_candwin();
304     virtual bool use_xft();
305 private:
306     PeLineWin *mPeWin;
307 };
308 
309 class ConvdispOs : public Convdisp {
310 public:
311     ConvdispOs(InputContext *, icxatr *, Connection *);
312     virtual ~ConvdispOs();
313     virtual void update_preedit();
314     virtual void clear_preedit();
315     virtual void update_icxatr();
316     virtual void move_candwin();
317     virtual bool use_xft();
318 
319 private:
320     void compose_preedit_array(TxPacket *);
321     void compose_feedback_array(TxPacket *);
322 
323     Connection *mConn;
324     C16 mImid, mIcid;
325     int mPrevLen;
326 };
327 
create_convdisp(int style,InputContext * k,icxatr * a,Connection * c)328 Convdisp *create_convdisp(int style, InputContext *k,
329 			  icxatr *a, Connection *c)
330 {
331     switch (style) {
332     case IS_ROOT_WINDOW:
333 	return new ConvdispRw(k, a);
334     case IS_OVER_THE_SPOT:
335 	return new ConvdispOv(k, a);
336     case IS_ON_THE_SPOT:
337 	return new ConvdispOs(k, a, c);
338     default:
339 	break;
340     }
341     return 0;
342 }
343 
344 //
345 // PeWin(PreEdit Window)
346 //
PeWin(Window pw,const char * im_lang,const char * encoding,const char * locale,Convdisp * cd)347 PeWin::PeWin(Window pw, const char *im_lang, const char *encoding, const char *locale, Convdisp *cd)
348 {
349     mParentWin = pw;
350     mConvdisp = cd;
351     int scr_num = DefaultScreen(XimServer::gDpy);
352     // tentative
353     mWidth = 1;
354     mHeight = 1;
355     mWin = XCreateSimpleWindow(XimServer::gDpy, mParentWin,
356 			       0, 0, mWidth, mHeight, 0,
357 			       BlackPixel(XimServer::gDpy, scr_num),
358 			       WhitePixel(XimServer::gDpy, scr_num));
359     XSetWindowAttributes attr;
360     attr.override_redirect = True;
361     XChangeWindowAttributes(XimServer::gDpy, mWin, CWOverrideRedirect,
362 			    &attr);
363     mPixmap = XCreatePixmap(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy),
364 			    mWidth, mHeight,
365 			    DefaultDepth(XimServer::gDpy, scr_num));
366 
367     mGC = XCreateGC(XimServer::gDpy, mPixmap, 0, 0);
368     mClearGC = XCreateGC(XimServer::gDpy, mPixmap, 0, 0);
369 
370     XSetBackground(XimServer::gDpy, mGC, WhitePixel(XimServer::gDpy, scr_num));
371     XSetForeground(XimServer::gDpy, mGC, BlackPixel(XimServer::gDpy, scr_num));
372     XSetForeground(XimServer::gDpy, mClearGC, WhitePixel(XimServer::gDpy, scr_num));
373     XSetBackground(XimServer::gDpy, mClearGC, BlackPixel(XimServer::gDpy, scr_num));
374 
375     add_window_watch(mWin, this, EXPOSE_MASK|STRUCTURE_NOTIFY_MASK);
376     mIsMapped = false; //not mapped now
377 
378     if (mConvdisp->use_xft() == true) {
379 #if HAVE_XFT_UTF8_STRING
380 	mXftFontSize = DEFAULT_FONT_SIZE;
381 	if (!gXftFont)
382 	    init_default_xftfont();
383 	if (!strcmp(gXftFontLocale, locale)) {
384 	    mXftFont = gXftFont;
385 	} else {
386 	    mXftFont = XftFontOpen(XimServer::gDpy,
387 			    DefaultScreen(XimServer::gDpy),
388 			    XFT_FAMILY, XftTypeString, gXftFontName,
389 			    XFT_PIXEL_SIZE, XftTypeDouble, (double)mXftFontSize,
390 			    (char *)NULL);
391 	}
392 	mXftDraw = XftDrawCreate(XimServer::gDpy, mPixmap,
393 			DefaultVisual(XimServer::gDpy, scr_num),
394 			DefaultColormap(XimServer::gDpy, scr_num));
395 	XColor dummyc, fg;
396 	XAllocNamedColor(XimServer::gDpy, DefaultColormap(XimServer::gDpy, scr_num),"black", &fg, &dummyc);
397 	mXftColorFg.color.red = dummyc.red;
398 	mXftColorFg.color.green = dummyc.green;
399 	mXftColorFg.color.blue = dummyc.blue;
400 	mXftColorFg.color.alpha = 0xffff;
401 	mXftColorFg.pixel = fg.pixel;
402 
403 	XAllocNamedColor(XimServer::gDpy, DefaultColormap(XimServer::gDpy, scr_num),"white", &fg, &dummyc);
404 	mXftColorFgRev.color.red = dummyc.red;
405 	mXftColorFgRev.color.green = dummyc.green;
406 	mXftColorFgRev.color.blue = dummyc.blue;
407 	mXftColorFgRev.color.alpha = 0xffff;
408 	mXftColorFgRev.pixel = fg.pixel;
409 #endif
410     } else {
411 	mFontset = choose_default_fontset(im_lang, locale);
412     }
413 
414     mEncoding = encoding;
415 
416     XFlush(XimServer::gDpy);
417 }
418 
~PeWin()419 PeWin::~PeWin()
420 {
421     if (mWin)
422 	release();
423 
424     XFreePixmap(XimServer::gDpy, mPixmap);
425 
426     XFreeGC(XimServer::gDpy, mGC);
427     XFreeGC(XimServer::gDpy, mClearGC);
428 #if HAVE_XFT_UTF8_STRING
429     if (mConvdisp->use_xft() == true) {
430 	XftDrawDestroy(mXftDraw);
431 	if (mXftFont != gXftFont)
432 	    XftFontClose(XimServer::gDpy, mXftFont);
433     }
434 #endif
435 
436     XFlush(XimServer::gDpy);
437 }
438 
release()439 void PeWin::release()
440 {
441     remove_window_watch(mWin);
442     XDestroyWindow(XimServer::gDpy, mWin);
443     mWin = 0;
444 }
445 
destroy(Window w)446 void PeWin::destroy(Window w)
447 {
448     if (mWin && mWin == w)
449 	mWin = 0;
450 }
451 
expose(Window w)452 void PeWin::expose(Window w)
453 {
454     XCopyArea(XimServer::gDpy, mPixmap, w, mGC,
455 	      0, 0, mWidth, mHeight, 0, 0);
456 
457     XFlush(XimServer::gDpy);
458 }
459 
draw_char(int x,int y,uchar ch,int stat)460 void PeWin::draw_char(int x, int y, uchar ch, int stat)
461 {
462     GC gc = mGC;
463     if (stat & PE_REVERSE)
464 	gc = mClearGC;
465 
466     char utf8[6];
467     int len = utf8_wctomb((unsigned char *)utf8, ch);
468     utf8[len] = '\0';
469 
470     if (mConvdisp->use_xft() == true) {
471 #ifdef HAVE_XFT_UTF8_STRING
472 	XGlyphInfo ginfo;
473 	XftTextExtentsUtf8(XimServer::gDpy, mXftFont, (unsigned char *)utf8, len, &ginfo);
474 	if (stat & PE_REVERSE) {
475 	    XftDrawRect(mXftDraw, &mXftColorFg, x, y - (mXftFontSize - 2), ginfo.xOff, mXftFontSize);
476 	    XftDrawStringUtf8(mXftDraw, &mXftColorFgRev, mXftFont, x, y, (unsigned char *)utf8, len);
477 	} else {
478 	    XftDrawStringUtf8(mXftDraw, &mXftColorFg, mXftFont, x, y, (unsigned char *)utf8, len);
479 	}
480 #endif
481     } else {
482 	if (!strcmp(mEncoding, "UTF-8")) {
483     	    XwcDrawImageString(XimServer::gDpy, mPixmap, mFontset,
484 			gc, x, y, &ch, 1);
485 	} else {
486 	    char *native_str;
487 	    XimIM *im = get_im_by_id(mConvdisp->get_context()->get_ic()->get_imid());
488 
489 	    native_str = im->utf8_to_native_str(utf8);
490 	    if (!native_str)
491 		return;
492 	    len = static_cast<int>(strlen(native_str));
493 	    XmbDrawImageString(XimServer::gDpy, mPixmap, mFontset,
494 			   gc, x, y, native_str, len);
495 	    free(native_str);
496 	}
497     }
498 }
499 
set_back(unsigned long p)500 void PeWin::set_back(unsigned long p)
501 {
502     mBack = static_cast<unsigned int>(p);
503     XSetBackground(XimServer::gDpy, mGC, p);
504     XSetForeground(XimServer::gDpy, mClearGC, p);
505 #if HAVE_XFT_UTF8_STRING
506     if (mConvdisp->use_xft() == true) {
507 	XColor xcolor;
508 	xcolor.pixel = p;
509 	XQueryColor(XimServer::gDpy, DefaultColormap(XimServer::gDpy, DefaultScreen(XimServer::gDpy)), &xcolor);
510 	mXftColorFgRev.pixel = p;
511 	mXftColorFgRev.color.red = xcolor.red;
512 	mXftColorFgRev.color.green = xcolor.green;
513 	mXftColorFgRev.color.blue = xcolor.blue;
514 	mXftColorFgRev.color.alpha = 0xffff;
515     }
516 #endif
517 }
518 
set_fore(unsigned long p)519 void PeWin::set_fore(unsigned long p)
520 {
521     mFore = static_cast<unsigned int>(p);
522     XSetForeground(XimServer::gDpy, mGC, p);
523     XSetBackground(XimServer::gDpy, mClearGC, p);
524 #if HAVE_XFT_UTF8_STRING
525     if (mConvdisp->use_xft() == true) {
526 	XColor xcolor;
527 	xcolor.pixel = p;
528 	XQueryColor(XimServer::gDpy, DefaultColormap(XimServer::gDpy, DefaultScreen(XimServer::gDpy)), &xcolor);
529 	mXftColorFg.pixel = p;
530 	mXftColorFg.color.red = xcolor.red;
531 	mXftColorFg.color.green = xcolor.green;
532 	mXftColorFg.color.blue = xcolor.blue;
533 	mXftColorFg.color.alpha = 0xffff;
534     }
535 #endif
536 }
537 
set_fontset(XFontSet f)538 void PeWin::set_fontset(XFontSet f)
539 {
540     if (f)
541 	mFontset = f;
542 }
543 
544 #if HAVE_XFT_UTF8_STRING
set_xftfont(const char * xfld)545 void PeWin::set_xftfont(const char *xfld)
546 {
547 	int size = get_fontsize(xfld);
548 	const char *locale = mConvdisp->get_locale_name();
549 
550 	if (!gXftFont)
551 	    init_default_xftfont();
552 	if (size != -1 && (mXftFontSize != size || strcmp(locale, gXftFontLocale))) {
553 	    if (mXftFont != gXftFont)
554 		XftFontClose(XimServer::gDpy, mXftFont);
555 
556 	    mXftFont = XftFontOpen(XimServer::gDpy,
557 			    DefaultScreen(XimServer::gDpy),
558 			    XFT_FAMILY, XftTypeString, gXftFontName,
559 			    XFT_PIXEL_SIZE, XftTypeDouble, (double)size,
560 			    (char *)NULL);
561 	    mXftFontSize = size;
562 	}
563 }
564 
get_fontsize(const char * xfld)565 int PeWin::get_fontsize(const char *xfld)
566 {
567 #define MAX_DIGIT_OF_PIXEL_SIZE	3
568     int size;
569     char str[MAX_DIGIT_OF_PIXEL_SIZE + 1];
570     const char *p = xfld;
571     int count = 0;
572     int i, j = 0;
573 
574     for (i = 0; i < (int)strlen(xfld); i++) {
575 	if (p[i] == '-')
576 	    count++;
577 	if (count == 7) {
578 	    i++;
579 	    while (p[i] != '-' && p[i] != '\0') {
580 		str[j] = p[i];
581 		i++;
582 		j++;
583 		if (j > MAX_DIGIT_OF_PIXEL_SIZE)
584 		    return -1;
585 	    }
586 	    str[j] = '\0';
587 	    break;
588 	}
589     }
590     if (!sscanf(str, "%d", &size))
591 	return -1;
592     return size;
593 }
594 #endif
595 
set_size(int w,int h)596 void PeWin::set_size(int w, int h)
597 {
598     if (w == mWidth && h == mHeight)
599 	return;
600 
601     XResizeWindow(XimServer::gDpy, mWin, w, h);
602     XFreePixmap(XimServer::gDpy, mPixmap);
603     mPixmap = XCreatePixmap(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy), w, h,
604 			    DefaultDepth(XimServer::gDpy, DefaultScreen(XimServer::gDpy)));
605 #if HAVE_XFT_UTF8_STRING
606     if (mConvdisp->use_xft() == true)
607 	XftDrawChange(mXftDraw, mPixmap);
608 #endif
609     mWidth = w;
610     mHeight = h;
611     clear();
612 }
613 
set_pos(int x,int y)614 void PeWin::set_pos(int x, int y)
615 {
616     XWindowChanges ch;
617     ch.x = x;
618     ch.y = y;
619     XConfigureWindow(XimServer::gDpy, mWin, CWX|CWY, &ch);
620 }
621 
do_map()622 void PeWin::do_map()
623 {
624     if (!mIsMapped) {
625 	XFlush(XimServer::gDpy);
626 	XMapRaised(XimServer::gDpy, mWin);
627 	mIsMapped = true;
628     }
629 }
630 
clear()631 void PeWin::clear()
632 {
633     XFillRectangle(XimServer::gDpy, mPixmap, mClearGC,
634 		   0, 0, mWidth, mHeight);
635 }
636 
draw()637 void PeWin::draw()
638 {
639     if (!mIsMapped)
640 	do_map();
641     else
642 	expose(mWin);
643 }
644 
unmap()645 void PeWin::unmap()
646 {
647     if (mIsMapped) {
648 	XUnmapWindow(XimServer::gDpy, mWin);
649 	XFlush(XimServer::gDpy);
650 	mIsMapped = false;
651     }
652 }
653 
654 //
655 // PeLineWin
656 //
657 #define PE_LINE_WIN_WIDTH	400
658 #define PE_LINE_WIN_HEIGHT	28
659 #define PE_LINE_WIN_FONT_POS_Y	20
660 #define PE_LINE_WIN_MARGIN_X	2
661 
PeLineWin(Window w,const char * im_lang,const char * encoding,const char * locale,Convdisp * cd)662 PeLineWin::PeLineWin(Window w, const char *im_lang, const char *encoding, const char *locale, Convdisp *cd) : PeWin(w, im_lang, encoding, locale, cd)
663 {
664     set_size(PE_LINE_WIN_WIDTH, PE_LINE_WIN_HEIGHT);
665     clear();
666 }
667 
~PeLineWin()668 PeLineWin::~PeLineWin()
669 {
670 }
671 
draw_pe(pe_stat * p)672 void PeLineWin::draw_pe(pe_stat *p)
673 {
674     clear();
675     calc_extent(p);
676     m_x = PE_LINE_WIN_MARGIN_X;
677     mCursorX = m_x;
678     mCharPos = 0;
679     std::list<pe_ustring>::iterator i;
680     for (i = p->ustrings.begin(); i != p->ustrings.end(); ++i) {
681 	draw_segment(&(*i));
682     }
683     draw_cursor();
684 }
685 
draw_cursor()686 void PeLineWin::draw_cursor()
687 {
688     XDrawLine(XimServer::gDpy, mPixmap, mGC,
689 		    mCursorX,
690 		    (PE_LINE_WIN_HEIGHT - PE_LINE_WIN_FONT_POS_Y) / 2 + 1,
691 		    mCursorX,
692 		    PE_LINE_WIN_FONT_POS_Y + 1);
693 }
694 
get_char_width(uchar ch)695 int PeLineWin::get_char_width(uchar ch)
696 {
697     int width = 0;
698     char utf8[6];
699 
700     int len = utf8_wctomb((unsigned char *)utf8, ch);
701     utf8[len] = '\0';
702 
703     if (mConvdisp->use_xft() == true) {
704 #ifdef HAVE_XFT_UTF8_STRING
705 	XGlyphInfo ginfo;
706 	XftTextExtentsUtf8(XimServer::gDpy, mXftFont, (unsigned char *)utf8,
707 			len, &ginfo);
708 	width = ginfo.xOff;
709 #endif
710     } else {
711 	XRectangle ink, logical;
712 
713 	if (!strcmp(mEncoding, "UTF-8")) {
714 	    XwcTextExtents(mFontset, &ch, 1, &ink, &logical);
715 	} else {
716 	    char *native_str;
717 	    XimIM *im = get_im_by_id(mConvdisp->get_context()->get_ic()->get_imid());
718 
719 	    native_str = im->utf8_to_native_str(utf8);
720 	    if (!native_str)
721 		return 0;
722 	    len = static_cast<int>(strlen(native_str));
723 	    XmbTextExtents(mFontset, native_str, len, &ink, &logical);
724 	    free(native_str);
725 	}
726 	width = logical.width;
727     }
728 
729     return width;
730 }
731 
draw_segment(pe_ustring * s)732 void PeLineWin::draw_segment(pe_ustring *s)
733 {
734     uString::iterator i;
735     int caret_pos = mConvdisp->get_caret_pos();
736 
737     for (i = s->s.begin(); i != s->s.end(); ++i) {
738 	uchar ch = *i;
739 	int width = get_char_width(ch);
740 	draw_char(m_x, PE_LINE_WIN_FONT_POS_Y, ch, s->stat);
741 	mCharPos++;
742 
743 	if (s->stat & PE_UNDERLINE) {
744 	    XDrawLine(XimServer::gDpy, mPixmap, mGC,
745 			    m_x, PE_LINE_WIN_FONT_POS_Y + UNDERLINE_HEIGHT,
746 			    m_x + width, PE_LINE_WIN_FONT_POS_Y + UNDERLINE_HEIGHT);
747 	}
748 	m_x += width;
749 	if (mCharPos == caret_pos)
750 	    mCursorX= m_x;
751     }
752 
753     switch (XimServer::gCandWinPosType) {
754     case Caret:
755 	mCandWinXOff = mCursorX;
756 	break;
757     case Right:
758 	mCandWinXOff = m_x;
759 	break;
760     case Left:
761     default:
762 	mCandWinXOff = 0;
763 	break;
764     }
765 }
766 
calc_segment_extent(pe_ustring * s)767 int PeLineWin::calc_segment_extent(pe_ustring *s)
768 {
769     int width = 0;
770     uString::iterator i;
771 
772     for (i = s->s.begin(); i != s->s.end(); ++i) {
773 	uchar ch = *i;
774 	width += get_char_width(ch);
775     }
776     return width;
777 }
778 
calc_extent(pe_stat * p)779 void PeLineWin::calc_extent(pe_stat *p)
780 {
781     int width = 0;
782     std::list<pe_ustring>::iterator i;
783 
784     for (i = p->ustrings.begin(); i != p->ustrings.end(); ++i)
785 	width += calc_segment_extent(&(*i));
786 
787     if (width < PE_LINE_WIN_WIDTH)
788 	set_size(PE_LINE_WIN_WIDTH, PE_LINE_WIN_HEIGHT);
789     else
790 	set_size(width + PE_LINE_WIN_MARGIN_X * 2, PE_LINE_WIN_HEIGHT);
791 }
792 
793 
794 //
795 // PeOvWin
796 //
PeOvWin(Window w,const char * im_lang,const char * encoding,const char * locale,Convdisp * cd)797 PeOvWin::PeOvWin(Window w, const char *im_lang, const char *encoding, const char *locale, Convdisp *cd) : PeWin(w, im_lang, encoding, locale, cd)
798 {
799     m_mask_pix = 0;
800     m_mask_pix_gc = 0;
801 }
802 
~PeOvWin()803 PeOvWin::~PeOvWin()
804 {
805     if (m_mask_pix) {
806 	XFreePixmap(XimServer::gDpy, m_mask_pix);
807 	XFreeGC(XimServer::gDpy, m_mask_pix_gc);
808     }
809 }
810 
set_size(int w,int h)811 void PeOvWin::set_size(int w, int h)
812 {
813     if (w == mWidth && h == mHeight)
814 	return;
815 
816     PeWin::set_size(w, h);
817     if (m_mask_pix) {
818 	XFreePixmap(XimServer::gDpy, m_mask_pix);
819 	m_mask_pix = XCreatePixmap(XimServer::gDpy, mWin,
820 				   mWidth, mHeight, 1);
821     }
822 }
823 
draw_ce(char_ent * ce,int len)824 void PeOvWin::draw_ce(char_ent *ce, int len)
825 {
826     if (m_mask_pix == 0) {
827 	m_mask_pix = XCreatePixmap(XimServer::gDpy, mWin, mWidth, mHeight, 1);
828 	m_mask_pix_gc = XCreateGC(XimServer::gDpy, m_mask_pix, 0, 0);
829     }
830 
831 
832     clear();
833     XSetForeground(XimServer::gDpy, m_mask_pix_gc,
834 		   BlackPixel(XimServer::gDpy,
835 			      DefaultScreen(XimServer::gDpy)));
836     XFillRectangle(XimServer::gDpy, m_mask_pix,
837 		   m_mask_pix_gc, 0, 0, mWidth, mHeight);
838     XSetForeground(XimServer::gDpy, m_mask_pix_gc,
839 		   WhitePixel(XimServer::gDpy,
840 			      DefaultScreen(XimServer::gDpy)));
841     int i;
842     for (i = 0; i < len; i++) {
843 	draw_a_ce(&ce[i]);
844     }
845     draw_cursor(ce);
846     XShapeCombineMask(XimServer::gDpy, mWin, ShapeBounding,
847 		      0, 0, m_mask_pix, ShapeSet);
848     do_map();
849 }
850 
851 #define CURSOR_WIDTH	1
draw_a_ce(char_ent * ce)852 void PeOvWin::draw_a_ce(char_ent *ce)
853 {
854     draw_char(ce->x, ce->y, ce->c, ce->stat);
855 
856     XFillRectangle(XimServer::gDpy, m_mask_pix, m_mask_pix_gc,
857 		   ce->x, ce->y - ce->height + 2,
858 		   ce->width + CURSOR_WIDTH, ce->height + UNDERLINE_HEIGHT - 1);
859     if (ce->stat & PE_UNDERLINE) {
860 	XDrawLine(XimServer::gDpy, mPixmap, mGC,
861 		  ce->x, ce->y + UNDERLINE_HEIGHT,
862 		  ce->x + ce->width, ce->y + UNDERLINE_HEIGHT);
863     }
864 }
865 
draw_cursor(char_ent * ce)866 void PeOvWin::draw_cursor(char_ent *ce)
867 {
868     int x;
869     int caret_pos = mConvdisp->get_caret_pos();
870     char_ent *caret_ce;
871 
872     if (caret_pos == 0) {
873 	caret_ce = &ce[caret_pos];
874 	x = caret_ce->x;
875     } else {
876 	caret_ce = &ce[caret_pos - 1];
877 	x = caret_ce->x + caret_ce->width;
878     }
879 
880     XDrawLine(XimServer::gDpy, mPixmap, mGC,
881 		    x, caret_ce->y - caret_ce->height,
882 		    x, caret_ce->y);
883 }
884 
885 //
886 //
887 //
Convdisp(InputContext * k,icxatr * a)888 Convdisp::Convdisp(InputContext *k, icxatr *a)
889 {
890     mKkContext = k;
891     m_atr = a;
892     mIMLang = get_im_lang_from_engine(k->get_engine_name());
893     mEncoding = k->get_ic()->get_encoding();
894     mLocaleName = k->get_locale_name();
895 }
896 
~Convdisp()897 Convdisp::~Convdisp()
898 {
899 }
900 
set_im_lang(const char * im_lang)901 void Convdisp::set_im_lang(const char *im_lang)
902 {
903     mIMLang = im_lang;
904 }
905 
set_locale_name(const char * locale)906 void Convdisp::set_locale_name(const char *locale)
907 {
908     mLocaleName = locale;
909 }
910 
get_locale_name()911 const char *Convdisp::get_locale_name()
912 {
913     return mLocaleName;
914 }
915 
set_pe(pe_stat * p)916 void Convdisp::set_pe(pe_stat *p)
917 {
918     m_pe = p;
919 }
920 
get_pe()921 uString Convdisp::get_pe()
922 {
923     uString s;
924     std::list<pe_ustring>::iterator it;
925     for (it = m_pe->ustrings.begin(); it != m_pe->ustrings.end(); ++it) {
926 	append_ustring(&s, &(*it).s);
927     }
928     return s;
929 }
930 
set_focus()931 void Convdisp::set_focus()
932 {
933     Canddisp *disp = canddisp_singleton();
934     disp->show();
935 }
936 
unset_focus()937 void Convdisp::unset_focus()
938 {
939     Canddisp *disp = canddisp_singleton();
940     disp->hide();
941 }
942 
get_context()943 InputContext *Convdisp::get_context()
944 {
945     return mKkContext;
946 }
947 
get_caret_pos()948 int Convdisp::get_caret_pos()
949 {
950     if (!m_pe)
951 	return 0;
952     return m_pe->caret_pos;
953 }
954 
update_caret_state()955 void Convdisp::update_caret_state()
956 {
957     if (!uim_scm_symbol_value_bool("bridge-show-input-state?"))
958 	return;
959 
960     Canddisp *disp = canddisp_singleton();
961     InputContext *focusedContext = InputContext::focusedContext();
962 
963     if (focusedContext && focusedContext == mKkContext) {
964 	if (mKkContext->isCaretStateShown())
965 	    disp->update_caret_state();
966 	else
967 	    disp->hide_caret_state();
968     }
969 }
970 
971 // Root window style
ConvdispRw(InputContext * k,icxatr * a)972 ConvdispRw::ConvdispRw(InputContext *k, icxatr *a) : Convdisp(k, a)
973 {
974     mPeWin = NULL;
975 }
976 
~ConvdispRw()977 ConvdispRw::~ConvdispRw()
978 {
979     if (mPeWin)
980 	delete mPeWin;
981 }
982 
update_preedit()983 void ConvdispRw::update_preedit()
984 {
985     if (!m_pe)
986 	return;
987 
988     if (!m_pe->get_char_count()) {
989 	clear_preedit();
990 	move_candwin(); // reset candwin position
991 	update_caret_state();
992 	return;
993     }
994 
995     // preedit string exists
996     if (!mPeWin)
997 	mPeWin = new PeLineWin(DefaultRootWindow(XimServer::gDpy), mIMLang, mEncoding, mLocaleName, this);
998 
999     if (m_atr->has_atr(ICA_ClientWindow)) {
1000     	int x, y;
1001 	Window win;
1002 	XWindowAttributes xattr;
1003 
1004 	XGetWindowAttributes(XimServer::gDpy, m_atr->client_window, &xattr);
1005 	XTranslateCoordinates(XimServer::gDpy, m_atr->client_window, DefaultRootWindow(XimServer::gDpy), 0, 0, &x, &y, &win);
1006 	mPeWin->set_pos(x, y + xattr.height);
1007     }
1008 
1009     mPeWin->do_map();
1010     mPeWin->draw_pe(m_pe);
1011     mPeWin->draw();
1012 
1013     move_candwin();
1014     update_caret_state();
1015 }
1016 
clear_preedit()1017 void ConvdispRw::clear_preedit()
1018 {
1019     delete mPeWin;
1020     mPeWin = NULL;
1021 }
1022 
update_icxatr()1023 void ConvdispRw::update_icxatr()
1024 {
1025 }
1026 
move_candwin()1027 void ConvdispRw::move_candwin()
1028 {
1029     InputContext *focusedContext = InputContext::focusedContext();
1030     if (!focusedContext || focusedContext != mKkContext)
1031 	return;
1032 
1033     if (m_atr->has_atr(ICA_ClientWindow)) {
1034 	int x, y;
1035 	Window win;
1036 	XWindowAttributes xattr;
1037 
1038 	XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
1039 			      DefaultRootWindow(XimServer::gDpy),
1040 			      0, 0, &x, &y, &win);
1041 
1042 	Canddisp *disp = canddisp_singleton();
1043 
1044 	XGetWindowAttributes(XimServer::gDpy, m_atr->client_window, &xattr);
1045 
1046 	if (mPeWin)
1047 	    x += mPeWin->mCandWinXOff;
1048 	disp->move(x, y + xattr.height + 28); // lower-left side under the preedit window
1049     }
1050 }
1051 
use_xft()1052 bool ConvdispRw::use_xft()
1053 {
1054     return m_atr->use_xft();
1055 }
1056 
1057 // Over the spot style
ConvdispOv(InputContext * k,icxatr * a)1058 ConvdispOv::ConvdispOv(InputContext *k, icxatr *a) : Convdisp(k, a)
1059 {
1060     m_ov_win = 0;
1061     m_candwin_x_off = 0;
1062     m_candwin_y_off = 0;
1063 #ifdef FLASHPLAYER7_WORKAROUND
1064     revised_spot_y = -1;
1065 #endif
1066 }
1067 
~ConvdispOv()1068 ConvdispOv::~ConvdispOv()
1069 {
1070     if (m_ov_win)
1071 	delete m_ov_win;
1072 }
1073 
update_preedit()1074 void ConvdispOv::update_preedit()
1075 {
1076     draw_preedit();
1077     move_candwin();
1078     update_caret_state();
1079 }
1080 
move_candwin()1081 void ConvdispOv::move_candwin()
1082 {
1083     InputContext *focusedContext = InputContext::focusedContext();
1084     if (!focusedContext || focusedContext != mKkContext)
1085 	return;
1086 
1087     if (m_atr->has_atr(ICA_SpotLocation) ) {
1088 	int x = -1, y = -1;
1089 	Window win;
1090 
1091 	if (m_atr->has_atr(ICA_ClientWindow)) {
1092 
1093 	    XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
1094 				  DefaultRootWindow(XimServer::gDpy),
1095 				  m_atr->spot_location.x,
1096 				  m_atr->spot_location.y,
1097 				  &x, &y, &win);
1098 	}
1099 
1100 	if (m_atr->has_atr(ICA_FocusWindow)) {
1101 
1102 	    XTranslateCoordinates(XimServer::gDpy, m_atr->focus_window,
1103 				  DefaultRootWindow(XimServer::gDpy),
1104 				  m_atr->spot_location.x,
1105 				  m_atr->spot_location.y,
1106 				  &x, &y, &win);
1107 	}
1108 #ifdef FLASHPLAYER7_WORKAROUND
1109 	if (revised_spot_y != -1) {
1110 	    XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
1111 				  DefaultRootWindow(XimServer::gDpy),
1112 				  m_atr->spot_location.x,
1113 				  revised_spot_y,
1114 				  &x, &y, &win);
1115 	}
1116 #endif
1117 	if (x > -1 && y > -1) {
1118 	    x += m_candwin_x_off;
1119 	    y += m_candwin_y_off;
1120 
1121 	    Canddisp *disp = canddisp_singleton();
1122 	    disp->move(x, y + UNDERLINE_HEIGHT + 1);
1123 	    m_atr->unset_change_mask(ICA_SpotLocation);
1124 	}
1125 #if 0
1126     } else if (m_atr->has_atr(ICA_SpotLocation)) {
1127 	int x = -1, y = -1;
1128 	Window win;
1129 	if (revised_spot_y != -1) {
1130 	    XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
1131 				  DefaultRootWindow(XimServer::gDpy),
1132 				  m_atr->spot_location.x,
1133 				  revised_spot_y,
1134 				  &x, &y, &win);
1135 	    Canddisp *disp = canddisp_singleton();
1136 	    disp->move(x, y + UNDERLINE_HEIGHT + 1);
1137 	}
1138 #endif
1139     }
1140 }
1141 
clear_preedit()1142 void ConvdispOv::clear_preedit()
1143 {
1144     delete m_ov_win;
1145     m_ov_win = NULL;
1146     m_candwin_x_off = 0;
1147     m_candwin_y_off = 0;
1148 }
1149 
validate_area()1150 void ConvdispOv::validate_area()
1151 {
1152     Window r, win;
1153     int x;
1154     unsigned int w, h, tmp;
1155     if (m_atr->has_atr(ICA_FocusWindow))
1156 	win = m_atr->focus_window;
1157     else
1158 	win = m_atr->client_window;
1159 
1160     XGetGeometry(XimServer::gDpy, win,
1161 		 &r, &x, &x, &w, &h, &tmp, &tmp);
1162     // Absurd... (cope with Qt from RedHat7.3, and maybe some other)
1163     m_atr->area.width = (unsigned short)w;
1164     m_atr->area.height = (unsigned short)h;
1165 }
1166 
update_icxatr()1167 void ConvdispOv::update_icxatr()
1168 {
1169 
1170     if (m_atr->is_changed(ICA_SpotLocation)) {
1171 	move_candwin();
1172 	update_caret_state();
1173     }
1174 
1175     if (!m_ov_win)
1176 	return;
1177 
1178     if (m_atr->is_changed(ICA_FocusWindow)) {
1179 	// Some clients send FocusWindow later
1180 	if (!check_win())
1181 	    return;
1182     }
1183 
1184     if (m_atr->is_changed(ICA_Area)) {
1185 	if (m_atr->area.width == 0)
1186 	    validate_area();
1187 #ifdef FLASHPLAYER9_WORKAROUND
1188 	if (m_atr->area.width == 500 && m_atr->area.height == 40) {
1189 	    validate_area();
1190 	    m_atr->area.x = 0;
1191 	    m_atr->area.y = 0;
1192 	}
1193 #endif
1194 	m_ov_win->set_size(m_atr->area.width, m_atr->area.height);
1195 	m_atr->unset_change_mask(ICA_Area);
1196     }
1197 
1198     if (m_atr->is_changed(ICA_Foreground)) {
1199 	m_ov_win->set_fore(m_atr->foreground_pixel);
1200 	m_atr->unset_change_mask(ICA_Foreground);
1201     }
1202     if (m_atr->is_changed(ICA_Background)) {
1203 	m_ov_win->set_back(m_atr->background_pixel);
1204 	m_atr->unset_change_mask(ICA_Background);
1205     }
1206     if (m_atr->is_changed(ICA_FontSet)) {
1207 	if (use_xft() == true) {
1208 #if HAVE_XFT_UTF8_STRING
1209 	    m_ov_win->set_xftfont(m_atr->font_set_name);
1210 #endif
1211 	} else {
1212 	    m_ov_win->set_fontset(m_atr->font_set);
1213 	}
1214 	m_atr->unset_change_mask(ICA_FontSet);
1215     }
1216 
1217     if (m_atr->is_changed(ICA_SpotLocation))
1218 	move_candwin();
1219 
1220     draw_preedit();
1221 }
1222 
draw_preedit()1223 void ConvdispOv::draw_preedit()
1224 {
1225     if (!m_pe)
1226 	return;
1227 
1228     m_ce_len = m_pe->get_char_count();
1229     if (!m_ce_len) {
1230 	clear_preedit();
1231 	return;
1232     }
1233 
1234     if (!check_win())
1235 	return;
1236 
1237     m_ce = (char_ent *)malloc(sizeof(char_ent) * m_ce_len);
1238     make_ce_array();
1239     layoutCharEnt();
1240     do_draw_preedit();
1241     free(m_ce);
1242     m_ov_win->draw();
1243     XFlush(XimServer::gDpy);
1244 }
1245 
do_draw_preedit()1246 void ConvdispOv::do_draw_preedit()
1247 {
1248 #ifdef FLASHPLAYER7_WORKAROUND
1249     revised_spot_y = -1;
1250 #endif
1251 
1252     if (m_atr->has_atr(ICA_Area)) {
1253 #ifdef FLASHPLAYER7_WORKAROUND
1254 	// Workaround for brain damaged flash player plugin (at least
1255 	// version 7.0 r25). --ekato
1256 	//
1257 	// Background: flashplayer plugin set same values for
1258 	// m_atr->area.y and m_atr->spot_location.y.
1259 	//
1260 	// 1. If preedit area goes beyond the parent window (browser),
1261 	// set the area at the upper side of client window.
1262 	//
1263 	// 2. If height for preedit is too narrow, make sure to set
1264 	// preedit within the client window.
1265 
1266 	if (m_atr->has_atr(ICA_SpotLocation) && (m_atr->area.y == m_atr->spot_location.y)) {
1267 	    XWindowAttributes clientattr, topattr;
1268 	    Window clientwin, topwin, win;
1269 	    int x, y;
1270 	    int font_height;
1271 
1272 	    topattr.height = 0;
1273 	    if (m_atr->has_atr(ICA_FocusWindow))
1274 		clientwin = m_atr->focus_window;
1275 	    else
1276 		clientwin = m_atr->client_window;
1277 
1278 	    XGetWindowAttributes(XimServer::gDpy, clientwin, &clientattr);
1279 	    topwin = getTopWindow(XimServer::gDpy, clientwin);
1280 	    if (topwin)
1281 		XGetWindowAttributes(XimServer::gDpy, topwin, &topattr);
1282 	    XTranslateCoordinates(XimServer::gDpy, clientwin,
1283 			    DefaultRootWindow(XimServer::gDpy),
1284 			    m_atr->area.x,
1285 			    m_atr->area.y,
1286 			    &x, &y, &win);
1287 	    font_height = get_ce_font_height(m_ce, m_ce_len);
1288 
1289 	    if (topattr.height) {
1290 		if (y > (topattr.height + topattr.y)) {
1291 		    // preedit area goes beyond the top window's geometry
1292 		    if ((y - m_atr->area.y) < topattr.y) {
1293 			// Set preedit at upper side of the top
1294 			// window.  But it may not work because it
1295 			// lacks height for window manager's title bar
1296 			// and some toolbars of browser...
1297 			m_ov_win->set_pos(m_atr->area.x, topattr.y - (y - m_atr->area.y));
1298 			revised_spot_y = font_height + topattr.y - (y - m_atr->area.y);
1299 		    } else {
1300 			// Set preedit at upper side of the flash
1301 			// player's window.
1302 			m_ov_win->set_pos(m_atr->area.x, 0);
1303 			revised_spot_y = font_height;
1304 		    }
1305 		} else {
1306 		    // preedit area is visible within the browser
1307 		    if ((clientattr.height - m_atr->area.y) < font_height + UNDERLINE_HEIGHT) {
1308 			// Make sure preedit+underline to be fit
1309 			// within the client window.
1310 			m_ov_win->set_pos(m_atr->area.x, clientattr.height - font_height - (UNDERLINE_HEIGHT + 1));
1311 			revised_spot_y = clientattr.height - (UNDERLINE_HEIGHT + 1);
1312 		    } else {
1313 			m_ov_win->set_pos(m_atr->area.x, m_atr->area.y);
1314 			revised_spot_y = font_height;
1315 		    }
1316 		}
1317 	    }
1318 	} else
1319 #endif // FLASHPLAYER7_WORKAROUND
1320 	    m_ov_win->set_pos(m_atr->area.x, m_atr->area.y);
1321     } else {
1322 	m_ov_win->set_pos(0, 0);
1323     }
1324     m_ov_win->draw_ce(m_ce, m_ce_len);
1325 }
1326 
check_win()1327 bool ConvdispOv::check_win()
1328 {
1329     if (!check_atr())
1330 	return false; // not enough information to map preedit
1331 
1332     if (m_ov_win && !m_atr->is_changed(ICA_FocusWindow))
1333 	return true; // no need to update window
1334 
1335     if (m_ov_win)
1336 	delete m_ov_win;
1337 
1338     m_atr->unset_change_mask(ICA_FocusWindow);
1339     m_atr->unset_change_mask(ICA_Foreground);
1340     m_atr->unset_change_mask(ICA_Background);
1341     m_atr->unset_change_mask(ICA_FontSet);
1342 
1343     Window w;
1344     if (m_atr->has_atr(ICA_FocusWindow))
1345 	w = m_atr->focus_window;
1346     else
1347 	w = m_atr->client_window;
1348 
1349     m_ov_win = new PeOvWin(w, mIMLang, mEncoding, mLocaleName, this);
1350     m_ov_win->set_size(m_atr->area.width, m_atr->area.height);
1351     m_ov_win->set_fore(m_atr->foreground_pixel);
1352     m_ov_win->set_back(m_atr->background_pixel);
1353     if (use_xft() == true) {
1354 #if HAVE_XFT_UTF8_STRING
1355 	m_ov_win->set_xftfont(m_atr->font_set_name);
1356 #endif
1357     } else {
1358 	m_ov_win->set_fontset(m_atr->font_set);
1359     }
1360 
1361     return true;
1362 }
1363 
check_atr()1364 bool ConvdispOv::check_atr()
1365 {
1366     if (!m_atr->has_atr(ICA_FocusWindow) &&
1367 	!m_atr->has_atr(ICA_ClientWindow))
1368 	return false;
1369 
1370     if (!m_atr->has_atr(ICA_SpotLocation)) {
1371 	// set at top-left corner unless SpotLocation is available
1372 	m_atr->spot_location.x = 0;
1373 	m_atr->spot_location.y = 0;
1374     }
1375     if (!m_atr->has_atr(ICA_Area) ||
1376 	m_atr->area.width == 0) {
1377 	validate_area();
1378 	m_atr->area.x = 0;
1379 	m_atr->area.y = 0;
1380     }
1381 #ifdef FLASHPLAYER9_WORKAROUND
1382     if (m_atr->has_atr(ICA_Area) &&
1383 	m_atr->area.width == 500 && m_atr->area.height == 40) {
1384 	validate_area();
1385 	m_atr->area.x = 0;
1386 	m_atr->area.y = 0;
1387     }
1388 #endif
1389     if (!m_atr->has_atr(ICA_FontSet)) {
1390 	if (use_xft() == false)
1391 	    m_atr->font_set = choose_default_fontset(mIMLang, mLocaleName);
1392     }
1393     if (!m_atr->has_atr(ICA_LineSpace)) {
1394 	m_atr->line_space = DEFAULT_FONT_SIZE;
1395     }
1396 
1397     if (!m_atr->has_atr(ICA_Foreground))
1398 	m_atr->foreground_pixel
1399 		= static_cast<C32>(BlackPixel(XimServer::gDpy,
1400 					      DefaultScreen(XimServer::gDpy)));
1401 
1402     if (!m_atr->has_atr(ICA_Background))
1403 	m_atr->background_pixel
1404 		= static_cast<C32>(WhitePixel(XimServer::gDpy,
1405 					      DefaultScreen(XimServer::gDpy)));
1406 
1407     return true;
1408 }
1409 
make_ce_array()1410 void ConvdispOv::make_ce_array()
1411 {
1412     std::list<pe_ustring>::iterator i;
1413     uString::iterator j;
1414     int s;
1415     int c = 0;
1416     for (i = m_pe->ustrings.begin(); i != m_pe->ustrings.end(); ++i) {
1417 	s = (*i).stat;
1418 	for (j = (*i).s.begin(); j != (*i).s.end(); ++j) {
1419 	    m_ce[c].c = *j;
1420 	    m_ce[c].stat = s;
1421 	    c++;
1422 	}
1423     }
1424 }
1425 
1426 // Dirty,,,
layoutCharEnt()1427 void ConvdispOv::layoutCharEnt()
1428 {
1429     int i;
1430     int x, y;
1431     int caret_pos = get_caret_pos();
1432     int right_limit = m_atr->area.width + m_atr->area.x;
1433 
1434     x = m_atr->spot_location.x;
1435     y = m_atr->spot_location.y;
1436 
1437     for (i = 0; i < m_ce_len; i++) {
1438 	uchar ch = m_ce[i].c;
1439 
1440 	char utf8[6];
1441 	int len;
1442 	if (use_xft() == true) {
1443 #if HAVE_XFT_UTF8_STRING
1444 	    len = utf8_wctomb((unsigned char *)utf8, ch);
1445 	    utf8[len] = '\0';
1446 
1447 	    XGlyphInfo ginfo;
1448 	    XftTextExtentsUtf8(XimServer::gDpy, m_ov_win->mXftFont, (unsigned char *)utf8, len, &ginfo);
1449 	    m_ce[i].width = ginfo.xOff;
1450 	    m_ce[i].height = m_ov_win->mXftFontSize;
1451 #endif
1452 	} else {
1453 	    XRectangle ink, logical;
1454 
1455 	    if (!strcmp(mEncoding, "UTF-8")) {
1456 		XwcTextExtents(m_atr->font_set, &ch, 1, &ink, &logical);
1457 		m_ce[i].width = logical.width;
1458 		m_ce[i].height = logical.height;
1459 	    } else {
1460 		len = utf8_wctomb((unsigned char *)utf8, ch);
1461 		utf8[len] = '\0';
1462 		XimIM *im = get_im_by_id(mKkContext->get_ic()->get_imid());
1463 		char *str = im->utf8_to_native_str(utf8);
1464 		if (!str) {
1465 		    logical.width = 0;
1466 		    logical.height = (unsigned short)((i > 0) ? m_ce[i - 1].height : 0);
1467 		} else {
1468 		    len = static_cast<int>(strlen(str));
1469 		    XmbTextExtents(m_atr->font_set, str, len, &ink, &logical);
1470 		    free(str);
1471 		}
1472 		m_ce[i].width = logical.width;
1473 		m_ce[i].height = logical.height;
1474 	    }
1475 	}
1476 
1477 	if (m_ce[i].width + x > right_limit) {
1478 	    // goto next line
1479 	    x = m_atr->area.x;
1480 	    y += (m_atr->line_space + UNDERLINE_HEIGHT);
1481 	}
1482 	m_ce[i].x = x - m_atr->area.x;
1483 	m_ce[i].y = y - m_atr->area.y;
1484 #ifdef FLASHPLAYER7_WORKAROUND
1485 	// workaround for brain damaged flash player plugin
1486 	if (m_ce[i].y == 0)
1487 	    m_ce[i].y += m_ce[i].height;
1488 #endif
1489 	x += m_ce[i].width;
1490     }
1491 
1492     switch (XimServer::gCandWinPosType) {
1493     case Caret:
1494 	if (caret_pos == 0) {
1495 	    m_candwin_x_off = m_ce[caret_pos].x - m_atr->spot_location.x;
1496 	    m_candwin_y_off = m_ce[caret_pos].y - m_atr->spot_location.y;
1497 	} else {
1498 	    m_candwin_x_off = m_ce[caret_pos - 1].x + m_ce[caret_pos - 1].width - m_atr->spot_location.x;
1499 	    m_candwin_y_off = m_ce[caret_pos - 1].y - m_atr->spot_location.y;
1500 	}
1501 	break;
1502     case Right:
1503 	m_candwin_x_off = m_ce[m_ce_len - 1].x + m_ce[m_ce_len - 1].width - m_atr->spot_location.x;
1504 	m_candwin_y_off = m_ce[m_ce_len - 1].y - m_atr->spot_location.y;
1505 	break;
1506     case Left:
1507     default:
1508 	break;
1509     }
1510 }
1511 
1512 #ifdef FLASHPLAYER7_WORKAROUND
get_ce_font_height(char_ent * ce,int len)1513 int ConvdispOv::get_ce_font_height(char_ent *ce, int len)
1514 {
1515     int i, h = 0;
1516     for (i = 0; i < len; i++) {
1517 	if (ce[i].height > h)
1518 	    h = ce[i].height;
1519     }
1520     return h;
1521 }
1522 #endif
1523 
use_xft()1524 bool ConvdispOv::use_xft()
1525 {
1526     return m_atr->use_xft();
1527 }
1528 
1529 // On the spot style
ConvdispOs(InputContext * k,icxatr * a,Connection * c)1530 ConvdispOs::ConvdispOs(InputContext *k, icxatr *a, Connection *c)
1531     : Convdisp(k, a)
1532 {
1533     XimIC *ic = k->get_ic();
1534     mConn = c;
1535     mImid = ic->get_imid();
1536     mIcid = ic->get_icid();
1537     mPrevLen = 0;
1538 }
1539 
~ConvdispOs()1540 ConvdispOs::~ConvdispOs()
1541 {
1542 }
1543 
update_preedit()1544 void ConvdispOs::update_preedit()
1545 {
1546     move_candwin();
1547     if (!m_pe)
1548 	return;
1549 
1550     TxPacket *t;
1551 
1552     int len, caret_pos;
1553     len = m_pe->get_char_count();
1554     caret_pos = m_pe->caret_pos;
1555 
1556     if (mPrevLen == 0 && len == 0)
1557 	return;
1558 
1559     if (mPrevLen == 0 && len) {
1560 	t = createTxPacket(XIM_PREEDIT_START, 0);
1561 	t->pushC16(mImid);
1562 	t->pushC16(mIcid);
1563 	mConn->push_passive_packet(t);
1564     }
1565 
1566     t = createTxPacket(XIM_PREEDIT_DRAW, 0);
1567     t->pushC16(mImid);
1568     t->pushC16(mIcid);
1569     t->pushC32(caret_pos);// caret
1570     t->pushC32(0); // chg_first
1571     t->pushC32(mPrevLen); // chg_length
1572 
1573     if (!m_pe->ustrings.empty())
1574 	t->pushC32(0);
1575     else
1576 	t->pushC32(3);
1577 
1578     compose_preedit_array(t);
1579     compose_feedback_array(t);
1580     mConn->push_passive_packet(t);
1581 
1582     if (mPrevLen && len == 0) {
1583 	t = createTxPacket(XIM_PREEDIT_DONE, 0);
1584 	t->pushC16(mImid);
1585 	t->pushC16(mIcid);
1586 	mConn->push_passive_packet(t);
1587     }
1588     mPrevLen = len;
1589 
1590     if (len) {
1591 	t = createTxPacket(XIM_PREEDIT_CARET, 0);
1592 	t->pushC16(mImid);
1593 	t->pushC16(mIcid);
1594 	t->pushC32(caret_pos);
1595 	t->pushC32(XIMAbsolutePosition);
1596 	t->pushC32(XIMIsPrimary);
1597 	mConn->push_passive_packet(t);
1598     }
1599 }
1600 
move_candwin()1601 void ConvdispOs::move_candwin()
1602 {
1603     InputContext *focusedContext = InputContext::focusedContext();
1604     if (!focusedContext || focusedContext != mKkContext)
1605 	return;
1606 
1607     if (m_atr->has_atr(ICA_ClientWindow)) {
1608 	int x, y;
1609 	Window win;
1610 	XWindowAttributes xattr;
1611 
1612 	if (!m_atr->has_atr(ICA_SpotLocation)) {
1613 	    m_atr->spot_location.x = 0;
1614 	    m_atr->spot_location.y = 0;
1615 	}
1616 
1617 	XTranslateCoordinates(XimServer::gDpy, m_atr->client_window,
1618 			      DefaultRootWindow(XimServer::gDpy),
1619 			      m_atr->spot_location.x,
1620 			      m_atr->spot_location.y,
1621 			      &x, &y, &win);
1622 
1623 	Canddisp *disp = canddisp_singleton();
1624 
1625 	if (m_atr->has_atr(ICA_SpotLocation))
1626 	    disp->move(x, y + 36); // 36 pixel seems too long.
1627 				   // Is there any way to get current preedit
1628 				   // height?
1629 	else {
1630 	    XGetWindowAttributes(XimServer::gDpy, m_atr->client_window,
1631 			      &xattr);
1632 	    //disp->move(x + xattr.width + 2, y + 2); //upper-right side
1633 	    disp->move(x, y + xattr.height + 2); //lower-left side
1634 	}
1635     }
1636 
1637 }
1638 
clear_preedit()1639 void ConvdispOs::clear_preedit()
1640 {
1641     mPrevLen = 0;
1642 }
1643 
update_icxatr()1644 void ConvdispOs::update_icxatr()
1645 {
1646 }
1647 
compose_preedit_array(TxPacket * t)1648 void ConvdispOs::compose_preedit_array(TxPacket *t)
1649 {
1650     uString s;
1651     std::list<pe_ustring>::iterator it;
1652     for (it = m_pe->ustrings.begin(); it != m_pe->ustrings.end(); ++it) {
1653 	append_ustring(&s, &(*it).s);
1654     }
1655 
1656     XimIM *im = get_im_by_id(mKkContext->get_ic()->get_imid());
1657     char *c = im->uStringToCtext(&s);
1658     int i, len = 0;
1659     if (c)
1660 	len = static_cast<int>(strlen(c));
1661     t->pushC16((C16)len); // LENGTH
1662     for (i = 0; i < len; i++) {
1663 	t->pushC8(c[i]); // CTEXT
1664     }
1665     len = pad4(len + 2);
1666     for (i = 0; i < len; i++) {
1667 	t->pushC8(0); // PADDING
1668     }
1669     free(c);
1670 }
1671 
compose_feedback_array(TxPacket * t)1672 void ConvdispOs::compose_feedback_array(TxPacket *t)
1673 {
1674     int i, len, stat, xstat;
1675     len = m_pe->get_char_count();
1676     t->pushC16((C16)(len * 4));
1677     t->pushC16(0);
1678     std::list<pe_ustring>::iterator it;
1679     for (it = m_pe->ustrings.begin(); it != m_pe->ustrings.end(); ++it) {
1680 	len = static_cast<int>((*it).s.size());
1681 	stat = (*it).stat;
1682 	xstat = FB_None;
1683 	if (stat & PE_REVERSE)
1684 	    xstat |= FB_Reverse;
1685 
1686 	if (stat & PE_UNDERLINE)
1687 	    xstat |= FB_Underline;
1688 
1689 	if (stat & PE_HILIGHT)
1690 	    xstat |= FB_Highlight;
1691 
1692 	for (i = 0; i < len; i++) {
1693 	    t->pushC32(xstat);
1694 	}
1695     }
1696 }
1697 
use_xft()1698 bool ConvdispOs::use_xft()
1699 {
1700     return true;
1701 }
1702 
1703 #ifdef FLASHPLAYER7_WORKAROUND
getTopWindow(Display * d,Window w)1704 static Window getTopWindow(Display *d, Window w)
1705 {
1706     Window root, parent, *children;
1707     unsigned int nchild;
1708     Status retval;
1709 
1710     for (;;) {
1711 	retval = XQueryTree(d, w, &root, &parent, &children, &nchild);
1712 	if (retval == 0)
1713 	    return 0;
1714 	if (children)
1715 	    XFree(children);
1716 	if (parent == root)
1717 	    break;
1718 	w = parent;
1719     }
1720     return w;
1721 };
1722 #endif
1723 
1724 /*
1725  * Local variables:
1726  *  c-indent-level: 4
1727  *  c-basic-offset: 4
1728  * End:
1729  */
1730