Improved input method support by M. Iwami. I fixed some typo, and simplified the code a bit.

* Painter:
  - preeditText(), dashedUnderline(): new methods for CJK support.

* GuiWorkArea:
  - inputMethodQuery(): new Qt inherited method for proper CJK support.
  - inputMethodEvent(): now properly take care of input methods.




git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@17671 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Abdelrazak Younes 2007-04-01 09:14:08 +00:00
parent 44c4c02c5d
commit 099a4adc92
4 changed files with 217 additions and 17 deletions

View File

@ -84,6 +84,39 @@ void Painter::buttonText(int x, int y, docstring const & str,
}
int Painter::preeditText(int x, int y, char_type c,
LyXFont const & font, preedit_style style)
{
LyXFont temp_font = font;
FontMetrics const & fm = theFontMetrics(font);
int ascent = fm.maxAscent();
int descent = fm.maxDescent();
int height = ascent + descent;
int width = fm.width(c);
switch (style) {
case preedit_default:
// default unselecting mode.
fillRectangle(x, y - height + 1, width, height, LColor::background);
dashedUnderline(font, x, y - descent + 1, width);
break;
case preedit_selecting:
// We are in selecting mode: white text on black background.
fillRectangle(x, y - height + 1, width, height, LColor::black);
temp_font.setColor(LColor::white);
break;
case preedit_cursor:
// The character comes with a cursor.
fillRectangle(x, y - height + 1, width, height, LColor::background);
underline(font, x, y - descent + 1, width);
break;
}
text(x, y - descent + 1, c, temp_font);
return width;
}
void Painter::underline(LyXFont const & f, int x, int y, int width)
{
FontMetrics const & fm = theFontMetrics(f);
@ -97,5 +130,20 @@ void Painter::underline(LyXFont const & f, int x, int y, int width)
fillRectangle(x, y + below, width, below + height, f.color());
}
void Painter::dashedUnderline(LyXFont const & f, int x, int y, int width)
{
FontMetrics const & fm = theFontMetrics(f);
int const below = max(fm.maxDescent() / 2, 2);
int height = max((fm.maxDescent() / 4) - 1, 1);
if (height >= 2)
height += below;
for (int n = 0; n < height; ++n)
line(x, y + below + n, x + width, y + below + n, f.color(), line_onoffdash);
}
} // namespace frontend
} // namespace lyx

View File

@ -66,6 +66,14 @@ public:
line_onoffdash //< dashes with spaces
};
/// possible character styles of preedit string.
/// This is used for CJK input method support.
enum preedit_style {
preedit_default, //< when unselecting, no cursor and dashed underline.
preedit_selecting, //< when selecting.
preedit_cursor //< with cursor.
};
virtual ~Painter() {}
/// draw a line from point to point
@ -157,11 +165,19 @@ public:
void buttonText(int x, int baseline, docstring const & s,
LyXFont const & font, bool mouseHover);
/// draw a character of a preedit string for cjk support.
int preeditText(int x, int y,
char_type c, LyXFont const & f, preedit_style style);
protected:
/// check the font, and if set, draw an underline
void underline(LyXFont const & f,
int x, int y, int width);
/// check the font, and if set, draw an dashed underline
void dashedUnderline(LyXFont const & f,
int x, int y, int width);
/// draw a bevelled button border
void buttonFrame(int x, int y, int w, int h);

View File

@ -28,6 +28,7 @@
#include "LColor.h"
#include "version.h"
#include "lyxrc.h"
#include "lyxtext.h"
#include "support/filetools.h" // LibFileSearch
#include "support/os.h"
@ -44,6 +45,7 @@
#include <QPainter>
#include <QScrollBar>
#include <QTimer>
#include <QInputContext>
#include <boost/bind.hpp>
#include <boost/current_function.hpp>
@ -160,7 +162,8 @@ SyntheticMouseEvent::SyntheticMouseEvent()
GuiWorkArea::GuiWorkArea(int w, int h, int id, LyXView & lyx_view)
: WorkArea(id, lyx_view), need_resize_(false), schedule_redraw_(false)
: WorkArea(id, lyx_view), need_resize_(false), schedule_redraw_(false),
preedit_lines_(1)
{
screen_ = QPixmap(viewport()->width(), viewport()->height());
cursor_ = new frontend::CursorWidget();
@ -299,6 +302,8 @@ void GuiWorkArea::mousePressEvent(QMouseEvent * e)
return;
}
inputContext()->reset();
FuncRequest const cmd(LFUN_MOUSE_PRESS, e->x(), e->y(),
q_button_state(e->button()));
dispatch(cmd, q_key_state(e->modifiers()));
@ -573,8 +578,16 @@ void GuiWorkArea::removeCursor()
void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
{
QString const & text = e->commitString();
if (!text.isEmpty()) {
QString const & commit_string = e->commitString();
docstring const & preedit_string
= qstring_to_ucs4(e->preeditString());
if(greyed_out_) {
e->ignore();
return;
}
if (!commit_string.isEmpty()) {
lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
<< " preeditString =" << fromqstr(e->preeditString())
@ -583,26 +596,145 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
int key = 0;
// FIXME Abdel 10/02/07: Remove?
// needed to make math superscript work on some systems
// ideally, such special coding should not be necessary
if (text == "^")
key = Qt::Key_AsciiCircum;
// FIXME Abdel 10/02/07: Minimal support for CJK, aka systems
// with input methods. What should we do with e->preeditString()?
// Do we need an inputMethodQuery() method?
// FIXME 2: we should take care also of UTF16 surrogates here.
for (int i = 0; i < text.size(); ++i) {
// FIXME: Needs for investigation, this key is not really used,
// the ctor below just check if key is different from 0.
QKeyEvent ev(QEvent::KeyPress, key, Qt::NoModifier, text[i]);
// FIXME Iwami 04/01/07: we should take care also of UTF16 surrogates here.
for (int i = 0; i < commit_string.size(); ++i) {
QKeyEvent ev(QEvent::KeyPress, key, Qt::NoModifier, commit_string[i]);
keyPressEvent(&ev);
}
}
// Hide the cursor during the kana-kanji transformation.
if (preedit_string.empty())
startBlinkingCursor();
else
stopBlinkingCursor();
// if last_width is last length of preedit string.
static int last_width = 0;
if (!last_width && preedit_string.empty()) {
e->accept();
return;
}
QLPainter pain(&screen_);
buffer_view_->updateMetrics(false);
paintText(*buffer_view_, pain);
LyXFont font = buffer_view_->cursor().getFont();
FontMetrics const & fm = theFontMetrics(font);
int height = fm.maxHeight();
int cur_x = cursor_->rect().left();
int cur_y = cursor_->rect().bottom();
// redraw area of preedit string.
update(0, cur_y - height, GuiWorkArea::width(),
(height + 1) * preedit_lines_);
if (preedit_string.empty()) {
last_width = 0;
preedit_lines_ = 1;
e->accept();
return;
}
// FIXME: Describe these variables.
last_width = 1;
size_t cur_pos = 0;
size_t rStart = 0;
size_t rLength = 0;
int cur_visible = 0;
QList<QInputMethodEvent::Attribute> const & att(e->attributes());
// get attributes of input method cursor.
for (int i = 0; i < att.size(); ++i) {
if (att.at(i).type == QInputMethodEvent::Cursor) {
cur_pos = att.at(i).start;
cur_visible = att.at(i).length;
break;
}
}
size_t preedit_length = preedit_string.length();
// get position of selection in input method.
// FIXME: isn't there a way to do this simplier?
if (cur_pos < preedit_length) {
for (int i = 0; i < att.size(); ++i) {
if (att.at(i).type == QInputMethodEvent::TextFormat) {
if (att.at(i).start <= int(cur_pos)
&& int(cur_pos) < att.at(i).start + att.at(i).length) {
rStart = att.at(i).start;
rLength = att.at(i).length;
if (cur_visible == 0)
cur_pos += rLength;
break;
}
}
}
}
else {
rStart = cur_pos;
rLength = 0;
}
int const right_margin = lyx::rightMargin();
Painter::preedit_style ps;
// Most often there would be only one line:
preedit_lines_ = 1;
for (size_t pos = 0; pos != preedit_length; ++pos) {
char_type const typed_char = preedit_string[pos];
// reset preedit string style
ps = Painter::preedit_default;
// if we reached the right extremity of the screen, go to next line.
if (cur_x + fm.width(typed_char) > GuiWorkArea::width() - right_margin) {
cur_x = right_margin;
cur_y += height + 1;
++preedit_lines_;
}
// preedit strings are displayed with dashed underline
// and partial strings are displayed white on black indicating
// that we are in selecting mode in the input method.
// FIXME: rLength == preedit_length is not a changing condition
// FIXME: should be put out of the loop.
if (pos >= rStart
&& pos < rStart + rLength
&& !(cur_pos < rLength && rLength == preedit_length))
ps = Painter::preedit_selecting;
if (pos == cur_pos
&& (cur_pos < rLength && rLength == preedit_length))
ps = Painter::preedit_cursor;
// draw one character and update cur_x.
cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
}
// update the preedit string screen area.
update(0, cur_y - preedit_lines_*height, GuiWorkArea::width(),
(height + 1) * preedit_lines_);
// Don't forget to accept the event!
e->accept();
}
QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const
{
QRect cur_r(0,0,0,0);
switch (query) {
// this is the CJK-specific composition window position.
case Qt::ImMicroFocus:
cur_r = cursor_->rect();
if (preedit_lines_ != 1)
cur_r.moveLeft(10);
cur_r.moveBottom(cur_r.bottom() + cur_r.height() * preedit_lines_);
// return lower right of cursor in LyX.
return cur_r;
default:
return QWidget::inputMethodQuery(query);
}
}
} // namespace frontend
} // namespace lyx

View File

@ -142,6 +142,8 @@ private:
void keyPressEvent(QKeyEvent * ev);
/// IM events
void inputMethodEvent(QInputMethodEvent * ev);
/// IM query
QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
public Q_SLOTS:
/// Adjust the LyX buffer view with the position of the scrollbar.
@ -173,6 +175,8 @@ private:
bool need_resize_;
///
bool schedule_redraw_;
///
int preedit_lines_;
};
} // namespace frontend