diff options
Diffstat (limited to 'linden/indra/newview/llpreviewscript.cpp')
-rw-r--r-- | linden/indra/newview/llpreviewscript.cpp | 2007 |
1 files changed, 2007 insertions, 0 deletions
diff --git a/linden/indra/newview/llpreviewscript.cpp b/linden/indra/newview/llpreviewscript.cpp new file mode 100644 index 0000000..c13da9b --- /dev/null +++ b/linden/indra/newview/llpreviewscript.cpp | |||
@@ -0,0 +1,2007 @@ | |||
1 | /** | ||
2 | * @file llpreviewscript.cpp | ||
3 | * @brief LLPreviewScript class implementation | ||
4 | * | ||
5 | * Copyright (c) 2002-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "llviewerprecompiledheaders.h" | ||
29 | |||
30 | #include "llpreviewscript.h" | ||
31 | |||
32 | #include "llassetstorage.h" | ||
33 | #include "llbutton.h" | ||
34 | #include "llcheckboxctrl.h" | ||
35 | #include "llcombobox.h" | ||
36 | #include "lldir.h" | ||
37 | #include "llinventorymodel.h" | ||
38 | #include "llkeyboard.h" | ||
39 | #include "lllineeditor.h" | ||
40 | |||
41 | #include "llresmgr.h" | ||
42 | #include "llscrollbar.h" | ||
43 | #include "llscrollcontainer.h" | ||
44 | #include "llscrolllistctrl.h" | ||
45 | #include "llslider.h" | ||
46 | #include "lscript_rt_interface.h" | ||
47 | #include "lscript_export.h" | ||
48 | #include "lltextbox.h" | ||
49 | #include "lltooldraganddrop.h" | ||
50 | #include "llvfile.h" | ||
51 | |||
52 | #include "llagent.h" | ||
53 | #include "llnotify.h" | ||
54 | #include "llmenugl.h" | ||
55 | #include "roles_constants.h" | ||
56 | #include "llselectmgr.h" | ||
57 | #include "llviewerinventory.h" | ||
58 | #include "llviewermenu.h" | ||
59 | #include "llviewerobject.h" | ||
60 | #include "llviewerobjectlist.h" | ||
61 | #include "llviewerregion.h" | ||
62 | #include "llkeyboard.h" | ||
63 | #include "llscrollcontainer.h" | ||
64 | #include "llcheckboxctrl.h" | ||
65 | #include "llselectmgr.h" | ||
66 | #include "lltooldraganddrop.h" | ||
67 | #include "llscrolllistctrl.h" | ||
68 | #include "lltextbox.h" | ||
69 | #include "llslider.h" | ||
70 | #include "viewer.h" | ||
71 | #include "lldir.h" | ||
72 | #include "llcombobox.h" | ||
73 | //#include "llfloaterchat.h" | ||
74 | #include "llviewerstats.h" | ||
75 | #include "llviewertexteditor.h" | ||
76 | #include "llviewerwindow.h" | ||
77 | #include "llvieweruictrlfactory.h" | ||
78 | #include "lluictrlfactory.h" | ||
79 | |||
80 | #include "viewer.h" | ||
81 | |||
82 | #include "llpanelinventory.h" | ||
83 | |||
84 | |||
85 | const char HELLO_LSL[] = | ||
86 | "default\n" | ||
87 | "{\n" | ||
88 | " state_entry()\n" | ||
89 | " {\n" | ||
90 | " llSay(0, \"Hello, Avatar!\");\n" | ||
91 | " }\n" | ||
92 | "\n" | ||
93 | " touch_start(integer total_number)\n" | ||
94 | " {\n" | ||
95 | " llSay(0, \"Touched.\");\n" | ||
96 | " }\n" | ||
97 | "}\n"; | ||
98 | const char HELP_LSL[] = "lsl_guide.html"; | ||
99 | |||
100 | const char DEFAULT_SCRIPT_NAME[] = "New Script"; | ||
101 | const char DEFAULT_SCRIPT_DESC[] = "(No Description)"; | ||
102 | |||
103 | const char ENABLED_RUNNING_CHECKBOX_LABEL[] = "Running"; | ||
104 | const char DISABLED_RUNNING_CHECKBOX_LABEL[] = "Public Objects cannot run scripts"; | ||
105 | |||
106 | // Description and header information | ||
107 | |||
108 | const S32 SCRIPT_BORDER = 4; | ||
109 | const S32 SCRIPT_PAD = 5; | ||
110 | const S32 SCRIPT_BUTTON_WIDTH = 128; | ||
111 | const S32 SCRIPT_BUTTON_HEIGHT = 24; // HACK: Use BTN_HEIGHT where possible. | ||
112 | const S32 LINE_COLUMN_HEIGHT = 14; | ||
113 | const S32 BTN_PAD = 8; | ||
114 | |||
115 | const S32 SCRIPT_EDITOR_MIN_HEIGHT = 2 * SCROLLBAR_SIZE + 2 * LLPANEL_BORDER_WIDTH + 128; | ||
116 | |||
117 | const S32 SCRIPT_MIN_WIDTH = | ||
118 | 2 * SCRIPT_BORDER + | ||
119 | 2 * SCRIPT_BUTTON_WIDTH + | ||
120 | SCRIPT_PAD + RESIZE_HANDLE_WIDTH + | ||
121 | SCRIPT_PAD; | ||
122 | |||
123 | const S32 SCRIPT_MIN_HEIGHT = | ||
124 | 2 * SCRIPT_BORDER + | ||
125 | 3*(SCRIPT_BUTTON_HEIGHT + SCRIPT_PAD) + | ||
126 | LINE_COLUMN_HEIGHT + | ||
127 | SCRIPT_EDITOR_MIN_HEIGHT; | ||
128 | |||
129 | const S32 MAX_EXPORT_SIZE = 1000; | ||
130 | |||
131 | const S32 SCRIPT_SEARCH_WIDTH = 300; | ||
132 | const S32 SCRIPT_SEARCH_HEIGHT = 120; | ||
133 | const S32 SCRIPT_SEARCH_LABEL_WIDTH = 50; | ||
134 | const S32 SCRIPT_SEARCH_BUTTON_WIDTH = 80; | ||
135 | const S32 TEXT_EDIT_COLUMN_HEIGHT = 16; | ||
136 | /// --------------------------------------------------------------------------- | ||
137 | /// LLFloaterScriptSearch | ||
138 | /// --------------------------------------------------------------------------- | ||
139 | class LLFloaterScriptSearch : public LLFloater | ||
140 | { | ||
141 | public: | ||
142 | LLFloaterScriptSearch(std::string title, LLRect rect, LLScriptEdCore* editor_core); | ||
143 | ~LLFloaterScriptSearch(); | ||
144 | |||
145 | static void show(LLScriptEdCore* editor_core); | ||
146 | static void onBtnSearch(void* userdata); | ||
147 | void handleBtnSearch(); | ||
148 | |||
149 | static void onBtnReplace(void* userdata); | ||
150 | void handleBtnReplace(); | ||
151 | |||
152 | static void onBtnReplaceAll(void* userdata); | ||
153 | void handleBtnReplaceAll(); | ||
154 | |||
155 | LLScriptEdCore* getEditorCore() { return mEditorCore; } | ||
156 | static LLFloaterScriptSearch* getInstance() { return sInstance; } | ||
157 | |||
158 | void open(); | ||
159 | |||
160 | private: | ||
161 | |||
162 | LLScriptEdCore* mEditorCore; | ||
163 | |||
164 | static LLFloaterScriptSearch* sInstance; | ||
165 | }; | ||
166 | |||
167 | LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL; | ||
168 | |||
169 | LLFloaterScriptSearch::LLFloaterScriptSearch(std::string title, LLRect rect, LLScriptEdCore* editor_core) | ||
170 | : LLFloater("script search",rect,title), mEditorCore(editor_core) | ||
171 | { | ||
172 | |||
173 | gUICtrlFactory->buildFloater(this,"floater_script_search.xml"); | ||
174 | |||
175 | childSetAction("search_btn", onBtnSearch,this); | ||
176 | childSetAction("replace_btn", onBtnReplace,this); | ||
177 | childSetAction("replace_all_btn", onBtnReplaceAll,this); | ||
178 | |||
179 | setDefaultBtn("search_btn"); | ||
180 | |||
181 | if (!getHost()) | ||
182 | { | ||
183 | LLRect curRect = getRect(); | ||
184 | translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop); | ||
185 | } | ||
186 | |||
187 | sInstance = this; | ||
188 | |||
189 | childSetFocus("search_text", TRUE); | ||
190 | } | ||
191 | |||
192 | //static | ||
193 | void LLFloaterScriptSearch::show(LLScriptEdCore* editor_core) | ||
194 | { | ||
195 | if (sInstance && sInstance->mEditorCore && sInstance->mEditorCore != editor_core) | ||
196 | { | ||
197 | sInstance->close(); | ||
198 | delete sInstance; | ||
199 | } | ||
200 | |||
201 | if (!sInstance) | ||
202 | { | ||
203 | S32 left = 0; | ||
204 | S32 top = 0; | ||
205 | gFloaterView->getNewFloaterPosition(&left,&top); | ||
206 | |||
207 | // sInstance will be assigned in the constructor. | ||
208 | new LLFloaterScriptSearch("Script Search",LLRect(left,top,left + SCRIPT_SEARCH_WIDTH,top - SCRIPT_SEARCH_HEIGHT),editor_core); | ||
209 | } | ||
210 | |||
211 | sInstance->open(); | ||
212 | } | ||
213 | |||
214 | LLFloaterScriptSearch::~LLFloaterScriptSearch() | ||
215 | { | ||
216 | sInstance = NULL; | ||
217 | } | ||
218 | |||
219 | // static | ||
220 | void LLFloaterScriptSearch::onBtnSearch(void *userdata) | ||
221 | { | ||
222 | LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; | ||
223 | self->handleBtnSearch(); | ||
224 | } | ||
225 | |||
226 | void LLFloaterScriptSearch::handleBtnSearch() | ||
227 | { | ||
228 | LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text"); | ||
229 | mEditorCore->mEditor->selectNext(childGetText("search_text"), caseChk->get()); | ||
230 | } | ||
231 | |||
232 | // static | ||
233 | void LLFloaterScriptSearch::onBtnReplace(void *userdata) | ||
234 | { | ||
235 | LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; | ||
236 | self->handleBtnReplace(); | ||
237 | } | ||
238 | |||
239 | void LLFloaterScriptSearch::handleBtnReplace() | ||
240 | { | ||
241 | LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text"); | ||
242 | mEditorCore->mEditor->replaceText(childGetText("search_text"), childGetText("replace_text"), caseChk->get()); | ||
243 | } | ||
244 | |||
245 | // static | ||
246 | void LLFloaterScriptSearch::onBtnReplaceAll(void *userdata) | ||
247 | { | ||
248 | LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata; | ||
249 | self->handleBtnReplaceAll(); | ||
250 | } | ||
251 | |||
252 | void LLFloaterScriptSearch::handleBtnReplaceAll() | ||
253 | { | ||
254 | LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text"); | ||
255 | mEditorCore->mEditor->replaceTextAll(childGetText("search_text"), childGetText("replace_text"), caseChk->get()); | ||
256 | } | ||
257 | |||
258 | void LLFloaterScriptSearch::open() | ||
259 | { | ||
260 | LLFloater::open(); | ||
261 | childSetFocus("search_text", TRUE); | ||
262 | } | ||
263 | /// --------------------------------------------------------------------------- | ||
264 | /// LLScriptEdCore | ||
265 | /// --------------------------------------------------------------------------- | ||
266 | |||
267 | LLScriptEdCore::LLScriptEdCore( | ||
268 | const std::string& name, | ||
269 | const LLRect& rect, | ||
270 | const std::string& sample, | ||
271 | const std::string& help, | ||
272 | const LLViewHandle& floater_handle, | ||
273 | void (*load_callback)(void*), | ||
274 | void (*save_callback)(void*, BOOL), | ||
275 | void* userdata, | ||
276 | S32 bottom_pad) | ||
277 | : | ||
278 | LLPanel( "name", rect ), | ||
279 | mSampleText(sample), | ||
280 | mHelpFile ( help ), | ||
281 | mEditor( NULL ), | ||
282 | mLoadCallback( load_callback ), | ||
283 | mSaveCallback( save_callback ), | ||
284 | mUserdata( userdata ), | ||
285 | mForceClose( FALSE ) | ||
286 | { | ||
287 | setFollowsAll(); | ||
288 | setBorderVisible(FALSE); | ||
289 | |||
290 | |||
291 | gUICtrlFactory->buildPanel(this, "floater_script_ed_panel.xml"); | ||
292 | |||
293 | mErrorList = LLUICtrlFactory::getScrollListByName(this, "lsl errors"); | ||
294 | |||
295 | mFunctions = LLUICtrlFactory::getComboBoxByName(this, "Insert..."); | ||
296 | |||
297 | childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this); | ||
298 | mFunctions->setLabel("Insert..."); | ||
299 | |||
300 | mEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Script Editor"); | ||
301 | mEditor->setReadOnlyBgColor(gColors.getColor( "ScriptBgReadOnlyColor" ) ); | ||
302 | mEditor->setFollowsAll(); | ||
303 | mEditor->setHandleEditKeysDirectly(TRUE); | ||
304 | mEditor->setEnabled(TRUE); | ||
305 | mEditor->setWordWrap(TRUE); | ||
306 | |||
307 | LLDynamicArray<const char*> funcs; | ||
308 | LLDynamicArray<const char*> tooltips; | ||
309 | for (S32 i = 0; i < gScriptLibrary.mNextNumber; i++) | ||
310 | { | ||
311 | // Make sure this isn't a god only function, or the agent is a god. | ||
312 | if (!gScriptLibrary.mFunctions[i]->mGodOnly || gAgent.isGodlike()) | ||
313 | { | ||
314 | funcs.put(gScriptLibrary.mFunctions[i]->mName); | ||
315 | tooltips.put(gScriptLibrary.mFunctions[i]->mDesc); | ||
316 | } | ||
317 | } | ||
318 | LLColor3 color(0.5f, 0.0f, 0.15f); | ||
319 | |||
320 | mEditor->loadKeywords(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini"), funcs, tooltips, color); | ||
321 | |||
322 | |||
323 | LLKeywordToken *token; | ||
324 | LLKeywords::word_token_map_t::iterator token_it; | ||
325 | for (token_it = mEditor->mKeywords.mWordTokenMap.begin(); token_it != mEditor->mKeywords.mWordTokenMap.end(); ++token_it) | ||
326 | { | ||
327 | token = token_it->second; | ||
328 | if (token->mColor == color) | ||
329 | mFunctions->add(wstring_to_utf8str(token->mToken)); | ||
330 | } | ||
331 | |||
332 | for (token_it = mEditor->mKeywords.mWordTokenMap.begin(); token_it != mEditor->mKeywords.mWordTokenMap.end(); ++token_it) | ||
333 | { | ||
334 | token = token_it->second; | ||
335 | if (token->mColor != color) | ||
336 | mFunctions->add(wstring_to_utf8str(token->mToken)); | ||
337 | } | ||
338 | |||
339 | |||
340 | childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this); | ||
341 | childSetAction("Save_btn", onBtnSave,this); | ||
342 | |||
343 | initMenu(); | ||
344 | |||
345 | |||
346 | // Do the work that addTabPanel() normally does. | ||
347 | //LLRect tab_panel_rect( 0, mRect.getHeight(), mRect.getWidth(), 0 ); | ||
348 | //tab_panel_rect.stretch( -LLPANEL_BORDER_WIDTH ); | ||
349 | //mCodePanel->setFollowsAll(); | ||
350 | //mCodePanel->translate( tab_panel_rect.mLeft - mCodePanel->getRect().mLeft, tab_panel_rect.mBottom - mCodePanel->getRect().mBottom); | ||
351 | //mCodePanel->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE ); | ||
352 | |||
353 | } | ||
354 | |||
355 | LLScriptEdCore::~LLScriptEdCore() | ||
356 | { | ||
357 | deleteBridges(); | ||
358 | |||
359 | // If the search window is up for this editor, close it. | ||
360 | LLFloaterScriptSearch* script_search = LLFloaterScriptSearch::getInstance(); | ||
361 | if (script_search && script_search->getEditorCore() == this) | ||
362 | { | ||
363 | script_search->close(); | ||
364 | delete script_search; | ||
365 | } | ||
366 | } | ||
367 | |||
368 | void LLScriptEdCore::initMenu() | ||
369 | { | ||
370 | |||
371 | LLMenuItemCallGL* menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Save"); | ||
372 | menuItem->setMenuCallback(onBtnSave, this); | ||
373 | menuItem->setEnabledCallback(hasChanged); | ||
374 | |||
375 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Revert All Changes"); | ||
376 | menuItem->setMenuCallback(onBtnUndoChanges, this); | ||
377 | menuItem->setEnabledCallback(hasChanged); | ||
378 | |||
379 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Undo"); | ||
380 | menuItem->setMenuCallback(onUndoMenu, this); | ||
381 | menuItem->setEnabledCallback(enableUndoMenu); | ||
382 | |||
383 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Redo"); | ||
384 | menuItem->setMenuCallback(onRedoMenu, this); | ||
385 | menuItem->setEnabledCallback(enableRedoMenu); | ||
386 | |||
387 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Cut"); | ||
388 | menuItem->setMenuCallback(onCutMenu, this); | ||
389 | menuItem->setEnabledCallback(enableCutMenu); | ||
390 | |||
391 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Copy"); | ||
392 | menuItem->setMenuCallback(onCopyMenu, this); | ||
393 | menuItem->setEnabledCallback(enableCopyMenu); | ||
394 | |||
395 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Paste"); | ||
396 | menuItem->setMenuCallback(onPasteMenu, this); | ||
397 | menuItem->setEnabledCallback(enablePasteMenu); | ||
398 | |||
399 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Select All"); | ||
400 | menuItem->setMenuCallback(onSelectAllMenu, this); | ||
401 | menuItem->setEnabledCallback(enableSelectAllMenu); | ||
402 | |||
403 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Search / Replace..."); | ||
404 | menuItem->setMenuCallback(onSearchMenu, this); | ||
405 | menuItem->setEnabledCallback(NULL); | ||
406 | |||
407 | menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Help..."); | ||
408 | menuItem->setMenuCallback(onBtnHelp, this); | ||
409 | menuItem->setEnabledCallback(NULL); | ||
410 | |||
411 | } | ||
412 | |||
413 | BOOL LLScriptEdCore::hasChanged(void* userdata) | ||
414 | { | ||
415 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
416 | if (!self || !self->mEditor) return FALSE; | ||
417 | |||
418 | return !self->mEditor->isPristine(); | ||
419 | } | ||
420 | |||
421 | void LLScriptEdCore::draw() | ||
422 | { | ||
423 | BOOL script_changed = !mEditor->isPristine(); | ||
424 | childSetEnabled("Save_btn", script_changed); | ||
425 | |||
426 | if( mEditor->hasFocus() ) | ||
427 | { | ||
428 | S32 line = 0; | ||
429 | S32 column = 0; | ||
430 | mEditor->getCurrentLineAndColumn( &line, &column, FALSE ); // don't include wordwrap | ||
431 | char cursor_pos[STD_STRING_BUF_SIZE]; | ||
432 | sprintf( cursor_pos, "Line %d, Column %d", line, column ); | ||
433 | childSetText("line_col", cursor_pos); | ||
434 | } | ||
435 | else | ||
436 | { | ||
437 | childSetText("line_col", ""); | ||
438 | } | ||
439 | |||
440 | LLPanel::draw(); | ||
441 | } | ||
442 | |||
443 | BOOL LLScriptEdCore::canClose() | ||
444 | { | ||
445 | if(mForceClose || mEditor->isPristine()) | ||
446 | { | ||
447 | return TRUE; | ||
448 | } | ||
449 | else | ||
450 | { | ||
451 | // Bring up view-modal dialog: Save changes? Yes, No, Cancel | ||
452 | gViewerWindow->alertXml("SaveChanges", LLScriptEdCore::handleSaveChangesDialog, this); | ||
453 | return FALSE; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | // static | ||
458 | void LLScriptEdCore::handleSaveChangesDialog( S32 option, void* userdata ) | ||
459 | { | ||
460 | LLScriptEdCore* self = (LLScriptEdCore*) userdata; | ||
461 | switch( option ) | ||
462 | { | ||
463 | case 0: // "Yes" | ||
464 | // close after saving | ||
465 | LLScriptEdCore::doSave( self, TRUE ); | ||
466 | break; | ||
467 | |||
468 | case 1: // "No" | ||
469 | self->mForceClose = TRUE; | ||
470 | // This will close immediately because mForceClose is true, so we won't | ||
471 | // infinite loop with these dialogs. JC | ||
472 | ((LLFloater*) self->getParent())->close(); | ||
473 | break; | ||
474 | |||
475 | case 2: // "Cancel" | ||
476 | default: | ||
477 | // If we were quitting, we didn't really mean it. | ||
478 | app_abort_quit(); | ||
479 | break; | ||
480 | } | ||
481 | } | ||
482 | |||
483 | // static | ||
484 | void LLScriptEdCore::onHelpWebDialog(S32 option, void* userdata) | ||
485 | { | ||
486 | LLScriptEdCore* corep = (LLScriptEdCore*)userdata; | ||
487 | |||
488 | switch(option) | ||
489 | { | ||
490 | case 0: | ||
491 | load_url_local_file(corep->mHelpFile.c_str()); | ||
492 | break; | ||
493 | default: | ||
494 | break; | ||
495 | } | ||
496 | } | ||
497 | |||
498 | // static | ||
499 | void LLScriptEdCore::onBtnHelp(void* userdata) | ||
500 | { | ||
501 | gViewerWindow->alertXml("WebLaunchLSLGuide", | ||
502 | onHelpWebDialog, | ||
503 | userdata); | ||
504 | } | ||
505 | |||
506 | // static | ||
507 | void LLScriptEdCore::onBtnInsertSample(void* userdata) | ||
508 | { | ||
509 | LLScriptEdCore* self = (LLScriptEdCore*) userdata; | ||
510 | |||
511 | // Insert sample code | ||
512 | self->mEditor->selectAll(); | ||
513 | self->mEditor->cut(); | ||
514 | self->mEditor->insertText(self->mSampleText); | ||
515 | } | ||
516 | |||
517 | // static | ||
518 | void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata) | ||
519 | { | ||
520 | LLScriptEdCore* self = (LLScriptEdCore*) userdata; | ||
521 | |||
522 | // Insert sample code | ||
523 | if(self->mEditor->getEnabled()) | ||
524 | { | ||
525 | self->mEditor->insertText(self->mFunctions->getSimple()); | ||
526 | } | ||
527 | self->mEditor->setFocus(TRUE); | ||
528 | } | ||
529 | |||
530 | // static | ||
531 | void LLScriptEdCore::doSave( void* userdata, BOOL close_after_save ) | ||
532 | { | ||
533 | if( gViewerStats ) | ||
534 | { | ||
535 | gViewerStats->incStat( LLViewerStats::ST_LSL_SAVE_COUNT ); | ||
536 | } | ||
537 | |||
538 | LLScriptEdCore* self = (LLScriptEdCore*) userdata; | ||
539 | |||
540 | if( self->mSaveCallback ) | ||
541 | { | ||
542 | self->mSaveCallback( self->mUserdata, close_after_save ); | ||
543 | } | ||
544 | } | ||
545 | |||
546 | // static | ||
547 | void LLScriptEdCore::onBtnSave(void* data) | ||
548 | { | ||
549 | // do the save, but don't close afterwards | ||
550 | doSave(data, FALSE); | ||
551 | } | ||
552 | |||
553 | // static | ||
554 | void LLScriptEdCore::onBtnUndoChanges( void* userdata ) | ||
555 | { | ||
556 | LLScriptEdCore* self = (LLScriptEdCore*) userdata; | ||
557 | if( !self->mEditor->tryToRevertToPristineState() ) | ||
558 | { | ||
559 | gViewerWindow->alertXml("ScriptCannotUndo", | ||
560 | LLScriptEdCore::handleReloadFromServerDialog, self); | ||
561 | } | ||
562 | } | ||
563 | |||
564 | void LLScriptEdCore::onSearchMenu(void* userdata) | ||
565 | { | ||
566 | LLScriptEdCore* sec = (LLScriptEdCore*)userdata; | ||
567 | LLFloaterScriptSearch::show(sec); | ||
568 | } | ||
569 | |||
570 | // static | ||
571 | void LLScriptEdCore::onUndoMenu(void* userdata) | ||
572 | { | ||
573 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
574 | if (!self || !self->mEditor) return; | ||
575 | self->mEditor->undo(); | ||
576 | } | ||
577 | |||
578 | // static | ||
579 | void LLScriptEdCore::onRedoMenu(void* userdata) | ||
580 | { | ||
581 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
582 | if (!self || !self->mEditor) return; | ||
583 | self->mEditor->redo(); | ||
584 | } | ||
585 | |||
586 | // static | ||
587 | void LLScriptEdCore::onCutMenu(void* userdata) | ||
588 | { | ||
589 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
590 | if (!self || !self->mEditor) return; | ||
591 | self->mEditor->cut(); | ||
592 | } | ||
593 | |||
594 | // static | ||
595 | void LLScriptEdCore::onCopyMenu(void* userdata) | ||
596 | { | ||
597 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
598 | if (!self || !self->mEditor) return; | ||
599 | self->mEditor->copy(); | ||
600 | } | ||
601 | |||
602 | // static | ||
603 | void LLScriptEdCore::onPasteMenu(void* userdata) | ||
604 | { | ||
605 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
606 | if (!self || !self->mEditor) return; | ||
607 | self->mEditor->paste(); | ||
608 | } | ||
609 | |||
610 | // static | ||
611 | void LLScriptEdCore::onSelectAllMenu(void* userdata) | ||
612 | { | ||
613 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
614 | if (!self || !self->mEditor) return; | ||
615 | self->mEditor->selectAll(); | ||
616 | } | ||
617 | |||
618 | // static | ||
619 | void LLScriptEdCore::onDeselectMenu(void* userdata) | ||
620 | { | ||
621 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
622 | if (!self || !self->mEditor) return; | ||
623 | self->mEditor->deselect(); | ||
624 | } | ||
625 | |||
626 | // static | ||
627 | BOOL LLScriptEdCore::enableUndoMenu(void* userdata) | ||
628 | { | ||
629 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
630 | if (!self || !self->mEditor) return FALSE; | ||
631 | return self->mEditor->canUndo(); | ||
632 | } | ||
633 | |||
634 | // static | ||
635 | BOOL LLScriptEdCore::enableRedoMenu(void* userdata) | ||
636 | { | ||
637 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
638 | if (!self || !self->mEditor) return FALSE; | ||
639 | return self->mEditor->canRedo(); | ||
640 | } | ||
641 | |||
642 | // static | ||
643 | BOOL LLScriptEdCore::enableCutMenu(void* userdata) | ||
644 | { | ||
645 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
646 | if (!self || !self->mEditor) return FALSE; | ||
647 | return self->mEditor->canCut(); | ||
648 | } | ||
649 | |||
650 | // static | ||
651 | BOOL LLScriptEdCore::enableCopyMenu(void* userdata) | ||
652 | { | ||
653 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
654 | if (!self || !self->mEditor) return FALSE; | ||
655 | return self->mEditor->canCopy(); | ||
656 | } | ||
657 | |||
658 | // static | ||
659 | BOOL LLScriptEdCore::enablePasteMenu(void* userdata) | ||
660 | { | ||
661 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
662 | if (!self || !self->mEditor) return FALSE; | ||
663 | return self->mEditor->canPaste(); | ||
664 | } | ||
665 | |||
666 | // static | ||
667 | BOOL LLScriptEdCore::enableSelectAllMenu(void* userdata) | ||
668 | { | ||
669 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
670 | if (!self || !self->mEditor) return FALSE; | ||
671 | return self->mEditor->canSelectAll(); | ||
672 | } | ||
673 | |||
674 | // static | ||
675 | BOOL LLScriptEdCore::enableDeselectMenu(void* userdata) | ||
676 | { | ||
677 | LLScriptEdCore* self = (LLScriptEdCore*)userdata; | ||
678 | if (!self || !self->mEditor) return FALSE; | ||
679 | return self->mEditor->canDeselect(); | ||
680 | } | ||
681 | |||
682 | // static | ||
683 | void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data) | ||
684 | { | ||
685 | LLScriptEdCore* self = (LLScriptEdCore*)user_data; | ||
686 | LLScrollListItem* item = self->mErrorList->getFirstSelected(); | ||
687 | if(item) | ||
688 | { | ||
689 | // *FIX: This fucked up little hack is here because we don't | ||
690 | // have a grep library. This is very brittle code. | ||
691 | S32 row = 0; | ||
692 | S32 column = 0; | ||
693 | const LLScrollListCell* cell = item->getColumn(0); | ||
694 | LLString line(cell->getText()); | ||
695 | line.erase(0, 1); | ||
696 | LLString::replaceChar(line, ',',' '); | ||
697 | LLString::replaceChar(line, ')',' '); | ||
698 | sscanf(line.c_str(), "%d %d", &row, &column); | ||
699 | //llinfos << "LLScriptEdCore::onErrorList() - " << row << ", " | ||
700 | //<< column << llendl; | ||
701 | self->mEditor->setCursor(row, column); | ||
702 | self->mEditor->setFocus(TRUE); | ||
703 | } | ||
704 | } | ||
705 | |||
706 | // static | ||
707 | void LLScriptEdCore::handleReloadFromServerDialog( S32 option, void* userdata ) | ||
708 | { | ||
709 | LLScriptEdCore* self = (LLScriptEdCore*) userdata; | ||
710 | switch( option ) | ||
711 | { | ||
712 | case 0: // "Yes" | ||
713 | if( self->mLoadCallback ) | ||
714 | { | ||
715 | self->mEditor->setText( "Loading..." ); | ||
716 | self->mLoadCallback( self->mUserdata ); | ||
717 | } | ||
718 | break; | ||
719 | |||
720 | case 1: // "No" | ||
721 | break; | ||
722 | |||
723 | default: | ||
724 | llassert(0); | ||
725 | break; | ||
726 | } | ||
727 | } | ||
728 | |||
729 | void LLScriptEdCore::selectFirstError() | ||
730 | { | ||
731 | // Select the first item; | ||
732 | mErrorList->selectFirstItem(); | ||
733 | onErrorList(mErrorList, this); | ||
734 | } | ||
735 | |||
736 | |||
737 | struct LLEntryAndEdCore | ||
738 | { | ||
739 | LLScriptEdCore* mCore; | ||
740 | LLEntryAndEdCore(LLScriptEdCore* core) : | ||
741 | mCore(core) | ||
742 | {} | ||
743 | }; | ||
744 | |||
745 | void LLScriptEdCore::deleteBridges() | ||
746 | { | ||
747 | S32 count = mBridges.count(); | ||
748 | LLEntryAndEdCore* eandc; | ||
749 | for(S32 i = 0; i < count; i++) | ||
750 | { | ||
751 | eandc = mBridges.get(i); | ||
752 | delete eandc; | ||
753 | mBridges[i] = NULL; | ||
754 | } | ||
755 | mBridges.reset(); | ||
756 | } | ||
757 | |||
758 | // virtual | ||
759 | BOOL LLScriptEdCore::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) | ||
760 | { | ||
761 | if(getVisible() && getEnabled()) | ||
762 | { | ||
763 | if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL))) | ||
764 | { | ||
765 | if(mSaveCallback) | ||
766 | { | ||
767 | // don't close after saving | ||
768 | mSaveCallback(mUserdata, FALSE); | ||
769 | } | ||
770 | |||
771 | return TRUE; | ||
772 | } | ||
773 | } | ||
774 | return FALSE; | ||
775 | } | ||
776 | |||
777 | /// --------------------------------------------------------------------------- | ||
778 | /// LLPreviewLSL | ||
779 | /// --------------------------------------------------------------------------- | ||
780 | |||
781 | struct LLScriptSaveInfo | ||
782 | { | ||
783 | LLUUID mItemUUID; | ||
784 | LLString mDescription; | ||
785 | LLTransactionID mTransactionID; | ||
786 | |||
787 | LLScriptSaveInfo(const LLUUID& uuid, const LLString& desc, LLTransactionID tid) : | ||
788 | mItemUUID(uuid), mDescription(desc), mTransactionID(tid) {} | ||
789 | }; | ||
790 | |||
791 | |||
792 | |||
793 | //static | ||
794 | void* LLPreviewLSL::createScriptEdPanel(void* userdata) | ||
795 | { | ||
796 | |||
797 | LLPreviewLSL *self = (LLPreviewLSL*)userdata; | ||
798 | |||
799 | self->mScriptEd = new LLScriptEdCore("script panel", | ||
800 | LLRect(), | ||
801 | HELLO_LSL, | ||
802 | HELP_LSL, | ||
803 | self->mViewHandle, | ||
804 | LLPreviewLSL::onLoad, | ||
805 | LLPreviewLSL::onSave, | ||
806 | self, | ||
807 | 0); | ||
808 | |||
809 | return self->mScriptEd; | ||
810 | } | ||
811 | |||
812 | |||
813 | LLPreviewLSL::LLPreviewLSL(const std::string& name, const LLRect& rect, | ||
814 | const std::string& title, const LLUUID& item_id ) | ||
815 | : LLPreview( name, rect, title, item_id, LLUUID::null, TRUE, | ||
816 | SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT ), | ||
817 | mPendingUploads(0) | ||
818 | { | ||
819 | |||
820 | LLRect curRect = rect; | ||
821 | |||
822 | |||
823 | LLCallbackMap::map_t factory_map; | ||
824 | factory_map["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this); | ||
825 | |||
826 | |||
827 | gUICtrlFactory->buildFloater(this,"floater_script_preview.xml", &factory_map); | ||
828 | |||
829 | moveResizeHandleToFront(); | ||
830 | |||
831 | |||
832 | LLInventoryItem* item = getItem(); | ||
833 | |||
834 | childSetCommitCallback("desc", LLPreview::onText, this); | ||
835 | childSetText("desc", item->getDescription()); | ||
836 | childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); | ||
837 | |||
838 | LLMultiFloater* hostp = getHost(); | ||
839 | |||
840 | if (!sHostp && !hostp && getAssetStatus() == PREVIEW_ASSET_UNLOADED) | ||
841 | { | ||
842 | loadAsset(); | ||
843 | } | ||
844 | |||
845 | setTitle(title); | ||
846 | |||
847 | if (!getHost()) | ||
848 | { | ||
849 | reshape(curRect.getWidth(), curRect.getHeight(), TRUE); | ||
850 | setRect(curRect); | ||
851 | } | ||
852 | } | ||
853 | |||
854 | |||
855 | void LLPreviewLSL::loadAsset() | ||
856 | { | ||
857 | // *HACK: we poke into inventory to see if it's there, and if so, | ||
858 | // then it might be part of the inventory library. If it's in the | ||
859 | // library, then you can see the script, but not modify it. | ||
860 | LLInventoryItem* item = gInventory.getItem(mItemUUID); | ||
861 | BOOL is_library = item | ||
862 | && !gInventory.isObjectDescendentOf(mItemUUID, | ||
863 | gAgent.getInventoryRootID()); | ||
864 | if(!item) | ||
865 | { | ||
866 | // do the more generic search. | ||
867 | getItem(); | ||
868 | } | ||
869 | if(item) | ||
870 | { | ||
871 | BOOL is_copyable = gAgent.allowOperation(PERM_COPY, | ||
872 | item->getPermissions(), GP_OBJECT_MANIPULATE); | ||
873 | BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, | ||
874 | item->getPermissions(), GP_OBJECT_MANIPULATE); | ||
875 | if (gAgent.isGodlike() || (is_copyable && (is_modifiable || is_library))) | ||
876 | { | ||
877 | LLUUID* new_uuid = new LLUUID(mItemUUID); | ||
878 | gAssetStorage->getInvItemAsset(LLHost::invalid, | ||
879 | gAgent.getID(), | ||
880 | gAgent.getSessionID(), | ||
881 | item->getPermissions().getOwner(), | ||
882 | LLUUID::null, | ||
883 | item->getUUID(), | ||
884 | item->getAssetUUID(), | ||
885 | item->getType(), | ||
886 | &LLPreviewLSL::onLoadComplete, | ||
887 | (void*)new_uuid, | ||
888 | TRUE); | ||
889 | mAssetStatus = PREVIEW_ASSET_LOADING; | ||
890 | } | ||
891 | else | ||
892 | { | ||
893 | mScriptEd->mEditor->setText("You are not allowed to view this script."); | ||
894 | mScriptEd->mEditor->makePristine(); | ||
895 | mScriptEd->mEditor->setEnabled(FALSE); | ||
896 | mScriptEd->mFunctions->setEnabled(FALSE); | ||
897 | mAssetStatus = PREVIEW_ASSET_LOADED; | ||
898 | } | ||
899 | childSetVisible("lock", !is_modifiable); | ||
900 | mScriptEd->childSetEnabled("Insert...", is_modifiable); | ||
901 | } | ||
902 | else | ||
903 | { | ||
904 | mScriptEd->mEditor->setText(HELLO_LSL); | ||
905 | mAssetStatus = PREVIEW_ASSET_LOADED; | ||
906 | } | ||
907 | } | ||
908 | |||
909 | |||
910 | BOOL LLPreviewLSL::canClose() | ||
911 | { | ||
912 | return mScriptEd->canClose(); | ||
913 | } | ||
914 | |||
915 | //override the llpreview open which attempts to load asset, load after xml ui made | ||
916 | void LLPreviewLSL::open() | ||
917 | { | ||
918 | LLFloater::open(); | ||
919 | } | ||
920 | |||
921 | // static | ||
922 | void LLPreviewLSL::onLoad(void* userdata) | ||
923 | { | ||
924 | LLPreviewLSL* self = (LLPreviewLSL*)userdata; | ||
925 | self->loadAsset(); | ||
926 | } | ||
927 | |||
928 | // static | ||
929 | void LLPreviewLSL::onSave(void* userdata, BOOL close_after_save) | ||
930 | { | ||
931 | LLPreviewLSL* self = (LLPreviewLSL*)userdata; | ||
932 | self->mCloseAfterSave = close_after_save; | ||
933 | self->saveIfNeeded(); | ||
934 | } | ||
935 | |||
936 | |||
937 | // Save needs to compile the text in the buffer. If the compile | ||
938 | // succeeds, then save both assets out to the database. If the compile | ||
939 | // fails, go ahead and save the text anyway so that the user doesn't | ||
940 | // get too fucked. | ||
941 | void LLPreviewLSL::saveIfNeeded() | ||
942 | { | ||
943 | // llinfos << "LLPreviewLSL::save()" << llendl; | ||
944 | if(!mScriptEd->mEditor->isPristine()) | ||
945 | { | ||
946 | mPendingUploads = 0; | ||
947 | mScriptEd->mErrorList->deleteAllItems(); | ||
948 | mScriptEd->mEditor->makePristine(); | ||
949 | |||
950 | // We need to update the asset information | ||
951 | LLTransactionID tid; | ||
952 | LLAssetID uuid; | ||
953 | tid.generate(); | ||
954 | uuid = tid.makeAssetID(gAgent.getSecureSessionID()); | ||
955 | char uuid_string[UUID_STR_LENGTH]; | ||
956 | uuid.toString(uuid_string); | ||
957 | char filename[LL_MAX_PATH]; | ||
958 | sprintf(filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); | ||
959 | FILE* fp = LLFile::fopen(filename, "wb"); | ||
960 | if(!fp) | ||
961 | { | ||
962 | llwarns << "Unable to write to " << filename << llendl; | ||
963 | LLScrollListItem* item = new LLScrollListItem(); | ||
964 | item->addColumn("Error writing to local file. Is your hard drive full?", LLFontGL::sSansSerifSmall); | ||
965 | mScriptEd->mErrorList->addItem(item); | ||
966 | return; | ||
967 | } | ||
968 | |||
969 | LLString utf8text = mScriptEd->mEditor->getText(); | ||
970 | //fprintf(fp, "%s|%s\n", LLAssetType::lookup(LLAssetType::AT_LSL_TEXT), | ||
971 | //uuid_string); | ||
972 | //fprintf(fp,"{\n%s}\n", text.c_str()); | ||
973 | fputs(utf8text.c_str(), fp); | ||
974 | fclose(fp); | ||
975 | fp = NULL; | ||
976 | |||
977 | // also write it out to the vfs for upload | ||
978 | LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_TEXT, LLVFile::APPEND); | ||
979 | S32 size = utf8text.length() + 1; | ||
980 | |||
981 | file.setMaxSize(size); | ||
982 | file.write((U8*)utf8text.c_str(), size); | ||
983 | |||
984 | LLInventoryItem *inv_item = getItem(); | ||
985 | |||
986 | // save it out to database | ||
987 | if(gAssetStorage && inv_item) | ||
988 | { | ||
989 | getWindow()->incBusyCount(); | ||
990 | mPendingUploads++; | ||
991 | LLScriptSaveInfo* info = NULL; | ||
992 | |||
993 | LLLineEditor* descEditor = LLUICtrlFactory::getLineEditorByName(this, "desc"); | ||
994 | |||
995 | info = new LLScriptSaveInfo(mItemUUID, | ||
996 | descEditor->getText(), | ||
997 | tid); | ||
998 | gAssetStorage->storeAssetData(tid, LLAssetType::AT_LSL_TEXT, &LLPreviewLSL::onSaveComplete, info); | ||
999 | } | ||
1000 | |||
1001 | char dst_filename[LL_MAX_PATH]; | ||
1002 | sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); | ||
1003 | char err_filename[LL_MAX_PATH]; | ||
1004 | sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); | ||
1005 | LLScrollListItem* item = NULL; | ||
1006 | const LLFontGL* err_font = gResMgr->getRes(LLFONT_OCRA); | ||
1007 | if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike())) | ||
1008 | { | ||
1009 | llinfos << "Compile failed!" << llendl; | ||
1010 | //char command[256]; | ||
1011 | //sprintf(command, "type %s\n", err_filename); | ||
1012 | //system(command); | ||
1013 | |||
1014 | // load the error file into the error scrolllist | ||
1015 | if(NULL != (fp = LLFile::fopen(err_filename, "r"))) | ||
1016 | { | ||
1017 | char buffer[MAX_STRING]; | ||
1018 | LLString line; | ||
1019 | while(!feof(fp)) | ||
1020 | { | ||
1021 | |||
1022 | fgets(buffer, MAX_STRING, fp); | ||
1023 | if(feof(fp)) | ||
1024 | { | ||
1025 | break; | ||
1026 | } | ||
1027 | else if(!buffer) | ||
1028 | { | ||
1029 | continue; | ||
1030 | } | ||
1031 | else | ||
1032 | { | ||
1033 | line.assign(buffer); | ||
1034 | LLString::stripNonprintable(line); | ||
1035 | item = new LLScrollListItem(); | ||
1036 | item->addColumn(line, err_font); | ||
1037 | mScriptEd->mErrorList->addItem(item); | ||
1038 | } | ||
1039 | } | ||
1040 | fclose(fp); | ||
1041 | mScriptEd->selectFirstError(); | ||
1042 | } | ||
1043 | } | ||
1044 | else | ||
1045 | { | ||
1046 | llinfos << "Compile worked!" << llendl; | ||
1047 | if(gAssetStorage) | ||
1048 | { | ||
1049 | // move the compiled file into the vfs for transport | ||
1050 | FILE* fp = LLFile::fopen(dst_filename, "rb"); | ||
1051 | LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_BYTECODE, LLVFile::APPEND); | ||
1052 | |||
1053 | fseek(fp, 0, SEEK_END); | ||
1054 | S32 size = ftell(fp); | ||
1055 | fseek(fp, 0, SEEK_SET); | ||
1056 | |||
1057 | file.setMaxSize(size); | ||
1058 | |||
1059 | const S32 buf_size = 65536; | ||
1060 | U8 copy_buf[buf_size]; | ||
1061 | while ((size = fread(copy_buf, 1, buf_size, fp))) | ||
1062 | { | ||
1063 | file.write(copy_buf, size); | ||
1064 | } | ||
1065 | fclose(fp); | ||
1066 | fp = NULL; | ||
1067 | getWindow()->incBusyCount(); | ||
1068 | mPendingUploads++; | ||
1069 | LLUUID* this_uuid = new LLUUID(mItemUUID); | ||
1070 | gAssetStorage->storeAssetData(tid, | ||
1071 | LLAssetType::AT_LSL_BYTECODE, | ||
1072 | &LLPreviewLSL::onSaveBytecodeComplete, | ||
1073 | (void**)this_uuid); | ||
1074 | } | ||
1075 | } | ||
1076 | |||
1077 | // get rid of any temp files left lying around | ||
1078 | LLFile::remove(filename); | ||
1079 | LLFile::remove(err_filename); | ||
1080 | LLFile::remove(dst_filename); | ||
1081 | } | ||
1082 | } | ||
1083 | |||
1084 | |||
1085 | // static | ||
1086 | void LLPreviewLSL::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed) | ||
1087 | { | ||
1088 | LLScriptSaveInfo* info = reinterpret_cast<LLScriptSaveInfo*>(user_data); | ||
1089 | if(0 == status) | ||
1090 | { | ||
1091 | if (info) | ||
1092 | { | ||
1093 | LLViewerInventoryItem* item; | ||
1094 | item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID); | ||
1095 | if(item) | ||
1096 | { | ||
1097 | LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); | ||
1098 | new_item->setAssetUUID(asset_uuid); | ||
1099 | new_item->setTransactionID(info->mTransactionID); | ||
1100 | new_item->updateServer(FALSE); | ||
1101 | gInventory.updateItem(new_item); | ||
1102 | gInventory.notifyObservers(); | ||
1103 | } | ||
1104 | else | ||
1105 | { | ||
1106 | llwarns << "Inventory item for script " << info->mItemUUID | ||
1107 | << " is no longer in agent inventory." << llendl | ||
1108 | } | ||
1109 | |||
1110 | // Find our window and close it if requested. | ||
1111 | LLPreviewLSL* self = (LLPreviewLSL*)LLPreview::find(info->mItemUUID); | ||
1112 | if (self) | ||
1113 | { | ||
1114 | getWindow()->decBusyCount(); | ||
1115 | self->mPendingUploads--; | ||
1116 | if (self->mPendingUploads <= 0 | ||
1117 | && self->mCloseAfterSave) | ||
1118 | { | ||
1119 | self->close(); | ||
1120 | } | ||
1121 | } | ||
1122 | } | ||
1123 | } | ||
1124 | else | ||
1125 | { | ||
1126 | llwarns << "Problem saving script: " << status << llendl; | ||
1127 | LLStringBase<char>::format_map_t args; | ||
1128 | args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status)); | ||
1129 | gViewerWindow->alertXml("SaveScriptFailReason", args); | ||
1130 | } | ||
1131 | delete info; | ||
1132 | } | ||
1133 | |||
1134 | // static | ||
1135 | void LLPreviewLSL::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed) | ||
1136 | { | ||
1137 | LLUUID* instance_uuid = (LLUUID*)user_data; | ||
1138 | LLPreviewLSL* self = NULL; | ||
1139 | if(instance_uuid) | ||
1140 | { | ||
1141 | self = LLPreviewLSL::getInstance(*instance_uuid); | ||
1142 | } | ||
1143 | if (0 == status) | ||
1144 | { | ||
1145 | if (self) | ||
1146 | { | ||
1147 | LLScrollListItem* item = new LLScrollListItem(); | ||
1148 | item->addColumn("Compile successful!", LLFontGL::sSansSerifSmall); | ||
1149 | self->mScriptEd->mErrorList->addItem(item); | ||
1150 | |||
1151 | // Find our window and close it if requested. | ||
1152 | self->getWindow()->decBusyCount(); | ||
1153 | self->mPendingUploads--; | ||
1154 | if (self->mPendingUploads <= 0 | ||
1155 | && self->mCloseAfterSave) | ||
1156 | { | ||
1157 | self->close(); | ||
1158 | } | ||
1159 | } | ||
1160 | } | ||
1161 | else | ||
1162 | { | ||
1163 | llwarns << "Problem saving LSL Bytecode (Preview)" << llendl; | ||
1164 | LLStringBase<char>::format_map_t args; | ||
1165 | args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status)); | ||
1166 | gViewerWindow->alertXml("SaveBytecodeFailReason", args); | ||
1167 | } | ||
1168 | delete instance_uuid; | ||
1169 | } | ||
1170 | |||
1171 | // static | ||
1172 | void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, | ||
1173 | void* user_data, S32 status) | ||
1174 | { | ||
1175 | lldebugs << "LLPreviewLSL::onLoadComplete: got uuid " << asset_uuid | ||
1176 | << llendl; | ||
1177 | LLUUID* item_uuid = (LLUUID*)user_data; | ||
1178 | LLPreviewLSL* preview = LLPreviewLSL::getInstance(*item_uuid); | ||
1179 | if( preview ) | ||
1180 | { | ||
1181 | if(0 == status) | ||
1182 | { | ||
1183 | LLVFile file(vfs, asset_uuid, type); | ||
1184 | S32 file_length = file.getSize(); | ||
1185 | |||
1186 | char* buffer = new char[file_length+1]; | ||
1187 | file.read((U8*)buffer, file_length); | ||
1188 | |||
1189 | // put a EOS at the end | ||
1190 | buffer[file_length] = 0; | ||
1191 | preview->mScriptEd->mEditor->setText(buffer); | ||
1192 | preview->mScriptEd->mEditor->makePristine(); | ||
1193 | delete [] buffer; | ||
1194 | LLInventoryItem* item = gInventory.getItem(*item_uuid); | ||
1195 | BOOL is_modifiable = FALSE; | ||
1196 | if(item | ||
1197 | && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), | ||
1198 | GP_OBJECT_MANIPULATE)) | ||
1199 | { | ||
1200 | is_modifiable = TRUE; | ||
1201 | } | ||
1202 | preview->mScriptEd->mEditor->setEnabled(is_modifiable); | ||
1203 | preview->mAssetStatus = PREVIEW_ASSET_LOADED; | ||
1204 | } | ||
1205 | else | ||
1206 | { | ||
1207 | if( gViewerStats ) | ||
1208 | { | ||
1209 | gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); | ||
1210 | } | ||
1211 | |||
1212 | if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || | ||
1213 | LL_ERR_FILE_EMPTY == status) | ||
1214 | { | ||
1215 | LLNotifyBox::showXml("ScriptMissing"); | ||
1216 | } | ||
1217 | else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) | ||
1218 | { | ||
1219 | LLNotifyBox::showXml("ScriptNoPermissions"); | ||
1220 | } | ||
1221 | else | ||
1222 | { | ||
1223 | LLNotifyBox::showXml("UnableToLoadScript"); | ||
1224 | } | ||
1225 | |||
1226 | preview->mAssetStatus = PREVIEW_ASSET_ERROR; | ||
1227 | llwarns << "Problem loading script: " << status << llendl; | ||
1228 | } | ||
1229 | } | ||
1230 | delete item_uuid; | ||
1231 | } | ||
1232 | |||
1233 | // static | ||
1234 | LLPreviewLSL* LLPreviewLSL::getInstance( const LLUUID& item_uuid ) | ||
1235 | { | ||
1236 | LLPreview* instance = NULL; | ||
1237 | preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid); | ||
1238 | if(found_it != LLPreview::sInstances.end()) | ||
1239 | { | ||
1240 | instance = found_it->second; | ||
1241 | } | ||
1242 | return (LLPreviewLSL*)instance; | ||
1243 | } | ||
1244 | |||
1245 | void LLPreviewLSL::reshape(S32 width, S32 height, BOOL called_from_parent) | ||
1246 | { | ||
1247 | LLPreview::reshape( width, height, called_from_parent ); | ||
1248 | |||
1249 | if( !isMinimized() ) | ||
1250 | { | ||
1251 | // So that next time you open a script it will have the same height and width | ||
1252 | // (although not the same position). | ||
1253 | gSavedSettings.setRect("PreviewScriptRect", mRect); | ||
1254 | } | ||
1255 | } | ||
1256 | |||
1257 | /// --------------------------------------------------------------------------- | ||
1258 | /// LLLiveLSLEditor | ||
1259 | /// --------------------------------------------------------------------------- | ||
1260 | |||
1261 | LLMap<LLUUID, LLLiveLSLEditor*> LLLiveLSLEditor::sInstances; | ||
1262 | |||
1263 | |||
1264 | |||
1265 | //static | ||
1266 | void* LLLiveLSLEditor::createScriptEdPanel(void* userdata) | ||
1267 | { | ||
1268 | |||
1269 | LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata; | ||
1270 | |||
1271 | self->mScriptEd = new LLScriptEdCore("script ed panel", | ||
1272 | LLRect(), | ||
1273 | HELLO_LSL, | ||
1274 | HELP_LSL, | ||
1275 | self->mViewHandle, | ||
1276 | &LLLiveLSLEditor::onLoad, | ||
1277 | &LLLiveLSLEditor::onSave, | ||
1278 | self, | ||
1279 | 0); | ||
1280 | |||
1281 | return self->mScriptEd; | ||
1282 | } | ||
1283 | |||
1284 | |||
1285 | LLLiveLSLEditor::LLLiveLSLEditor(const std::string& name, | ||
1286 | const LLRect& rect, | ||
1287 | const std::string& title, | ||
1288 | const LLUUID& object_id, | ||
1289 | const LLUUID& item_id) : | ||
1290 | LLFloater(name, rect, title, TRUE, SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT), | ||
1291 | mObjectID(object_id), | ||
1292 | mItemID(item_id), | ||
1293 | mScriptEd(NULL), | ||
1294 | mAskedForRunningInfo(FALSE), | ||
1295 | mHaveRunningInfo(FALSE), | ||
1296 | mCloseAfterSave(FALSE), | ||
1297 | mPendingUploads(0) | ||
1298 | { | ||
1299 | |||
1300 | |||
1301 | BOOL is_new = FALSE; | ||
1302 | if(mItemID.isNull()) | ||
1303 | { | ||
1304 | mItemID.generate(); | ||
1305 | is_new = TRUE; | ||
1306 | } | ||
1307 | |||
1308 | |||
1309 | LLLiveLSLEditor::sInstances.addData(mItemID ^ mObjectID, this); | ||
1310 | |||
1311 | |||
1312 | |||
1313 | LLCallbackMap::map_t factory_map; | ||
1314 | factory_map["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this); | ||
1315 | |||
1316 | moveResizeHandleToFront(); | ||
1317 | |||
1318 | gUICtrlFactory->buildFloater(this,"floater_live_lsleditor.xml", &factory_map); | ||
1319 | |||
1320 | |||
1321 | childSetCommitCallback("running", LLLiveLSLEditor::onRunningCheckboxClicked, this); | ||
1322 | childSetEnabled("running", FALSE); | ||
1323 | |||
1324 | childSetAction("Reset",&LLLiveLSLEditor::onReset,this); | ||
1325 | childSetEnabled("Reset", TRUE); | ||
1326 | |||
1327 | |||
1328 | mScriptEd->mEditor->makePristine(); | ||
1329 | loadAsset(is_new); | ||
1330 | mScriptEd->mEditor->setFocus(TRUE); | ||
1331 | |||
1332 | |||
1333 | if (!getHost()) | ||
1334 | { | ||
1335 | LLRect curRect = getRect(); | ||
1336 | translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop); | ||
1337 | } | ||
1338 | |||
1339 | |||
1340 | setTitle(title); | ||
1341 | |||
1342 | } | ||
1343 | |||
1344 | LLLiveLSLEditor::~LLLiveLSLEditor() | ||
1345 | { | ||
1346 | LLLiveLSLEditor::sInstances.removeData(mItemID ^ mObjectID); | ||
1347 | } | ||
1348 | |||
1349 | |||
1350 | void LLLiveLSLEditor::loadAsset(BOOL is_new) | ||
1351 | { | ||
1352 | //llinfos << "LLLiveLSLEditor::loadAsset()" << llendl; | ||
1353 | if(!is_new) | ||
1354 | { | ||
1355 | LLViewerObject* object = gObjectList.findObject(mObjectID); | ||
1356 | if(object) | ||
1357 | { | ||
1358 | // HACK! we "know" that mItemID refers to a LLViewerInventoryItem... | ||
1359 | LLViewerInventoryItem* item = (LLViewerInventoryItem*)object->getInventoryObject(mItemID); | ||
1360 | if(item | ||
1361 | && (gAgent.allowOperation(PERM_COPY, item->getPermissions(), | ||
1362 | GP_OBJECT_MANIPULATE) | ||
1363 | || gAgent.isGodlike())) | ||
1364 | { | ||
1365 | mItem = new LLViewerInventoryItem(item); | ||
1366 | //llinfos << "asset id " << mItem->getAssetUUID() << llendl; | ||
1367 | } | ||
1368 | |||
1369 | if(!gAgent.isGodlike() | ||
1370 | && (item | ||
1371 | && (!gAgent.allowOperation(PERM_COPY, item->getPermissions(), | ||
1372 | GP_OBJECT_MANIPULATE) | ||
1373 | || !gAgent.allowOperation(PERM_MODIFY, | ||
1374 | item->getPermissions(), GP_OBJECT_MANIPULATE)))) | ||
1375 | |||
1376 | { | ||
1377 | mScriptEd->mEditor->setText("You are not allowed to view this script."); | ||
1378 | mScriptEd->mEditor->makePristine(); | ||
1379 | mScriptEd->mEditor->setEnabled(FALSE); | ||
1380 | } | ||
1381 | else if(mItem.notNull()) | ||
1382 | { | ||
1383 | // request the text from the object | ||
1384 | LLUUID* user_data = new LLUUID(mItemID ^ mObjectID); | ||
1385 | gAssetStorage->getInvItemAsset(object->getRegion()->getHost(), | ||
1386 | gAgent.getID(), | ||
1387 | gAgent.getSessionID(), | ||
1388 | item->getPermissions().getOwner(), | ||
1389 | object->getID(), | ||
1390 | item->getUUID(), | ||
1391 | item->getAssetUUID(), | ||
1392 | item->getType(), | ||
1393 | &LLLiveLSLEditor::onLoadComplete, | ||
1394 | (void*)user_data, | ||
1395 | TRUE); | ||
1396 | LLMessageSystem* msg = gMessageSystem; | ||
1397 | msg->newMessageFast(_PREHASH_GetScriptRunning); | ||
1398 | msg->nextBlockFast(_PREHASH_Script); | ||
1399 | msg->addUUIDFast(_PREHASH_ObjectID, mObjectID); | ||
1400 | msg->addUUIDFast(_PREHASH_ItemID, mItemID); | ||
1401 | msg->sendReliable(object->getRegion()->getHost()); | ||
1402 | mAskedForRunningInfo = TRUE; | ||
1403 | } | ||
1404 | else | ||
1405 | { | ||
1406 | mScriptEd->mEditor->setText(""); | ||
1407 | mScriptEd->mEditor->makePristine(); | ||
1408 | } | ||
1409 | |||
1410 | if(item | ||
1411 | && !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), | ||
1412 | GP_OBJECT_MANIPULATE)) | ||
1413 | { | ||
1414 | mScriptEd->mEditor->setEnabled(FALSE); | ||
1415 | } | ||
1416 | // This is commented out, because we don't completely | ||
1417 | // handle script exports yet. | ||
1418 | /* | ||
1419 | // request the exports from the object | ||
1420 | gMessageSystem->newMessage("GetScriptExports"); | ||
1421 | gMessageSystem->nextBlock("ScriptBlock"); | ||
1422 | gMessageSystem->addUUID("AgentID", gAgent.getID()); | ||
1423 | U32 local_id = object->getLocalID(); | ||
1424 | gMessageSystem->addData("LocalID", &local_id); | ||
1425 | gMessageSystem->addUUID("ItemID", mItemID); | ||
1426 | LLHost host(object->getRegion()->getIP(), | ||
1427 | object->getRegion()->getPort()); | ||
1428 | gMessageSystem->sendReliable(host); | ||
1429 | */ | ||
1430 | } | ||
1431 | } | ||
1432 | else | ||
1433 | { | ||
1434 | mScriptEd->mEditor->setText(HELLO_LSL); | ||
1435 | //mScriptEd->mEditor->setText(""); | ||
1436 | //mScriptEd->mEditor->makePristine(); | ||
1437 | LLPermissions perm; | ||
1438 | perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, gAgent.getGroupID()); | ||
1439 | perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER); | ||
1440 | mItem = new LLViewerInventoryItem(mItemID, | ||
1441 | mObjectID, | ||
1442 | perm, | ||
1443 | LLUUID::null, | ||
1444 | LLAssetType::AT_LSL_TEXT, | ||
1445 | LLInventoryType::IT_LSL, | ||
1446 | DEFAULT_SCRIPT_NAME, | ||
1447 | DEFAULT_SCRIPT_DESC, | ||
1448 | LLSaleInfo::DEFAULT, | ||
1449 | LLInventoryItem::II_FLAGS_NONE, | ||
1450 | time_corrected()); | ||
1451 | } | ||
1452 | } | ||
1453 | |||
1454 | // static | ||
1455 | void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, | ||
1456 | LLAssetType::EType type, | ||
1457 | void* user_data, S32 status) | ||
1458 | { | ||
1459 | lldebugs << "LLLiveLSLEditor::onLoadComplete: got uuid " << asset_id | ||
1460 | << llendl; | ||
1461 | LLLiveLSLEditor* instance = NULL; | ||
1462 | LLUUID* xored_id = (LLUUID*)user_data; | ||
1463 | |||
1464 | if( LLLiveLSLEditor::sInstances.checkData(*xored_id) ) | ||
1465 | { | ||
1466 | if( LL_ERR_NOERR == status ) | ||
1467 | { | ||
1468 | instance = LLLiveLSLEditor::sInstances[*xored_id]; | ||
1469 | instance->loadScriptText(vfs, asset_id, type); | ||
1470 | } | ||
1471 | else | ||
1472 | { | ||
1473 | if( gViewerStats ) | ||
1474 | { | ||
1475 | gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); | ||
1476 | } | ||
1477 | |||
1478 | if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || | ||
1479 | LL_ERR_FILE_EMPTY == status) | ||
1480 | { | ||
1481 | LLNotifyBox::showXml("ScriptMissing"); | ||
1482 | } | ||
1483 | else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) | ||
1484 | { | ||
1485 | LLNotifyBox::showXml("ScriptNoPermissions"); | ||
1486 | } | ||
1487 | else | ||
1488 | { | ||
1489 | LLNotifyBox::showXml("UnableToLoadScript"); | ||
1490 | } | ||
1491 | } | ||
1492 | } | ||
1493 | |||
1494 | delete xored_id; | ||
1495 | } | ||
1496 | |||
1497 | void LLLiveLSLEditor::loadScriptText(const char* filename) | ||
1498 | { | ||
1499 | FILE* file = LLFile::fopen(filename, "rb"); | ||
1500 | if(file) | ||
1501 | { | ||
1502 | // read in the whole file | ||
1503 | fseek(file, 0L, SEEK_END); | ||
1504 | S32 file_length = ftell(file); | ||
1505 | fseek(file, 0L, SEEK_SET); | ||
1506 | char* buffer = new char[file_length+1]; | ||
1507 | fread(buffer, file_length, 1, file); | ||
1508 | fclose(file); | ||
1509 | buffer[file_length] = 0; | ||
1510 | mScriptEd->mEditor->setText(buffer); | ||
1511 | mScriptEd->mEditor->makePristine(); | ||
1512 | delete[] buffer; | ||
1513 | } | ||
1514 | else | ||
1515 | { | ||
1516 | llwarns << "Error opening " << filename << llendl; | ||
1517 | } | ||
1518 | } | ||
1519 | |||
1520 | void LLLiveLSLEditor::loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type) | ||
1521 | { | ||
1522 | LLVFile file(vfs, uuid, type); | ||
1523 | S32 file_length = file.getSize(); | ||
1524 | char *buffer = new char[file_length + 1]; | ||
1525 | file.read((U8*)buffer, file_length); | ||
1526 | |||
1527 | if (file.getLastBytesRead() != file_length || | ||
1528 | file_length <= 0) | ||
1529 | { | ||
1530 | llwarns << "Error reading " << uuid << ":" << type << llendl; | ||
1531 | } | ||
1532 | |||
1533 | buffer[file_length] = '\0'; | ||
1534 | |||
1535 | mScriptEd->mEditor->setText(buffer); | ||
1536 | mScriptEd->mEditor->makePristine(); | ||
1537 | delete[] buffer; | ||
1538 | |||
1539 | } | ||
1540 | |||
1541 | |||
1542 | void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata ) | ||
1543 | { | ||
1544 | LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata; | ||
1545 | LLViewerObject* object = gObjectList.findObject( self->mObjectID ); | ||
1546 | LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(self, "running"); | ||
1547 | BOOL running = runningCheckbox->get(); | ||
1548 | //self->mRunningCheckbox->get(); | ||
1549 | if( object ) | ||
1550 | { | ||
1551 | LLMessageSystem* msg = gMessageSystem; | ||
1552 | msg->newMessageFast(_PREHASH_SetScriptRunning); | ||
1553 | msg->nextBlockFast(_PREHASH_AgentData); | ||
1554 | msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); | ||
1555 | msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); | ||
1556 | msg->nextBlockFast(_PREHASH_Script); | ||
1557 | msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectID); | ||
1558 | msg->addUUIDFast(_PREHASH_ItemID, self->mItemID); | ||
1559 | msg->addBOOLFast(_PREHASH_Running, running); | ||
1560 | msg->sendReliable(object->getRegion()->getHost()); | ||
1561 | } | ||
1562 | else | ||
1563 | { | ||
1564 | runningCheckbox->set(!running); | ||
1565 | gViewerWindow->alertXml("CouldNotStartStopScript"); | ||
1566 | } | ||
1567 | } | ||
1568 | |||
1569 | void LLLiveLSLEditor::onReset(void *userdata) | ||
1570 | { | ||
1571 | LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata; | ||
1572 | |||
1573 | LLViewerObject* object = gObjectList.findObject( self->mObjectID ); | ||
1574 | if(object) | ||
1575 | { | ||
1576 | LLMessageSystem* msg = gMessageSystem; | ||
1577 | msg->newMessageFast(_PREHASH_ScriptReset); | ||
1578 | msg->nextBlockFast(_PREHASH_AgentData); | ||
1579 | msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); | ||
1580 | msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); | ||
1581 | msg->nextBlockFast(_PREHASH_Script); | ||
1582 | msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectID); | ||
1583 | msg->addUUIDFast(_PREHASH_ItemID, self->mItemID); | ||
1584 | msg->sendReliable(object->getRegion()->getHost()); | ||
1585 | } | ||
1586 | else | ||
1587 | { | ||
1588 | gViewerWindow->alertXml("CouldNotStartStopScript"); | ||
1589 | } | ||
1590 | } | ||
1591 | |||
1592 | void LLLiveLSLEditor::draw() | ||
1593 | { | ||
1594 | if(getVisible()) | ||
1595 | { | ||
1596 | LLViewerObject* object = gObjectList.findObject(mObjectID); | ||
1597 | LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(this, "running"); | ||
1598 | if(object && mAskedForRunningInfo && mHaveRunningInfo) | ||
1599 | { | ||
1600 | if(object->permAnyOwner()) | ||
1601 | { | ||
1602 | runningCheckbox->setLabel(ENABLED_RUNNING_CHECKBOX_LABEL); | ||
1603 | runningCheckbox->setEnabled(TRUE); | ||
1604 | } | ||
1605 | else | ||
1606 | { | ||
1607 | runningCheckbox->setLabel(DISABLED_RUNNING_CHECKBOX_LABEL); | ||
1608 | runningCheckbox->setEnabled(FALSE); | ||
1609 | // *FIX: Set it to false so that the ui is correct for | ||
1610 | // a box that is released to public. It could be | ||
1611 | // incorrect after a release/claim cycle, but will be | ||
1612 | // correct after clicking on it. | ||
1613 | runningCheckbox->set(FALSE); | ||
1614 | } | ||
1615 | } | ||
1616 | else if(!object) | ||
1617 | { | ||
1618 | // HACK: Display this information in the title bar. | ||
1619 | // Really ought to put in main window. | ||
1620 | setTitle("Script (object out of range)"); | ||
1621 | runningCheckbox->setEnabled(FALSE); | ||
1622 | // object may have fallen out of range. | ||
1623 | mHaveRunningInfo = FALSE; | ||
1624 | } | ||
1625 | LLFloater::draw(); | ||
1626 | } | ||
1627 | } | ||
1628 | |||
1629 | struct LLLiveLSLSaveData | ||
1630 | { | ||
1631 | LLLiveLSLSaveData(const LLUUID& id, const LLViewerInventoryItem* item, BOOL active); | ||
1632 | LLUUID mObjectID; | ||
1633 | LLPointer<LLViewerInventoryItem> mItem; | ||
1634 | BOOL mActive; | ||
1635 | }; | ||
1636 | |||
1637 | LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id, | ||
1638 | const LLViewerInventoryItem* item, | ||
1639 | BOOL active) : | ||
1640 | mObjectID(id), | ||
1641 | mActive(active) | ||
1642 | { | ||
1643 | llassert(item); | ||
1644 | mItem = new LLViewerInventoryItem(item); | ||
1645 | } | ||
1646 | |||
1647 | void LLLiveLSLEditor::saveIfNeeded() | ||
1648 | { | ||
1649 | llinfos << "LLLiveLSLEditor::saveIfNeeded()" << llendl; | ||
1650 | LLViewerObject* object = gObjectList.findObject(mObjectID); | ||
1651 | if(!object) | ||
1652 | { | ||
1653 | gViewerWindow->alertXml("SaveScriptFailObjectNotFound"); | ||
1654 | return; | ||
1655 | } | ||
1656 | |||
1657 | // get the latest info about it. We used to be losing the script | ||
1658 | // name on save, because the viewer object version of the item, | ||
1659 | // and the editor version would get out of synch. Here's a good | ||
1660 | // place to synch them back up. | ||
1661 | // HACK! we "know" that mItemID refers to a LLInventoryItem... | ||
1662 | LLInventoryItem* inv_item = (LLInventoryItem*)object->getInventoryObject(mItemID); | ||
1663 | if(inv_item) | ||
1664 | { | ||
1665 | mItem->copy(inv_item); | ||
1666 | } | ||
1667 | |||
1668 | // Don't need to save if we're pristine | ||
1669 | if(mScriptEd->mEditor->isPristine()) | ||
1670 | { | ||
1671 | return; | ||
1672 | } | ||
1673 | |||
1674 | mPendingUploads = 0; | ||
1675 | |||
1676 | // save the script | ||
1677 | mScriptEd->mEditor->makePristine(); | ||
1678 | mScriptEd->mErrorList->deleteAllItems(); | ||
1679 | |||
1680 | // set up the save on the local machine. | ||
1681 | mScriptEd->mEditor->makePristine(); | ||
1682 | LLTransactionID tid; | ||
1683 | LLAssetID uuid; | ||
1684 | tid.generate(); | ||
1685 | uuid = tid.makeAssetID(gAgent.getSecureSessionID()); | ||
1686 | mItem->setAssetUUID(uuid); | ||
1687 | mItem->setTransactionID(tid); | ||
1688 | |||
1689 | // write out the data, and store it in the asset database | ||
1690 | char uuid_string[UUID_STR_LENGTH]; | ||
1691 | uuid.toString(uuid_string); | ||
1692 | char filename[LL_MAX_PATH]; | ||
1693 | sprintf(filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); | ||
1694 | FILE* fp = LLFile::fopen(filename, "wb"); | ||
1695 | if(!fp) | ||
1696 | { | ||
1697 | llwarns << "Unable to write to " << filename << llendl; | ||
1698 | LLScrollListItem* item = new LLScrollListItem(); | ||
1699 | item->addColumn("Error writing to local file. Is your hard drive full?", LLFontGL::sSansSerifSmall); | ||
1700 | mScriptEd->mErrorList->addItem(item); | ||
1701 | return; | ||
1702 | } | ||
1703 | LLString utf8text = mScriptEd->mEditor->getText(); | ||
1704 | fputs(utf8text.c_str(), fp); | ||
1705 | fclose(fp); | ||
1706 | |||
1707 | LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(this, "running"); | ||
1708 | |||
1709 | // save it out to database | ||
1710 | if(gAssetStorage) | ||
1711 | { | ||
1712 | // write it out to the vfs for upload | ||
1713 | LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_TEXT, LLVFile::APPEND); | ||
1714 | S32 size = utf8text.length() + 1; | ||
1715 | |||
1716 | file.setMaxSize(size); | ||
1717 | file.write((U8*)utf8text.c_str(), size); | ||
1718 | |||
1719 | getWindow()->incBusyCount(); | ||
1720 | mPendingUploads++; | ||
1721 | LLLiveLSLSaveData* data = new LLLiveLSLSaveData(mObjectID, | ||
1722 | mItem, | ||
1723 | runningCheckbox->get()); | ||
1724 | gAssetStorage->storeAssetData(tid, LLAssetType::AT_LSL_TEXT, &onSaveTextComplete, (void*)data, FALSE); | ||
1725 | } | ||
1726 | |||
1727 | #if LL_WINDOWS | ||
1728 | // This major hack was inserted because sometimes compilation | ||
1729 | // would fail because it couldn't open this file... I decided | ||
1730 | // to make a loop until open was successful. This seems to be | ||
1731 | // a problem specific to ntfs. | ||
1732 | fp = NULL; | ||
1733 | const U32 MAX_TRIES = 20; | ||
1734 | U32 tries = MAX_TRIES; | ||
1735 | while((!fp) && --tries) | ||
1736 | { | ||
1737 | ms_sleep(17); | ||
1738 | fp = LLFile::fopen(filename, "r"); | ||
1739 | if(!fp) | ||
1740 | { | ||
1741 | llwarns << "Trying to open the source file " << filename | ||
1742 | << " again" << llendl; | ||
1743 | } | ||
1744 | else | ||
1745 | { | ||
1746 | fclose(fp); | ||
1747 | } | ||
1748 | } | ||
1749 | fp = NULL; | ||
1750 | #endif | ||
1751 | |||
1752 | char dst_filename[LL_MAX_PATH]; | ||
1753 | sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); | ||
1754 | char err_filename[LL_MAX_PATH]; | ||
1755 | sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); | ||
1756 | LLScrollListItem* item = NULL; | ||
1757 | const LLFontGL* err_font = gResMgr->getRes(LLFONT_OCRA); | ||
1758 | if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike())) | ||
1759 | { | ||
1760 | // load the error file into the error scrolllist | ||
1761 | llinfos << "Compile failed!" << llendl; | ||
1762 | if(NULL != (fp = LLFile::fopen(err_filename, "r"))) | ||
1763 | { | ||
1764 | char buffer[MAX_STRING]; | ||
1765 | LLString line; | ||
1766 | while(!feof(fp)) | ||
1767 | { | ||
1768 | |||
1769 | fgets(buffer, MAX_STRING, fp); | ||
1770 | if(feof(fp)) | ||
1771 | { | ||
1772 | break; | ||
1773 | } | ||
1774 | else if(!buffer) | ||
1775 | { | ||
1776 | continue; | ||
1777 | } | ||
1778 | else | ||
1779 | { | ||
1780 | line.assign(buffer); | ||
1781 | LLString::stripNonprintable(line); | ||
1782 | item = new LLScrollListItem(); | ||
1783 | item->addColumn(line, err_font); | ||
1784 | mScriptEd->mErrorList->addItem(item); | ||
1785 | } | ||
1786 | } | ||
1787 | fclose(fp); | ||
1788 | mScriptEd->selectFirstError(); | ||
1789 | // don't set the asset id, because we want to save the | ||
1790 | // script, even though the compile failed. | ||
1791 | //mItem->setAssetUUID(LLUUID::null); | ||
1792 | object->saveScript(mItem, FALSE, false); | ||
1793 | dialog_refresh_all(); | ||
1794 | } | ||
1795 | } | ||
1796 | else | ||
1797 | { | ||
1798 | llinfos << "Compile worked!" << llendl; | ||
1799 | mScriptEd->mErrorList->addSimpleItem("Compile successful, saving..."); | ||
1800 | if(gAssetStorage) | ||
1801 | { | ||
1802 | llinfos << "LLLiveLSLEditor::saveAsset " | ||
1803 | << mItem->getAssetUUID() << llendl; | ||
1804 | |||
1805 | // move the compiled file into the vfs for transport | ||
1806 | FILE* fp = LLFile::fopen(dst_filename, "rb"); | ||
1807 | LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_BYTECODE, LLVFile::APPEND); | ||
1808 | |||
1809 | fseek(fp, 0, SEEK_END); | ||
1810 | S32 size = ftell(fp); | ||
1811 | fseek(fp, 0, SEEK_SET); | ||
1812 | |||
1813 | file.setMaxSize(size); | ||
1814 | |||
1815 | const S32 buf_size = 65536; | ||
1816 | U8 copy_buf[buf_size]; | ||
1817 | while ((size = fread(copy_buf, 1, buf_size, fp))) | ||
1818 | { | ||
1819 | file.write(copy_buf, size); | ||
1820 | } | ||
1821 | fclose(fp); | ||
1822 | fp = NULL; | ||
1823 | |||
1824 | getWindow()->incBusyCount(); | ||
1825 | mPendingUploads++; | ||
1826 | LLLiveLSLSaveData* data = NULL; | ||
1827 | data = new LLLiveLSLSaveData(mObjectID, | ||
1828 | mItem, | ||
1829 | runningCheckbox->get()); | ||
1830 | gAssetStorage->storeAssetData(tid, | ||
1831 | LLAssetType::AT_LSL_BYTECODE, | ||
1832 | &LLLiveLSLEditor::onSaveBytecodeComplete, | ||
1833 | (void*)data); | ||
1834 | dialog_refresh_all(); | ||
1835 | } | ||
1836 | } | ||
1837 | |||
1838 | // get rid of any temp files left lying around | ||
1839 | LLFile::remove(filename); | ||
1840 | LLFile::remove(err_filename); | ||
1841 | LLFile::remove(dst_filename); | ||
1842 | |||
1843 | // If we successfully saved it, then we should be able to check/uncheck the running box! | ||
1844 | runningCheckbox->setLabel(ENABLED_RUNNING_CHECKBOX_LABEL); | ||
1845 | runningCheckbox->setEnabled(TRUE); | ||
1846 | } | ||
1847 | |||
1848 | void LLLiveLSLEditor::onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed) | ||
1849 | { | ||
1850 | LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data; | ||
1851 | |||
1852 | if (status) | ||
1853 | { | ||
1854 | llwarns << "Unable to save text for a script." << llendl; | ||
1855 | LLStringBase<char>::format_map_t args; | ||
1856 | args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status)); | ||
1857 | gViewerWindow->alertXml("CompileQueueSaveText", args); | ||
1858 | } | ||
1859 | else | ||
1860 | { | ||
1861 | LLLiveLSLEditor* self = sInstances.getIfThere(data->mItem->getUUID() ^ data->mObjectID); | ||
1862 | if (self) | ||
1863 | { | ||
1864 | self->getWindow()->decBusyCount(); | ||
1865 | self->mPendingUploads--; | ||
1866 | if (self->mPendingUploads <= 0 | ||
1867 | && self->mCloseAfterSave) | ||
1868 | { | ||
1869 | self->close(); | ||
1870 | } | ||
1871 | } | ||
1872 | } | ||
1873 | delete data; | ||
1874 | data = NULL; | ||
1875 | } | ||
1876 | |||
1877 | |||
1878 | void LLLiveLSLEditor::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed) | ||
1879 | { | ||
1880 | LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data; | ||
1881 | if(!data) | ||
1882 | return; | ||
1883 | if(0 ==status) | ||
1884 | { | ||
1885 | llinfos << "LSL Bytecode saved" << llendl; | ||
1886 | LLUUID xor_id = data->mItem->getUUID() ^ data->mObjectID; | ||
1887 | LLLiveLSLEditor* self = sInstances.getIfThere(xor_id); | ||
1888 | if(self) | ||
1889 | { | ||
1890 | // Tell the user that the compile worked. | ||
1891 | self->mScriptEd->mErrorList->addSimpleItem("Save complete."); | ||
1892 | // close the window if this completes both uploads | ||
1893 | self->getWindow()->decBusyCount(); | ||
1894 | self->mPendingUploads--; | ||
1895 | if (self->mPendingUploads <= 0 | ||
1896 | && self->mCloseAfterSave) | ||
1897 | { | ||
1898 | self->close(); | ||
1899 | } | ||
1900 | } | ||
1901 | LLViewerObject* object = gObjectList.findObject(data->mObjectID); | ||
1902 | if(object) | ||
1903 | { | ||
1904 | object->saveScript(data->mItem, data->mActive, false); | ||
1905 | dialog_refresh_all(); | ||
1906 | //LLToolDragAndDrop::dropScript(object, ids->first, | ||
1907 | // LLAssetType::AT_LSL_TEXT, FALSE); | ||
1908 | } | ||
1909 | } | ||
1910 | else | ||
1911 | { | ||
1912 | llinfos << "Problem saving LSL Bytecode (Live Editor)" << llendl; | ||
1913 | llwarns << "Unable to save a compiled script." << llendl; | ||
1914 | |||
1915 | LLStringBase<char>::format_map_t args; | ||
1916 | args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status)); | ||
1917 | gViewerWindow->alertXml("CompileQueueSaveBytecode", args); | ||
1918 | } | ||
1919 | char uuid_string[UUID_STR_LENGTH]; | ||
1920 | data->mItem->getAssetUUID().toString(uuid_string); | ||
1921 | char dst_filename[LL_MAX_PATH]; | ||
1922 | sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); | ||
1923 | LLFile::remove(dst_filename); | ||
1924 | sprintf(dst_filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str()); | ||
1925 | LLFile::remove(dst_filename); | ||
1926 | delete data; | ||
1927 | } | ||
1928 | |||
1929 | BOOL LLLiveLSLEditor::canClose() | ||
1930 | { | ||
1931 | return (mScriptEd->canClose()); | ||
1932 | } | ||
1933 | |||
1934 | // static | ||
1935 | void LLLiveLSLEditor::onLoad(void* userdata) | ||
1936 | { | ||
1937 | LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; | ||
1938 | self->loadAsset(); | ||
1939 | } | ||
1940 | |||
1941 | // static | ||
1942 | void LLLiveLSLEditor::onSave(void* userdata, BOOL close_after_save) | ||
1943 | { | ||
1944 | LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; | ||
1945 | self->mCloseAfterSave = close_after_save; | ||
1946 | self->saveIfNeeded(); | ||
1947 | } | ||
1948 | |||
1949 | // static | ||
1950 | LLLiveLSLEditor* LLLiveLSLEditor::show(const LLUUID& script_id, const LLUUID& object_id) | ||
1951 | { | ||
1952 | LLLiveLSLEditor* instance = NULL; | ||
1953 | LLUUID xored_id = script_id ^ object_id; | ||
1954 | if(LLLiveLSLEditor::sInstances.checkData(xored_id)) | ||
1955 | { | ||
1956 | // Move the existing view to the front | ||
1957 | instance = LLLiveLSLEditor::sInstances[xored_id]; | ||
1958 | instance->open(); | ||
1959 | } | ||
1960 | return instance; | ||
1961 | } | ||
1962 | |||
1963 | // static | ||
1964 | void LLLiveLSLEditor::hide(const LLUUID& script_id, const LLUUID& object_id) | ||
1965 | { | ||
1966 | LLUUID xored_id = script_id ^ object_id; | ||
1967 | if( LLLiveLSLEditor::sInstances.checkData( xored_id ) ) | ||
1968 | { | ||
1969 | LLLiveLSLEditor* instance = LLLiveLSLEditor::sInstances[xored_id]; | ||
1970 | if(instance->getParent()) | ||
1971 | { | ||
1972 | instance->getParent()->removeChild(instance); | ||
1973 | } | ||
1974 | delete instance; | ||
1975 | } | ||
1976 | } | ||
1977 | |||
1978 | // static | ||
1979 | void LLLiveLSLEditor::processScriptRunningReply(LLMessageSystem* msg, void**) | ||
1980 | { | ||
1981 | LLUUID item_id; | ||
1982 | LLUUID object_id; | ||
1983 | msg->getUUIDFast(_PREHASH_Script, _PREHASH_ObjectID, object_id); | ||
1984 | msg->getUUIDFast(_PREHASH_Script, _PREHASH_ItemID, item_id); | ||
1985 | LLUUID xored_id = item_id ^ object_id; | ||
1986 | if(LLLiveLSLEditor::sInstances.checkData(xored_id)) | ||
1987 | { | ||
1988 | LLLiveLSLEditor* instance = LLLiveLSLEditor::sInstances[xored_id]; | ||
1989 | instance->mHaveRunningInfo = TRUE; | ||
1990 | BOOL running; | ||
1991 | msg->getBOOLFast(_PREHASH_Script, _PREHASH_Running, running); | ||
1992 | LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(instance, "running"); | ||
1993 | runningCheckbox->set(running); | ||
1994 | } | ||
1995 | } | ||
1996 | |||
1997 | void LLLiveLSLEditor::reshape(S32 width, S32 height, BOOL called_from_parent) | ||
1998 | { | ||
1999 | LLFloater::reshape( width, height, called_from_parent ); | ||
2000 | |||
2001 | if( !isMinimized() ) | ||
2002 | { | ||
2003 | // So that next time you open a script it will have the same height and width | ||
2004 | // (although not the same position). | ||
2005 | gSavedSettings.setRect("PreviewScriptRect", mRect); | ||
2006 | } | ||
2007 | } | ||