gecko-dev/lib/layout/edtjava.cpp
1998-03-28 02:44:41 +00:00

1429 lines
44 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
//
// Java-related editor code.
//
#ifdef EDITOR
#include "editor.h"
// Local header
#ifdef EDITOR_JAVA
#include "java.h"
#include "libi18n.h"
#include "xp_file.h"
#define JRI_NO_CPLUSPLUS
#ifdef XP_MAC
#include "n_p_composer_PluginManager.h"
#include "n_plugin_composer_Composer.h"
#include "n_p_composer_MozillaCallback.h"
#else
#include "netscape_plugin_composer_PluginManager.h"
#include "netscape_plugin_composer_Composer.h"
#include "netscape_plugin_composer_MozillaCallback.h"
#endif
// This C header file doesn't have C++ linkage #ifdefs.
extern "C" {
#include "zip.h"
};
struct netscape_plugin_composer_PluginManager;
#include "jri.h"
// Interface for calling editor plugins
#define PLUGIN_FAIL 0
#define PLUGIN_CANCEL 1
#define PLUGIN_OK 2
#define PLUGIN_NEWTEXT 3
typedef void (*ComposerPluginCallbackFn)(jint, jint, struct java_lang_Object*);
void EDT_ComposerPluginCallback(jint, jint, struct java_lang_Object*);
#ifdef XP_WIN16
const int32 MAX_STRING=65530L;
#else
// const int32 MAX_STRING=500; // For testing
const int32 MAX_STRING=1L<<30;
#endif
class CEditorPluginInterface {
public:
static XP_Bool IsValidInterface(CEditorPluginInterface* pInterface);
CEditorPluginInterface(MWContext* pContext, ComposerPluginCallbackFn callback);
~CEditorPluginInterface();
static void Register(char* plugin);
static int32 GetNumberOfCategories();
static int32 GetNumberOfPlugins(int32 type);
static char* GetCategoryName(int32 type);
static char* GetPluginName(int32 type, int32 index);
static char* GetPluginHint(int32 type, int32 index);
static int32 GetNumberOfEncoders();
static char* GetEncoderName(int32 index);
static XP_Bool GetEncoderNeedsQuantizedSource(int32 index);
static char* GetEncoderHint(int32 index);
static char* GetEncoderFileExtension(int32 index);
static char* GetEncoderFileType(int32 index);
XP_Bool StartEncoder(int32 index, int32 width, int32 height, char** pixels,
char* fileName, EDT_ImageEncoderCallbackFn doneFunction, void* doneFunctionArgument);
XP_Bool Perform(int32 type, int32 index, EDT_ImageEncoderCallbackFn doneFunction, void* hook,netscape_javascript_JSObject *jsobject);
XP_Bool Perform(char* pClassName, EDT_ImageEncoderCallbackFn doneFunction, void* hook,netscape_javascript_JSObject *jsobject);
void Perform(char* pEvent, char* pDocURL, XP_Bool bCanChangeDocument, XP_Bool bCanCancel,
EDT_ImageEncoderCallbackFn doneFunction, void* hook,netscape_javascript_JSObject *jsobject);
XP_Bool IsPluginActive();
void StopPlugin();
void PluginCallback(int32 action, struct java_lang_Object* pArgument);
static XP_Bool PluginsExist();
protected:
struct java_lang_String* GetBaseURL();
struct java_lang_String* GetDocument();
void GetWorkDirectoryAndURL(struct java_lang_String*& workDirectory,
struct java_lang_String*& workDirectoryURL);
XP_Bool Perform2(char* pClassName, int32 type, int32 index, XP_Bool bCanChangeDocument, XP_Bool bCanCancel,
EDT_ImageEncoderCallbackFn doneFunction, void* hook, netscape_javascript_JSObject *jsobject);
void PluginFailed(struct java_lang_String* pWhy);
void PluginSucceeded();
void NewText(struct java_lang_String* pText);
CEditBuffer* GetBuffer();
private:
void ImageEncoderFinished(EDT_ImageEncoderStatus status);
void DocumentIsTooLarge(int32 docLengthInBytes, int msgid);
static netscape_plugin_composer_PluginManager* Manager();
struct netscape_plugin_composer_Composer* Composer();
static JRIEnv* Env();
static XP_Bool g_bJavaInitialized;
static JRIEnv* g_pEnv;
static JRIGlobalRef g_pManager;
static TXP_GrowableArray_pChar g_Plugin;
static TXP_GrowableArray_CEditorPluginInterface g_Interface;
XP_Bool m_bPluginActive; // True if an image encoder or a plugin is active.
XP_Bool m_bImageEncoder; // True if we're exectuing an image encoder.
XP_Bool m_bCanChangeDocument;
XP_Bool m_bCanCancel;
XP_Bool m_bChangedDocument; // True if the current plug-in has changed the document.
JRIGlobalRef m_pComposer;
EDT_ImageEncoderCallbackFn m_doneFunction;
void* m_doneFunctionArgument;
MWContext* m_pContext;
ComposerPluginCallbackFn m_callback;
};
#endif // EDITOR_JAVA
// End of local header
// Editor Plugin edt.h API functions.
void EDT_RegisterPlugin(char* csFileSpec){
#ifdef EDITOR_JAVA
CEditorPluginInterface::Register(csFileSpec);
#else
XP_TRACE(("EDT_RegisterPlugin(\"%s\")"));
#endif
}
int32 EDT_NumberOfPluginCategories(){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetNumberOfCategories();
}
#endif
return 0;
}
int32 EDT_NumberOfPlugins(int32 category){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetNumberOfPlugins(category);
}
#endif
return 0;
}
char* EDT_GetPluginCategoryName(int32 pluginIndex){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetCategoryName(pluginIndex);
}
#endif
return 0;
}
char* EDT_GetPluginName(int32 category, int32 index){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetPluginName(category, index);
}
#endif
return 0;
}
char* EDT_GetPluginMenuHelp(int32 category, int32 index){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetPluginHint(category, index);
}
#endif
return 0;
}
XP_Bool EDT_PerformPlugin(MWContext *pContext, int32 category, int32 index, EDT_ImageEncoderCallbackFn doneFunction, void* hook){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
netscape_javascript_JSObject *t_object=LJ_GetMochaWindow(pContext);
return ((CEditorPluginInterface*) pEditBuffer->GetPlugins())->Perform(category, index, doneFunction, hook, t_object);
}
#endif
return FALSE;
}
XP_Bool EDT_PerformPluginByClassName(MWContext *pContext, char* pClassName, EDT_ImageEncoderCallbackFn doneFunction, void* hook){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) FALSE;
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
netscape_javascript_JSObject *t_object=LJ_GetMochaWindow(pContext);
return ((CEditorPluginInterface*) pEditBuffer->GetPlugins())->Perform(pClassName, doneFunction, hook, t_object);
}
#endif
return FALSE;
}
void EDT_PerformEvent(MWContext *pContext, char* pEvent, char* pDocURL, XP_Bool bCanChangeDocument, XP_Bool bCanCancel,
EDT_ImageEncoderCallbackFn doneFunction, void* hook){
GET_WRITABLE_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
netscape_javascript_JSObject *t_object=LJ_GetMochaWindow(pContext);
((CEditorPluginInterface*) pEditBuffer->GetPlugins())->Perform(pEvent, pDocURL, bCanChangeDocument, bCanCancel,
doneFunction, hook, t_object);
return;
}
#endif
if ( doneFunction){
(*doneFunction)(ED_IMAGE_ENCODER_OK, hook);
}
}
XP_Bool EDT_IsPluginActive(MWContext* pContext){
GET_EDIT_BUF_OR_RETURN_READY_OR_NOT(pContext, pEditBuffer) FALSE;
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return ((CEditorPluginInterface*) pEditBuffer->GetPlugins())->IsPluginActive();
}
#endif
return FALSE;
}
void EDT_StopPlugin(MWContext* pContext){
GET_EDIT_BUF_OR_RETURN_READY_OR_NOT(pContext, pEditBuffer);
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
((CEditorPluginInterface*) pEditBuffer->GetPlugins())->StopPlugin();
}
#endif
}
#ifdef EDITOR_JAVA
void EDT_ComposerPluginCallback(jint hook1, jint action1, struct java_lang_Object* pArg){
CEditorPluginInterface* pInterface = (CEditorPluginInterface*) hook1;
if ( ! CEditorPluginInterface::IsValidInterface(pInterface )) {
XP_ASSERT(FALSE); // A spurrious callback.
return;
}
int32 action = (int32) action1;
if ( pInterface ) {
pInterface->PluginCallback(action, pArg);
}
}
#endif
int32 EDT_NumberOfEncoders(){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetNumberOfEncoders();
}
#endif
return 0;
}
char* EDT_GetEncoderName(int32 index){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetEncoderName(index);
}
#endif
return 0;
}
char* EDT_GetEncoderFileExtension(int32 index){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetEncoderFileExtension(index);
}
#endif
return 0;
}
char* EDT_GetEncoderFileType(int32 index){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetEncoderFileType(index);
}
#endif
return 0;
}
char* EDT_GetEncoderMenuHelp(int32 index){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetEncoderHint(index);
}
#endif
return 0;
}
XP_Bool EDT_GetEncoderNeedsQuantizedSource(int32 index){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
return CEditorPluginInterface::GetEncoderNeedsQuantizedSource(index);
}
#endif
return FALSE;
}
XP_Bool EDT_StartEncoder(MWContext* pContext, int32 index, int32 width, int32 height, char** pixels,
char* csFileName, EDT_ImageEncoderCallbackFn doneFunction,
void* doneArgument){
#ifdef EDITOR_JAVA
GET_EDIT_BUF_OR_RETURN_READY_OR_NOT(pContext, pEditBuffer) FALSE;
if ( EditorPluginManager_PluginsExist() ) {
return ((CEditorPluginInterface*) pEditBuffer->GetPlugins())->StartEncoder(index, width, height, pixels,
csFileName, doneFunction, doneArgument);
}
#endif
return FALSE;
}
void EDT_StopEncoder(MWContext* pContext){
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
EDT_StopPlugin(pContext);
}
#endif
}
#ifdef EDITOR_JAVA
struct PreOpenCallbackHook {
CEditorPluginInterface* pInterface;
EDT_PreOpenCallbackFn doneFunction;
void* hook;
char* pURL;
};
static void edt_SetURL(void* hook3, char* pURL){
PreOpenCallbackHook* hook2 = (PreOpenCallbackHook*) hook3;
char* pOldURL = hook2->pURL;
hook2->pURL = pURL ? XP_STRDUP(pURL) : 0;
XP_FREE(pOldURL);
}
#ifndef XP_OS2
static
#else
extern "OPTLINK"
#endif
void edt_PreOpenDoneFunction(EDT_ImageEncoderStatus status, void* hook3){
PreOpenCallbackHook* hook2 = (PreOpenCallbackHook*) hook3;
EDT_PreOpenCallbackFn doneFunction = hook2->doneFunction;
void* hook = hook2->hook;
char* pURL = hook2->pURL;
if ( doneFunction ) {
(*doneFunction)(status == ED_IMAGE_ENCODER_USER_CANCELED, pURL, hook);
}
CEditorPluginInterface* epi = hook2->pInterface;
if (pURL){
XP_FREE(pURL);
}
delete epi;
delete hook2;
}
#endif
void EDT_PreOpen(MWContext *pErrorContext,char* _pURL,
EDT_PreOpenCallbackFn doneFunction, void* hook){
// Make local copy, so we can change it if we want.
char *pURL = _pURL ? XP_STRDUP(_pURL) : 0;
// Always ignore target or query appendages
edt_StripAtHashOrQuestionMark(pURL);
if (!pURL || !*pURL) {
// Canceled.
if (doneFunction) {
(*doneFunction)(TRUE, _pURL, hook);
}
XP_FREEIF(pURL);
return;
}
XP_Bool bValid = FALSE;
// Only allow editing of appropriate types of URLs.
int urlType = NET_URL_Type(pURL);
if (urlType == FILE_TYPE_URL ||
urlType == FTP_TYPE_URL ||
urlType == HTTP_TYPE_URL ||
urlType == SECURE_HTTP_TYPE_URL) {
bValid = TRUE;
}
// Test for platform-specific filename if URL type is unknown.
if (urlType == 0) {
// don't make assumption that the user wants a local file
// If it's not a valid URL, then the user probably wants a remote file
// the following behavior is consistent with the browser location field
bValid = TRUE;
}
if (!bValid) {
// Don't report errors if pErrorContext is NULL.
if (pErrorContext != NULL) {
// Tell user that we are rejecting their URL.
char *tmplate = XP_GetString(XP_EDT_CANT_EDIT_URL);
char *msg = NULL;
if (tmplate) {
msg = PR_smprintf(tmplate,pURL);
}
if (msg) {
FE_Alert(pErrorContext,msg);
XP_FREE(msg);
}
else {
XP_ASSERT(0);
}
}
// TRUE says to cancel opening URL.
if (doneFunction) {
(*doneFunction)(TRUE, pURL, hook);
}
XP_FREE(pURL);
return;
}
#ifdef EDITOR_JAVA
if ( EditorPluginManager_PluginsExist() ) {
CEditorPluginInterface* epi = new CEditorPluginInterface(0, &EDT_ComposerPluginCallback);
char temp[500];
XP_SPRINTF(temp, "edit:%s", pURL);
PreOpenCallbackHook* hook2 = new PreOpenCallbackHook;
hook2->pInterface = epi;
hook2->doneFunction = doneFunction;
hook2->pURL = pURL ? XP_STRDUP(pURL) : 0;
hook2->hook = hook;
netscape_javascript_JSObject *t_object=LJ_GetMochaWindow(pErrorContext);
if ( epi->Perform(temp, &edt_PreOpenDoneFunction, hook2, t_object) ){
XP_FREE(pURL);
return;
}
// Failed, so clean up
delete epi;
delete hook2;
}
#endif
if ( doneFunction ){
// FALSE means don't cancel.
(*doneFunction)(FALSE, pURL, hook);
}
XP_FREE(pURL);
}
struct PreCloseCallbackHook {
EDT_PreCloseCallbackFn doneFunction;
void* hook;
};
#ifndef XP_OS2
static
#else
extern "OPTLINK"
#endif
void edt_PreCloseDoneFunction(EDT_ImageEncoderStatus, void* hook2){
// Ignore status.
if (!hook2) {
XP_ASSERT(0);
return;
}
PreCloseCallbackHook *preClose = (PreCloseCallbackHook *)hook2;
if ( preClose->doneFunction )
(*preClose->doneFunction)(preClose->hook);
delete preClose;
}
void EDT_PreClose(MWContext *pMWContext,char* pURL,
EDT_PreCloseCallbackFn doneFunction, void* hook) {
PreCloseCallbackHook *hook2 = new PreCloseCallbackHook;
if (!hook2) {
XP_ASSERT(0);
return;
}
hook2->doneFunction = doneFunction;
hook2->hook = hook;
// EDT_PerformEvent takes care of #ifdef EDITOR_JAVA.
EDT_PerformEvent(pMWContext,"close",pURL,FALSE,FALSE,edt_PreCloseDoneFunction,(void *)hook2);
}
// End of edt.h API functions
#ifdef EDITOR_JAVA
#define JAVA_EE_OR_RETURN JRIEnv* ee = Env(); if ( ! ee ) return
// Helper class for calling editor plugins
extern "C" {
JRIEnv* npn_getJavaEnv();
}
EditorPluginManager EditorPluginManager_new(MWContext* pContext) {
return new CEditorPluginInterface(pContext, &EDT_ComposerPluginCallback);
}
void EditorPluginManager_delete(EditorPluginManager a){
CEditorPluginInterface* b = (CEditorPluginInterface*) a;
delete b;
}
XP_Bool EditorPluginManager_IsPluginActive(EditorPluginManager a) {
CEditorPluginInterface* b = (CEditorPluginInterface*) a;
if ( CEditorPluginInterface::PluginsExist() && b ) {
return b->IsPluginActive();
}
return FALSE;
}
XP_Bool EditorPluginManager_PluginsExist() {
return CEditorPluginInterface::PluginsExist();
}
// This method is a modified version of intl_makeJavaString in modules/edtplug/lj_init.c
// Really would like this to be standardized.
PRIVATE PRBool onlyascii2(char* str, int len)
{
int i;
for(i = 0 ; i < len ; i++, str++)
{
if((*str & 0x80) != 0x00)
return PR_FALSE;
}
return PR_TRUE;
}
PRIVATE struct java_lang_String*
intl_makeJavaString2(JRIEnv* env, int16 encoding, char* str, int len)
{
// First, convert from encoding to win_csid
encoding &= ~CS_AUTO;
if ((encoding == 0) ||
((STATEFUL != (encoding & STATEFUL) ) && onlyascii2(str,len) )
)
{
return JRI_NewStringUTF(env, str, len);
}
int16 win_csid = INTL_DocToWinCharSetID(encoding) & ~CS_AUTO;
char *str_in_win_csid = NULL;
int len_in_win_csid = 0;
XP_Bool needConvertToWincsid = (encoding != win_csid);
XP_Bool needToFreeMem = needConvertToWincsid;
if(needConvertToWincsid)
{
str_in_win_csid = (char*)INTL_ConvertLineWithoutAutoDetect(
(int16)encoding, win_csid, (unsigned char*) str, (uint32) len);
if(NULL == str_in_win_csid )
{
str_in_win_csid = str;
len_in_win_csid = len;
needToFreeMem = FALSE;
}
else
{
len_in_win_csid = (int)XP_STRLEN( str_in_win_csid );
}
}
else
{
str_in_win_csid = str;
len_in_win_csid = len;
}
// Second, convert from win_csid to Unicode
INTL_Unicode* q;
int unicodelen, realunicodelen;
unicodelen = INTL_TextToUnicodeLen(win_csid,
(unsigned char*)str_in_win_csid, (int32)len_in_win_csid);
struct java_lang_String* result;
if ((q = (INTL_Unicode*) XP_ALLOC(sizeof(INTL_Unicode) * unicodelen)) != NULL) {
realunicodelen = INTL_TextToUnicode(win_csid,
(unsigned char*)str_in_win_csid, len_in_win_csid, q, unicodelen );
result = JRI_NewString(env, (const jchar*)q, realunicodelen);
XP_FREE(q);
} else {
XP_ASSERT(q);
result = JRI_NewStringUTF(env, str, len);
}
// Finally, let's free some memory
if(needToFreeMem && str_in_win_csid)
{
XP_FREE(str_in_win_csid);
}
return result;
}
// Returns length accurately.
// returns NULL if the string is too large for the platform.
PRIVATE char*
intl_makeStringFromJava(JRIEnv* env, int16 encoding, struct java_lang_String* jstr, int32& length)
{
const jchar * juc = JRI_GetStringChars(env, jstr);
uint32 jucLen = JRI_GetStringLength(env, jstr);
if ( jucLen > (uint32) MAX_STRING ) {
length = jucLen;
return 0;
}
encoding &= ~CS_AUTO;
int16 win_csid = INTL_DocToWinCharSetID(encoding) & ~CS_AUTO;
length = INTL_UnicodeToStrLen(win_csid, (INTL_Unicode*) juc, jucLen);
if ( length > MAX_STRING ) {
return 0;
}
char* text_in_win_csid = (char*) XP_ALLOC(length + 1);
if ( ! text_in_win_csid ) {
return 0;
}
INTL_UnicodeToStr(win_csid, (INTL_Unicode*) juc, jucLen, (unsigned char*) text_in_win_csid, length + 1);
char* text_in_encoding = NULL;
if(encoding == win_csid)
{
text_in_encoding = text_in_win_csid;
}
else
{
text_in_encoding = (char*)INTL_ConvertLineWithoutAutoDetect(
win_csid, (int16)encoding, (unsigned char*) text_in_win_csid,
(uint32) XP_STRLEN(text_in_win_csid));
if ( ! text_in_encoding ){
text_in_encoding = text_in_win_csid;
}
else {
XP_FREE(text_in_win_csid);
}
}
return text_in_encoding;
}
XP_Bool CEditorPluginInterface::PluginsExist(){
return g_Plugin.Size() > 0;
}
JRIGlobalRef CEditorPluginInterface::g_pManager;
JRIEnv* CEditorPluginInterface::g_pEnv;
XP_Bool CEditorPluginInterface::g_bJavaInitialized;
JRIEnv* CEditorPluginInterface::Env(){
if ( ! g_bJavaInitialized ) {
g_bJavaInitialized = TRUE;
g_pEnv = npn_getJavaEnv();
if ( g_pEnv == NULL ) {
XP_TRACE(("CEditorPluginInterface::Env() - Could not start Java."));
return NULL;
}
JRIEnv* ee = g_pEnv;
JRI_ExceptionClear(ee);
use_netscape_plugin_composer_PluginManager(ee);
use_netscape_plugin_composer_Composer(ee);
use_netscape_plugin_composer_MozillaCallback(ee);
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't find PluginManager class."));
return g_pEnv;
}
g_pManager = JRI_NewGlobalRef(ee, netscape_plugin_composer_PluginManager_new(ee,
class_netscape_plugin_composer_PluginManager(ee)));
if ( ! Manager() || JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't create PluginManager instance."));
}
// Tell the plugin manager about the plugins
int size = g_Plugin.Size();
for(int i = 0; i < size; i++ ){
char* pPlugin = g_Plugin[i];
// Until we get java based local file i/o working, help out the plugin by
// reading the ini text here.
static const char* kComposerIni = "netscape_plugin_composer.ini";
zip_t* pZip = zip_open(pPlugin);
if ( pZip ) {
struct stat status;
if ( zip_stat(pZip, kComposerIni, &status)){
int32_t len = status.st_size;
if ( len > 0 ) {
char* pBuffer = new char[len];
if ( zip_get(pZip, kComposerIni, pBuffer, len) ){
struct java_lang_String* pluginINI = (struct java_lang_String*)
JRI_NewStringUTF(ee, pBuffer, len);
struct java_lang_String* pluginFileName = (struct java_lang_String*)
JRI_NewStringUTF(ee, pPlugin, XP_STRLEN(pPlugin));
JRI_ExceptionClear(ee);
netscape_plugin_composer_PluginManager_registerPlugin(ee, Manager(),
pluginFileName, pluginINI);
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't registerPlugin %s", pPlugin));
}
}
}
}
zip_close(pZip);
}
}
}
return g_pEnv;
}
netscape_plugin_composer_PluginManager* CEditorPluginInterface::Manager(){
JAVA_EE_OR_RETURN 0;
return (netscape_plugin_composer_PluginManager*) JRI_GetGlobalRef(ee, g_pManager);
}
netscape_plugin_composer_Composer* CEditorPluginInterface::Composer(){
JAVA_EE_OR_RETURN 0;
return (netscape_plugin_composer_Composer*) JRI_GetGlobalRef(ee, m_pComposer);
}
TXP_GrowableArray_pChar CEditorPluginInterface::g_Plugin;
TXP_GrowableArray_CEditorPluginInterface CEditorPluginInterface::g_Interface;
CEditorPluginInterface::CEditorPluginInterface(MWContext* pContext, ComposerPluginCallbackFn callback){
m_pContext = pContext;
m_callback = callback;
m_bPluginActive = FALSE;
m_doneFunction = 0;
m_pComposer = 0;
m_bImageEncoder = FALSE;
JAVA_EE_OR_RETURN;
m_pComposer = JRI_NewGlobalRef(ee, netscape_plugin_composer_Composer_new(ee,
class_netscape_plugin_composer_Composer(ee),
(jint) this, (jint) m_callback, (jint) ee));
g_Interface.Add(this);
}
CEditorPluginInterface::~CEditorPluginInterface(){
int size = g_Interface.Size();
int i = 0;
for(i = 0; i < size; i++ ){
if ( g_Interface[i] == this ) {
break;
}
}
if ( i < size ) {
for(i++; i < size; i++){
g_Interface[i-1] = g_Interface[i];
}
g_Interface.SetSize(size-1);
}
else {
XP_ASSERT(FALSE); // We didn't find ourselves in the interface registry.
}
JAVA_EE_OR_RETURN;
JRI_DisposeGlobalRef(ee, m_pComposer);
#if 0
netscape_plugin_composer_PluginManager::_unuse(ee);
#endif
}
XP_Bool CEditorPluginInterface::IsValidInterface(CEditorPluginInterface* pInterface){
int i = 0;
int size = g_Interface.Size();
for(i = 0; i < size; i++ ){
if ( g_Interface[i] == pInterface ) {
return TRUE;
}
}
return FALSE;
}
void CEditorPluginInterface::Register(char* plugin){
// This method is called before Java is initialized.
// It is designed so that it doesn't need Java to run.
// The names of the plugin files are saved until later.
//
// This way, we don't start Java until the Composer is started.
int size = g_Plugin.Size();
for(int i = 0; i < size; i++ ){
if ( XP_STRCMP(g_Plugin[i], plugin) == 0 ) {
return;
}
}
// If We've started Java, it's too late to add the plug-in.
if ( g_bJavaInitialized ) {
// to do: Allow Composer plug-ins to be added after starting.
return;
}
// We don't have to add this plugin to the class path because
// LJ_AddToClassPath puts every .zip or .jar file in
// the plug-ins directory into the class path.
// Save the plugin name.
int len = XP_STRLEN(plugin);
char* pCopy = (char*) XP_ALLOC(len+1);
if ( pCopy ) {
XP_STRCPY(pCopy, plugin);
g_Plugin.Add(pCopy);
}
}
int32 CEditorPluginInterface::GetNumberOfCategories(){
if ( ! PluginsExist() ) return 0;
JAVA_EE_OR_RETURN 0;
if ( ! Manager() ) {
return 0;
}
JRI_ExceptionClear(ee);
int numPlugins = netscape_plugin_composer_PluginManager_getNumberOfCategories(ee, Manager());
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't getNumberOfCategories."));
return 0;
}
return numPlugins;
}
int32 CEditorPluginInterface::GetNumberOfPlugins(int32 type){
if ( ! PluginsExist() ) return 0;
JAVA_EE_OR_RETURN 0;
if ( ! Manager() ) {
return 0;
}
JRI_ExceptionClear(ee);
int numPlugins = netscape_plugin_composer_PluginManager_getNumberOfPlugins(ee, Manager(), type);
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't getNumberOfPlugins."));
return 0;
}
return numPlugins;
}
static char*
getPlatformString(JRIEnv* ee, java_lang_String* name)
{
if (name == NULL || JRI_ExceptionOccurred(ee)){
#ifdef DEBUG
JRI_ExceptionDescribe(ee);
#endif
XP_TRACE(("Couldn't get composer plugin field."));
return NULL;
}
/* Get the platform encoding of the string based on the browser's default csid */
char* result = (char*) JRI_GetStringPlatformChars(ee, name, NULL, 0);
if (result == NULL) {
XP_TRACE(("Couldn't convert unicode string to platform string."));
return NULL;
}
else if (JRI_ExceptionOccurred(ee)) {
#ifdef DEBUG
JRI_ExceptionDescribe(ee);
#endif
XP_TRACE(("Couldn't convert unicode string to platform string."));
return NULL;
}
return result;
}
char* CEditorPluginInterface::GetCategoryName(int32 type){
char* result = 0;
if ( ! PluginsExist() ) return result;
JAVA_EE_OR_RETURN result;
if ( ! Manager() ) {
return result;
}
JRI_ExceptionClear(ee);
java_lang_String* name = netscape_plugin_composer_PluginManager_getCategoryName(ee, Manager(), type);
return getPlatformString(ee, name);
}
char* CEditorPluginInterface::GetPluginName(int32 type, int32 index){
char* result = 0;
if ( ! PluginsExist() ) return result;
JAVA_EE_OR_RETURN result;
if ( ! Manager() ) {
return result;
}
JRI_ExceptionClear(ee);
java_lang_String* name = netscape_plugin_composer_PluginManager_getPluginName(ee, Manager(), type, index);
return getPlatformString(ee, name);
}
char* CEditorPluginInterface::GetPluginHint(int32 type, int32 index){
char* result = 0;
if ( ! PluginsExist() ) return result;
JAVA_EE_OR_RETURN result;
if ( ! Manager() ) {
return result;
}
JRI_ExceptionClear(ee);
java_lang_String* hint = netscape_plugin_composer_PluginManager_getPluginHint(ee, Manager(), type, index);
return getPlatformString(ee, hint);
}
int32 CEditorPluginInterface::GetNumberOfEncoders(){
if ( ! PluginsExist() ) return 0;
JAVA_EE_OR_RETURN 0;
if ( ! Manager() ) {
return 0;
}
JRI_ExceptionClear(ee);
int32 result = netscape_plugin_composer_PluginManager_getNumberOfEncoders(ee, Manager());
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't getNumberOfEncoders."));
return 0;
}
return result;
}
char* CEditorPluginInterface::GetEncoderName(int32 index){
char* result = 0;
if ( ! PluginsExist() ) return result;
JAVA_EE_OR_RETURN result;
if ( ! Manager() ) {
return result;
}
JRI_ExceptionClear(ee);
java_lang_String* hint = netscape_plugin_composer_PluginManager_getEncoderName(ee, Manager(), index);
return getPlatformString(ee, hint);
}
char* CEditorPluginInterface::GetEncoderHint(int32 index){
char* result = 0;
if ( ! PluginsExist() ) return result;
JAVA_EE_OR_RETURN result;
if ( ! Manager() ) {
return result;
}
JRI_ExceptionClear(ee);
java_lang_String* hint = netscape_plugin_composer_PluginManager_getEncoderHint(ee, Manager(), index);
return getPlatformString(ee, hint);
}
char* CEditorPluginInterface::GetEncoderFileExtension(int32 index){
char* result = 0;
if ( ! PluginsExist() ) return result;
JAVA_EE_OR_RETURN result;
if ( ! Manager() ) {
return result;
}
JRI_ExceptionClear(ee);
java_lang_String* hint = netscape_plugin_composer_PluginManager_getEncoderFileExtension(ee, Manager(), index);
return getPlatformString(ee, hint);
}
char* CEditorPluginInterface::GetEncoderFileType(int32 index){
char* result = 0;
if ( ! PluginsExist() ) return result;
JAVA_EE_OR_RETURN result;
if ( ! Manager() ) {
return result;
}
JRI_ExceptionClear(ee);
java_lang_String* hint = netscape_plugin_composer_PluginManager_getEncoderFileType(ee, Manager(), index);
return getPlatformString(ee, hint);
}
XP_Bool CEditorPluginInterface::GetEncoderNeedsQuantizedSource(int32 index){
XP_Bool result = FALSE;
if ( ! PluginsExist() ) return result;
JAVA_EE_OR_RETURN result;
if ( ! Manager() ) {
return result;
}
JRI_ExceptionClear(ee);
result = netscape_plugin_composer_PluginManager_getEncoderNeedsQuantizedSource(ee, Manager(), index);
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't getEncoderNeedsQuantizedSource."));
return result;
}
return result;
}
XP_Bool CEditorPluginInterface::StartEncoder(int32 index, int32 width, int32 height, char** pixels,
char* csFileName, EDT_ImageEncoderCallbackFn doneFunction,
void* doneArgument){
if ( ! PluginsExist() ) return FALSE;
JAVA_EE_OR_RETURN FALSE;
if ( ! Manager() ) {
return FALSE;
}
m_bCanChangeDocument = FALSE;
m_bCanCancel = TRUE;
m_bImageEncoder = TRUE;
// Convert the file name
struct java_lang_String* javaFileName = (struct java_lang_String*)
JRI_NewStringUTF(ee, csFileName, XP_STRLEN(csFileName));
jarrayArray javaPixels = (jarrayArray) JRI_NewObjectArray(ee, height,
JRI_FindClass(ee, JRISigArray(JRISigByte)) , NULL);
{
// Convert the pixels to Java data struct
int32 byteLength = width * 3;
// Fill it up with the lines
for(int hh = 0; hh < height; hh++){
jbyteArray a = (jbyteArray) JRI_NewByteArray(ee, byteLength, pixels[hh]);
JRI_SetObjectArrayElement(ee, javaPixels, hh, a);
}
}
// Save the callback info.
m_doneFunction = doneFunction;
m_doneFunctionArgument = doneArgument;
m_bPluginActive = TRUE;
// call the encoder
JRI_ExceptionClear(ee);
XP_Bool result = (XP_Bool)
netscape_plugin_composer_PluginManager_startEncoder(ee, Manager(), Composer(), index, width, height, javaPixels, javaFileName);
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't start encoder."));
m_bPluginActive = FALSE;
result = FALSE;
}
return result;
}
XP_Bool CEditorPluginInterface::Perform(int32 type, int32 index, EDT_ImageEncoderCallbackFn doneFunction, void* hook, netscape_javascript_JSObject *jsobject){
return Perform2(NULL, type, index, TRUE, TRUE, doneFunction, hook, jsobject);
}
XP_Bool CEditorPluginInterface::Perform(char* pPluginName, EDT_ImageEncoderCallbackFn doneFunction, void* hook, netscape_javascript_JSObject *jsobject){
return Perform2(pPluginName, 0, 0, TRUE, TRUE, doneFunction, hook, jsobject);
}
void CEditorPluginInterface::Perform(char* pEvent, char* pDocURL, XP_Bool bCanChangeDocument, XP_Bool bCanCancel,
EDT_ImageEncoderCallbackFn doneFunction, void* hook, netscape_javascript_JSObject *jsobject){
char *pEventName = NULL;
StrAllocCat(pEventName,pEvent);
StrAllocCat(pEventName,":");
if ( pDocURL ) {
StrAllocCat(pEventName,pDocURL);
}
XP_Bool result = Perform2(pEventName, 0, 0, bCanChangeDocument, bCanCancel, doneFunction, hook, jsobject);
XP_FREE(pEventName);
if ( !result && doneFunction ) {
(*doneFunction)(ED_IMAGE_ENCODER_EXCEPTION, hook);
}
}
XP_Bool CEditorPluginInterface::Perform2(char* pPluginName, int32 type, int32 index,
XP_Bool bCanChangeDocument, XP_Bool bCanCancel,
EDT_ImageEncoderCallbackFn doneFunction, void* hook, netscape_javascript_JSObject *jsobject){
m_bCanChangeDocument = bCanChangeDocument;
m_bCanCancel = bCanCancel;
m_bImageEncoder = FALSE;
if ( ! pPluginName && ! PluginsExist() ) {
return FALSE;
}
if ( m_bPluginActive ) {
return FALSE;
}
JAVA_EE_OR_RETURN FALSE;
if ( ! Manager() ) {
return FALSE;
}
struct java_lang_String* document = GetDocument();
// It is OK for the document to be NULL if we have
// no buffer. (This can happen during an edit event.)
// Otherwise, it is an error condition. NULL is returned
// when the document is too large for Win16.
if ( document==NULL && GetBuffer() != NULL){
return FALSE;
}
struct java_lang_String* base = GetBaseURL();
struct java_lang_String* workDirectoryURL = 0;
struct java_lang_String* workDirectory = 0;
GetWorkDirectoryAndURL(workDirectory, workDirectoryURL);
m_bChangedDocument = FALSE;
// call the plugin
JRI_ExceptionClear(ee);
XP_Bool bResult = FALSE;
m_doneFunction = doneFunction;
m_doneFunctionArgument = hook;
if ( pPluginName ) {
// Call plugin by class name.
struct java_lang_String* pluginName = (struct java_lang_String*)
JRI_NewStringUTF(ee, pPluginName, XP_STRLEN(pPluginName));
JRI_ExceptionClear(ee);
bResult = netscape_plugin_composer_PluginManager_performPluginByName(ee, Manager(), Composer(),
pluginName, document, base, workDirectory, workDirectoryURL, jsobject);
}
else {
// Call plugin by menu category and index
JRI_ExceptionClear(ee);
bResult = netscape_plugin_composer_PluginManager_performPlugin(ee, Manager(), Composer(),
type, index, document, base, workDirectory, workDirectoryURL, jsobject);
}
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
bResult = FALSE;
}
if ( bResult == FALSE ) {
XP_TRACE(("Couldn't perform."));
m_doneFunction = 0;
m_doneFunctionArgument = 0;
return FALSE;
}
m_bPluginActive = TRUE;
if ( GetBuffer() ){
GetBuffer()->m_pContext->waitingMode = 1;
}
return TRUE;
}
struct java_lang_String* CEditorPluginInterface::GetBaseURL(){
if ( GetBuffer() == 0 ) {
return 0;
}
JAVA_EE_OR_RETURN 0;
char* pBase = LO_GetBaseURL(GetBuffer()->m_pContext); // an alias. We don't own the result.
int32 iBaseLen = XP_STRLEN(pBase);
struct java_lang_String* base = (struct java_lang_String*)
JRI_NewStringUTF(ee, pBase, iBaseLen);
return base;
}
struct java_lang_String* CEditorPluginInterface::GetDocument(){
if ( GetBuffer() == 0 ) {
return 0;
}
JAVA_EE_OR_RETURN 0;
XP_HUGE_CHAR_PTR pDocData = 0;
int32 docLength = 0;
docLength = GetBuffer()->WriteToBuffer(&pDocData, TRUE);
if ( docLength > MAX_STRING ) {
DocumentIsTooLarge(docLength, XP_EDT_CP_DOCUMENT_TOO_LARGE_READ);
return 0;
}
int32 unicodeByteLength = 2 * (int32) INTL_TextToUnicodeLen(GetBuffer()->GetDocCharSetID(), (unsigned char*)pDocData, docLength);
if ( unicodeByteLength > MAX_STRING ) {
DocumentIsTooLarge(unicodeByteLength, XP_EDT_CP_DOCUMENT_TOO_LARGE_READ);
return 0;
}
struct java_lang_String* document = (struct java_lang_String*)
intl_makeJavaString2(ee, GetBuffer()->GetDocCharSetID(), pDocData, docLength);
if ( pDocData ) {
XP_HUGE_FREE(pDocData);
}
return document;
}
void CEditorPluginInterface::DocumentIsTooLarge(int32 docLengthInBytes, int msgid){
// Tell the user that the document is too big.
char* tmplate = XP_GetString(msgid);
if (tmplate) {
char* msg = PR_smprintf(tmplate,docLengthInBytes, MAX_STRING);
if (msg) {
FE_Alert(m_pContext,msg);
XP_FREE(msg);
}
}
}
void CEditorPluginInterface::GetWorkDirectoryAndURL(
struct java_lang_String*& workDirectory,
struct java_lang_String*& workDirectoryURL){
workDirectory = 0;
workDirectoryURL = 0;
if ( GetBuffer() == 0 ) {
return;
}
JAVA_EE_OR_RETURN;
// Get the work directory
char* pWork = NULL; // We own the storage
char* pBase = LO_GetBaseURL(GetBuffer()->m_pContext); // an alias. We don't own the result.
int baseURLType = NET_URL_Type(pBase);
if (baseURLType == FILE_TYPE_URL) {
// Get the file name out of the base URL
XP_Bool bValidBase = XP_ConvertUrlToLocalFile(pBase, &pWork);
XP_FREE(pWork);
pWork = NULL;
if ( bValidBase) {
// OK, the base file exists. Now we need pWork to hold the directory rather than the
// file.
char* pTemp = XP_STRDUP(pBase);
if ( pTemp != NULL ) {
char* pEnd = pTemp + XP_STRLEN(pTemp) - 1;
while ( pEnd > pTemp && *pEnd != '/') {
pEnd--;
}
if (* pEnd == '/') {
*pEnd = '\0';
XP_ConvertUrlToLocalFile(pTemp, &pWork);
}
XP_FREE(pTemp);
}
}
}
if ( pWork == NULL ) {
// If we can't use document current directory, use document temp directory.
CEditCommandLog *pLog = CGlobalHistoryGroup::GetGlobalHistoryGroup()->GetLog(GetBuffer());
if (pLog) {
char *pxpURL = pLog->GetDocTempDir();
if (pxpURL) {
pWork = WH_FileName(pxpURL,xpURL);
XP_FREE(pxpURL);
}
}
}
if ( pWork != NULL ) {
int32 iWorkLen = XP_STRLEN(pWork);
workDirectory = (struct java_lang_String*)
JRI_NewStringUTF(ee, pWork, iWorkLen);
// And what's the URL that corresponds to the work directory?
char* pWorkURL = XP_PlatformFileToURL(pWork);
int32 iWorkURLLen = XP_STRLEN(pWorkURL);
workDirectoryURL = (struct java_lang_String*)
JRI_NewStringUTF(ee, pWorkURL, iWorkURLLen);
#ifdef DEBUG_rhess
fprintf(stderr, " workDir::[ %s ]\n", pWork);
fprintf(stderr, " workDirURL::[ %s ]\n", pWorkURL);
#endif
XP_FREE(pWork);
XP_FREE(pWorkURL);
}
#ifdef DEBUG_rhess
else {
fprintf(stderr, " workDir::[ NULL ]\n");
fprintf(stderr, " workDirURL::[ NULL ]\n");
}
#endif
}
XP_Bool CEditorPluginInterface::IsPluginActive(){
return m_bPluginActive;
}
void CEditorPluginInterface::StopPlugin(){
if ( ! PluginsExist() ) return;
JAVA_EE_OR_RETURN;
JRI_ExceptionClear(ee);
netscape_plugin_composer_PluginManager_stopPlugin(ee, Manager(), Composer());
if ( JRI_ExceptionOccurred(ee)){
JRI_ExceptionDescribe(ee);
XP_TRACE(("Couldn't stop plugin."));
}
m_bPluginActive = FALSE;
if ( GetBuffer()){
if ( m_bChangedDocument) {
GetBuffer()->EndBatchChanges();
}
GetBuffer()->m_pContext->waitingMode = 0;
}
}
void CEditorPluginInterface::PluginCallback(int32 action,
struct java_lang_Object* pArgument)
{
if ( ! PluginsExist() ) return;
switch ( action ) {
case PLUGIN_FAIL:
case PLUGIN_CANCEL:
// The plugin has failed.
if ( m_bCanCancel ) {
PluginFailed((struct java_lang_String*) pArgument);
}
else {
PluginSucceeded();
}
break;
case PLUGIN_OK:
// The plugin has succeeded.
PluginSucceeded();
break;
case PLUGIN_NEWTEXT:
// The plugin has changed the text.
NewText((struct java_lang_String*) pArgument);
break;
default:
// Unknown callack
XP_TRACE(("Unknown Composer Plugin Callback: %d\n", action));
break;
}
}
void CEditorPluginInterface::PluginFailed(struct java_lang_String* pWhy){
XP_TRACE(("CEditorPluginInterface::PluginFailed."));
m_bPluginActive = FALSE;
ImageEncoderFinished(pWhy ? ED_IMAGE_ENCODER_EXCEPTION : ED_IMAGE_ENCODER_USER_CANCELED);
if ( m_bImageEncoder ) return;
// The user canceled, or the plugin threw an exception. Back out
// any change the user may have made to the document.
if ( GetBuffer() ){
if (m_bChangedDocument) {
GetBuffer()->EndBatchChanges();
}
GetBuffer()->m_pContext->waitingMode = 0;
if (m_bChangedDocument) {
GetBuffer()->Undo(); // After this call, GetBuffer() is deleted.
}
}
}
void CEditorPluginInterface::PluginSucceeded(){
XP_TRACE(("CEditorPluginInterface::PluginSucceeded."));
m_bPluginActive = FALSE;
ImageEncoderFinished(ED_IMAGE_ENCODER_OK);
if ( m_bImageEncoder ) return;
if ( GetBuffer() ){
if (m_bChangedDocument) {
GetBuffer()->EndBatchChanges();
}
GetBuffer()->m_pContext->waitingMode = 0;
}
}
void CEditorPluginInterface::ImageEncoderFinished(EDT_ImageEncoderStatus status){
if ( ! m_doneFunction ) {
return;
}
(*m_doneFunction)(status, m_doneFunctionArgument);
m_doneFunction = 0;
m_doneFunctionArgument = 0;
}
void CEditorPluginInterface::NewText(struct java_lang_String* pText){
XP_TRACE(("CEditorPluginInterface::NewText."));
JAVA_EE_OR_RETURN;
if ( pText && m_bCanChangeDocument ) {
int32 length;
int16 csid = 0;
if ( GetBuffer() ) {
csid = GetBuffer()->GetDocCharSetID();
}
char* pElementsAsChar =
intl_makeStringFromJava(ee, csid, pText, length);
if ( !pElementsAsChar ){
DocumentIsTooLarge(length, XP_EDT_CP_DOCUMENT_TOO_LARGE_WRITE);
return;
}
if ( m_bChangedDocument == FALSE ) {
m_bChangedDocument = TRUE;
if ( GetBuffer() ) {
GetBuffer()->BeginBatchChanges(kPerformPluginCommandID);
}
}
if ( pElementsAsChar ) {
if ( GetBuffer() ) {
length++; // for '\0' at end.
XP_HUGE_CHAR_PTR pElements = (XP_HUGE_CHAR_PTR) XP_HUGE_ALLOC(length);
for(int32 i = 0; i < length; i++ ) {
pElements[i] = pElementsAsChar[i];
}
GetBuffer()->ReadFromBuffer(pElements);
// At this point GetBuffer() is deleted.
XP_HUGE_FREE(pElements);
}
else { // Call-back hack for setting the URL during preOpen
edt_SetURL(m_doneFunctionArgument, pElementsAsChar);
}
XP_FREE(pElementsAsChar);
}
}
}
CEditBuffer* CEditorPluginInterface::GetBuffer(){
if ( ! m_pContext ) return 0;
GET_EDIT_BUF_OR_RETURN_READY_OR_NOT(m_pContext, pEditBuffer) NULL;
return pEditBuffer;
}
#endif // EDITOR_JAVA
#endif // EDITOR