[aquaskk-changes 262] CVS update: AquaSKK

Tomotaka SUWA t-suw****@users*****
2006年 12月 2日 (土) 16:54:14 JST


Index: AquaSKK/AquaSKKServer.mm
diff -u AquaSKK/AquaSKKServer.mm:1.2 AquaSKK/AquaSKKServer.mm:1.2.2.1
--- AquaSKK/AquaSKKServer.mm:1.2	Wed Apr 26 22:36:12 2006
+++ AquaSKK/AquaSKKServer.mm	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /* -*- objc -*-
-  $Id: AquaSKKServer.mm,v 1.2 2006/04/26 13:36:12 t-suwa Exp $
+  $Id: AquaSKKServer.mm,v 1.2.2.1 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -65,7 +65,7 @@
     NSArray* content = [[PreferencesController sharedController] contentForDictionarySet];
 
     // Ž«‘ƒT[ƒo[‚ð‹N“®‚·‚é
-    DictionarySet::theInstance().initialize((CFArrayRef)content);
+    DictionarySet::theInstance().Initialize((CFArrayRef)content);
 
     // ƒƒbƒZ[ƒWƒnƒ“ƒhƒ‰‚ð‹N“®‚·‚é
     ServerMessageReceiver::start(kAquaSKKServerRunLoopMode);
@@ -79,7 +79,7 @@
 
 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
     // Ž«‘ƒT[ƒo[‚ð—Ž‚Æ‚·
-    DictionarySet::theInstance().terminate();
+    DictionarySet::theInstance().Terminate();
 
     // skkserv ƒGƒ~ƒ…ƒŒ[ƒVƒ‡ƒ“‚ðŽ~‚ß‚é
     skkserv::theInstance().stop();
Index: AquaSKK/ChangeLog
diff -u AquaSKK/ChangeLog:1.33.2.2 AquaSKK/ChangeLog:1.33.2.3
--- AquaSKK/ChangeLog:1.33.2.2	Tue Nov 28 23:36:08 2006
+++ AquaSKK/ChangeLog	Sat Dec  2 16:54:13 2006
@@ -1,3 +1,28 @@
+2006-12-02  Tomotaka SUWA  <t.suw****@mac*****>
+
+	* Dictionary.h: SKK Ž«‘‘€ì—p‚̃†[ƒeƒBƒŠƒeƒBƒNƒ‰ƒX‚ðƒŠƒtƒ@ƒNƒ^ƒŠ
+	ƒ“ƒOB
+
+	* SKKDictionary.cpp: Dictionary.h ‚Æ“K‡‚·‚é‚悤‚ɏC³B 
+
+	* DictionarySet.*: CppCFString ‚ðŽg‚í‚È‚¢‚悤‚ɕύXB‘S‘Ì“I‚ɃŠƒtƒ@
+	ƒNƒ^ƒŠƒ“ƒOB
+
+	* AquaSKKServer.mm, PreferencesController.mm,
+	ServerMessageReceiver.mm: DictionarySet ‚̃Cƒ“ƒ^ƒtƒF[ƒX‚É“K‡‚·‚é
+	‚悤‚ɏC³B
+
+	* KanjiConversionMode.cpp: ‘—‚è‚ ‚èƒGƒ“ƒgƒŠ‚ÌŒŸõŒ‹‰Ê‚ɁA‘—‚艼–¼‚ð
+	•t‰Á‚·‚é‚悤‚ɕύXB‚Ü‚½A‘—‚è‚ ‚èƒGƒ“ƒgƒŠ‚ðíœ‚·‚éê‡A‘—‚艼–¼
+	‚𑗐M‚µ‚È‚¢‚悤‚ɕύXB
+
+	* skkserv.cpp: CppCFString ‚ðŽg‚í‚È‚¢‚悤‚ɕύXB
+
+	* socketstream.h: SO_REUSEADDR ‚ðŽg‚¤‚悤‚ɕύXB
+
+	* WordRegisterMode.cpp: ƒNƒŠƒbƒvƒ{[ƒh‚Ì’†g‚ð UTF-8 ‚Ŏ擾‚·‚é‚悤
+	‚ɕύXB
+
 2006-11-28  Tomotaka SUWA  <t.suw****@mac*****>
 
 	* Japanese.lproj/Preferences.nib: ƒ†[ƒU[Ž«‘‚̃‰ƒxƒ‹‚ðC³B
Index: AquaSKK/Dictionary.h
diff -u AquaSKK/Dictionary.h:1.4.2.1 AquaSKK/Dictionary.h:1.4.2.2
--- AquaSKK/Dictionary.h:1.4.2.1	Mon Nov 27 15:16:27 2006
+++ AquaSKK/Dictionary.h	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
-/*
-  $Id: Dictionary.h,v 1.4.2.1 2006/11/27 06:16:27 t-suwa Exp $
+/* -*- C++ -*-
+  $Id: Dictionary.h,v 1.4.2.2 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -28,7 +28,9 @@
 #include <string>
 #include <numeric>
 
+// ======================================================================
 // 抽象辞書クラス(文字列は全て UTF-8)
+// ======================================================================
 class Dictionary {
 public:
     virtual ~Dictionary() {};
@@ -45,7 +47,9 @@
     virtual std::string findOkuriNasi(const std::string& query) = 0;
 };
 
+// ======================================================================
 // 抽象ユーザー辞書クラス(文字列は全て UTF-8)
+// ======================================================================
 class UserDictionary: public Dictionary {
 public:
     virtual ~UserDictionary() {};
@@ -62,36 +66,54 @@
     virtual void removeOkuriNasi(const std::string& index, const std::string& kanji) = 0;
 };
 
+#pragma mark -- Utility --
+
+// ======================================================================
 // オブジェクトを連結するファンクタ
-struct SKKConcatObject {
+// ======================================================================
+struct ConcatDescription {
+    char delimiter_;
+
+    ConcatDescription(char delim) : delimiter_(delim) {}
+
     template<typename T>
     std::string operator()(const std::string& seed, const T& obj) const {
-	return seed + "/" + obj.Description();
+	return seed + delimiter_ + obj.Description();
     }
 };
 
-#pragma mark -- Utility --
+struct ConcatWord {
+    char delimiter_;
+
+    ConcatWord(char delim) : delimiter_(delim) {}
+
+    template<typename T>
+    std::string operator()(const std::string& seed, const T& obj) const {
+	return seed + delimiter_ + obj.Word();
+    }
+};
 
+// ======================================================================
 // 部分文字列を返していく単純なパーサ
+// ======================================================================
 class SKKEntryParser {
     const std::string& target_;
     const bool isOkuriAri_;	// 対象が「送りあり」かどうか
+    std::string::size_type pos1_;
+    std::string::size_type pos2_;
 
-    std::string fetch(bool first = false) const {
-	static std::string::size_type pos1;
-	static std::string::size_type pos2;
-
+    std::string fetch(bool first = false) {
 	if(first) {
-	    pos1 = pos2 = 0;
+	    pos1_ = pos2_ = 0;
 	}
 
-	pos1 = target_.find_first_not_of('/', pos2);
-	pos2 = target_.find_first_of('/', pos1);
+	pos1_ = target_.find_first_not_of('/', pos2_);
+	pos2_ = target_.find_first_of('/', pos1_);
 
 	// 見つかった?
-	if(pos1 != std::string::npos && pos2 != std::string::npos) {
-	    if(!isOkuriAri_ || target_[pos1] != '[') {
-		return target_.substr(pos1, pos2 - pos1);
+	if(pos1_ != std::string::npos && pos2_ != std::string::npos) {
+	    if(!isOkuriAri_ || target_[pos1_] != '[') {
+		return target_.substr(pos1_, pos2_ - pos1_);
 	    }
 	}
 	return std::string();
@@ -101,15 +123,17 @@
     SKKEntryParser(const std::string& str, bool isOkuriAri = false) : target_(str), isOkuriAri_(isOkuriAri) {
 	// empty
     }
-    std::string First() const {
+    std::string First() {
 	return fetch(true);
     }
-    std::string Next() const {
+    std::string Next() {
 	return fetch();
     }
 };
 
+// ======================================================================
 // 単一の変換候補(/候補;註釈/ → /word_;annotation_/ → Description())
+// ======================================================================
 class SKKCandidate {
     std::string word_;
     std::string annotation_;
@@ -141,10 +165,10 @@
     bool IsEmpty() const {
 	return word_.empty();
     }
-    std::string Word() const {
+    const std::string& Word() const {
 	return word_;
     }
-    std::string Annotation() const {
+    const std::string& Annotation() const {
 	return annotation_;
     }
     std::string Description() const {
@@ -160,10 +184,13 @@
 typedef std::vector<SKKCandidate> SKKCandidateContainer;
 typedef SKKCandidateContainer::iterator SKKCandidateIterator;
 
+// ======================================================================
 // 送りありエントリのヒント情報([く/書/掻/欠/] の部分)
+// ======================================================================
 class SKKOkuriHint {
     std::string kana_;
     SKKCandidateContainer candidates_;
+    int pos_;
 
     void setup(const std::string& str) {
 	std::string ret;
@@ -175,14 +202,13 @@
 	}
     }
 
-    SKKCandidate fetchCandidate(bool first = false) const {
-	static int i;
+    SKKCandidate fetchCandidate(bool first = false) {
 	if(first) {
-	    i = 0;
+	    pos_ = 0;
 	}
 
-	if(i < Count()) {
-	    return candidates_[i ++];
+	if(pos_ < Count()) {
+	    return candidates_[pos_ ++];
 	}
 	return SKKCandidate();
     }
@@ -204,17 +230,17 @@
     bool IsEmpty() const {
 	return kana_.empty();
     }
-    std::string Kana() const {
+    const std::string& Kana() const {
 	return kana_;
     }
     void SetKana(const std::string& kana) {
 	kana_ = kana;
     }
 
-    SKKCandidate First() const {
+    SKKCandidate First() {
 	return fetchCandidate(true);
     }
-    SKKCandidate Next() const {
+    SKKCandidate Next() {
 	return fetchCandidate();
     }
     int Count() const {
@@ -222,6 +248,12 @@
     }
 
     void Add(const SKKCandidate& theCandidate) {
+	SKKCandidateIterator i = std::find(candidates_.begin(), candidates_.end(), theCandidate);
+	if(i == candidates_.end()) {
+	    candidates_.push_back(theCandidate);
+	}
+    }
+    void Update(const SKKCandidate& theCandidate) {
 	Remove(theCandidate);
 	candidates_.insert(candidates_.begin(), theCandidate);
     }
@@ -229,7 +261,10 @@
 	candidates_.erase(std::remove(candidates_.begin(), candidates_.end(), theCandidate), candidates_.end());
     }
     std::string Description() const {
-	return '[' + std::accumulate(candidates_.begin(), candidates_.end(), Kana(), SKKConcatObject()) + "/]";
+	return '[' + std::accumulate(candidates_.begin(), candidates_.end(), Kana(), ConcatDescription('/')) + "/]";
+    }
+    std::string Word() const {
+	return '[' + std::accumulate(candidates_.begin(), candidates_.end(), Kana(), ConcatWord('/')) + "/]";
     }
 
     bool operator==(const SKKOkuriHint& rhs) const {
@@ -240,7 +275,9 @@
 typedef std::vector<SKKOkuriHint> SKKOkuriHintContainer;
 typedef SKKOkuriHintContainer::iterator SKKOkuriHintIterator;
 
+// ======================================================================
 // SKK 辞書の一行を表現するクラス
+// ======================================================================
 class SKKEntry {
     bool isOkuriAri_;		// 送りありかどうか
     std::string key_;		// 見出し語
@@ -248,6 +285,8 @@
     SKKOkuriHintContainer hints_;      // 送りありエントリのヒント情報
     std::string::size_type pos1_;
     std::string::size_type pos2_;
+    int cand_pos_;
+    int hint_pos_;
 
     std::string firstHint(const std::string& str) {
 	pos1_ = pos2_ = str.find("/[");
@@ -281,46 +320,60 @@
 	}
     }
 
-    SKKCandidate fetchCandidate(bool first = false) const {
-	static int i;
+    SKKCandidate fetchCandidate(bool first = false) {
 	if(first) {
-	    i = 0;
+	    cand_pos_ = 0;
 	}
 
-	if(i < Count()) {
-	    return candidates_[i ++];
+	if(cand_pos_ < Count()) {
+	    return candidates_[cand_pos_ ++];
 	}
 	return SKKCandidate();
     }
-    SKKOkuriHint fetchHint(bool first = false) const {
-	static int i;
+    SKKOkuriHint fetchHint(bool first = false) {
 	if(first) {
-	    i = 0;
+	    hint_pos_ = 0;
 	}
 
-	if(i < HintCount()) {
-	    return hints_[i ++];
+	if(hint_pos_ < HintCount()) {
+	    return hints_[hint_pos_ ++];
 	}
 	return SKKOkuriHint();
     }
 
-    SKKCandidate updateCandidate(const SKKCandidate& theCandidate) {
+    SKKCandidateIterator getCandidateIterator(const SKKCandidate& theCandidate) {
+	return std::find(candidates_.begin(), candidates_.end(), theCandidate);
+    }
+
+    void addCandidate(const SKKCandidate& theCandidate) {
+	// 註釈を含む完全なエントリを探す
+	SKKCandidateIterator i = getCandidateIterator(theCandidate);
+
+	// 見つかった?
+	if(i != candidates_.end()) {
+	    return;
+	}
+
+	// 末尾に追加
+	candidates_.push_back(theCandidate);
+    }
+
+    void updateCandidate(const SKKCandidate& theCandidate) {
 	SKKCandidate target;
 
 	// 註釈を含む完全なエントリを探す
-	SKKCandidateIterator i = std::find(candidates_.begin(), candidates_.end(), theCandidate);
+	SKKCandidateIterator i = getCandidateIterator(theCandidate);
+
 	// 見つかった?
 	if(i != candidates_.end()) {
 	    target = *i;
-	    RemoveOkuriNasi(target);
+	    Remove(target);
 	} else {
 	    target = theCandidate;
 	}
 
 	// 先頭に追加
 	candidates_.insert(candidates_.begin(), target);
-
-	return target;
     }
 
     SKKEntry() {}
@@ -357,10 +410,10 @@
     }
 
     // 候補に関する操作
-    SKKCandidate First() const {
+    SKKCandidate First() {
 	return fetchCandidate(true);
     }
-    SKKCandidate Next() const {
+    SKKCandidate Next() {
 	return fetchCandidate();
     }
     int Count() const {
@@ -368,39 +421,76 @@
     }
 
     // ヒントに関する操作
-    SKKOkuriHint FirstHint() const {
+    SKKOkuriHint FirstHint() {
 	return fetchHint(true);
     }
-    SKKOkuriHint NextHint() const {
+    SKKOkuriHint NextHint() {
 	return fetchHint();
     }
     int HintCount() const {
 	return hints_.size();
     }
+    void AddHint(const SKKOkuriHint& hint) {
+	SKKOkuriHintIterator i = std::find(hints_.begin(), hints_.end(), hint);
+	if(i == hints_.end()) {
+	    hints_.push_back(hint);
+	}
+    }
+
+    void Add(const SKKEntry& src) {
+	for(unsigned i = 0; i < src.candidates_.size(); ++ i) {
+	    addCandidate(src.candidates_[i]);
+	}
+
+	for(unsigned i = 0; i < src.hints_.size(); ++ i) {
+	    AddHint(src.hints_[i]);
+	}
+    }
 
     // 送りあり候補の追加
-    void AddOkuriAri(const SKKCandidate& theCandidate, const std::string& okuri) {
-	SKKCandidate target = updateCandidate(theCandidate);
+    void Add(const SKKCandidate& theCandidate, const std::string& okuri) {
+	addCandidate(theCandidate);
 
-	// ヒントが見つかれば更新する
+	// ヒントと一致すれば追加する
 	for(SKKOkuriHintIterator i = hints_.begin(); i != hints_.end(); ++ i) {
 	    if(i->Kana() == okuri) {
-		i->Add(target);
+		i->Add(theCandidate);
 		return;
 	    }
 	}
 
-	// 見つからなければ新規のヒントを追加
-	hints_.insert(hints_.begin(), SKKOkuriHint(okuri, target));
+	// 見つからなければ新規のヒントを末尾に追加
+	hints_.push_back(SKKOkuriHint(okuri, theCandidate));
     }
+
     // 送りなし候補の追加
-    void AddOkuriNasi(const SKKCandidate& theCandidate) {
+    void Add(const SKKCandidate& theCandidate) {
+	addCandidate(theCandidate);
+    }
+
+    // 送りあり候補の更新
+    void Update(const SKKCandidate& theCandidate, const std::string& okuri) {
 	updateCandidate(theCandidate);
+
+	// ヒントが見つかれば更新する
+	for(SKKOkuriHintIterator i = hints_.begin(); i != hints_.end(); ++ i) {
+	    if(i->Kana() == okuri) {
+		i->Update(theCandidate);
+		return;
+	    }
+	}
+
+	// 見つからなければ新規のヒントを先頭に追加
+	hints_.insert(hints_.begin(), SKKOkuriHint(okuri, theCandidate));
     }
 
-    // 送りあり候補の削除
-    void RemoveOkuriAri(const SKKCandidate& theCandidate) {
-	RemoveOkuriNasi(theCandidate);
+    void Update(const SKKCandidate& theCandidate) {
+	updateCandidate(theCandidate);
+    }
+
+    // 候補の削除
+    void Remove(const SKKCandidate& theCandidate) {
+	candidates_.erase(std::remove(candidates_.begin(), candidates_.end(), theCandidate), candidates_.end());
 
 	// ヒントも削除
 	for(SKKOkuriHintIterator i = hints_.begin(); i != hints_.end(); /* empty */) {
@@ -412,23 +502,33 @@
 	    }
 	}
     }
-    // 送りなし候補の削除
-    void RemoveOkuriNasi(const SKKCandidate& theCandidate) {
-	candidates_.erase(std::remove(candidates_.begin(), candidates_.end(), theCandidate), candidates_.end());
-    }
 
-    std::string Key() const {
+    const std::string& Key() const {
 	return key_;
     }
-    std::string Candidate() const {
+
+    std::string Candidate(char delimiter = '/') const {
 	std::string ret;
-	ret = std::accumulate(candidates_.begin(), candidates_.end(), ret, SKKConcatObject());
+	ret = std::accumulate(candidates_.begin(), candidates_.end(), ret, ConcatDescription(delimiter));
+
 	if(hints_.empty()) {
-	    return ret + "/";
+	    return ret + delimiter;
 	}
 
-	return std::accumulate(hints_.begin(), hints_.end(), ret, SKKConcatObject()) + "/";
+	return std::accumulate(hints_.begin(), hints_.end(), ret, ConcatDescription(delimiter)) + delimiter;
     }
+
+    std::string Join(char delimiter = '/') const {
+	std::string ret;
+	ret = std::accumulate(candidates_.begin(), candidates_.end(), ret, ConcatWord(delimiter));
+
+	if(hints_.empty()) {
+	    return ret + delimiter;
+	}
+
+	return std::accumulate(hints_.begin(), hints_.end(), ret, ConcatWord(delimiter)) + delimiter;
+    }
+
     std::string Description() const {
 	return Key() + " " + Candidate();
     }
Index: AquaSKK/DictionarySet.cpp
diff -u AquaSKK/DictionarySet.cpp:1.5.2.2 AquaSKK/DictionarySet.cpp:1.5.2.3
--- AquaSKK/DictionarySet.cpp:1.5.2.2	Tue Nov 28 23:36:08 2006
+++ AquaSKK/DictionarySet.cpp	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /*
-  $Id: DictionarySet.cpp,v 1.5.2.2 2006/11/28 14:36:08 t-suwa Exp $
+  $Id: DictionarySet.cpp,v 1.5.2.3 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -22,11 +22,8 @@
 */
 
 #include <iostream>
-#include <set>
 #include <sstream>
 #include "BIMClientServer.h"
-#include "CppCFString.h"
-#include "OkuriganaEntry.h"
 #include "Dictionary.h"
 #include "SKKDictionary.h"
 #include "KotoeriDictionary.h"
@@ -37,58 +34,6 @@
 // 定数など
 const char* SKK_USER_DICT_PATH = "skk-jisyo.utf8";
 
-// エントリー変換
-class SKKEntryConverter {
-public:
-    // SKKEntry → std::vector<OkuriganaEntry> 変換
-    static std::vector<OkuriganaEntry> OkuriAri(const SKKEntry& entry) {
-	// まずヒント情報から追加する
-	std::vector<OkuriganaEntry> result;
-	for(SKKOkuriHint hint = entry.FirstHint(); !hint.IsEmpty(); hint = entry.NextHint()) {
-	    OkuriganaEntry okuri;
-
-	    okuri.setKana(CppCFString(hint.Kana().c_str(), kCFStringEncodingUTF8));
-	    for(SKKCandidate candidate = hint.First(); !candidate.IsEmpty(); candidate = hint.Next()) {
-		okuri.add(CppCFString(candidate.Description().c_str(), kCFStringEncodingUTF8));
-	    }
-	    result.push_back(okuri);
-	}
-
-	// 次に、変換候補を追加する
-	OkuriganaEntry wild;
-	for(SKKCandidate candidate = entry.First(); !candidate.IsEmpty(); candidate = entry.Next()) {
-	    wild.add(CppCFString(candidate.Description().c_str(), kCFStringEncodingUTF8));
-	}
-	result.push_back(wild);
-
-	return result;
-    }
-
-    // SKKEntry → std::vector<CppCFString> 変換
-    static std::vector<CppCFString> OkuriNasi(const SKKEntry& entry) {
-	return CppCFString(entry.Candidate().c_str(), kCFStringEncodingUTF8).split('/');
-    }
-};
-
-// 重複したエントリを削除する
-static void removeRedundantItems(std::vector<CppCFString>& candidates) {
-    std::set<CppCFString> check;
-    std::vector<CppCFString> result;
-
-    for(std::vector<CppCFString>::iterator iter = candidates.begin(); iter != candidates.end(); ++ iter) {
-	// 註釈部分は比較しない
-	std::string str = iter->toStdString(kCFStringEncodingUTF8);
-	std::string::size_type pos = str.find_first_of(';');
-	CppCFString target(str.substr(0, pos).c_str(), kCFStringEncodingUTF8);
-
-	if(check.find(target) == check.end()) {
-	    check.insert(check.lower_bound(target), target);
-	    result.push_back(target);
-	}
-    }
-    result.swap(candidates);
-}
-
 // 辞書削除ファンクタ
 struct DeleteDictionary {
     void operator()(std::pair<std::string, Dictionary*> entry) {
@@ -166,17 +111,19 @@
     // 古い情報をクリアする
     prefs_.clear();
 
-    // ユーザー辞書を先頭に入れる
+    // ユーザー辞書をキャッシュの先頭に入れる
     entry.active = true;
     entry.type = SKKDictionaryType;
     entry.location = SKKConfig::LibraryDirectory() + SKK_USER_DICT_PATH;
     prefs_.push_back(entry);
     cache[generateID(entry)] = userdict_;
+
+    // ユーザー辞書をセットから削除
     if(dicts_.find(generateID(entry)) != dicts_.end()) {
 	dicts_.erase(generateID(entry));
     }
 
-    // 定義された辞書を全てロードする
+    // 全ての辞書をキャッシュにロードする
     for(CFIndex index = 0; index < CFArrayGetCount(arrayRef); ++ index) {
 	CFDictionaryRef dictRef = (CFDictionaryRef)CFRetain(CFArrayGetValueAtIndex(arrayRef, index));
 
@@ -198,7 +145,7 @@
 	    cache[generateID(entry)] = iter->second;
 	    dicts_.erase(generateID(entry));
 	} else {
-	    // キャッシュにもない?
+	    // キャッシュにもなければ作る
 	    if(cache.find(generateID(entry)) == cache.end()) {
 		cache[generateID(entry)] = createDictionary(entry);
 	    }
@@ -223,7 +170,7 @@
 }
 
 DictionarySet::~DictionarySet() {
-    terminate();
+    Terminate();
 }
 
 DictionarySet& DictionarySet::theInstance() {
@@ -231,268 +178,124 @@
     return obj;
 }
 
-void DictionarySet::initialize(CFArrayRef arrayRef) {
+void DictionarySet::Initialize(CFArrayRef arrayRef) {
     // 辞書をロードする
     load(arrayRef);
 }
 
-void DictionarySet::terminate() {
+void DictionarySet::Terminate() {
     // 全ての辞書を削除する
     std::for_each(dicts_.begin(), dicts_.end(), DeleteDictionary());
     dicts_.clear();
 }
 
-CppCFString DictionarySet::skkserv_style_search(const CppCFString& query) {
-    // skkservスタイルでの檢索を行ふ。
-    // クエリの形式は、「せんとう」「けs」のやうなもの。末尾に空白文字は付けない。
-    // リプライの形式は、「/先頭/戦闘/銭湯/」のやうに、辭書の形式そのまま。無ければ空文字列。
-    if(query.length() == 0) {
-	return CppCFString();
-    }
-
-    // 送り假名は有るか?
-    if(query[0] > 0xff && query[query.length() - 1] >= 'a' && query[query.length() - 1] <= 'z') {
-	// OkuriganaEntry.kana => OkuriganaEntry
-	std::map<CppCFString, OkuriganaEntry> cands;
-
-	std::string key = query.toStdString(kCFStringEncodingUTF8);
-
-	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
-	    // グループ辞書の場合、既に候補が見つかっていれば検索をやめる
-	    if(iter->type == GroupingDictionaryType && !cands.empty()) {
-		break;
-	    }
-
-	    Dictionary* dict = dicts_[generateID(*iter)];
-	    std::string result = dict->findOkuriAri(key);
-	    if(result.empty()) {
-		continue;
-	    }
+std::string DictionarySet::CompleteEntry(const std::string& key, char result_delimiter) {
+    return userdict_->findCompletions(key, result_delimiter);
+}
 
-	    std::vector<OkuriganaEntry> okuriEntries
-		= SKKEntryConverter::OkuriAri(SKKEntry::ParseOkuriAri(key, result));
+std::string DictionarySet::FindOkuriAri(const std::string& key, const std::string& okuri, char result_delimiter) {
+    SKKEntry strict_match = SKKEntry::CreateOkuriAri(key);
+    SKKEntry normal_match = strict_match;
 
-	    for(std::vector<OkuriganaEntry>::iterator e = okuriEntries.begin(); e != okuriEntries.end(); ++ e) {
-		OkuriganaEntry& entry = cands[e->getKana()];
-		entry.setKana(e->getKana());
-		entry.getCandidates().insert(entry.getCandidates().begin(),
-					     e->getCandidates().begin(), e->getCandidates().end());
-	    }
+    for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
+	// グループ辞書の場合、既に候補が見つかっていれば検索をやめる
+	if(iter->type == GroupingDictionaryType && (strict_match.Count() > 0 || normal_match.Count() > 0)) {
+	    break;
 	}
 
-	// 全てのエントリについて、重複してゐるものを削除しながら、リプ
-	// ライを組立てる。
-	CppCFString reply;
-	for(std::map<CppCFString, OkuriganaEntry>::iterator e = cands.begin(); e != cands.end(); ++ e) {
-	    OkuriganaEntry& entry = e->second;
-	    entry.removeRedundantItems();
-	    if(entry.isWild()) {
-		reply.append(join('/', entry.getCandidates())).append('/');
-	    } else {
-		reply.append('[').append(entry.getKana()).append('/').
-		    append(join('/', entry.getCandidates())).append("/]/");
-	    }
-	}
-	if(reply.length() > 0) {
-	    reply.insert(0, '/');
+	Dictionary* dict = dicts_[generateID(*iter)];
+	std::string result = dict->findOkuriAri(key);
+	if(result.empty()) {
+	    continue;
 	}
-	return reply;
-    } else {
-	// 送り假名が無い。
-	std::vector<CppCFString> cands;
-	std::string key = query.toStdString(kCFStringEncodingUTF8);
-
-	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
-	    // グループ辞書の場合、既に候補が見つかっていれば検索をやめる
-	    if(iter->type == GroupingDictionaryType && !cands.empty()) {
-		break;
-	    }
 
-	    Dictionary* dict = dicts_[generateID(*iter)];
-	    std::string result = dict->findOkuriNasi(key);
-	    if(result.empty()) {
-		continue;
-	    }
+	// 分解
+	SKKEntry entry = SKKEntry::ParseOkuriAri(key, result);
 
-	    std::vector<CppCFString> entries = SKKEntryConverter::OkuriNasi(SKKEntry::ParseOkuriNasi(key, result));
-	    cands.insert(cands.end(), entries.begin(), entries.end());
-	}
+	// ヒント情報を使う
+	for(SKKOkuriHint hint = entry.FirstHint(); !hint.IsEmpty(); hint = entry.NextHint()) {
+	    if(!okuri.empty()) {
+		SKKEntry* match;
+		if(hint.Kana() == okuri) {
+		    match = &strict_match;
+		} else {
+		    match = &normal_match;
+		}
 
-	// 重複してゐるものを削除。
-	removeRedundantItems(cands);
+		for(SKKCandidate cand = hint.First(); !cand.IsEmpty(); cand = hint.Next()) {
+		    match->Add(cand);
+		}
+	    } else {
+		// skkserv スタイルの検索に対しては、ヒント情報も返す
+		strict_match.AddHint(hint);
+	    }
+	}
 
-	if(cands.size() > 0) {
-	    return CppCFString('/').append(join('/', cands)).append('/');
-	} else {
-	    return CppCFString();
+	// 候補を追加する
+	for(SKKCandidate cand = entry.First(); !cand.IsEmpty(); cand = entry.Next()) {
+	    normal_match.Add(cand);
 	}
     }
-}
 
-CppCFString DictionarySet::search(const CppCFString& query) {
-    bool has_okuri = query[0] == '+';
-    CppCFString query_str(query.substring(1)); // 先頭の+,-を消す。
-
-    if(has_okuri) {
-	int pos_space = query_str.indexOf(SKK_MSG_DELIMITER);
-	CppCFString root = query_str.substring(0, pos_space); // 「+なでr る」なら「なでr」
-	CppCFString okuri = query_str.substring(pos_space + 1); // 「+なでr る」なら「る」
-
-	std::vector<CppCFString> cand_strictly_matched;
-	std::vector<CppCFString> cand_unstrictly_matched;
-
-	std::string key = root.toStdString(kCFStringEncodingUTF8);
-
-	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
-	    // グループ辞書の場合、既に候補が見つかっていれば検索をやめる
-	    if(iter->type == GroupingDictionaryType &&
-	       (!cand_strictly_matched.empty() || !cand_unstrictly_matched.empty())) {
-		break;
-	    }
+    strict_match.Add(normal_match);
 
-	    Dictionary* dict = dicts_[generateID(*iter)];
-	    std::string result = dict->findOkuriAri(key);
-	    if(result.empty()) {
-		continue;
-	    }
+    return strict_match.Join(result_delimiter);
+}
 
-	    std::vector<OkuriganaEntry> okuriEntries
-		= SKKEntryConverter::OkuriAri(SKKEntry::ParseOkuriAri(key, result));
+std::string DictionarySet::FindOkuriNasi(const std::string& key, char result_delimiter) {
+    SKKEntry match = SKKEntry::CreateOkuriNasi(key);
 
-	    for(std::vector<OkuriganaEntry>::iterator e = okuriEntries.begin(); e != okuriEntries.end(); ++ e) {
-		if(e->getKana() == okuri) {
-		    cand_strictly_matched.insert(cand_strictly_matched.end(),
-						 e->getCandidates().begin(), e->getCandidates().end());
-		} else {
-		    cand_unstrictly_matched.insert(cand_unstrictly_matched.end(),
-						   e->getCandidates().begin(), e->getCandidates().end());
-		}
-	    }
+    for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
+	// グループ辞書の場合、既に候補が見つかっていれば検索をやめる
+	if(iter->type == GroupingDictionaryType && match.Count() > 0) {
+	    break;
 	}
 
-	std::vector<CppCFString> candidates = cand_strictly_matched;
-	candidates.insert(candidates.end(), cand_unstrictly_matched.begin(), cand_unstrictly_matched.end());
-
-	// candidatesの重複をチェック。重複していたら後にあるものを削除。
-	removeRedundantItems(candidates);
-
-	// この時点でcandidatesに入っているのは漢字の部分だけなので、
-	// 全ての要素に送り仮名を付ける。同時にスペースを[20]に変換。
-	// 註釈が付いていれば、削除する。(暫定)
-	for(std::vector<CppCFString>::iterator ite = candidates.begin(); ite != candidates.end(); ++ ite) {
-	    const int semicolon_pos = ite->indexOf(';');
-	    if(semicolon_pos != -1) {
-		ite->erase(semicolon_pos, ite->length());
-	    }
-	    ite->append(okuri);
+	Dictionary* dict = dicts_[generateID(*iter)];
+	std::string result = dict->findOkuriNasi(key);
+	if(result.empty()) {
+	    continue;
 	}
 
-	return join(SKK_MSG_DELIMITER, candidates);
-    } else {
-	std::vector<CppCFString> candidates;
-	std::string key = query_str.toStdString(kCFStringEncodingUTF8);
-
-	for(DictionaryPrefIterator iter = prefs_.begin(); iter != prefs_.end(); ++ iter) {
-	    // グループ辞書の場合、既に候補が見つかっていれば検索をやめる
-	    if(iter->type == GroupingDictionaryType && !candidates.empty()) {
-		break;
-	    }
-
-	    Dictionary* dict = dicts_[generateID(*iter)];
-	    std::string result = dict->findOkuriNasi(key);
-	    if(result.empty()) {
-		continue;
-	    }
+	// 分解
+	SKKEntry entry = SKKEntry::ParseOkuriNasi(key, result);
 
-	    std::vector<CppCFString> entries = SKKEntryConverter::OkuriNasi(SKKEntry::ParseOkuriNasi(key, result));
-	    candidates.insert(candidates.end(), entries.begin(), entries.end());
+	for(SKKCandidate cand = entry.First(); !cand.IsEmpty(); cand = entry.Next()) {
+	    match.Add(cand);
 	}
+    }
 
-	// candidatesの重複をチェック。重複していたら後にあるものを削除。
-	removeRedundantItems(candidates);
-
-	// 註釈が付いていれば、削除する。(暫定)
-	for(std::vector<CppCFString>::iterator ite = candidates.begin(); ite != candidates.end(); ++ ite) {
-	    const int semicolon_pos = ite->indexOf(';');
-	    if(semicolon_pos != -1) {
-		ite->erase(semicolon_pos, ite->length());
-	    }
-	}
+    return match.Join(result_delimiter);
+}
 
-	return join(SKK_MSG_DELIMITER, candidates);
+void DictionarySet::RegisterOkuriAri(const std::string& key, const std::string& okuri, const std::string& entry) {
+    if(key.empty() || entry.empty() || okuri.empty()) {
+	std::cerr << "AquaSKK: Invalid registration received" << std::endl;
+    } else {
+	userdict_->registerOkuriAri(key, okuri, entry);
     }
 }
 
-void DictionarySet::registerToUserDic(const CppCFString& query) {
-    // 送り仮名あり?「+おくr り 送」
-    if(query[0] == '+') {
-	int pos_first_space = query.indexOf(SKK_MSG_DELIMITER);
-	int pos_second_space = query.indexOf(SKK_MSG_DELIMITER, pos_first_space + 1);
-
-	std::string index = query.substring(1, pos_first_space).toStdString(kCFStringEncodingUTF8);
-	std::string okuri = query.substring(pos_first_space + 1, pos_second_space).toStdString(kCFStringEncodingUTF8);
-	std::string kanji = query.substring(pos_second_space + 1).toStdString(kCFStringEncodingUTF8);
-
-	if(index.length() == 0 || okuri.length() == 0 || kanji.length() == 0) {
-	    std::cerr << "AquaSKK: Invalid registration received; index:"
-		      << index.length() << " okuri:" << okuri.length()
-		      << " kanji:" << kanji.length() << " (len)" << std::endl;
-	} else {
-	    userdict_->registerOkuriAri(index, okuri, kanji);
-	}
+void DictionarySet::RegisterOkuriNasi(const std::string& key, const std::string& entry) {
+    if(key.empty() || entry.empty()) {
+	std::cerr << "AquaSKK: Invalid registration received" << std::endl;
     } else {
-	// 送り仮名無し。「-かな 仮名」
-	int pos_space = query.indexOf(SKK_MSG_DELIMITER);
-
-	std::string index = query.substring(1, pos_space).toStdString(kCFStringEncodingUTF8);
-	std::string kanji = query.substring(pos_space + 1).toStdString(kCFStringEncodingUTF8);
-
-	if(index.length() == 0 || kanji.length() == 0) {
-	    std::cerr << "AquaSKK: Invalid registration received; index:"
-		      << index.length() << " kanji:" << kanji.length()
-		      << " (len)" << std::endl;
-	} else {
-	    userdict_->registerOkuriNasi(index, kanji);
-	}
+	userdict_->registerOkuriNasi(key, entry);
     }
 }
 
-void DictionarySet::removeFromUserDic(const CppCFString& query) {
-    // 送り仮名あり?「+おくr り 送」
-    if(query[0] == '+') {
-	int pos_first_space = query.indexOf(SKK_MSG_DELIMITER);
-	int pos_second_space = query.indexOf(SKK_MSG_DELIMITER, pos_first_space + 1);
-
-	std::string index = query.substring(1, pos_first_space).toStdString(kCFStringEncodingUTF8);
-	std::string okuri = query.substring(pos_first_space + 1, pos_second_space).toStdString(kCFStringEncodingUTF8);
-	std::string kanji = query.substring(pos_second_space + 1).toStdString(kCFStringEncodingUTF8);
-
-	if(index.length() == 0 || okuri.length() == 0 || kanji.length() == 0) {
-	    std::cerr << "AquaSKK: Invalid removal received; index:"
-		      << index.length() << " okuri:" << okuri.length()
-		      << " kanji:" << kanji.length() << " (len)" << std::endl;
-	} else {
-	    userdict_->removeOkuriAri(index, kanji);
-	}
+void DictionarySet::RemoveOkuriAri(const std::string& key, const std::string& entry) {
+    if(key.empty() || entry.empty()) {
+	std::cerr << "AquaSKK: Invalid removal received" << std::endl;
     } else {
-	// 送り仮名無し。「-かな 仮名」
-	int pos_space = query.indexOf(SKK_MSG_DELIMITER);
-
-	std::string index = query.substring(1, pos_space).toStdString(kCFStringEncodingUTF8);
-	std::string kanji = query.substring(pos_space + 1).toStdString(kCFStringEncodingUTF8);
-
-	if(index.length() == 0 || kanji.length() == 0) {
-	    std::cerr << "AquaSKK: Invalid removal received; index:"
-		      << index.length() << " kanji:" << kanji.length()
-		      << " (len)" << std::endl;
-	} else {
-	    userdict_->removeOkuriNasi(index, kanji);
-	}
+	userdict_->removeOkuriAri(key, entry);
     }
 }
 
-CppCFString DictionarySet::searchCompletions(const CppCFString& query) {
-    std::string result = userdict_->findCompletions(query.toStdString(kCFStringEncodingUTF8), SKK_MSG_DELIMITER);
-
-    return CppCFString(result.c_str(), kCFStringEncodingUTF8);
+void DictionarySet::RemoveOkuriNasi(const std::string& key, const std::string& entry) {
+    if(key.empty() || entry.empty()) {
+	std::cerr << "AquaSKK: Invalid removal received" << std::endl;
+    } else {
+	userdict_->removeOkuriNasi(key, entry);
+    }
 }
Index: AquaSKK/DictionarySet.h
diff -u AquaSKK/DictionarySet.h:1.2.2.2 AquaSKK/DictionarySet.h:1.2.2.3
--- AquaSKK/DictionarySet.h:1.2.2.2	Tue Nov 28 23:36:08 2006
+++ AquaSKK/DictionarySet.h	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /* -*- c++ -*-
-  $Id: DictionarySet.h,v 1.2.2.2 2006/11/28 14:36:08 t-suwa Exp $
+  $Id: DictionarySet.h,v 1.2.2.3 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -28,7 +28,6 @@
 #include <vector>
 #include <map>
 
-class CppCFString;
 class Dictionary;
 class UserDictionary;
 
@@ -69,14 +68,23 @@
 public:
     static DictionarySet& theInstance();
 
-    void initialize(CFArrayRef arrayRef);
-    void terminate();
+    void Initialize(CFArrayRef arrayRef);
+    void Terminate();
 
-    CppCFString skkserv_style_search(const CppCFString& query);
-    CppCFString search(const CppCFString& query);
-    void registerToUserDic(const CppCFString& query);
-    void removeFromUserDic(const CppCFString& query);
-    CppCFString searchCompletions(const CppCFString& query);
+    // 補完
+    std::string CompleteEntry(const std::string& key, char result_delimiter);
+
+    // 検索
+    std::string FindOkuriAri(const std::string& key, const std::string& okuri, char result_delimiter);
+    std::string FindOkuriNasi(const std::string& key, char result_delimiter);
+
+    // 登録
+    void RegisterOkuriAri(const std::string& key, const std::string& entry, const std::string& okuri);
+    void RegisterOkuriNasi(const std::string& key, const std::string& entry);
+
+    // 削除
+    void RemoveOkuriAri(const std::string& key, const std::string& entry);
+    void RemoveOkuriNasi(const std::string& key, const std::string& entry);
 };
 
 #endif
Index: AquaSKK/KanjiConversionMode.cpp
diff -u AquaSKK/KanjiConversionMode.cpp:1.8 AquaSKK/KanjiConversionMode.cpp:1.8.2.1
--- AquaSKK/KanjiConversionMode.cpp:1.8	Mon Jun 12 23:14:48 2006
+++ AquaSKK/KanjiConversionMode.cpp	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /*
-  $Id: KanjiConversionMode.cpp,v 1.8 2006/06/12 14:14:48 t-suwa Exp $
+  $Id: KanjiConversionMode.cpp,v 1.8.2.1 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -1010,6 +1010,13 @@
     current_candidate_index = 0;
     ::askServerTheCandidates(cfdata_query, candidates);
 
+    // ‘—‚艼–¼‚ð•t—^‚·‚é
+    if(hasOkuri) {
+	for(unsigned i = 0; i < candidates.size(); ++ i) {
+	    candidates[i] += okuri;
+	}
+    }
+
     // ”’l•ÏŠ·‚ª—LŒø‚©H
     if(!hasOkuri && ClientConfiguration::theInstance().useNumericConversion() && numconv_.Setup(root)) {
 	std::vector<CppCFString> result;
@@ -1049,7 +1056,6 @@
 	} else {
 	    query.append(HiraganaInputMode::convert(root)).append(okuri_head);
 	}
-	query.append(SKK_MSG_DELIMITER).append(HiraganaInputMode::convert(okuri));
 	query.append(SKK_MSG_DELIMITER).append(
 	    candidates[current_candidate_index].clone().eraseLast(okuri.length())); // ‘—‚艼–¼‚ðÁ‚·B
     } else {
Index: AquaSKK/PreferencesController.mm
diff -u AquaSKK/PreferencesController.mm:1.8 AquaSKK/PreferencesController.mm:1.8.2.1
--- AquaSKK/PreferencesController.mm:1.8	Tue May 23 23:47:01 2006
+++ AquaSKK/PreferencesController.mm	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /*  -*- objc -*-
-  $Id: PreferencesController.mm,v 1.8 2006/05/23 14:47:01 t-suwa Exp $
+  $Id: PreferencesController.mm,v 1.8.2.1 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -211,7 +211,7 @@
     }
 
     // Ä“xAŽ«‘ƒT[ƒo[‚ð‰Šú‰»‚·‚é
-    DictionarySet::theInstance().initialize((CFArrayRef)[self contentForDictionarySet]);
+    DictionarySet::theInstance().Initialize((CFArrayRef)[self contentForDictionarySet]);
 
     // skkserv ƒGƒ~ƒ…ƒŒ[ƒVƒ‡ƒ“‚̍ċN“®
     skkserv& skkserv = skkserv::theInstance();
Index: AquaSKK/SKKDictionary.cpp
diff -u AquaSKK/SKKDictionary.cpp:1.12.2.1 AquaSKK/SKKDictionary.cpp:1.12.2.2
--- AquaSKK/SKKDictionary.cpp:1.12.2.1	Mon Nov 27 15:16:27 2006
+++ AquaSKK/SKKDictionary.cpp	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /*
-  $Id: SKKDictionary.cpp,v 1.12.2.1 2006/11/27 06:16:27 t-suwa Exp $
+  $Id: SKKDictionary.cpp,v 1.12.2.2 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -465,7 +465,7 @@
 	okuriAri_.erase(std::remove(okuriAri_.begin(), okuriAri_.end(), *i), okuriAri_.end());
     }
 
-    entry.AddOkuriAri(SKKCandidate(kanji), okuri);
+    entry.Update(SKKCandidate(kanji), okuri);
     okuriAri_.push_front(SKKPair(entry.Key(), entry.Candidate()));
 
     // 保存する
@@ -482,7 +482,7 @@
 	okuriNasi_.erase(std::remove(okuriNasi_.begin(), okuriNasi_.end(), *i), okuriNasi_.end());
     }
 
-    entry.AddOkuriNasi(SKKCandidate(kanji));
+    entry.Update(SKKCandidate(kanji));
     okuriNasi_.push_front(SKKPair(entry.Key(), entry.Candidate()));
 
     // 保存する
@@ -498,7 +498,7 @@
     }
 
     SKKEntry entry = SKKEntry::ParseOkuriAri(i->first, i->second);
-    entry.RemoveOkuriAri(SKKCandidate(kanji));
+    entry.Remove(SKKCandidate(kanji));
 
     // まだ候補が残っている?
     if(entry.Count() > 0) {
@@ -522,7 +522,7 @@
     }
 
     SKKEntry entry = SKKEntry::ParseOkuriNasi(i->first, i->second);
-    entry.RemoveOkuriNasi(SKKCandidate(kanji));
+    entry.Remove(SKKCandidate(kanji));
 
     // まだ候補が残っている?
     if(entry.Count() > 0) {
Index: AquaSKK/ServerMessageReceiver.mm
diff -u AquaSKK/ServerMessageReceiver.mm:1.7 AquaSKK/ServerMessageReceiver.mm:1.7.2.1
--- AquaSKK/ServerMessageReceiver.mm:1.7	Sat Jul 15 09:49:00 2006
+++ AquaSKK/ServerMessageReceiver.mm	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /* -*- objc -*-
-  $Id: ServerMessageReceiver.mm,v 1.7 2006/07/15 00:49:00 t-suwa Exp $
+  $Id: ServerMessageReceiver.mm,v 1.7.2.1 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -136,9 +136,24 @@
 }
 
 CppCFData ServerMessageReceiver::searchWord(const CppCFData& attachment) {
-    // ’ljÁƒf[ƒ^‚Æ‚µ‚ÄUniChar‚Ì”z—ñ‚ðŽæ‚éB
-    CppCFString query(attachment.getData());
-    CppCFString reply(DictionarySet::theInstance().search(query));
+    CppCFString data(attachment.getData());
+
+    std::string query = data.toStdString(kCFStringEncodingUTF8).substr(1);
+    std::string result;
+
+    // ‘—‚è‚ ‚èH
+    if(data[0] == '+') {
+	int pos = query.find_first_of(SKK_MSG_DELIMITER);
+
+	std::string key = query.substr(0, pos);
+	std::string okuri = query.substr(pos + 1);
+
+	result = DictionarySet::theInstance().FindOkuriAri(key, okuri, SKK_MSG_DELIMITER);
+    } else {
+	result = DictionarySet::theInstance().FindOkuriNasi(query, SKK_MSG_DELIMITER);
+    }
+    
+    CppCFString reply(result.c_str(), kCFStringEncodingUTF8);
 
     return CppCFData().own(reply.toCFData());
 }
@@ -198,15 +213,46 @@
 }
 
 void ServerMessageReceiver::registerThisToUserDic(const CppCFData& attachment) {
-    CppCFString query(attachment.getData());
+    CppCFString data(attachment.getData());
+
+    std::string query = data.toStdString(kCFStringEncodingUTF8).substr(1);
+
+    // ‘—‚è‚ ‚èH
+    if(data[0] == '+') {
+	int pos1 = query.find_first_of(SKK_MSG_DELIMITER);
+	int pos2 = query.find_first_of(SKK_MSG_DELIMITER, pos1 + 1);
+
+	std::string key = query.substr(0, pos1);
+	std::string okuri = query.substr(pos1 + 1, pos2 - pos1 - 1);
+	std::string kanji = query.substr(pos2 + 1);
 
-    DictionarySet::theInstance().registerToUserDic(query);
+	DictionarySet::theInstance().RegisterOkuriAri(key, okuri, kanji);
+    } else {
+	int pos = query.find_first_of(SKK_MSG_DELIMITER);
+
+	std::string key = query.substr(0, pos);
+	std::string kanji = query.substr(pos + 1);
+
+	DictionarySet::theInstance().RegisterOkuriNasi(key, kanji);
+    }
 }
 
 void ServerMessageReceiver::removeThisFromUserDic(const CppCFData& attachment) {
-    CppCFString query(attachment.getData());
+    CppCFString data(attachment.getData());
+
+    std::string query = data.toStdString(kCFStringEncodingUTF8).substr(1);
 
-    DictionarySet::theInstance().removeFromUserDic(query);
+    int pos = query.find_first_of(SKK_MSG_DELIMITER);
+
+    std::string key = query.substr(0, pos);
+    std::string kanji = query.substr(pos + 1);
+
+    // ‘—‚è‚ ‚èH
+    if(data[0] == '+') {
+	DictionarySet::theInstance().RemoveOkuriAri(key, kanji);
+    } else {
+	DictionarySet::theInstance().RemoveOkuriNasi(key, kanji);
+    }
 }
 
 void ServerMessageReceiver::showAboutBox() {
@@ -219,7 +265,11 @@
 
 CppCFData ServerMessageReceiver::fetchCompletions(const CppCFData& attachment) {
     CppCFString query(attachment.getData());
-    CppCFString reply(DictionarySet::theInstance().searchCompletions(query));
+
+    std::string key = query.toStdString(kCFStringEncodingUTF8);
+    std::string result = DictionarySet::theInstance().CompleteEntry(key, SKK_MSG_DELIMITER);
+
+    CppCFString reply(result.c_str(), kCFStringEncodingUTF8);
 
     return CppCFData().own(reply.toCFData());
 }
Index: AquaSKK/WordRegisterMode.cpp
diff -u AquaSKK/WordRegisterMode.cpp:1.5.2.1 AquaSKK/WordRegisterMode.cpp:1.5.2.2
--- AquaSKK/WordRegisterMode.cpp:1.5.2.1	Tue Nov 28 23:36:08 2006
+++ AquaSKK/WordRegisterMode.cpp	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /*
-  $Id: WordRegisterMode.cpp,v 1.5.2.1 2006/11/28 14:36:08 t-suwa Exp $
+  $Id: WordRegisterMode.cpp,v 1.5.2.2 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -124,6 +124,7 @@
     }
 	
     PasteboardRef pasteboard;
+
     PasteboardCreate(kPasteboardClipboard, &pasteboard);
     PasteboardSynchronize(pasteboard);
 
@@ -131,22 +132,16 @@
     PasteboardGetItemCount(pasteboard, &items);
     for(UInt32 pos = 0; pos < items; ++ pos) {
 	PasteboardItemID id;
-	CFDataRef data;
-
 	// index ‚Í 1 based
 	if(PasteboardGetItemIdentifier(pasteboard, pos + 1, &id) < 0) continue;
 
+	CFDataRef data;
 	CppCFString str;
-#if MAC_OS_X_VERSION_MAX_ALLOWED>=1040
-	if(PasteboardCopyItemFlavorData(pasteboard, id, kUTTypeUTF16ExternalPlainText, &data) == 0) {
-	    str = CppCFString(data);
-#else
-	if(PasteboardCopyItemFlavorData(pasteboard, id, CFSTR("public.utf16-plain-text"), &data) == 0) {
+	if(PasteboardCopyItemFlavorData(pasteboard, id, CFSTR("public.utf8-plain-text"), &data) == 0) {
 	    CFStringRef tmp = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(data), CFDataGetLength(data),
-						      kCFStringEncodingUnicode, false);
+						      kCFStringEncodingUTF8, false);
 	    str = CppCFString(tmp);
 	    CFRelease(tmp);
-#endif
 	} else {
 	    if(PasteboardCopyItemFlavorData(pasteboard, id, CFSTR("com.apple.traditional-mac-plain-text"), &data) < 0) {
 		continue;
Index: AquaSKK/skkserv.cpp
diff -u AquaSKK/skkserv.cpp:1.3.2.1 AquaSKK/skkserv.cpp:1.3.2.2
--- AquaSKK/skkserv.cpp:1.3.2.1	Tue Nov 28 23:36:08 2006
+++ AquaSKK/skkserv.cpp	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /*
-  $Id: skkserv.cpp,v 1.3.2.1 2006/11/28 14:36:08 t-suwa Exp $
+  $Id: skkserv.cpp,v 1.3.2.2 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -23,8 +23,8 @@
 
 #include <iostream>
 #include <string>
+#include <cctype>
 #include <unistd.h>
-#include "CppCFString.h"
 #include "DictionarySet.h"
 #include "jconv.h"
 #include "skkserv.h"
@@ -98,13 +98,22 @@
             sock >> word;
             sock.get();
 
-	    CppCFString query(word.c_str(), kCFStringEncodingEUC_JP);
-	    CppCFString reply(DictionarySet::theInstance().skkserv_style_search(query));
+	    std::string key;
+	    std::string result;
+
+	    jconv::convert_eucj_to_utf8(word, key);
+
+	    // 検索文字列の最後が [a-z] なら『送りあり』
+	    if(key.size() > 0 && std::isalpha(key[key.size() - 1])) {
+		result = DictionarySet::theInstance().FindOkuriAri(key, std::string(), '/');
+	    } else {
+		result = DictionarySet::theInstance().FindOkuriNasi(key, '/');
+	    }
 
 	    // 見つかった?
-	    if(reply.length() > 0) {
+	    if(!result.empty()) {
 		std::string candidates;
-		jconv::convert_utf8_to_eucj(reply.toStdString(kCFStringEncodingUTF8), candidates);
+		jconv::convert_utf8_to_eucj(result, candidates);
 		sock << "1" << candidates << "\n";
 	    } else {
 		sock << "4" << word << "\n";
Index: AquaSKK/socketstream.h
diff -u AquaSKK/socketstream.h:1.2 AquaSKK/socketstream.h:1.2.2.1
--- AquaSKK/socketstream.h:1.2	Wed Apr 26 22:36:12 2006
+++ AquaSKK/socketstream.h	Sat Dec  2 16:54:13 2006
@@ -1,5 +1,5 @@
 /*  -*- c++ -*-
-  $Id: socketstream.h,v 1.2 2006/04/26 13:36:12 t-suwa Exp $
+  $Id: socketstream.h,v 1.2.2.1 2006/12/02 07:54:13 t-suwa Exp $
 
   MacOS X implementation of the SKK input method.
 
@@ -339,6 +339,8 @@
             if(sock_ == -1) {
 		return false;
 	    }
+	    int optval = 1;
+	    setsockopt(sock_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
         }
         sockaddr_in addr;
         addr.sin_family = AF_INET;


aquaskk-changes メーリングリストの案内