見出し画像

Notes C API探訪: データベース/ディレクトリ情報をUIに反映する

前回の記事で、FilePageクラスを定義しました。FilePageクラスは、TabPageクラスを継承したタブページの派生クラスで、中身としては、ブックマークと呼んでいる「パス名」と「サーバー名」で構成されたデータを元に、データベース/ディレクトリを開いて、取得できる情報を表に出力する機能を持ちます。

「データベース/ディレクトリを開く」という機能は、Notes C APIのNSFDbOpen関数やNSFDbOpenExtended関数などを使うわけですが、これらは「データベースを開く」だけでなく「ディレクトリを開く」こともできることが、ある意味特殊です。そのため、先のFilePageクラスは、「DatabasePage」としなかったのです。データベースかディレクトリかは、開いた後でNSFDbModeGet関数を使って判定をします。

さて、FilePageクラスで表を出力するために情報をロードしますが、データベースとディレクトリには情報そのものが違います。ディレクトリはパス情報くらいですが、データベースには、パス情報に加え、タイトルやデータベースID、割当情報などいろんなものがあります。
そこで、判定によってデータベースか、ディレクトリかがわかったら、それぞれに特化したデータのロードプロセスが必要になります。そこでローダー関数オブジェクトの出番です。

まずはディレクトリ用のローダー関数オブジェクトを見てみます。

// filepage/directorypage.h

#ifndef DIRECTORYPAGE_H
#define DIRECTORYPAGE_H

#include "../filepage.h"

class DirectoryLoader : public FilePage::Loader
{
public:
 virtual void operator ()(FilePage *pPage, nxpp::DbPtr dbPtr) override;
};

#endif // DIRECTORYPAGE_H

続けてソースファイルです。

// filepage/directorypage.cpp

#include "directorypage.h"

void DirectoryLoader::operator ()(FilePage *pPage, nxpp::DbPtr dbPtr) {
 addPaths(pPage, dbPtr);
}

ディレクトリについては、パスの情報のみ表示しています。継承元のFilePage::Loaderが持っているaddPathsメソッドで、パス情報を表に書き出しています。

次はデータベース用のローダー関数オブジェクトを見てみます。

// filepage/databasepage.h

#ifndef DATABASEPAGE_H
#define DATABASEPAGE_H

#include "../filepage.h"

class DatabaseLoader : public FilePage::Loader
{
public:
 virtual void operator ()(FilePage *pPage, nxpp::DbPtr dbPtr) override;

private:
 /**
  * @brief データベース情報の書き込み
  */
 void addDbInfo(FilePage *pPage, nxpp::DbPtr dbPtr);

 /**
  * @brief DBIDの書き込み
  */
 void addDbId(FilePage *pPage, nxpp::DbPtr dbPtr);

 /**
  * @brief データベース拡張割当情報の書き込み
  */
 void addDbQuotaInfoExt(FilePage *pPage, nxpp::DbPtr dbPtr);
};

class DatabaseUpdater : public FilePage::Updater {
public:
 virtual void operator ()(
     FilePage *pPage,
     nxpp::DbPtr dbPtr,
     QStandardItem *item
     ) override;
};

#endif // DATABASEPAGE_H

データベースのローダーでは、タイトルなどのデータベース情報、データベースID、拡張割当情報などを表に追加できるようになっています。また、FilePage::Updaterから派生したDatabaseUpdaterクラスは、編集した内容をデータベースに書き込むことができます。

では、ソースファイルも見ていきます。

// filepage/databasepage.cpp

#include "databasepage.h"
#include <nxpp/qt/nxpp_qt_lmbcs.hpp>
#include <nxpp/nxpp_number.hpp>
#include <nxpp/qt/nxpp_qt_timedate.hpp>
#include <nxpp/nxpp_utils.hpp>

void DatabaseLoader::operator ()(FilePage *pPage, nxpp::DbPtr dbPtr) {
 addPaths(pPage, dbPtr);
 addDbInfo(pPage, dbPtr);
 addDbId(pPage, dbPtr);
 addDbQuotaInfoExt(pPage, dbPtr);
}

void DatabaseLoader::addDbInfo(FilePage *pPage, nxpp::DbPtr dbPtr) {
 // DB情報を取得
 auto dbInfo = dbPtr->getDbInfo();

 // DB情報を格納するリストを作成
 QMap<WORD, QPair<QString,QString>> list;

 // DB情報の定数とラベルのマップデータを作成
 const QMap<WORD, QString> map {
   { INFOPARSE_TITLE, pPage->tr("Title") },
   { INFOPARSE_CATEGORIES, pPage->tr("Categories") },
   { INFOPARSE_CLASS, pPage->tr("Class") },
   { INFOPARSE_DESIGN_CLASS, pPage->tr("Design Class") }
 };

 // DB情報の定数リストを作成
 auto whats = map.keys();

 // 定数リストを巡回
 for (int i = 0; i < whats.length(); ++i) {
   auto key = whats[i];

   // DB情報から定数相当のデータを抽出
   auto text = dbInfo.extract(key);

   // モデルにキーとDB情報のパートデータを追加
   auto items = pPage->addRowToMainModel(
         map.value(key),
         fromLmbcsQ(text)
         );

   // ラベルを編集不可にする
   items.first->setEditable(false);

   // ラベルアイテムのユーザーロールに識別子を登録(更新時に使用)
   items.first->setData(QString("DbInfo/%1").arg(key));
 }
}

void DatabaseLoader::addDbId(FilePage *pPage, nxpp::DbPtr dbPtr) {
 // DBIDを取得
 DBID dbid = dbPtr->getID();

 // コンバーターを作成
 nxpp::TimeDateToTextConverter converter;

 // 日時フォーマットを作成
 auto tfmt = std::make_shared<nxpp::TimeFormat>();

 // タイムゾーンを常に表示
 tfmt->setZone(TZFMT_ALWAYS);

 // カスタムした日時フォーマットをコンバーターに設定
 converter.setFormat(tfmt);

 // DBIDを日時としてテキストに変換
 auto timeDate = converter(&dbid);

 // DBIDをバイナリテキストと日時でモデルに追加
 auto items = pPage->addRowToMainModel(
       "DBID",
       QString("%1 (%2)")
       .arg(QString::fromStdString(nxpp::hex(dbid, true)))
       .arg(fromLmbcs(timeDate))
       );

 // ラベル、値ともに編集不可
 items.first->setEditable(false);
 items.second->setEditable(false);
}

void DatabaseLoader::addDbQuotaInfoExt(FilePage *pPage, nxpp::DbPtr dbPtr) {
 // DBの拡張割当情報を取得
 DBQUOTAINFOEXT quota = dbPtr->getQuotaInfoExt();

 // 数値コンバーターを2つ作成
 nxpp::NumberToTextConverter converter1, converter2;

 // 数値フォーマットを2つ作成
 auto nfmt1 = std::make_shared<nxpp::NumberFormat>();
 auto nfmt2 = std::make_shared<nxpp::NumberFormat>();

 // 1つ目の数値フォーマットを3桁区切りで設定
 nfmt1->setPunctuated(true);

 // 2つ目の数値フォーマットをバイト表示で設定
 nfmt2->setFormat(NFMT_FIXED);
 nfmt2->setBytes(true);

 // 2つの数値フォーマットをそれぞれコンバーターに設定
 converter1.setFormat(nfmt1);
 converter2.setFormat(nfmt2);

 // DBの拡張割当情報の各パートデータを取得
 auto warnThreshold = static_cast<NUMBER>(quota.WarningThreshold);
 auto sizeLimit = static_cast<NUMBER>(quota.SizeLimit);
 auto currentDbSize = static_cast<NUMBER>(quota.CurrentDbSize);
 auto maxDbSize = static_cast<NUMBER>(quota.MaxDbSize);
 auto quotaMethod = static_cast<NUMBER>(quota.QuotaMethod);
 auto currentUsage = static_cast<NUMBER>(quota.CurrentUsage);
 auto currentSizeUsed = static_cast<NUMBER>(quota.CurrentSizeUsed);

 // 各パートデータをマップ化
 QVariantMap map {
   { pPage->tr("WarningThreshold"), fromLmbcsQ(converter1(&warnThreshold)) },
   { pPage->tr("SizeLimit"), fromLmbcsQ(converter1(&sizeLimit)) },
   { pPage->tr("CurrentDbSize"), fromLmbcsQ(converter1(&currentDbSize)) },
   { pPage->tr("MaxDbSize"), fromLmbcsQ(converter2(&maxDbSize)) },
   { pPage->tr("QuotaMethod"), fromLmbcsQ(converter1(&quotaMethod)) },
   { pPage->tr("CurrentUsage"), fromLmbcsQ(converter1(&currentUsage)) },
   { pPage->tr("CurrentSizeUsed"), fromLmbcsQ(converter1(&currentSizeUsed)) }
 };

 // パートデータ毎にモデルに追加
 for (auto key : map.keys()) {
   auto items = pPage->addRowToMainModel(key, map.value(key));
   items.first->setEditable(false);
   items.second->setEditable(false);
 }
}

void DatabaseUpdater::operator ()(
   FilePage *pPage,
   nxpp::DbPtr dbPtr,
   QStandardItem *item
   ) {

 // ページからモデルを取得
 auto model = pPage->mainModel();

 // 更新アイテムからインデックスを取得
 QModelIndex index1 = model->indexFromItem(item);

 // インデックスが2列目でなければ中止
 if (index1.column() != 1) return;

 // 同じ行の1列目(ラベル)を取得
 QModelIndex index0 = model->index(index1.row(), 0);
 QStandardItem *label = model->itemFromIndex(index0);

 // 1列目のユーザーデータロールを取得
 QString keyData = label->data().toString();

 // データがDB情報を表していなければ中止
 QRegExp rx(R"(DbInfo/(\d))");
 if (!rx.exactMatch(keyData)) return;

 // DB情報のパート定数を取得
 uint type = rx.cap(1).toUInt();

 // アイテムから更新されたテキストデータを取得
 QString newData = item->data(Qt::EditRole).toString();

 // DB情報を再取得
 nxpp::DbInfo dbInfo = dbPtr->getDbInfo();

 // DB情報を編集した内容で上書き
 dbInfo.modify(type, toLmbcsQ(newData));

 // DB情報を更新
 dbPtr->setDbInfo(dbInfo);

 // コンソール出力
 emit pPage->consoleLog(
       QString("Changed type: %1(%2) => %3")
       .arg(type)
       .arg(keyData)
       .arg(newData)
       );
}

まず、データベース情報については、こちらの記事でAPIを、こちらの記事でラッパークラスをご紹介しています。DBIDについてはこちらの記事こちらの記事でご紹介しています。

データベースの拡張割当情報DBQUOTAINFOEXT構造体については、まだご紹介していないので、次回の記事で探訪してみたいと思います。

それでは、ここまでの実装を実演してみます(新しい箇所は未翻訳です)。

まとめ

Notes C APIを使って、データベース/ディレクトリの情報を取得し、データベース情報については、更新もすることができました。以前も書いたと思いますが、データベースタイトルについては、このアプリで変更しても、Notesクライアントのワークスペース上では変わっていません。デスクトップデータとしてキャッシュされていると思われます。

この記事が気に入ったらサポートをしてみませんか?