CDデータのテンプレート化 - リッチテキスト - 「NotesPeek」をQtでリメイク
前回は、リッチテキストの要素である「CDデータ」を、未定義のものでもバイナリの値を並べて表示できるCdOtherItemクラスと、ライブラリクラスとしてのntlx::cd::Otherクラス、そしてその親クラス(と言うより、インターフェース、純粋仮想関数しか持っていない)となるntlx::Cdクラスを作成しました。定義の手法に関しては、我が座右の書、Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングを参考にさせていただいています。改めて感謝です。
ここで「CDデータ」と呼んでいるものについて簡単におさらいしておきます。
- Notesデータベースに含まれるもの → Note文書(ドキュメント、設計文書、ACLなど)
- Note文書に含まれるもの → アイテム(文字列、数値、日時、リッチテキスト、画像、ファイル、設計要素(フィールド、列など))
- リッチテキストアイテムに含まれるもの → CDデータ(テキスト、段落、画像、表、色、ホットスポットなど)
細かい点は大目に見ていただき、ここで伝えたいのは、CDデータはリッチテキストアイテムの構成要素であるということです。
リファレンスを見ると、「CD」とは「Composite Data」の略であることが分かるので、「データ」が重複しているのは否めませんが、「CD」という接頭辞が一つの大きなグループという位置づけにもなっており、特定の要素を表さない汎用的な名称として、あえて「CDデータ」と呼ばせていただいています。
さて、前回はCdクラスからcd::Otherクラスを派生させました。今回のゴールはCDTEXTというリッチテキストの中のテキスト要素を専用表示させるため、cd::Textクラスを派生させます。ただし、Cdクラスとは別にCDデータが共通して行う処理も多いので、テンプレートクラスcd::Baseを間に挟みます。
// ntlx/cd/base.h #ifndef NTLX_CD_BASE_H #define NTLX_CD_BASE_H #include <ntlx/cd.h> #if defined(NT) #pragma pack(push, 1) #endif #include <ods.h> #if defined(NT) #pragma pack(pop) #endif namespace ntlx { namespace cd { template <typename CDxxx, WORD _CDxxx, WORD SIG_CD_xxx> class NTLXSHARED_EXPORT Base : public Cd { public: Base(char** ppRecord) { ODSReadMemory(ppRecord, _CDxxx, &record_, 1); } Base(const Base& other) : record_(other.record_) { } Base& operator=(const Base& other) { if (this != &other) record_ = other.record_; return *this; } virtual WORD getSignature() const { return SIG_CD_xxx; } virtual WORD odsLength() const { return record_.Header.Length + record_.Header.Length % 2; } protected: mutable CDxxx record_; }; } // namespace cd } // namespace ntlx #endif // NTLX_CD_BASE_H
純粋仮想メソッドCd::toString以外は、テンプレートとした構造体CDxxxを使って表すことができます。CDxxxに当てはまる構造体は300以上もあるので、テンプレートクラスにするこの方法は、理にかなっていてとてもいいと思います。これはほぼそっくり座右の書のアイデアをお借りしています。
- CDxxx: CDデータの先頭を表す構造体
- _CDxxx: CDデータシンボル(ODSLength用)
- SIG_CD_xxx: CDデータシグネチャ(CD構造体先頭用)
これを親として、cd::Textクラスを定義します。
// ntlx/cd/text.h #ifndef NTLX_CD_TEXT_H #define NTLX_CD_TEXT_H #include <ntlx/cd/base.h> #if defined(NT) #pragma pack(push, 1) #endif #include <editods.h> #if defined(NT) #pragma pack(pop) #endif namespace ntlx { namespace cd { class NTLXSHARED_EXPORT Text : public Base<CDTEXT, _CDTEXT, SIG_CD_TEXT> { public: Text(char** ppRecord); Text(const Text& other); Text& operator=(const Text& other); QString value() const; virtual QString toString() const; private: QString value_; }; } // namespace cd } // namespace ntlx #endif // NTLX_CD_TEXT_H
// cd/text.cpp #include "ntlx/cd/text.h" #include "ntlx/lmbcs.h" namespace ntlx { namespace cd { Text::Text(char** ppRecord) : Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>(ppRecord) , value_() { WORD len = record_.Header.Length - ODSLength(_CDTEXT); Lmbcs lmbcs(*ppRecord, len); value_ = lmbcs.toQString(); nullToCR(value_); } Text::Text(const Text &other) : Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>(other) , value_(other.value_) { } Text& Text::operator=(const Text& other) { Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>::operator=(other); if (this != &other) value_ = other.value_; return *this; } QString Text::value() const { return value_; } QString Text::toString() const { return value(); } } // namespace cd } // namespace ntlx
cd::Text固有の実装としてはQString value_メンバ変数を持っています。テキストのCDデータは、CDTEXT構造体に続いてLmbcs形式のテキストが収められているので、BaseコンストラクタがCDTEXT構造体分のデータを取得したあとに、Lmbcs文字列をvalue_に、QStringに変換しながら格納しています。Baseコンストラクタが呼び出しているODSReadMemoryは、CDデータ構造体を読み込むと、メモリのポインタをそのサイズ分だけ移動してくれるので便利です。
これをNSFinderに実装してみた例は以下の通りです。
前回までの、汎用的なCDデータの出力ではなくて、CDTEXT専用の出力として機能しています。
未知のCDデータの表示 - リッチテキスト - 「NotesPeek」をQtでリメイク
ちょっと前の記事「伝説のツール「NotesPeek」をQtでリメイク開発記2017-7-23 - Chiburu Systemsのブログ」で、リッチテキストについて着手したことをご報告しました。この中で、リッチテキストは多種多様な「CDxxxx」という複合データ(Composite Date)によって構成されているとお伝えしました。
ここで簡単にまとめてみます。
- リッチテキストはCDデータの連続体である。
- CDデータは9.0.1において150種類以上ある。
- 1つのCDデータはヘッダー情報とコンテンツ情報でできている。
- ヘッダー情報はサイズによってBSIG、WSIG、LSIGの3種類ある。
座右の書Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングでは、紙面の都合で代表的なCDデータのクラス実装例を紹介するにとどめていますが、一方でどんなCDデータでも取り込んだり書き込んだりできる「CdOtherクラス」というのを紹介していました。ポイントは、CDデータのポインタが渡されると、ヘッダー情報を正しく分析して、BSIG、WSIG、LSIGのいずれかであることを判別して、コンテンツ情報を切り分けるようになっていることです。ヘッダー情報とコンテンツ情報を正しく分離できれば、あとの作業は共通化することができます。
本プロジェクトのNSFinderでは、いったんはCDTEXTのみを判別してテキスト化し、その他のCDデータはいったん無視する方針で実装しましたが、やはりきちんと実装していきたいので、まずはCdOtherのような汎用CDデータクラスで処理できるようにします。
// ntlx/cd.h #ifndef NTLX_CD_H #define NTLX_CD_H #include <ntlx_global.h> #include <ntlx/status.h> namespace ntlx { class NTLXSHARED_EXPORT Cd { public: virtual WORD getSignature() const = 0; virtual WORD odsLength() const = 0; virtual QString toString() const = 0; }; } // namespace ntlx #endif // NTLX_CD_H
まず、元となるクラス「ntlx::Cd」です。シグネチャの取得、データ長の取得、そして文字列に変換するメソッドをすべて純粋仮想関数で定義しているインターフェースクラスです。
これを継承した汎用クラスが「ntlx::cd::Other」です。
// ntlx/cd/other.h #ifndef NTLX_CD_OTHER_H #define NTLX_CD_OTHER_H #include <ntlx/cd.h> namespace ntlx { namespace cd { class NTLXSHARED_EXPORT Other : public Cd { public: Other(char** ppRecord); Other(const Other& other); Other& operator=(const Other& other); virtual WORD getSignature() const; virtual WORD odsLength() const; virtual QString toString() const; protected: QByteArray record_; }; } // namespace cd } // namespace ntlx #endif // NTLX_CD_OTHER_H
本家Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングでは、CdOtherとして紹介されていましたが、ここでは2層の名前空間の中で定義しています。また、データ本体の扱い方を、char文字配列からQByteArrayに変更しました。QByteArrayの方が扱いがはるかに楽であるためです。
// cd/other.cpp #include "ntlx/cd/other.h" #include <QStringList> #if defined(NT) #pragma pack(push, 1) #endif #include <ods.h> #if defined(NT) #pragma pack(pop) #endif namespace ntlx { namespace cd { Other::Other(char** ppRecord) : Cd() , record_() { int len = 0; switch (((uchar*)*ppRecord)[1]) { case 0x00: // Long Signature len = ((LSIG*)*ppRecord)->Length; break; case 0xff: // Word Signature len = ((WSIG*)*ppRecord)->Length; break; default: // Byte Signature len = ((BSIG*)*ppRecord)->Length; break; } record_ = QByteArray(*ppRecord, len); } Other::Other(const Other& other) : Cd() , record_(other.record_) { } Other& Other::operator=(const Other& other) { if (this != &other) record_ = other.record_; return *this; } WORD Other::getSignature() const { switch (((uchar*)record_.constData())[1]) { case 0x00: return ((LSIG*)record_.constData())->Signature; case 0xff: return ((WSIG*)record_.constData())->Signature; } return ((BSIG*)record_.constData())->Signature; } WORD Other::odsLength() const { return (WORD)(record_.length() + record_.length() % 2); } QString Other::toString() const { QStringList list; for (auto it = record_.constBegin(); it != record_.constEnd(); ++it) { uchar b = (uchar)(*it); list.append(QString("%1").arg((uint)b, 4, 16, QChar('0')).right(2)); } return list.join(" "); } } // namespace cd } // namespace ntlx
ほとんど本家Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングを参考にしています。
odsLengthメソッドは実データサイズではなくODSとしてメモリ上にどれくらいの長さを持っているかということで、これが必ず偶数値を取ることから、「~% 2」の値が加算されています。
toStringメソッドはQtのライブラリを使って、バイトデータを16進数文字列に変換しています。
このOtherクラスを用いてNSFinderを実装した例(Windows)をご紹介しておきます。
伝説のツール「NotesPeek」をQtでリメイク開発記2017-8-6
前回は、設計文書のうち、フォーム文書を展開してみました。今回はその続きです。
前回も触れましたが、Notesの設計文書は基本的に「文書クラス」というものでその性質が分けられますが、バージョンが進むうちに文書クラスの範囲(WORDビットの下位12ビット)では到底カバーできず、「フラグ」というテキストを併用することで正しく設計文書を認識することができます。
設計文書のフラグは、「$Flags」という単なるテキストフィールドです。
// stdnames.h /* Design flags */ #define DESIGN_FLAGS "$Flags"
このフィールドに、1文字1文字に意味を持たせた文字フラグが列記されています。代表的なフラグを抜粋してみます。
// stdnames.h // for ALL #define DESIGN_FLAG_DEFAULT_DESIGN 'd' /* ALL: Indicates the default design note for it's class (used for VIEW) */ // for FORM #define DESIGN_FLAG_FILE 'g' /* FORM: file design element */ #define DESIGN_FLAG_IMAGE_RESOURCE 'i' /* FORM: Note is a shared image resource */ #define DESIGN_FLAG_XSPPAGE 'K' /* FORM: with "g", design element is an xpage, much like a file resource, but special! */ #define DESIGN_FLAG_SUBFORM 'U' /* FORM: Indicates that a form is a subform.*/ #define DESIGN_FLAG_WEBPAGE 'W' /* FORM: Note is a WEBPAGE */ #define DESIGN_FLAG_FRAMESET '#' /* FORM: Indicates that this is a frameset note */ // for VIEW #define DESIGN_FLAG_CALENDAR_VIEW 'c' /* VIEW: Indicates a form is a calendar style view. */ #define DESIGN_FLAG_FOLDER_VIEW 'F' /* VIEW: This is a V4 folder view. */ #define DESIGN_FLAG_SHARED_COL '^' /* VIEW: shared column design element */ // for FILTER(AGENT) #define DESIGN_FLAG_LOTUSSCRIPT_AGENT 'L' /* FILTER: If its LOTUSSCRIPT */ #define DESIGN_FLAG_SCRIPTLIB 's' /* FILTER: A database global script library note */ #define DESIGN_FLAG_JAVA_AGENT 'J' /* FILTER: If its Java */ #define DESIGN_FLAG_DATABASESCRIPT 't' /* FILTER: A database script note */ #define DESIGN_FLAG_SERVLET 'z' /* FILTER: this is a servlet, not an agent! */ // Others #define DESIGN_FLAG_JAVASCRIPT_LIBRARY 'h' /* Javascript library. */ #define DESIGN_FLAG_CONNECTION_RESOURCE 'k' /* Data Connection Resource (DCR) for 3rd party database */ #define DESIGN_FLAG_JSP '<' /* this design element is a jsp */ #define DESIGN_FLAG_SACTIONS 'y' /* Shared actions note */ #define DESIGN_FLAG_STYLESHEET_RESOURCE '=' /* Style Sheet Resource (SSR) */ #define DESIGN_FLAG_XSP_CC ';' /* note class form, a custom control */ #define DESIGN_FLAG_JAVAFILE '[' /* Java design element */
サブフォーム、フレームセット、ファイルリソース、イメージリソース、XPagesなどはフォームクラスから分岐しています。 フォルダ、カレンダービュー、共有列はビュークラスから分岐しています。 LotusScriptエージェント、Javaエージェント、データベーススクリプトなどはフィルタークラスから分岐しています。サーブレットも含まれていますが、どういうことでしょう?JSPも見受けられますね。
これらはフラグなので、文字列単位で比較するのではなく、所定の1文字が含まれていれば「フラグが立っている」ことになります。例えば、メールDBの「(GroupCalendarDlg)」というサブフォームには、以下のフラグが混在しています。
DESIGN_FLAG_ADD // A DESIGN_FLAG_NO_COMPOSE // C DESIGN_FLAG_SUBFORM // U DESIGN_FLAG_SUBFORM_NORENDER // x DESIGN_FLAG_HIDE_FROM_V3 // 3
$Flagsフィールドには、「CUA3x」という文字列で存在しています。1文字1文字の意味を抽出することで、この設計文書にはどんな属性が含まれているのかがわかります。
リメイクアプリ「nsfinder」では、今回これを確認できるようにしてみました。
$Flagsフィールドだけでは、意味がわかりづらいので、「Design flags」というプロパティを設け、1つ1つのフラグ文字列に紐付く意味がわかるようにしました。
NotesPeekでは、これをアイコンのレベルでわかるようにしています。Notesの設計文書を、NotesPeekのまねをすることなく、アイコンレベルで表記できるようにするのは至難の業かもしれません。まずは文字ベースで表記できるようにし、実装に余裕が出てきたらアイコンも検討してみましょう。
伝説のツール「NotesPeek」をQtでリメイク開発記2017-7-30
前回はリッチテキストに対して切り込みましたが、今回は設計要素(設計文書)に対して切り込みを入れます。
Notesの設計要素は、広義では「文書(Note)」に当たります。もし、データとしての「文書」と分けるのであれば、「Document」がNotesで一般的に使われる文書に当たります。設計要素もデータ文書も、保存形式としては「Note」という単位で保存されるんですね。これが「Notes」という名前の所以なのかもしれません。
では、API的にはどのように設計要素を区別するのでしょうか。
文書(Note)には「文書クラス」というパラメータがあります。一般的なものを列挙してみます。
シグネチャ | 値 | 意味 |
---|---|---|
NOTE_CLASS_DOCUMENT | 0x0001 | データ文書/ドキュメント |
NOTE_CLASS_INFO | 0x0002 | データベースについて |
NOTE_CLASS_FORM | 0x0004 | フォーム |
NOTE_CLASS_VIEW | 0x0008 | ビュー |
NOTE_CLASS_ICON | 0x0010 | アイコン |
NOTE_CLASS_DESIGN | 0x0020 | 設計文書を含む内部ビュー |
NOTE_CLASS_ACL | 0x0040 | アクセス制御リスト |
NOTE_CLASS_HELP_INDEX | 0x0080 | Notesクライアントのヘルプのインデックス |
NOTE_CLASS_HELP | 0x0100 | データベースの使い方 |
NOTE_CLASS_FILTER | 0x0200 | エージェント |
NOTE_CLASS_FIELD | 0x0400 | 共有フィールド |
NOTE_CLASS_REPLFORMULA | 0x0800 | 複製式 |
「設計文書を含む内部ビュー」?「ヘルプのインデックス」?さらりと書きましたが、今ひとつピンときません。愛読書Notes/Domino APIプログラミング―C++とSTLによる実践的プログラミングでも、このあたりを特別掘り下げているわけではないようです。今回は遺憾ながらその点についてはスルーします。
さて、設計要素のフォームやビューは、確かに文書クラスによってドキュメントとは区別されているのは分かりました。しかし、設計要素はこれだけだったでしょうか。サブフォームは?フレームセットは?フォルダは?共有アクションは?XPagesは?
安心して下さい。あります。
ただ、このようないわゆる「後発の」設計要素は文書クラスではなく、フラグ($Flagsアイテム)によって区別されます。フラグは単なる文字列で、サブフォームは'U'という文字で識別します。シグネチャとしては「DESIGN_FLAG_SUBFORM」として定義されています。後付けのゴチャゴチャ感は否めませんが、とにもかくも、文書クラスでざっくり分けたあとに、フラグで詳しく設計文書を識別する必要があります。サブフォームの場合は「フォームクラス」の「サブフォームフラグを含む」設計文書を見つければいいということになります。
それでは、今回の開発進捗の報告です。
今回は、設計要素、特にフォームを中心に設計要素を見られるようにしてみました。
「Form Elements」というカテゴリを展開すると、文書クラスがフォームである設計文書を見ることができます。この画像ではメールデータベースを見ていますが、GIF画像だらけですね。GIF画像も文書クラスではフォームに分類されるということになります。その下にはXML文書も確認できますが、これは「ファイル」というくくりの設計文書になっていると思います。
メールフォームのアイテムを展開して見てみます。これはドキュメントではなくフォームの設計文書なので、「$SubForm_RepIDs」のような見慣れないアイテムも存在します。これはきっとサブフォーム用の何かでしょう。
さらに、「$Body」というコンポジットアイテム、いわゆるリッチテキストを見てみます。これは、フォームの中身の設計を表していて、ドキュメントのリッチテキストではお目にかかれない、フィールド用のデータ(EXT2_FIELD)も見ることができています。
伝説のツール「NotesPeek」をQtでリメイク開発記2017-7-23
さて、ようやくリッチテキストにメスを入れる時が来ました。The time has come!
リッチテキストは、まぎれもなくアイテム(フィールド)なので、テキストフィールドや数値フィールドと同じように、アイテム型(WORD)を先頭に持つバイナリデータです。
アイテム型 | 値 |
---|---|
TYPE_TEXT | テキストフィールドデータ |
TYPE_NUMBER | 数値フィールドデータ |
TYPE_TIME | 日時フィールドデータ |
TYPE_COMPOSITE | リッチテキストフィールドデータ |
なので、肝心なのは値の並べ方がどのようになっているかということになります。
リッチテキストは、書式付きの文字列や図、添付ファイルのようなデータが混在します。そのため、その複合データを混在させるために、データの種類と長さを表す「シグネチャ」と各データ固有の情報とを一組としたデータの羅列で構成されます。
例えば、リッチテキストに、何の変哲もない文字列を1行だけ入れたとします。これをNSFinderで覗いています。
NSFinderで「Body」フィールドを見てみると、以下のように「(CD)PABDEFINITION」、「(CD)PABREFERENCE」、「(CD)TEXT」の3つで構成されているのがわかります。
CDPABDEFINITIONは、段落の定義になります。
typedef struct { WSIG Header; /* Used to quickly recognize structure type */ WORD PABID; /* ID of this PAB */ WORD JustifyMode; /* paragraph justification type */ WORD LineSpacing; /* (2*(Line Spacing-1)) (0:1,1:1.5,2:2,etc) */ WORD ParagraphSpacingBefore; /* # LineSpacing units above para */ WORD ParagraphSpacingAfter; /* # LineSpacing units below para */ WORD LeftMargin; /* leftmost margin, twips rel to abs left */ /* (16 bits = about 44") */ WORD RightMargin; /* rightmost margin, twips rel to abs right */ /* (16 bits = about 44") */ /* Special value "0" means right margin */ /* will be placed 1" from right edge of */ /* paper, regardless of paper size. */ WORD FirstLineLeftMargin; /* leftmost margin on first line */ /* (16 bits = about 44") */ WORD Tabs; /* number of tab stops in table */ SWORD Tab[MAXTABS]; /* table of tab stop positions, negative */ /* value means decimal tab */ /* (15 bits = about 22") */ WORD Flags; /* paragraph attribute flags - PABFLAG_xxx */ DWORD TabTypes; /* 2 bits per tab */ WORD Flags2; /* extra paragraph attribute flags - PABFLAG2_xxx */ } CDPABDEFINITION;
先頭にある「WSIG Header」がシグネチャです。PABIDは段落定義のIDで、PABREFERENCEから参照されます。JustifyModeは行揃えの定義です。その他にマージンやタブの定義も見て取れます。
CDPABREFERENCEは、段落定義を参照します。
typedef struct { BSIG Header; WORD PABID; /* ID number of the CDPABDEFINITION */ /* used by this paragraph */ } CDPABREFERENCE;
参照だけなので、シグネチャ部分を除けばPABIDのみのシンプルな構造です。
CDTEXTは、文字列データです。
typedef struct { WSIG Header; /* Tag and length */ FONTID FontID; /* Font ID */ /* The 8-bit text string follows... */ } CDTEXT; typedef struct { #ifdef LITTLE_ENDIAN_ORDER BYTE Face; /* Font face (FONT_FACE_xxx) */ BYTE Attrib; /* Attributes (ISBOLD,etc) */ BYTE Color; /* Color index (NOTES_COLOR_xxx) */ BYTE PointSize; /* Size of font in points */ #else BYTE PointSize; /* Size of font in points */ BYTE Color; /* Color index (NOTES_COLOR_xxx) */ BYTE Attrib; /* Attributes (ISBOLD,etc) */ BYTE Face; /* Font face (FONT_FACE_xxx) */ #endif } FONTIDFIELDS;
FONTID自体は単なるDWORD型ですが、実はFONTIDFIELDSという構造体でもあります。ご覧の通り、フォントの書式設定が含まれています。
CDTEXTのデータだけでは、文字列本体は取得できません。CDTEXTに続いて文字列データがLMBCS形式で存在します。Headerデータには値の全体の長さが含まれるので、その全体の長さからCDTEXT構造体のサイズ分引けば、残りが文字列の長さとなります。
各CD〜の先頭には「BSIG Header」や「WSIG Header」がありましたが、そのほかに「LSIG Header」が存在します。これらはデータの長さに応じて「Byte長シグネチャ」「Word長シグネチャ」「Long長シグネチャ」を表しています。前述のCDPABREFERENCEのような参照IDのみのような短いもの、テキストのような一般的なもの、画像のような巨大なもの、それぞれに合わせて使い分けられています。
Notesのアイテム型もそこそこ種類がありますが、リッチテキストの中身を構成するコンポジットデータの種類はその数をしのぐでしょう。ただし、このリッチテキストアイテムを制することができれば、他の書式(HTMLやWord、その他リッチテキスト書式など)との相互変換をする場合に必要になるので、対応できるコンポジットデータの数を増やしておくことは、非常に大きな武器になることは間違いないでしょう。
伝説のツール「NotesPeek」をQtでリメイク開発記2017-7-17
リッチテキストの分解をする前に、どうしてもやっておきたいことがありました。今回はそちらの開発について。
前回、文書のアイテム(フィールド)を子アイテムにし、すべてのアイテムのデータについて、一律でバイナリ形式の文字列に変更して、表記することにしました。今回は、テキスト型、数値型、日時型とそれぞれのリスト型を、それぞれの表現に見合った形式で表記し直すことにしました。これらのデータ型は基本中の基本なので、リッチテキストに着手する前にどうしてもやっておきたかったのです。
最初は単一テキスト型です。
次に単一数値型です。
次に単一日時型です。
次はテキストリスト型です。
次は数値リスト型です。
最後に日時リスト型です。
数値リストと日時リストは波括弧({})2つでくくられており、1つめがシングルリスト、2つめがペアリスト(範囲型のリスト)になっています。今回のサンプルには出てきていませんが、日時リスト型が範囲のみで定義されていれば、以下のような出力になるはずです。
{}{2017/07/17 11:59:58 - 2017/07/17 23:59:58,2017/07/17 11:59:58 - 2017/07/17 23:59:58}
これで懸念事項は完了したので、次回はいよいよリッチテキストにメスを入れます。
伝説のツール「NotesPeek」をQtでリメイク開発記2017-7-10
先週から主旨を「開発記」に変更しての2回目です。
今週からいよいよ一階層下に進めます。文書に含まれるフィールド(アイテム)について展開していきます。まずは、NotesPeekのフィールドを展開した様子を見てみます。
文書を展開すると、デフォルトの「Note Information(文書情報)」があり、以降は文書内に含まれるフィールドのリストが展開されます。フィールドの種類によって、1つ1つにタイプを表すアイコンが割り当てられているのが印象的です。よく見ると、読者や作成者のフィールドにも個別にアイコンが割り当てられています。「読者」や「作成者」は、正確には「Type(型)」ではなく「フラグ」であり、フラグ付きのテキスト、またはテキストリスト型になります。
今回はここまでをゴールとしたかったんですが、諸事情によりアイコンは一律、値の表記方法もバイナリ形式で一律となっています。次の画面は、先ほどのNotesPeekと同じ文書を展開した時の画面サンプルです。
微妙に表記方法が違ったり、フィールドの並び順がABC順になっていなかったりしますが、おおよそフィールドデータをリストアップする機能についてはざっくり実装できました。
次回以降の目標としては、データ型によって値の表記方法を変えてデータを見やすくしたり、フィールドの並び順を統一するなどがあるでしょう。また、さらに深く進めていき、リッチテキストの内部データをリストアップすることも、近いうちに実装していこうと思います。リッチテキストが解析できると、他の形式とコンバートに使えるので、技術的にもいろいろと武器になると思います。ではまた来週。