notifierの設定
イラストレータのネイティブオブジェクトには各種イベントが盛り込まれています。これらを使うにはC++でプラグインを書かなければいけないのですが、複雑なプロジェクトとなるとC++で全てを賄うのは少々骨が折れます。そこで、AdobeExtensionSDKとExtendScriptの連携をうまく利用できればネイティブオブジェクトが検知したイベントに反応する形でCEPエンジンを介してExtendScriptを実行出来るといった曲芸を可能とします。
こういった機能はAdobeExtensionSDKではHotAdapterプラグインとして提供されているものですが、CEP向けのリリースは遅れています。しかしながら、機能としては単純なために、実装はそう難易度の高いものでもなかったりします。
今回はサンプルスクリプトを利用して類似の機能を実現し、ネイティブからExtendscriptまで縦断する実装のテストを行います。
今回利用するプロジェクトは「Adobe Illustrator CC 2014 SDK」に含まれる「MarkedObjects」プラグインさんです。みなさまSDKのプロジェクトを一度、俯瞰した上で続きを確認していただけたらと思います。このプラグインはUIをHTML5 Extensionで構成しています。ですから、プラグインをコンパイルしてインストールしただけでは動作しません。エクステンションをパッケージしてインストールする必要があります。パッケージにはZXPSignCmdが必要です。ダウンロードは以前の解説をご覧ください。使い方はコマンドラインから以下、
ZXPSignCmd -sign extensionPath /path/extension.zxp keyfile.p12 password
この様なパラメータの与え方で実行します。開発環境の場合、一々インストールするのはめんどうですので、PlayersDebugModeを設定しておくとよいでしょう。そうすることで、エクステンションパネルのフォルダをそのままextensionsフォルダに投入することで認識させることができます。まあ、インストーラを用意していますが…
では、重要な部分のご説明です。今回はSuitesとか予めインポートされていますのでヘッダファイル周りはノータッチです。まずチェックするのは「/MarkedObjectsPlugin.cpp」ファイルです。ここではイベントの登録等を行います。
//MarkedObjectsPlugin.cpp
…
error = sAINotifier->AddNotifier( fPluginRef, “MarkedObjectsPlugin”,
kAIArtSelectionChangedNotifier, &artSelectionChangedNotifier);
はい、この部分がリスナの設定を行っている部分です。拾うのは選択範囲が変更された場合です。更に下にまいります。
…
ASErr MarkedObjectsPlugin::Notify( AINotifierMessage *message )
{
ASErr error = kNoErr;
try
{
if ( message->notifier == artSelectionChangedNotifier )
{
GetMarkedObjects();
fMarkedObjectsPanelController->UpdateMarkedObjectSelected();
if ( !IsMarkedObjectSelected() )
fMarkedObjectsPanelController->Clear();
fMarkedObjectsPanelController->UpdateBoundingBoxInfo();
}
if ( message->notifier == documentChangedNotifier )
{
fMarkedObjectsPanelController->Clear();
fMarkedObjectsPanelController->UpdateMarkedObjectSelected();
GetMarkedObjects();
}
if ( ( message->notifier == documentSaveNotifierBefore ) || ( message->notifier == documentSaveAsNotifierBefore ) )
{
if (objectManager != NULL)
{
objectManager->MakeLayerTemplate( true );
}
}
if ( ( message->notifier == documentSaveNotifierAfter ) || ( message->notifier == documentSaveAsNotifierAfter ) )
{
if (objectManager != NULL)
{
if (objectManager->HasMarkedObjects())
{
ai::FilePath fileSpec;
sAIDocument->GetDocumentFileSpecification( fileSpec );
WriteMarkedPoints( fileSpec );
}
}
}
if ( message->notifier == fRegisterEventNotifierHandle)
{
fMarkedObjectsPanelController->RegisterCSXSEventListeners();
}
if (message->notifier == brightnessChangedNotifier)
{
error = fMarkedObjectsPanelController->SendTheme();
}
if (strcmp(message->type, kHideLayerNotifier) == 0)
{
this->HideMarkedObjectsLayer(fMarkedObjectsPanelController->GetHideButtonStatus());
}
if (strcmp(message->type, kResetNumberIDNotifier) == 0)
{
ResetIDs();
GetMarkedObjects();
UnSelectAllArt();
}
if (strcmp(message->type, kSaveObjectNotifier) == 0)
{
WriteMarkedPoints( fMarkedObjectsPanelController->GetFilePath());
}
if (strcmp(message->type, kPreferencesSetNotifier) == 0)
{
DoModalPrefs();
GetMarkedObjects();
}
if (strcmp(message->type, kObjectSelectedNotifier) == 0)
{
fMarkedObjectsPanelController->NewGlobalObjectSelected();
}
if (message->notifier == fShutdownApplicationNotifier)
{
if(fResourceManagerHandle != NULL)
{
error = sAIUser->DisposeCursorResourceMgr(fResourceManagerHandle);
fResourceManagerHandle = NULL;
&nbs
p; }
}
}
catch(…)
{
return ‘EPN ‘;
}
return error;
}
…
実際にリスナが反応した場合に呼び出されるファンクションです。ASErrという構造体が返されますが、この辺りのストラクトについては一度チェックしておいて下さい。Notifierが伝達する構造体はmessageです。ここではnotifierの内容によって処理の振り分けを行います。今回必要な部分は
if ( message->notifier == artSelectionChangedNotifier )
{
GetMarkedObjects();
fMarkedObjectsPanelController->UpdateMarkedObjectSelected();
if ( !IsMarkedObjectSelected() )
fMarkedObjectsPanelController->Clear();
fMarkedObjectsPanelController->UpdateBoundingBoxInfo();
}
の「artSelectionChangedNotifier 」 です。ここでは選択範囲の変化に応じて選択範囲がMarkedObjectであるかどうかを判断し、MarkedObjectであればバウンディングボックスの情報を取得します。
//MarkedObjectsPanelController.cpp
…
void MarkedObjectsPanelController::UpdateMarkedObjectSelected( void )
{
// Construct XML string
if ( fMarkedObjectManager == NULL)
return;
int count = fMarkedObjectManager->GetMarkedObjectSize();
if ( count <= 0 )
return;
for(int i = count -1; i >= 0; i–)
{
MarkedObject* markedObject = fMarkedObjectManager->GetMarkedObject(i);
if ( markedObject == NULL ) return;
if ( markedObject->IsSelected() )
{
UpdatePanelByMarkedObject(markedObject, i);
break;
}
}
}
こちらがUpdateMarkedObjectSelected ファンクションですね。引数がvoidですから返り値を持ちません。このファンクションはひと通りの調査で選択されているのがMarkedObjectだと判断した場合、UpdatePanelByMarkedObjectファンクションをコールします。
…
void MarkedObjectsPanelController::UpdatePanelByMarkedObject(MarkedObject *markedObject, int index)
{
if ( markedObject == NULL ) return;
AIRealPoint location = markedObject->GetLocation();
stringstream myString;
myString << “<payload><item>” << index << “</item><label>” << markedObject->GetLabel()
<< “</label><ID>” << markedObject->GetID() << “</ID>” << “<locx>” << location.h
<< “</locx>” << “<locy>” << location.v << “</locy></payload>”;
std::string xmlstring = myString.str();
csxs::event::Event event = {“com.adobe.csxs.events.MarkedObjectsUpdatePanel”,
csxs::event::kEventScope_Application,
“MarkedObjectsUpdatePanel”,
NULL,
xmlstring.c_str()};
fPPLib.DispatchEvent(&event);
}
そして、こちらがUpdatePanelByMarkedObject ファンクションです。markedObjectに関する情報をテキストストリームを利用して組み立てます。最終的にはstr()関数を利用してxmlデータを組み立てます。
csxsイベントクラス経由でdispatchする事でペイロードをエクステンション上のJavascriptに引き渡します。
CSInterface側では…
csInterface.addEventListener("com.adobe.csxs.events.MarkedObjectsUpdatePanel", updatePanel);
予め上記の様にリスナが設定してあり、プラグイン側よりdispatchされると。2つ目の引数で指定されるファンクションが実行されます。今回はここを改変してExtendscriptへdispatchされたパラメータの一部を転送しています。
function updatePanel(event) {
var xmlData = $.parseXML(event.data);
var $xml = $(xmlData);
if ($xml.find(‘item’).text() != -1) {
var x = $xml.find(‘locx’).text();
var y = $xml.find(‘locy’).text();
var label = $xml.find(‘label’).text();
var id = $xml.find(‘ID’).text();
var itemIndex = $xml.find(‘item’).text();
$(‘#homeX’).val(x);
$(‘#homeY’).val(y);
$(‘#id’).val(id);
$(‘#label’).val(label);
$(‘input[name=”submit”]’).removeAttr(‘disabled’);
var csi = new CSInterface();
csi.evalScript(‘$._ai_callES.doESFunction(‘ + x + ‘,’ + y + ‘)’);
}
}
最後にExtendScriptへとパラメータを渡してevalします。
$._ai_callES = {
doESFunction : function (x,y) {
alert (“x position : ” + x + ” y position : ” + y + “\ralert from Extendscript.” );
},
init : function(){
alert(“initialise panel…”);
}
}
ExtendScript側では受け取った引数をalertします。
最後に実行イメージを