// OVIMGeneric.cpp: Generic Input Method, reads .cin // // Copyright (c) 2004-2006 The OpenVanilla Project (http://openvanilla.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 OpenVanilla 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. // #define OV_DEBUG #include "OVIMGeneric.h" #include "CIN-Defaults.h" using namespace std; OVCINList *cinlist=NULL; extern "C" unsigned int OVGetLibraryVersion() { return OV_VERSION; } extern "C" int OVInitializeLibrary(OVService *s, const char *libpath) { if (cinlist) { // already initializde return false; } const char *pathsep=s->pathSeparator(); cinlist = new OVCINList(pathsep); if (!cinlist) return false; // will be something like this on OS X: // ~/Library/OpenVanilla/version/UserSpace/OVIMGeneric/ string userpath=s->userSpacePath("OVIMGeneric"); // will be something like this on OS X: // /Library/OpenVanilla/version/Modules/OVIMGeneric/ string datapath=string(libpath) + string(pathsep) + string("OVIMGeneric"); murmur("OVIMGeneric initializing"); Watch watch; int loaded=0; watch.start(); loaded += cinlist->load(userpath.c_str(), ".cin"); watch.stop(); murmur("Loaded modules from %s in %1.3f", userpath.c_str(), watch.getSec()); watch.start(); loaded += cinlist->load(datapath.c_str(), ".cin"); watch.stop(); murmur("Loaded modules from %s in %1.3f", datapath.c_str(), watch.getSec()); if (!loaded) { murmur ("OVIMGeneric: nothing loaded, init failed"); return false; } return true; } extern "C" OVModule *OVGetModuleFromLibrary(int x) { if ((size_t)x >= cinlist->count()) return NULL; return new OVIMGeneric(cinlist->cinInfo((size_t)x)); } GenericKeySequence::GenericKeySequence(OVCIN* cintab) { cinTable=cintab; } bool GenericKeySequence::valid(char c) { string inKey; inKey.push_back(c); if (!cinTable->isValidKey(inKey)) return false; return true; } bool GenericKeySequence::add(char c) { if (valid(c) == 0) return false; return OVKeySequenceSimple::add(c); } string *GenericKeySequence::compose(string *s) { for (int i=0; i outStringVectorRef; if (cinTable->getCharVectorByKey(inKey, outStringVectorRef) > 0) s->append(outStringVectorRef[0]); } return s; } OVIMGeneric::OVIMGeneric(const OVCINInfo& ci) : cininfo(ci), cintab(NULL) { idstr = "OVIMGeneric-" + cininfo.shortfilename; } OVIMGeneric::~OVIMGeneric() { if (cintab) delete cintab; } const char* OVIMGeneric::identifier() { return idstr.c_str(); } const char* OVIMGeneric::localizedName(const char* locale) { if (!strcasecmp(locale, "zh_TW")) return cininfo.tcname.c_str(); if (!strcasecmp(locale, "zh_CN")) return cininfo.scname.c_str(); return cininfo.ename.c_str(); } int OVIMGeneric::initialize(OVDictionary* global, OVService* srv, const char*) { Watch watch; watch.start(); if (!cintab) { cintab=new OVCIN(cininfo.longfilename.c_str()); } watch.stop(); murmur("OVIMGeneric: initializing %s in %1.3f sec", identifier(), watch.getSec()); update(global, srv); return 1; } void OVIMGeneric::update(OVDictionary* global, OVService*) { CINSetDefaults(cininfo.shortfilename.c_str(), global); cfgMaxSeqLen=global->getInteger(CIN_MAXSEQLEN); cfgBeep=global->getInteger(CIN_WARNINGBEEP); cfgAutoCompose=global->getInteger(CIN_AUTOCOMPOSE); cfgHitMaxAndCompose=global->getInteger(CIN_HITMAX); if(!global->getInteger(CIN_SHIFTSELECTIONKEY)) doShiftSelKey = false; else doShiftSelKey = true; } OVInputMethodContext *OVIMGeneric::newContext() { return new OVGenericContext(this, cintab); } void OVGenericContext::updateDisplay(OVBuffer *buf) { buf->clear(); // murmur("UpdateDisplay"); if (keyseq.length()) { string *ms= new string; keyseq.compose(ms); buf->append(ms->c_str()); delete ms; } buf->update(); } void OVGenericContext::clear() { keyseq.clear(); autocomposing=false; candi.cancel(); } int OVGenericContext::keyEvent(OVKeyCode *key, OVBuffer *buf, OVCandidate *textbar, OVService *srv) { if (candi.onDuty()) { if (!autocomposing) return candidateEvent(key, buf, textbar, srv); if (key->code() == ovkDown || key->code() == ovkLeft || key->code() == ovkUp || key->code() == ovkRight || (!candi.onePage() && key->code()==ovkSpace)) return candidateEvent(key, buf, textbar, srv); string output; if (candi.select(key->code(), output)) { buf->clear()->append(output.c_str())->send(); keyseq.clear(); cancelAutoCompose(textbar); return 1; } } if (!keyseq.length() && !isprint(key->code())) return 0; if (key->code()==ovkEsc) { cancelAutoCompose(textbar); buf->clear()->update(); keyseq.clear(); return 1; } if (key->code() == ovkDelete || key->code() == ovkBackspace) { keyseq.remove(); updateDisplay(buf); if (!keyseq.length() && autocomposing) cancelAutoCompose(textbar); // if autocomposing is on if (keyseq.length() && parent->isAutoCompose()) { if (cintab->getWordVectorByChar(keyseq.getSeq(), candidateStringVector)) { autocomposing=true; compose(buf, textbar, srv); } else if (candi.onDuty()) cancelAutoCompose(textbar); } return 1; } if (keyseq.length() && key->code()==ovkSpace) { // there the candiate window has only one page, space key // will send out the first candidate if (autocomposing && candi.onDuty()) { keyseq.clear(); autocomposing=false; cancelAutoCompose(textbar); return candidateEvent(key, buf, textbar, srv); } autocomposing=false; return compose(buf, textbar, srv); } // we send back any CTRL/OPT/CMD key combination // In OV 0.7 this part will be processed by // pre-IM key filters if (key->isOpt() || key->isCommand() || key->isCtrl()) { if (!buf->isEmpty()) { cancelAutoCompose(textbar); buf->clear()->update(); keyseq.clear(); } return 0; } // shift and capslock processing // Shift processing is disabled. if (isprint(key->code()) && (key->isCapslock() /*|| key->isShift()*/)) { if (key->isCapslock()) { char cbuf[2]; if (key->isShift()) sprintf(cbuf, "%c", toupper(key->code())); else sprintf(cbuf, "%c", tolower(key->code())); buf->append(cbuf); } //else if (key->isShift()) buf->appendChar(key->lower()); cancelAutoCompose(textbar); keyseq.clear(); buf->send(); return 1; } if (isprint(key->code()) && keyseq.valid(static_cast(key->code())) && /*!key->isShift() &&*/ !key->isCapslock()) { if (keyseq.length() > 0 && // prevent to the exception of parent->maxSeqLen() == 0 keyseq.length() == parent->maxSeqLen()) { updateDisplay(buf); if (parent->isBeep()) srv->beep(); return 1; } keyseq.add(key->code()); // murmur("add %d", key->code()); if (keyseq.length() == parent->maxSeqLen() && parent->isHitMaxAndCompose()) { autocomposing=false; cancelAutoCompose(textbar); return compose(buf, textbar, srv); } updateDisplay(buf); if (cintab->isEndKey(static_cast(key->code()))) { autocomposing=false; cancelAutoCompose(textbar); return compose(buf, textbar, srv); } // if autocomposing is on if (parent->isAutoCompose()) { if (cintab->getWordVectorByChar(keyseq.getSeq(), candidateStringVector)) { autocomposing=1; compose(buf, textbar, srv); } else if (candi.onDuty()) cancelAutoCompose(textbar); } return 1; } if (!buf->isEmpty()) { if (parent->isBeep()) srv->beep(); return 1; } if (isprint(key->code())) { char sb[2]; sprintf(sb, "%c", key->code()); buf->append(sb)->send(); return 1; } return 0; } void OVGenericContext::cancelAutoCompose(OVCandidate *textbar) { autocomposing=false; candi.cancel(); textbar->hide()->clear(); } int OVGenericContext::compose(OVBuffer *buf, OVCandidate *textbar, OVService *srv) { if (!keyseq.length()) return 0; size_t size = cintab->getWordVectorByChar(keyseq.getSeq(), candidateStringVector); if (size == 0) { if (parent->isBeep()) srv->beep(); return 1; } if (size ==1 && !autocomposing) { buf->clear()->append(candidateStringVector[0].c_str())->send(); keyseq.clear(); return 1; } if (!autocomposing) { buf->clear()->append(candidateStringVector[0].c_str())->update(); keyseq.clear(); } string currentSelKey = cintab->getSelKey(); if(parent->isShiftSelKey()) currentSelKey = " " + currentSelKey; candi.prepare(&candidateStringVector, const_cast(currentSelKey.c_str()), textbar); return 1; } int OVGenericContext::candidateEvent(OVKeyCode *key, OVBuffer *buf, OVCandidate *textbar, OVService *srv) { if (key->code() == ovkEsc || key->code() == ovkBackspace) { textbar->hide()->clear(); candi.cancel(); buf->clear()->update(); return 1; } if (key->code() == ovkDown || key->code() == ovkRight || (!candi.onePage() && key->code()==ovkSpace)) { candi.pageDown()->update(textbar); return 1; } if (key->code() == ovkUp || key->code() == ovkLeft) { candi.pageUp()->update(textbar); return 1; } // enter == first candidate // space (when candidate list has only one page) == first candidate char c=key->code(); if (key->code() == ovkReturn || (candi.onePage() && key->code()==ovkSpace)) c=candi.getSelKey()[0]; string output; if (candi.select(c, output)) { buf->clear()->append(output.c_str())->send(); candi.cancel(); textbar->hide()->clear(); return 1; } string inKey; inKey.push_back(c); if (cintab->isValidKey(inKey) || cintab->isEndKey(c)) { string output; candi.select(candi.getSelKey()[0], output); buf->clear()->append(output.c_str())->send(); keyseq.add(c); updateDisplay(buf); candi.cancel(); textbar->hide()->clear(); if(cintab->isEndKey(c)) compose(buf, textbar, srv); return 1; } if (parent->isBeep()) srv->beep(); return 1; }