[go: up one dir, main page]

Menu

[80c337]: / lpackage.cpp  Maximize  Restore  History

Download this file

310 lines (279 with data), 11.4 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#include "lpackage.h"
#include "lobscript.h"
#include "htmlutilities.h"
#include "qtextdocumentlobs.h"
#ifdef LOB_WORD_PROCESSING_TOOLS_AVAILABLE
#include <QTextDocument>
#include "lurchdocumentconverter.h"
#endif
LurchPackage::LurchPackage ()
: QObject(), environment( NULL )
{
// NOTE: This same code is repeated in the copy ctor; if you change one, change both
int i = allInstances.indexOf( NULL );
if ( i == -1 )
allInstances << this;
else
allInstances[i] = this;
codeCache.setMaxCost( 30 );
}
LurchPackage::LurchPackage ( const LurchPackage& /*other*/ )
: QObject(), environment( NULL )
{
// NOTE: This same code is repeated in the non-copy ctor; if you change one, change both
int i = allInstances.indexOf( NULL );
if ( i == -1 )
allInstances << this;
else
allInstances[i] = this;
codeCache.setMaxCost( 30 );
}
LurchPackage::~LurchPackage ()
{
int i = index();
if ( i > -1 )
allInstances[i] = NULL;
}
Lob LurchPackage::asDocument ( bool /*forDisplay*/ ) const
{
return Lob::newDocument( getURN() );
}
void LurchPackage::setup ( QScriptValue scope )
{
setupSlotsAsFunctions( scope );
}
void LurchPackage::setEnvironment ( LurchEnvironment* env )
{
// first, disconnect any signals from the old environment to this package
// (irrelevant for this class, but quite handy for all its descendant classes)
if ( environment )
disconnect( environment, 0, this, 0 );
// now store the new package pointer
environment = env;
}
void LurchPackage::initialize ()
{
// pass
}
void LurchPackage::cacheProgram ( QString name, QString code )
{
QStringList bits = Lob::splitLurchURN( getURN() );
if ( bits.empty() )
bits << "unknown package";
codeCache.insert( getURN() + name, new QScriptProgram( code, name + " of " + bits[0] ) );
}
const QScriptProgram& LurchPackage::program ( QString name ) const
{
return *codeCache[getURN() + name];
}
QScriptValue dispatcher ( QScriptContext* context, QScriptEngine* engine )
{
QScriptValue data = context->callee().data();
// ensure that the data contains an index that refers to a package
QScriptValue indexSV = data.property( "packageIndex" );
if ( !indexSV.isValid() )
return context->throwError( QScriptContext::UnknownError,
QString( "dispatcher: No packageIndex datum for %1" )
.arg( context->callee().toString() ) );
int index = ( int )( indexSV.toInteger() );
LurchPackage* package = LurchPackage::lookupPackage( index );
if ( package == NULL )
return context->throwError( QString( "dispatcher: No package found with index %1" )
.arg( index ) );
// ensure that the data contains a name
QScriptValue nameSV = data.property( "slotName" );
if ( !nameSV.isValid() )
return context->throwError( QScriptContext::UnknownError,
QString( "dispatcher: No slotName datum for %1" )
.arg( context->callee().toString() ) );
QString str = nameSV.toString();
char* name = new char[str.length()+1];
strcpy( name, str.toLatin1().constData() );
// try to call the named slot in the package
QScriptValue result;
bool success = QMetaObject::invokeMethod( package, name, Qt::DirectConnection,
Q_RETURN_ARG( QScriptValue, result ),
Q_ARG( QScriptContext*, context ),
Q_ARG( QScriptEngine*, engine ) );
delete name;
if ( !success ) {
QStringList bits = Lob::splitLurchURN( package->getURN() );
return context->throwError( QScriptContext::TypeError,
QString( "dispatcher: Could not invoke %1 in package "
"%2 by %3" )
.arg( context->callee().toString() )
.arg( bits[0] ).arg( bits[1] ) );
}
// success! return result
return result;
}
void LurchPackage::setupSlotsAsFunctions ( QScriptValue scope )
{
const QMetaObject* mo = metaObject();
QScriptEngine* engine = scope.engine();
for ( int i = mo->methodOffset() ; i <= mo->methodCount() ; i++ ) {
QMetaMethod m = mo->method( i );
QString name = m.methodSignature();
name = name.left( name.indexOf( "(" ) );
if ( ( m.methodType() == QMetaMethod::Slot ) && name.startsWith( "script_" ) ) {
QScriptValue func = engine->newFunction( dispatcher );
QScriptValue data = engine->newObject();
data.setProperty( "slotName", engine->toScriptValue( name ) );
data.setProperty( "packageIndex", engine->toScriptValue( index() ) );
func.setData( data );
QString helpdata = getHelpDataFor( mo->className(), name );
if ( !helpdata.isEmpty() )
func.setProperty( "helpHTML", helpdata );
scope.setProperty( name.mid( 7 ), func );
}
}
}
QString LurchPackage::source ( QString filename )
{
QFile f( filename );
if ( !f.open( QIODevice::ReadOnly ) )
return QString();
QTextStream in( &f );
return in.readAll();
}
QList<LurchPackage*> LurchPackage::allInstances;
QCache<QString,QScriptProgram> LurchPackage::codeCache;
int LurchPackage::index ()
{
return allInstances.indexOf( this );
}
LurchPackage* LurchPackage::lookupPackage ( int index )
{
if ( ( index < 0 ) || ( index >= allInstances.count() ) )
return NULL;
return allInstances[index];
}
#if 0
// this used to make sense, but isn't needed anymore
LurchPackage* LurchPackage::lookupPackage ( QString urn )
{
// first let's ensure there's no null pointers in there, for paranoia sake
while ( allInstances.removeOne( NULL ) ) { /* keep doing it */ }
// ok now search
foreach ( LurchPackage* package, allInstances )
if ( package->getURN() == urn )
return package;
return NULL;
}
#endif
QStringList LurchPackage::allURNs ()
{
return urn2id().keys();
}
LurchPackage* LurchPackage::create ( QString urn )
{
return urn2id().contains( urn ) ?
( LurchPackage* )( QMetaType::create( urn2id()[urn] ) ) :
NULL;
}
int LurchPackage::registerSubclass ( QString urn, int id )
{
return urn2id().contains( urn ) ? urn2id()[urn] : ( urn2id()[urn] = id );
}
QMap<QString,int>& LurchPackage::urn2id ()
{
static QMap<QString,int> result;
return result;
}
#define TEXT_HEAD "<OMS name=\"text\" cd=\"LurchPackage\"/>"
Lob LurchPackage::documentSkeleton ( QStringList dependencies ) const
{
Lob result = Lob::newDocument( getURN(), dependencies );
result.setAttribute( "isPackage", "LurchCore", Lob::fromXML( "<OMSTR>true</OMSTR>" ).child() );
QStringList bits = Lob::splitLurchURN( getURN() );
QString opening = QString( "<h1>%1</h1>"
"<p><font color=\"#ff0000\"><i>Packages are virtual documents "
"built into Lurch, and are therefore READ-ONLY.</i></font></p>"
"<table border=0>"
"<tr><td>Author:</td><td>%2</td></tr>"
"<tr><td>Language:</td><td>%3</td></tr>"
"<tr><td>Version:</td><td>%4</td></tr>"
"</table>" )
.arg( bits[0] ).arg( bits[1] ).arg( bits[2] ).arg( bits[3] );
addText( result, opening );
#ifndef LOB_WORD_PROCESSING_TOOLS_AVAILABLE
Lob firstParagraph = result.child( result.numChildren() - 1 );
QString code =
"var TEXT_HEAD = Lob( '" TEXT_HEAD "' );\n"
"handle = function ( feature, L ) {\n"
" if ( feature != 'representation' ) return false;\n"
" // see LurchPackage::addText():\n"
" if ( ( L.type == OmTypes.Application )\n"
" && L.child().equivalentTo( TEXT_HEAD )\n"
" && ( L.child( 1 ).type == OmTypes.String ) )\n"
" return '<html>' + L.child( 1 ).basicValue + '</html>';\n"
"};";
code = QString( "if ( document().URN == '%1' ) { " + code + " }" ).arg( getURN() );
firstParagraph.child( 1 ).addChild( Lob::fromXML(
"<OMATTR>"
" <OMATP>"
" <OMS name=\"script language\" cd=\"LurchCore\"/>"
" <OMSTR>Javascript</OMSTR>"
" <OMS name=\"script type\" cd=\"LurchCore\"/>"
" <OMSTR>auto-run</OMSTR>"
" </OMATP>"
" <OMSTR>" + escapeXML( code ) + "</OMSTR>"
"</OMATTR>" ).child() );
#endif
return result;
}
void LurchPackage::addHelpAttribute ( Lob document, QString helpURN )
{
document.setAttribute( "helpURN", "LurchCore",
Lob::fromXML( "<OMSTR>" + helpURN + "</OMSTR>" ).child() );
}
void LurchPackage::addText ( Lob document, QString text )
{
#ifdef LOB_WORD_PROCESSING_TOOLS_AVAILABLE
QTextDocument tmp;
QTextCursor c( &tmp );
c.insertHtml( text );
Lob frame = LurchDocumentConverter::structureToLob( tmp.rootFrame() );
while ( frame.numChildren() > 1 )
document.addChild( frame.child( 1 ) ); // just move it--faster than copying
#else
Lob paragraph = Lob::fromXML( "<OMA></OMA>" ).child();
paragraph.addChild( BlockSym() );
paragraph.addChild( Lob::fromXML( "<OMA>" TEXT_HEAD
"<OMSTR>" + escapeXML( text ) + "</OMSTR></OMA>" )
.child() );
document.addChild( paragraph );
#endif
}
void LurchPackage::addScript ( Lob document, QString code )
{
addText( document, "<pre>" + syntaxHighlightJavascript( code ) + "</pre>" );
}
void LurchPackage::describeSetupSlotsAsFunctions ( Lob document ) const
{
QString result = "<p>This package defines the following JavaScript functions implemented"
"in the C++ code for the package.</p>"
"<table border=1>"
"<tr><td>JavaScript function</td><td>C++ implementation</td></tr>";
const QMetaObject* mo = metaObject();
int count = 0;
for ( int i = mo->methodOffset() ; i <= mo->methodCount() ; i++ ) {
QMetaMethod m = mo->method( i );
QString name = m.methodSignature();
name = name.left( name.indexOf( "(" ) );
if ( ( m.methodType() == QMetaMethod::Slot ) && name.startsWith( "script_" ) ) {
// QString helpdata = getHelpDataFor( mo->className(), name );
// if ( !helpdata.isEmpty() ) ...
result += "<tr><td><pre>" + name.mid( 7 ) + "()</pre></td>"
"<td><pre>" + name + "(QScriptContext*,QScriptEngine*)</pre></td></tr>";
count++;
}
}
if ( count > 0 )
addText( document, result + "</table>" );
}
void LurchPackage::describeLoadedScriptCode ( Lob document, QString filename )
{
addText( document, "<p>This package runs the following script code.</p>" );
addScript( document, source( filename ) );
}