diff options
author | Jacek Antonelli | 2009-09-03 01:40:48 -0500 |
---|---|---|
committer | Jacek Antonelli | 2009-09-03 01:40:48 -0500 |
commit | d6a1eca66144e88fdd59ad230f1610e75f548cd7 (patch) | |
tree | da8bde4e457d47303c0c29babf2aaec4f836a1d7 /linden | |
parent | Updated some python scripts to not use deprecated modules. (diff) | |
download | meta-impy-d6a1eca66144e88fdd59ad230f1610e75f548cd7.zip meta-impy-d6a1eca66144e88fdd59ad230f1610e75f548cd7.tar.gz meta-impy-d6a1eca66144e88fdd59ad230f1610e75f548cd7.tar.bz2 meta-impy-d6a1eca66144e88fdd59ad230f1610e75f548cd7.tar.xz |
Applied object import/export patch.
Diffstat (limited to 'linden')
-rw-r--r-- | linden/indra/newview/CMakeLists.txt | 2 | ||||
-rw-r--r-- | linden/indra/newview/app_settings/settings.xml | 16 | ||||
-rw-r--r-- | linden/indra/newview/llviewermenu.cpp | 145 | ||||
-rw-r--r-- | linden/indra/newview/llviewerobjectlist.cpp | 26 | ||||
-rw-r--r-- | linden/indra/newview/primbackup.cpp | 1101 | ||||
-rw-r--r-- | linden/indra/newview/primbackup.h | 131 | ||||
-rw-r--r-- | linden/indra/newview/skins/default/xui/en-us/menu_pie_object.xml | 6 | ||||
-rw-r--r-- | linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml | 8 |
8 files changed, 1420 insertions, 15 deletions
diff --git a/linden/indra/newview/CMakeLists.txt b/linden/indra/newview/CMakeLists.txt index 34aa29f..1f7b301 100644 --- a/linden/indra/newview/CMakeLists.txt +++ b/linden/indra/newview/CMakeLists.txt | |||
@@ -431,6 +431,7 @@ set(viewer_SOURCE_FILES | |||
431 | llxmlrpctransaction.cpp | 431 | llxmlrpctransaction.cpp |
432 | noise.cpp | 432 | noise.cpp |
433 | pipeline.cpp | 433 | pipeline.cpp |
434 | primbackup.cpp | ||
434 | ) | 435 | ) |
435 | 436 | ||
436 | set(VIEWER_BINARY_NAME "imprudence-bin" CACHE STRING | 437 | set(VIEWER_BINARY_NAME "imprudence-bin" CACHE STRING |
@@ -831,6 +832,7 @@ set(viewer_HEADER_FILES | |||
831 | macmain.h | 832 | macmain.h |
832 | noise.h | 833 | noise.h |
833 | pipeline.h | 834 | pipeline.h |
835 | primbackup.h | ||
834 | randgauss.h | 836 | randgauss.h |
835 | VertexCache.h | 837 | VertexCache.h |
836 | VorbisFramework.h | 838 | VorbisFramework.h |
diff --git a/linden/indra/newview/app_settings/settings.xml b/linden/indra/newview/app_settings/settings.xml index 5c76185..3408146 100644 --- a/linden/indra/newview/app_settings/settings.xml +++ b/linden/indra/newview/app_settings/settings.xml | |||
@@ -3116,6 +3116,22 @@ | |||
3116 | <integer>0</integer> | 3116 | <integer>0</integer> |
3117 | </array> | 3117 | </array> |
3118 | </map> | 3118 | </map> |
3119 | <key>FloaterPrimImport</key> | ||
3120 | <map> | ||
3121 | <key>Comment</key> | ||
3122 | <string>Rectangle for import/export</string> | ||
3123 | <key>Persist</key> | ||
3124 | <integer>1</integer> | ||
3125 | <key>Type</key> | ||
3126 | <string>Rect</string> | ||
3127 | <key>Value</key> | ||
3128 | <array> | ||
3129 | <integer>0</integer> | ||
3130 | <integer>25</integer> | ||
3131 | <integer>400</integer> | ||
3132 | <integer>0</integer> | ||
3133 | </array> | ||
3134 | </map> | ||
3119 | <key>FloaterRegionInfo</key> | 3135 | <key>FloaterRegionInfo</key> |
3120 | <map> | 3136 | <map> |
3121 | <key>Comment</key> | 3137 | <key>Comment</key> |
diff --git a/linden/indra/newview/llviewermenu.cpp b/linden/indra/newview/llviewermenu.cpp index 2940bdd..ba1ef9a 100644 --- a/linden/indra/newview/llviewermenu.cpp +++ b/linden/indra/newview/llviewermenu.cpp | |||
@@ -209,6 +209,7 @@ | |||
209 | #include "llwaterparammanager.h" | 209 | #include "llwaterparammanager.h" |
210 | 210 | ||
211 | #include "lltexlayer.h" | 211 | #include "lltexlayer.h" |
212 | #include "primbackup.h" | ||
212 | 213 | ||
213 | void init_client_menu(LLMenuGL* menu); | 214 | void init_client_menu(LLMenuGL* menu); |
214 | void init_server_menu(LLMenuGL* menu); | 215 | void init_server_menu(LLMenuGL* menu); |
@@ -2104,6 +2105,143 @@ class LLObjectMute : public view_listener_t | |||
2104 | } | 2105 | } |
2105 | }; | 2106 | }; |
2106 | 2107 | ||
2108 | class LLObjectEnableCopyUUID : public view_listener_t | ||
2109 | { | ||
2110 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
2111 | { | ||
2112 | LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); | ||
2113 | bool new_value = (object != NULL); | ||
2114 | |||
2115 | gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value); | ||
2116 | return true; | ||
2117 | } | ||
2118 | }; | ||
2119 | |||
2120 | class LLObjectCopyUUID : public view_listener_t | ||
2121 | { | ||
2122 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
2123 | { | ||
2124 | LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); | ||
2125 | if (!object) return true; | ||
2126 | |||
2127 | LLUUID id = object->getID(); | ||
2128 | |||
2129 | char buffer[UUID_STR_LENGTH]; | ||
2130 | id.toString(buffer); | ||
2131 | |||
2132 | |||
2133 | gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer)); | ||
2134 | |||
2135 | LLSelectMgr::getInstance()->deselectAll(); | ||
2136 | return true; | ||
2137 | } | ||
2138 | }; | ||
2139 | |||
2140 | |||
2141 | class LLObjectEnableExport : public view_listener_t | ||
2142 | { | ||
2143 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
2144 | { | ||
2145 | LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); | ||
2146 | bool new_value = (object != NULL); | ||
2147 | if (new_value) | ||
2148 | { | ||
2149 | LLVOAvatar* avatar = find_avatar_from_object(object); | ||
2150 | new_value = (avatar == NULL); | ||
2151 | } | ||
2152 | if(new_value) | ||
2153 | { | ||
2154 | |||
2155 | struct ff : public LLSelectedNodeFunctor | ||
2156 | { | ||
2157 | ff(const LLSD& data) : LLSelectedNodeFunctor() | ||
2158 | ,userdata(data) | ||
2159 | { | ||
2160 | |||
2161 | } | ||
2162 | const LLSD& userdata; | ||
2163 | virtual bool apply(LLSelectNode* node) | ||
2164 | { | ||
2165 | if(gAgent.getID()!=node->mPermissions->getCreator()) | ||
2166 | { | ||
2167 | llwarns<<"Incorrect permission to export"<<llendl; | ||
2168 | return false; | ||
2169 | } | ||
2170 | return true; | ||
2171 | } | ||
2172 | }; | ||
2173 | |||
2174 | #ifdef LL_GRID_PERMISSIONS | ||
2175 | |||
2176 | ff * the_ff=new ff(userdata); | ||
2177 | if(LLSelectMgr::getInstance()->getSelection()->applyToNodes(the_ff,false)) | ||
2178 | { | ||
2179 | gMenuHolder->findControl(userdata["control"].asString())->setValue(true); | ||
2180 | } | ||
2181 | else | ||
2182 | { | ||
2183 | gMenuHolder->findControl(userdata["control"].asString())->setValue(false); | ||
2184 | } | ||
2185 | return true; | ||
2186 | } | ||
2187 | |||
2188 | gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value); | ||
2189 | return true; | ||
2190 | #else | ||
2191 | } | ||
2192 | gMenuHolder->findControl(userdata["control"].asString())->setValue(true); | ||
2193 | return true; | ||
2194 | #endif | ||
2195 | |||
2196 | } | ||
2197 | }; | ||
2198 | |||
2199 | class LLObjectExport : public view_listener_t | ||
2200 | { | ||
2201 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
2202 | { | ||
2203 | LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); | ||
2204 | if (!object) return true; | ||
2205 | |||
2206 | LLVOAvatar* avatar = find_avatar_from_object(object); | ||
2207 | |||
2208 | if (!avatar) | ||
2209 | { | ||
2210 | primbackup::getInstance()->pre_export_object(); | ||
2211 | } | ||
2212 | |||
2213 | return true; | ||
2214 | } | ||
2215 | }; | ||
2216 | |||
2217 | |||
2218 | class LLObjectEnableImport : public view_listener_t | ||
2219 | { | ||
2220 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
2221 | { | ||
2222 | gMenuHolder->findControl(userdata["control"].asString())->setValue(TRUE); | ||
2223 | return true; | ||
2224 | } | ||
2225 | }; | ||
2226 | |||
2227 | class LLObjectImport : public view_listener_t | ||
2228 | { | ||
2229 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
2230 | { | ||
2231 | primbackup::getInstance()->import_object(FALSE); | ||
2232 | return true; | ||
2233 | } | ||
2234 | }; | ||
2235 | |||
2236 | class LLObjectImportUpload : public view_listener_t | ||
2237 | { | ||
2238 | bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) | ||
2239 | { | ||
2240 | primbackup::getInstance()->import_object(TRUE); | ||
2241 | return true; | ||
2242 | } | ||
2243 | }; | ||
2244 | |||
2107 | bool handle_go_to() | 2245 | bool handle_go_to() |
2108 | { | 2246 | { |
2109 | // JAMESDEBUG try simulator autopilot | 2247 | // JAMESDEBUG try simulator autopilot |
@@ -9652,6 +9790,10 @@ void initialize_menus() | |||
9652 | addMenu(new LLObjectBuy(), "Object.Buy"); | 9790 | addMenu(new LLObjectBuy(), "Object.Buy"); |
9653 | addMenu(new LLObjectEdit(), "Object.Edit"); | 9791 | addMenu(new LLObjectEdit(), "Object.Edit"); |
9654 | addMenu(new LLObjectInspect(), "Object.Inspect"); | 9792 | addMenu(new LLObjectInspect(), "Object.Inspect"); |
9793 | addMenu(new LLObjectCopyUUID(), "Object.CopyUUID"); | ||
9794 | addMenu(new LLObjectExport(), "Object.Export"); | ||
9795 | addMenu(new LLObjectImport(), "Object.Import"); | ||
9796 | addMenu(new LLObjectImportUpload(), "Object.ImportUpload"); | ||
9655 | 9797 | ||
9656 | addMenu(new LLObjectEnableOpen(), "Object.EnableOpen"); | 9798 | addMenu(new LLObjectEnableOpen(), "Object.EnableOpen"); |
9657 | addMenu(new LLObjectEnableTouch(), "Object.EnableTouch"); | 9799 | addMenu(new LLObjectEnableTouch(), "Object.EnableTouch"); |
@@ -9662,6 +9804,9 @@ void initialize_menus() | |||
9662 | addMenu(new LLObjectEnableReportAbuse(), "Object.EnableReportAbuse"); | 9804 | addMenu(new LLObjectEnableReportAbuse(), "Object.EnableReportAbuse"); |
9663 | addMenu(new LLObjectEnableMute(), "Object.EnableMute"); | 9805 | addMenu(new LLObjectEnableMute(), "Object.EnableMute"); |
9664 | addMenu(new LLObjectEnableBuy(), "Object.EnableBuy"); | 9806 | addMenu(new LLObjectEnableBuy(), "Object.EnableBuy"); |
9807 | addMenu(new LLObjectEnableCopyUUID(), "Object.EnableCopyUUID"); | ||
9808 | addMenu(new LLObjectEnableExport(), "Object.EnableExport"); | ||
9809 | addMenu(new LLObjectEnableImport(), "Object.EnableImport"); | ||
9665 | 9810 | ||
9666 | /*addMenu(new LLObjectVisibleTouch(), "Object.VisibleTouch"); | 9811 | /*addMenu(new LLObjectVisibleTouch(), "Object.VisibleTouch"); |
9667 | addMenu(new LLObjectVisibleCustomTouch(), "Object.VisibleCustomTouch"); | 9812 | addMenu(new LLObjectVisibleCustomTouch(), "Object.VisibleCustomTouch"); |
diff --git a/linden/indra/newview/llviewerobjectlist.cpp b/linden/indra/newview/llviewerobjectlist.cpp index 9ccbf99..78c1730 100644 --- a/linden/indra/newview/llviewerobjectlist.cpp +++ b/linden/indra/newview/llviewerobjectlist.cpp | |||
@@ -74,6 +74,8 @@ | |||
74 | 74 | ||
75 | #include "llappviewer.h" | 75 | #include "llappviewer.h" |
76 | 76 | ||
77 | #include "primbackup.h" | ||
78 | |||
77 | extern F32 gMinObjectDistance; | 79 | extern F32 gMinObjectDistance; |
78 | extern BOOL gAnimateTextures; | 80 | extern BOOL gAnimateTextures; |
79 | 81 | ||
@@ -163,8 +165,6 @@ U64 LLViewerObjectList::getIndex(const U32 local_id, | |||
163 | 165 | ||
164 | BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object) | 166 | BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object) |
165 | { | 167 | { |
166 | if(object.getRegion()) | ||
167 | { | ||
168 | U32 local_id = object.mLocalID; | 168 | U32 local_id = object.mLocalID; |
169 | LLHost region_host = object.getRegion()->getHost(); | 169 | LLHost region_host = object.getRegion()->getHost(); |
170 | U32 ip = region_host.getAddress(); | 170 | U32 ip = region_host.getAddress(); |
@@ -176,9 +176,6 @@ BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object) | |||
176 | return sIndexAndLocalIDToUUID.erase(indexid) > 0 ? TRUE : FALSE; | 176 | return sIndexAndLocalIDToUUID.erase(indexid) > 0 ? TRUE : FALSE; |
177 | } | 177 | } |
178 | 178 | ||
179 | return FALSE ; | ||
180 | } | ||
181 | |||
182 | void LLViewerObjectList::setUUIDAndLocal(const LLUUID &id, | 179 | void LLViewerObjectList::setUUIDAndLocal(const LLUUID &id, |
183 | const U32 local_id, | 180 | const U32 local_id, |
184 | const U32 ip, | 181 | const U32 ip, |
@@ -222,6 +219,11 @@ void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp, | |||
222 | 219 | ||
223 | updateActive(objectp); | 220 | updateActive(objectp); |
224 | 221 | ||
222 | if(!just_created) | ||
223 | primbackup::getInstance()->prim_update(objectp); | ||
224 | |||
225 | |||
226 | |||
225 | if (just_created) | 227 | if (just_created) |
226 | { | 228 | { |
227 | gPipeline.addObject(objectp); | 229 | gPipeline.addObject(objectp); |
@@ -251,6 +253,9 @@ void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp, | |||
251 | objectp->mCreateSelected = false; | 253 | objectp->mCreateSelected = false; |
252 | gViewerWindow->getWindow()->decBusyCount(); | 254 | gViewerWindow->getWindow()->decBusyCount(); |
253 | gViewerWindow->getWindow()->setCursor( UI_CURSOR_ARROW ); | 255 | gViewerWindow->getWindow()->setCursor( UI_CURSOR_ARROW ); |
256 | |||
257 | primbackup::getInstance()->newprim(objectp); | ||
258 | |||
254 | } | 259 | } |
255 | } | 260 | } |
256 | 261 | ||
@@ -827,17 +832,10 @@ void LLViewerObjectList::removeDrawable(LLDrawable* drawablep) | |||
827 | 832 | ||
828 | for (S32 i = 0; i < drawablep->getNumFaces(); i++) | 833 | for (S32 i = 0; i < drawablep->getNumFaces(); i++) |
829 | { | 834 | { |
830 | LLFace* facep = drawablep->getFace(i) ; | 835 | LLViewerObject* objectp = drawablep->getFace(i)->getViewerObject(); |
831 | if(facep) | ||
832 | { | ||
833 | LLViewerObject* objectp = facep->getViewerObject(); | ||
834 | if(objectp) | ||
835 | { | ||
836 | mSelectPickList.erase(objectp); | 836 | mSelectPickList.erase(objectp); |
837 | } | 837 | } |
838 | } | 838 | } |
839 | } | ||
840 | } | ||
841 | 839 | ||
842 | BOOL LLViewerObjectList::killObject(LLViewerObject *objectp) | 840 | BOOL LLViewerObjectList::killObject(LLViewerObject *objectp) |
843 | { | 841 | { |
@@ -914,7 +912,7 @@ void LLViewerObjectList::killAllObjects() | |||
914 | if (!mMapObjects.empty()) | 912 | if (!mMapObjects.empty()) |
915 | { | 913 | { |
916 | llwarns << "Some objects still on map object list!" << llendl; | 914 | llwarns << "Some objects still on map object list!" << llendl; |
917 | mMapObjects.clear(); | 915 | mActiveObjects.clear(); |
918 | } | 916 | } |
919 | } | 917 | } |
920 | 918 | ||
diff --git a/linden/indra/newview/primbackup.cpp b/linden/indra/newview/primbackup.cpp new file mode 100644 index 0000000..cd21047 --- /dev/null +++ b/linden/indra/newview/primbackup.cpp | |||
@@ -0,0 +1,1101 @@ | |||
1 | |||
2 | #include "llviewerprecompiledheaders.h" | ||
3 | #include "llviewermenu.h" | ||
4 | |||
5 | |||
6 | // system library includes | ||
7 | #include <iostream> | ||
8 | #include <fstream> | ||
9 | #include <sstream> | ||
10 | |||
11 | // linden library includes | ||
12 | #include "llfilepicker.h" | ||
13 | #include "indra_constants.h" | ||
14 | #include "llsdserialize.h" | ||
15 | #include "llsdutil.h" | ||
16 | |||
17 | #include "llcallbacklist.h" | ||
18 | |||
19 | // newview includes | ||
20 | #include "llagent.h" | ||
21 | #include "llselectmgr.h" | ||
22 | #include "lltoolplacer.h" | ||
23 | |||
24 | #include "lltexturecache.h" | ||
25 | |||
26 | #include "llnotify.h" | ||
27 | |||
28 | #include "llapr.h" | ||
29 | #include "lldir.h" | ||
30 | #include "llimage.h" | ||
31 | #include "lllfsthread.h" | ||
32 | #include "llviewercontrol.h" | ||
33 | #include "llassetuploadresponders.h" | ||
34 | #include "lleconomy.h" | ||
35 | #include "llhttpclient.h" | ||
36 | #include "lluploaddialog.h" | ||
37 | #include "lldir.h" | ||
38 | #include "llinventorymodel.h" // gInventory | ||
39 | #include "llviewercontrol.h" // gSavedSettings | ||
40 | #include "llviewermenu.h" // gMenuHolder | ||
41 | #include "llagent.h" | ||
42 | #include "llfilepicker.h" | ||
43 | #include "llfloateranimpreview.h" | ||
44 | #include "llfloaterbuycurrency.h" | ||
45 | #include "llfloaterimagepreview.h" | ||
46 | #include "llfloaternamedesc.h" | ||
47 | #include "llfloatersnapshot.h" | ||
48 | #include "llinventorymodel.h" // gInventory | ||
49 | #include "llresourcedata.h" | ||
50 | #include "llstatusbar.h" | ||
51 | #include "llviewercontrol.h" // gSavedSettings | ||
52 | #include "llviewerimagelist.h" | ||
53 | #include "lluictrlfactory.h" | ||
54 | #include "llviewermenu.h" // gMenuHolder | ||
55 | #include "llviewerregion.h" | ||
56 | #include "llviewerstats.h" | ||
57 | #include "llviewerwindow.h" | ||
58 | #include "llappviewer.h" | ||
59 | #include "lluploaddialog.h" | ||
60 | // Included to allow LLTextureCache::purgeTextures() to pause watchdog timeout | ||
61 | #include "llappviewer.h" | ||
62 | #include "lltransactiontypes.h" | ||
63 | |||
64 | #include "primbackup.h" | ||
65 | |||
66 | #include "llviewerobjectlist.h" | ||
67 | |||
68 | primbackup* primbackup::sInstance = 0; | ||
69 | |||
70 | class importResponder: public LLNewAgentInventoryResponder | ||
71 | { | ||
72 | public: | ||
73 | |||
74 | importResponder(const LLSD& post_data, | ||
75 | const LLUUID& vfile_id, | ||
76 | LLAssetType::EType asset_type) | ||
77 | : LLNewAgentInventoryResponder(post_data, vfile_id, asset_type) | ||
78 | { | ||
79 | } | ||
80 | |||
81 | |||
82 | //virtual | ||
83 | virtual void uploadComplete(const LLSD& content) | ||
84 | { | ||
85 | lldebugs << "LLNewAgentInventoryResponder::result from capabilities" << llendl; | ||
86 | |||
87 | LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString()); | ||
88 | LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString()); | ||
89 | |||
90 | // Update L$ and ownership credit information | ||
91 | // since it probably changed on the server | ||
92 | if (asset_type == LLAssetType::AT_TEXTURE || | ||
93 | asset_type == LLAssetType::AT_SOUND || | ||
94 | asset_type == LLAssetType::AT_ANIMATION) | ||
95 | { | ||
96 | gMessageSystem->newMessageFast(_PREHASH_MoneyBalanceRequest); | ||
97 | gMessageSystem->nextBlockFast(_PREHASH_AgentData); | ||
98 | gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); | ||
99 | gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); | ||
100 | gMessageSystem->nextBlockFast(_PREHASH_MoneyData); | ||
101 | gMessageSystem->addUUIDFast(_PREHASH_TransactionID, LLUUID::null ); | ||
102 | gAgent.sendReliableMessage(); | ||
103 | |||
104 | // LLStringUtil::format_map_t args; | ||
105 | // args["[AMOUNT]"] = llformat("%d",LLGlobalEconomy::Singleton::getInstance()->getPriceUpload()); | ||
106 | // LLNotifyBox::showXml("UploadPayment", args); | ||
107 | } | ||
108 | |||
109 | // Actually add the upload to viewer inventory | ||
110 | llinfos << "Adding " << content["new_inventory_item"].asUUID() << " " | ||
111 | << content["new_asset"].asUUID() << " to inventory." << llendl; | ||
112 | if(mPostData["folder_id"].asUUID().notNull()) | ||
113 | { | ||
114 | LLPermissions perm; | ||
115 | U32 next_owner_perm; | ||
116 | perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); | ||
117 | if (mPostData["inventory_type"].asString() == "snapshot") | ||
118 | { | ||
119 | next_owner_perm = PERM_ALL; | ||
120 | } | ||
121 | else | ||
122 | { | ||
123 | next_owner_perm = PERM_MOVE | PERM_TRANSFER; | ||
124 | } | ||
125 | perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, next_owner_perm); | ||
126 | S32 creation_date_now = time_corrected(); | ||
127 | LLPointer<LLViewerInventoryItem> item | ||
128 | = new LLViewerInventoryItem(content["new_inventory_item"].asUUID(), | ||
129 | mPostData["folder_id"].asUUID(), | ||
130 | perm, | ||
131 | content["new_asset"].asUUID(), | ||
132 | asset_type, | ||
133 | inventory_type, | ||
134 | mPostData["name"].asString(), | ||
135 | mPostData["description"].asString(), | ||
136 | LLSaleInfo::DEFAULT, | ||
137 | LLInventoryItem::II_FLAGS_NONE, | ||
138 | creation_date_now); | ||
139 | gInventory.updateItem(item); | ||
140 | gInventory.notifyObservers(); | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | llwarns << "Can't find a folder to put it in" << llendl; | ||
145 | } | ||
146 | |||
147 | // remove the "Uploading..." message | ||
148 | LLUploadDialog::modalUploadFinished(); | ||
149 | |||
150 | primbackup::getInstance()->update_map(content["new_asset"].asUUID()); | ||
151 | primbackup::getInstance()->upload_next_asset(); | ||
152 | |||
153 | } | ||
154 | |||
155 | }; | ||
156 | |||
157 | |||
158 | |||
159 | class CacheReadResponder : public LLTextureCache::ReadResponder | ||
160 | { | ||
161 | public: | ||
162 | CacheReadResponder(const LLUUID& id, LLImageFormatted* image) | ||
163 | : mFormattedImage(image), mID(id) | ||
164 | { | ||
165 | setImage(image); | ||
166 | } | ||
167 | void setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal) | ||
168 | { | ||
169 | if(imageformat==IMG_CODEC_TGA && mFormattedImage->getCodec()==IMG_CODEC_J2C) | ||
170 | { | ||
171 | llwarns<<"Bleh its a tga not saving"<<llendl; | ||
172 | mFormattedImage=NULL; | ||
173 | mImageSize=0; | ||
174 | return; | ||
175 | } | ||
176 | |||
177 | if (mFormattedImage.notNull()) | ||
178 | { | ||
179 | llassert_always(mFormattedImage->getCodec() == imageformat); | ||
180 | mFormattedImage->appendData(data, datasize); | ||
181 | } | ||
182 | else | ||
183 | { | ||
184 | mFormattedImage = LLImageFormatted::createFromType(imageformat); | ||
185 | mFormattedImage->setData(data,datasize); | ||
186 | } | ||
187 | mImageSize = imagesize; | ||
188 | mImageLocal = imagelocal; | ||
189 | } | ||
190 | |||
191 | virtual void completed(bool success) | ||
192 | { | ||
193 | if(success && (mFormattedImage.notNull()) && mImageSize>0) | ||
194 | { | ||
195 | |||
196 | llinfos << "SUCCESS getting texture "<<mID<< llendl; | ||
197 | |||
198 | std::string name; | ||
199 | mID.toString(name); | ||
200 | llinfos << "Saving to "<<(primbackup::getInstance()->getfolder()+"//"+name)<<llendl; | ||
201 | if(!mFormattedImage->save(primbackup::getInstance()->getfolder()+"//"+name)) | ||
202 | { | ||
203 | llinfos << "FAIL saving texture "<<mID<< llendl; | ||
204 | } | ||
205 | |||
206 | } | ||
207 | else | ||
208 | { | ||
209 | if(!success) | ||
210 | llwarns << "FAIL NOT SUCCESSFUL getting texture "<<mID<< llendl; | ||
211 | if(mFormattedImage.isNull()) | ||
212 | llwarns << "FAIL image is NULL "<<mID<< llendl; | ||
213 | } | ||
214 | |||
215 | primbackup::getInstance()->m_nexttextureready=true; | ||
216 | //JUST SAY NO TO APR DEADLOCKING | ||
217 | //primbackup::getInstance()->export_next_texture(); | ||
218 | } | ||
219 | private: | ||
220 | LLPointer<LLImageFormatted> mFormattedImage; | ||
221 | LLUUID mID; | ||
222 | }; | ||
223 | |||
224 | |||
225 | |||
226 | primbackup::primbackup() | ||
227 | : LLFloater( std::string("Prim Import Floater") ) | ||
228 | { | ||
229 | LLUICtrlFactory::getInstance()->buildFloater( this, "floater_prim_import.xml" ); | ||
230 | |||
231 | // reposition floater from saved settings | ||
232 | LLRect rect = gSavedSettings.getRect( "FloaterPrimImport" ); | ||
233 | reshape( rect.getWidth(), rect.getHeight(), FALSE ); | ||
234 | setRect( rect ); | ||
235 | |||
236 | running=false; | ||
237 | textures.clear(); | ||
238 | assetmap.clear(); | ||
239 | current_asset=LLUUID::null; | ||
240 | m_retexture=false; | ||
241 | close(); | ||
242 | } | ||
243 | |||
244 | |||
245 | //////////////////////////////////////////////////////////////////////////////// | ||
246 | // | ||
247 | primbackup* primbackup::getInstance() | ||
248 | { | ||
249 | if ( ! sInstance ) | ||
250 | sInstance = new primbackup(); | ||
251 | |||
252 | return sInstance; | ||
253 | } | ||
254 | |||
255 | primbackup::~primbackup() | ||
256 | { | ||
257 | // save position of floater | ||
258 | gSavedSettings.setRect( "FloaterPrimImport", getRect() ); | ||
259 | sInstance = 0; | ||
260 | } | ||
261 | |||
262 | void primbackup::draw() | ||
263 | { | ||
264 | LLFloater::draw(); | ||
265 | } | ||
266 | |||
267 | void primbackup::show() | ||
268 | { | ||
269 | // set the title | ||
270 | setTitle( "stuff" ); | ||
271 | m_curobject=1; | ||
272 | m_curprim=0; | ||
273 | m_objects=0; | ||
274 | m_prims=0; | ||
275 | m_textures=0; | ||
276 | m_curtexture=0; | ||
277 | rezcount=0; | ||
278 | |||
279 | // make floater appear | ||
280 | setVisibleAndFrontmost(); | ||
281 | } | ||
282 | |||
283 | |||
284 | void primbackup::onClose( bool app_quitting ) | ||
285 | { | ||
286 | setVisible( false ); | ||
287 | // HACK for fast XML iteration replace with: | ||
288 | // destroy(); | ||
289 | } | ||
290 | |||
291 | void primbackup::updateexportnumbers() | ||
292 | { | ||
293 | |||
294 | std::stringstream sstr; | ||
295 | LLUICtrl * ctrl=this->getChild<LLUICtrl>("name_label"); | ||
296 | |||
297 | sstr<<"Export Progress \n"; | ||
298 | |||
299 | sstr << "Remaining Textures "<<textures.size()<<"\n"; | ||
300 | ctrl->setValue(LLSD("Text")=sstr.str()); | ||
301 | |||
302 | } | ||
303 | |||
304 | |||
305 | void primbackup::updateimportnumbers() | ||
306 | { | ||
307 | std::stringstream sstr; | ||
308 | LLUICtrl * ctrl=this->getChild<LLUICtrl>("name_label"); | ||
309 | |||
310 | if(m_retexture) | ||
311 | { | ||
312 | sstr << " Textures uploads remaining : "<<textures.size()<<"\n"; | ||
313 | ctrl->setValue(LLSD("Text")=sstr.str()); | ||
314 | } | ||
315 | else | ||
316 | { | ||
317 | sstr << " Textures uploads N/A \n"; | ||
318 | ctrl->setValue(LLSD("Text")=sstr.str()); | ||
319 | } | ||
320 | sstr << " Objects "<<this->m_curobject<<"/"<<this->m_objects<<"\n"; | ||
321 | ctrl->setValue(LLSD("Text")=sstr.str()); | ||
322 | |||
323 | sstr << " Rez "<<this->rezcount<<"/"<<this->m_prims; | ||
324 | ctrl->setValue(LLSD("Text")=sstr.str()); | ||
325 | |||
326 | sstr << " Build "<<this->m_curprim<<"/"<<this->m_prims; | ||
327 | ctrl->setValue(LLSD("Text")=sstr.str()); | ||
328 | |||
329 | |||
330 | } | ||
331 | |||
332 | void primbackup::pre_export_object() | ||
333 | { | ||
334 | textures.clear(); | ||
335 | llsd.clear(); | ||
336 | this_group.clear(); | ||
337 | |||
338 | // Open the file save dialog | ||
339 | LLFilePicker& file_picker = LLFilePicker::instance(); | ||
340 | if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_XML ) ) | ||
341 | { | ||
342 | // User canceled save. | ||
343 | return; | ||
344 | } | ||
345 | |||
346 | file_name = file_picker.getCurFile(); | ||
347 | folder = gDirUtilp->getDirName(file_name); | ||
348 | |||
349 | export_state=EXPORT_INIT; | ||
350 | gIdleCallbacks.addFunction(exportworker, NULL); | ||
351 | } | ||
352 | |||
353 | void primbackup::exportworker(void *userdata) | ||
354 | { | ||
355 | primbackup::getInstance()->updateexportnumbers(); | ||
356 | |||
357 | switch(primbackup::getInstance()->export_state) | ||
358 | { | ||
359 | case EXPORT_INIT: | ||
360 | { | ||
361 | primbackup::getInstance()->show(); | ||
362 | LLSelectMgr::getInstance()->getSelection()->ref(); | ||
363 | |||
364 | struct ff : public LLSelectedNodeFunctor | ||
365 | { | ||
366 | virtual bool apply(LLSelectNode* node) | ||
367 | { | ||
368 | if(gAgent.getID()!=node->mPermissions->getCreator()) | ||
369 | { | ||
370 | #ifdef LL_GRID_PERMISSIONS | ||
371 | return false; | ||
372 | #else | ||
373 | return true; | ||
374 | #endif | ||
375 | } | ||
376 | return true; | ||
377 | } | ||
378 | } func; | ||
379 | |||
380 | if(LLSelectMgr::getInstance()->getSelection()->applyToNodes(&func,false)) | ||
381 | primbackup::getInstance()->export_state=EXPORT_STRUCTURE; | ||
382 | else | ||
383 | { | ||
384 | llwarns<<"Incorrect permission to export"<<llendl; | ||
385 | primbackup::getInstance()->export_state=EXPORT_DONE; | ||
386 | primbackup::getInstance()->close(); | ||
387 | gIdleCallbacks.deleteFunction(exportworker); | ||
388 | LLSelectMgr::getInstance()->getSelection()->unref(); | ||
389 | |||
390 | } | ||
391 | break; | ||
392 | } | ||
393 | |||
394 | break; | ||
395 | case EXPORT_STRUCTURE: | ||
396 | { | ||
397 | struct ff : public LLSelectedObjectFunctor | ||
398 | { | ||
399 | virtual bool apply(LLViewerObject* object) | ||
400 | { | ||
401 | object->boostTexturePriority(TRUE); | ||
402 | LLViewerObject::child_list_t children = object->getChildren(); | ||
403 | children.push_front(object); //push root onto list | ||
404 | LLSD prim_llsd=primbackup::getInstance()->prims_to_llsd(children); | ||
405 | LLSD stuff; | ||
406 | stuff["root_position"] = object->getPosition().getValue(); | ||
407 | stuff["root_rotation"] = ll_sd_from_quaternion(object->getRotation()); | ||
408 | stuff["group_body"] = prim_llsd; | ||
409 | primbackup::getInstance()->llsd["data"].append(stuff); | ||
410 | return true; | ||
411 | } | ||
412 | } func; | ||
413 | |||
414 | primbackup::getInstance()->export_state=EXPORT_LLSD; | ||
415 | LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func,false); | ||
416 | LLSelectMgr::getInstance()->getSelection()->unref(); | ||
417 | |||
418 | break; | ||
419 | } | ||
420 | case EXPORT_TEXTURES: | ||
421 | if(primbackup::getInstance()->m_nexttextureready==false) | ||
422 | return; | ||
423 | |||
424 | //Ok we got work to do | ||
425 | primbackup::getInstance()->m_nexttextureready=false; | ||
426 | |||
427 | if(primbackup::getInstance()->textures.empty()) | ||
428 | { | ||
429 | primbackup::getInstance()->export_state=EXPORT_DONE; | ||
430 | return; | ||
431 | } | ||
432 | |||
433 | primbackup::getInstance()->export_next_texture(); | ||
434 | break; | ||
435 | |||
436 | case EXPORT_LLSD: | ||
437 | { | ||
438 | // Create a file stream and write to it | ||
439 | llofstream export_file(primbackup::getInstance()->file_name); | ||
440 | LLSDSerialize::toPrettyXML(primbackup::getInstance()->llsd, export_file); | ||
441 | export_file.close(); | ||
442 | primbackup::getInstance()->m_nexttextureready=true; | ||
443 | primbackup::getInstance()->export_state=EXPORT_TEXTURES; | ||
444 | } | ||
445 | break; | ||
446 | case EXPORT_DONE: | ||
447 | llinfos<<"Backup complete"<<llendl | ||
448 | gIdleCallbacks.deleteFunction(exportworker); | ||
449 | primbackup::getInstance()->close(); | ||
450 | break; | ||
451 | } | ||
452 | } | ||
453 | |||
454 | LLSD primbackup::prims_to_llsd(LLViewerObject::child_list_t child_list) | ||
455 | { | ||
456 | |||
457 | LLViewerObject* object; | ||
458 | LLSD llsd; | ||
459 | |||
460 | char localid[16]; | ||
461 | |||
462 | for (LLViewerObject::child_list_t::iterator i = child_list.begin(); i != child_list.end(); ++i) | ||
463 | { | ||
464 | object=(*i); | ||
465 | LLUUID id = object->getID(); | ||
466 | |||
467 | llinfos << "Exporting prim " << object->getID().asString() << llendl; | ||
468 | |||
469 | // Create an LLSD object that represents this prim. It will be injected in to the overall LLSD | ||
470 | // tree structure | ||
471 | LLSD prim_llsd; | ||
472 | |||
473 | if (!object->isRoot()) | ||
474 | { | ||
475 | |||
476 | // Parent id | ||
477 | snprintf(localid, sizeof(localid), "%u", object->getSubParent()->getLocalID()); | ||
478 | prim_llsd["parent"] = localid; | ||
479 | } | ||
480 | |||
481 | // Transforms | ||
482 | prim_llsd["position"] = object->getPosition().getValue(); | ||
483 | prim_llsd["scale"] = object->getScale().getValue(); | ||
484 | prim_llsd["rotation"] = ll_sd_from_quaternion(object->getRotation()); | ||
485 | |||
486 | // Flags | ||
487 | prim_llsd["shadows"] = object->flagCastShadows(); | ||
488 | prim_llsd["phantom"] = object->flagPhantom(); | ||
489 | prim_llsd["physical"] = (BOOL)(object->mFlags & FLAGS_USE_PHYSICS); | ||
490 | |||
491 | // Volume params | ||
492 | LLVolumeParams params = object->getVolume()->getParams(); | ||
493 | prim_llsd["volume"] = params.asLLSD(); | ||
494 | |||
495 | // Extra paramsb6fab961-af18-77f8-cf08-f021377a7244 | ||
496 | if (object->isFlexible()) | ||
497 | { | ||
498 | // Flexible | ||
499 | LLFlexibleObjectData* flex = (LLFlexibleObjectData*)object->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE); | ||
500 | prim_llsd["flexible"] = flex->asLLSD(); | ||
501 | } | ||
502 | if (object->getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT)) | ||
503 | { | ||
504 | // Light | ||
505 | LLLightParams* light = (LLLightParams*)object->getParameterEntry(LLNetworkData::PARAMS_LIGHT); | ||
506 | prim_llsd["light"] = light->asLLSD(); | ||
507 | } | ||
508 | if (object->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT)) | ||
509 | { | ||
510 | // Sculpt | ||
511 | LLSculptParams* sculpt = (LLSculptParams*)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT); | ||
512 | prim_llsd["sculpt"] = sculpt->asLLSD(); | ||
513 | |||
514 | LLUUID sculpt_texture=sculpt->getSculptTexture(); | ||
515 | bool alreadyseen=false; | ||
516 | std::list<LLUUID>::iterator iter; | ||
517 | for(iter = textures.begin(); iter != textures.end() ; iter++) | ||
518 | { | ||
519 | if( (*iter)==sculpt_texture) | ||
520 | alreadyseen=true; | ||
521 | } | ||
522 | if(alreadyseen==false) | ||
523 | { | ||
524 | llinfos << "Found a sculpt texture, adding to list "<<sculpt_texture<<llendl; | ||
525 | textures.push_back(sculpt_texture); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | // Textures | ||
530 | LLSD te_llsd; | ||
531 | U8 te_count = object->getNumTEs(); | ||
532 | for (U8 i = 0; i < te_count; i++) | ||
533 | { | ||
534 | bool alreadyseen=false; | ||
535 | te_llsd.append(object->getTE(i)->asLLSD()); | ||
536 | std::list<LLUUID>::iterator iter; | ||
537 | for(iter = textures.begin(); iter != textures.end() ; iter++) | ||
538 | { | ||
539 | if( (*iter)==object->getTE(i)->getID()) | ||
540 | alreadyseen=true; | ||
541 | } | ||
542 | if(alreadyseen==false) | ||
543 | textures.push_back(object->getTE(i)->getID()); | ||
544 | } | ||
545 | prim_llsd["textures"] = te_llsd; | ||
546 | |||
547 | // The keys in the primitive maps do not have to be localids, they can be any | ||
548 | // string. We simply use localids because they are a unique identifier | ||
549 | snprintf(localid, sizeof(localid), "%u", object->getLocalID()); | ||
550 | llsd[(const char*)localid] = prim_llsd; | ||
551 | } | ||
552 | |||
553 | updateexportnumbers(); | ||
554 | |||
555 | return llsd; | ||
556 | } | ||
557 | |||
558 | |||
559 | void primbackup::export_next_texture() | ||
560 | { | ||
561 | if(textures.empty()) | ||
562 | { | ||
563 | llinfos << "Finished exporting textures "<<llendl; | ||
564 | return; | ||
565 | } | ||
566 | |||
567 | std::list<LLUUID>::iterator iter; | ||
568 | iter = textures.begin(); | ||
569 | |||
570 | LLUUID id; | ||
571 | |||
572 | while(1) | ||
573 | { | ||
574 | if(iter==textures.end()) | ||
575 | { | ||
576 | m_nexttextureready=true; | ||
577 | return; | ||
578 | } | ||
579 | |||
580 | id=(*iter); | ||
581 | |||
582 | LLViewerImage * imagep = gImageList.hasImage(id); | ||
583 | if(imagep!=NULL) | ||
584 | { | ||
585 | S32 cur_discard = imagep->getDiscardLevel(); | ||
586 | if(cur_discard>0) | ||
587 | { | ||
588 | if(imagep->getBoostLevel()!=LLViewerImage::BOOST_PREVIEW) | ||
589 | imagep->setBoostLevel(LLViewerImage::BOOST_PREVIEW); //we want to force discard 0 this one does this. | ||
590 | } | ||
591 | else | ||
592 | { | ||
593 | break; | ||
594 | } | ||
595 | } | ||
596 | else | ||
597 | { | ||
598 | llwarns<<" We *DONT* have the texture "<<llendl; | ||
599 | } | ||
600 | iter++; | ||
601 | } | ||
602 | |||
603 | textures.remove(id); | ||
604 | |||
605 | llinfos<<"Requesting texture "<<id<<llendl; | ||
606 | LLImageJ2C * mFormattedImage = new LLImageJ2C; | ||
607 | CacheReadResponder* responder = new CacheReadResponder(id, mFormattedImage); | ||
608 | LLAppViewer::getTextureCache()->readFromCache(id,LLWorkerThread::PRIORITY_HIGH,0,999999,responder); | ||
609 | } | ||
610 | |||
611 | |||
612 | |||
613 | void primbackup::import_object(bool upload) | ||
614 | { | ||
615 | textures.clear(); | ||
616 | assetmap.clear(); | ||
617 | current_asset=LLUUID::null; | ||
618 | |||
619 | this->m_retexture=upload; | ||
620 | |||
621 | // Open the file open dialog | ||
622 | LLFilePicker& file_picker = LLFilePicker::instance(); | ||
623 | if( !file_picker.getOpenFile( LLFilePicker::FFLOAD_XML ) ) | ||
624 | { | ||
625 | // User canceled save. | ||
626 | return; | ||
627 | } | ||
628 | std::string file_name = file_picker.getFirstFile().c_str(); | ||
629 | folder = gDirUtilp->getDirName(file_name); | ||
630 | |||
631 | llifstream import_file(file_name); | ||
632 | LLSDSerialize::fromXML(llsd, import_file); | ||
633 | import_file.close(); | ||
634 | |||
635 | show(); | ||
636 | |||
637 | //Get the texture map | ||
638 | |||
639 | LLSD::map_const_iterator prim_it; | ||
640 | LLSD::array_const_iterator prim_arr_it; | ||
641 | |||
642 | this->m_curobject=1; | ||
643 | this->m_curprim=1; | ||
644 | this->m_objects=llsd["data"].size(); | ||
645 | this->m_prims=0; | ||
646 | rezcount=0; | ||
647 | |||
648 | updateimportnumbers(); | ||
649 | |||
650 | for( prim_arr_it = llsd["data"].beginArray(); prim_arr_it != llsd["data"].endArray(); prim_arr_it++) | ||
651 | { | ||
652 | |||
653 | LLSD llsd2; | ||
654 | llsd2=(*prim_arr_it)["group_body"]; | ||
655 | |||
656 | for( prim_it = llsd2.beginMap(); prim_it != llsd2.endMap(); prim_it++) | ||
657 | { | ||
658 | LLSD prim_llsd; | ||
659 | prim_llsd=llsd2[prim_it->first]; | ||
660 | LLSD::array_iterator text_it; | ||
661 | std::list<LLUUID>::iterator iter; | ||
662 | |||
663 | if(prim_llsd.has("sculpt")) | ||
664 | { | ||
665 | LLSculptParams* sculpt=new LLSculptParams(); | ||
666 | sculpt->fromLLSD(prim_llsd["sculpt"]); | ||
667 | LLUUID orig=sculpt->getSculptTexture(); | ||
668 | bool alreadyseen=false; | ||
669 | for(iter = textures.begin(); iter != textures.end() ; iter++) | ||
670 | { | ||
671 | if( (*iter)==orig) | ||
672 | alreadyseen=true; | ||
673 | } | ||
674 | if(alreadyseen==false) | ||
675 | { | ||
676 | llinfos << "Found a new SCULPT texture to upload "<<orig<<llendl; | ||
677 | textures.push_back(orig); | ||
678 | } | ||
679 | } | ||
680 | |||
681 | |||
682 | LLSD te_llsd; | ||
683 | te_llsd=prim_llsd["textures"]; | ||
684 | |||
685 | |||
686 | for(text_it=te_llsd.beginArray(); text_it !=te_llsd.endArray(); text_it++) | ||
687 | { | ||
688 | LLSD the_te; | ||
689 | the_te=(*text_it); | ||
690 | LLTextureEntry te; | ||
691 | te.fromLLSD(the_te); | ||
692 | |||
693 | te.getID(); | ||
694 | bool alreadyseen=false; | ||
695 | |||
696 | for(iter = textures.begin(); iter != textures.end() ; iter++) | ||
697 | { | ||
698 | if( (*iter)==te.getID()) | ||
699 | alreadyseen=true; | ||
700 | } | ||
701 | if(alreadyseen==false) | ||
702 | { | ||
703 | llinfos << "Found a new texture to upload "<<te.getID()<<llendl; | ||
704 | textures.push_back(te.getID()); | ||
705 | } | ||
706 | } | ||
707 | |||
708 | } | ||
709 | } | ||
710 | |||
711 | if(m_retexture==TRUE) | ||
712 | upload_next_asset(); | ||
713 | else | ||
714 | import_object1a(); | ||
715 | } | ||
716 | |||
717 | LLVector3 primbackup::offset_agent(LLVector3 offset) | ||
718 | { | ||
719 | LLVector3 pos= gAgent.getPositionAgent(); | ||
720 | LLQuaternion agent_rot=LLQuaternion(gAgent.getAtAxis(),gAgent.getLeftAxis(),gAgent.getUpAxis()); | ||
721 | pos=(offset*agent_rot+pos); | ||
722 | return pos; | ||
723 | } | ||
724 | |||
725 | void primbackup::rez_agent_offset(LLVector3 offset) | ||
726 | { | ||
727 | // This will break for a sitting agent | ||
728 | LLToolPlacer* mPlacer = new LLToolPlacer(); | ||
729 | mPlacer->setObjectType(LL_PCODE_CUBE); | ||
730 | //LLVector3 pos=offset_agent(offset); | ||
731 | mPlacer->placeObject(offset.mV[0], offset.mV[1],offset.mV[2]); | ||
732 | } | ||
733 | |||
734 | void primbackup::import_object1a() | ||
735 | { | ||
736 | running=true; | ||
737 | |||
738 | show(); | ||
739 | |||
740 | group_prim_import_iter=llsd["data"].beginArray(); | ||
741 | root_root_pos=(*group_prim_import_iter)["root_position"]; | ||
742 | |||
743 | this->m_objects=llsd["data"].size(); | ||
744 | this->m_curobject=1; | ||
745 | import_next_object(); | ||
746 | } | ||
747 | |||
748 | void primbackup::import_next_object() | ||
749 | { | ||
750 | toselect.clear(); | ||
751 | rezcount=0; | ||
752 | |||
753 | this_group=(*group_prim_import_iter)["group_body"]; | ||
754 | prim_import_iter=this_group.beginMap(); | ||
755 | |||
756 | m_curprim=0; | ||
757 | m_prims=this_group.size(); | ||
758 | updateimportnumbers(); | ||
759 | LLVector3 lgpos=(*group_prim_import_iter)["root_position"]; | ||
760 | |||
761 | group_offset=lgpos-root_root_pos; | ||
762 | root_pos=offset_agent(LLVector3(2.0,0,0)); | ||
763 | root_rot=ll_quaternion_from_sd((*group_prim_import_iter)["root_rotation"]); | ||
764 | |||
765 | rez_agent_offset(LLVector3(0.0,2.0,0.0)); | ||
766 | // Now we must wait for the callback when ViewerObjectList gets the new objects and we have the correct number selected | ||
767 | } | ||
768 | |||
769 | // This function takes a pointer to a viewerobject and applys the prim definition that prim_llsd has | ||
770 | void primbackup::xmltoprim(LLSD prim_llsd,LLViewerObject * object) | ||
771 | { | ||
772 | LLUUID id = object->getID(); | ||
773 | expecting_update = object->getID(); | ||
774 | LLSelectMgr::getInstance()->selectObjectAndFamily(object); | ||
775 | |||
776 | if(prim_llsd.has("parent")) | ||
777 | { | ||
778 | //we are not the root node. | ||
779 | LLVector3 pos=prim_llsd["position"]; | ||
780 | LLQuaternion rot=ll_quaternion_from_sd(prim_llsd["rotation"]); | ||
781 | object->setPositionRegion((pos*root_rot)+(root_pos+group_offset)); | ||
782 | object->setRotation(rot*root_rot); | ||
783 | } | ||
784 | else | ||
785 | { | ||
786 | object->setPositionRegion(root_pos+group_offset); | ||
787 | LLQuaternion rot=ll_quaternion_from_sd(prim_llsd["rotation"]); | ||
788 | object->setRotation(rot); | ||
789 | } | ||
790 | |||
791 | object->setScale(prim_llsd["scale"]); | ||
792 | |||
793 | if(prim_llsd.has("shadows")) | ||
794 | if(prim_llsd["shadows"].asInteger()==1) | ||
795 | object->setFlags(FLAGS_CAST_SHADOWS,true); | ||
796 | |||
797 | if(prim_llsd.has("phantom")) | ||
798 | if(prim_llsd["phantom"].asInteger()==1) | ||
799 | object->setFlags(FLAGS_PHANTOM,true); | ||
800 | |||
801 | if(prim_llsd.has("physical")) | ||
802 | if(prim_llsd["physical"].asInteger()==1) | ||
803 | object->setFlags(FLAGS_USE_PHYSICS,true); | ||
804 | |||
805 | // Volume params | ||
806 | LLVolumeParams volume_params = object->getVolume()->getParams(); | ||
807 | volume_params.fromLLSD(prim_llsd["volume"]) ; | ||
808 | object->updateVolume(volume_params); | ||
809 | |||
810 | if(prim_llsd.has("sculpt")) | ||
811 | { | ||
812 | LLSculptParams* sculpt=new LLSculptParams(); | ||
813 | sculpt->fromLLSD(prim_llsd["sculpt"]); | ||
814 | |||
815 | //TODO check if map is valid and only set texture is map is valid and changes | ||
816 | |||
817 | if(assetmap[sculpt->getSculptTexture()].notNull()) | ||
818 | { | ||
819 | LLUUID replacment=assetmap[sculpt->getSculptTexture()]; | ||
820 | sculpt->setSculptTexture(replacment); | ||
821 | } | ||
822 | |||
823 | object->setParameterEntry(LLNetworkData::PARAMS_SCULPT,(LLNetworkData&)(*sculpt),true); | ||
824 | } | ||
825 | |||
826 | if(prim_llsd.has("light")) | ||
827 | { | ||
828 | LLLightParams * light=new LLLightParams(); | ||
829 | light->fromLLSD(prim_llsd["light"]); | ||
830 | object->setParameterEntry(LLNetworkData::PARAMS_LIGHT,(LLNetworkData&)(*light),true); | ||
831 | } | ||
832 | |||
833 | if(prim_llsd.has("flexible")) | ||
834 | { | ||
835 | LLFlexibleObjectData* flex=new LLFlexibleObjectData(); | ||
836 | flex->fromLLSD(prim_llsd["flexible"]); | ||
837 | object->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE,(LLNetworkData&)(*flex),true); | ||
838 | } | ||
839 | |||
840 | |||
841 | // Textures | ||
842 | LLSD te_llsd; | ||
843 | llinfos << "Processing textures for prim" << llendl; | ||
844 | |||
845 | te_llsd=prim_llsd["textures"]; | ||
846 | |||
847 | LLSD::array_iterator text_it; | ||
848 | U8 i=0; | ||
849 | i=0; | ||
850 | |||
851 | for(text_it=te_llsd.beginArray(); text_it !=te_llsd.endArray(); text_it++) | ||
852 | { | ||
853 | LLSD the_te; | ||
854 | the_te=(*text_it); | ||
855 | LLTextureEntry te; | ||
856 | te.fromLLSD(the_te); | ||
857 | |||
858 | if(assetmap[te.getID()].notNull()) | ||
859 | { | ||
860 | LLUUID replacment=assetmap[te.getID()]; | ||
861 | te.setID(replacment); | ||
862 | } | ||
863 | |||
864 | object->setTE(i,te); // | ||
865 | i++; | ||
866 | } | ||
867 | |||
868 | llinfos << "Textures done!" << llendl; | ||
869 | |||
870 | //bump the iterator now so the callbacks hook together nicely | ||
871 | //if(prim_import_iter!=this_group.endMap()) | ||
872 | // prim_import_iter++; | ||
873 | |||
874 | object->sendRotationUpdate(); | ||
875 | object->sendTEUpdate(); | ||
876 | object->sendShapeUpdate(); | ||
877 | LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_SCALE |UPD_POSITION); | ||
878 | |||
879 | LLSelectMgr::getInstance()->deselectAll(); | ||
880 | } | ||
881 | |||
882 | //This is fired when the update packet is processed so we know the prim settings have stuck | ||
883 | void primbackup::prim_update(LLViewerObject* object) | ||
884 | { | ||
885 | if(!running) | ||
886 | return; | ||
887 | |||
888 | if(object!=NULL) | ||
889 | if(object->mID!=expecting_update) | ||
890 | return; | ||
891 | |||
892 | m_curprim++; | ||
893 | updateimportnumbers(); | ||
894 | |||
895 | prim_import_iter++; | ||
896 | |||
897 | LLUUID x; | ||
898 | expecting_update=x.null; | ||
899 | |||
900 | if(prim_import_iter==this_group.endMap()) | ||
901 | { | ||
902 | llinfos<<"Trying to link"<<llendl; | ||
903 | |||
904 | if(toselect.size()>1) | ||
905 | { | ||
906 | std::reverse(toselect.begin(),toselect.end()); | ||
907 | //Now link | ||
908 | LLSelectMgr::getInstance()->deselectAll(); | ||
909 | LLSelectMgr::getInstance()->selectObjectAndFamily(toselect,true); | ||
910 | LLSelectMgr::getInstance()->sendLink(); | ||
911 | LLViewerObject * root=toselect.back(); | ||
912 | root->setRotation(root_rot); | ||
913 | } | ||
914 | |||
915 | this->m_curobject++; | ||
916 | group_prim_import_iter++; | ||
917 | if(group_prim_import_iter!=llsd["data"].endArray()) | ||
918 | { | ||
919 | import_next_object(); | ||
920 | return; | ||
921 | } | ||
922 | |||
923 | running=false; | ||
924 | this->close(); | ||
925 | return; | ||
926 | } | ||
927 | |||
928 | LLSD prim_llsd; | ||
929 | prim_llsd=this_group[prim_import_iter->first]; | ||
930 | |||
931 | if(toselect.empty()) | ||
932 | { | ||
933 | llwarns << "error: ran out of objects to mod" << llendl; | ||
934 | return; | ||
935 | } | ||
936 | |||
937 | if(prim_import_iter!=this_group.endMap()) | ||
938 | { | ||
939 | //rez_agent_offset(LLVector3(1.0,0,0)); | ||
940 | LLSD prim_llsd=this_group[prim_import_iter->first]; | ||
941 | process_iter++; | ||
942 | xmltoprim(prim_llsd,(*process_iter)); | ||
943 | } | ||
944 | } | ||
945 | |||
946 | // Callback when we rez a new object when the importer is running. | ||
947 | bool primbackup::newprim(LLViewerObject * pobject) | ||
948 | { | ||
949 | if(running) | ||
950 | { | ||
951 | rezcount++; | ||
952 | toselect.push_back(pobject); | ||
953 | updateimportnumbers(); | ||
954 | prim_import_iter++; | ||
955 | |||
956 | if(prim_import_iter!=this_group.endMap()) | ||
957 | { | ||
958 | |||
959 | pobject->setPosition(this->offset_agent(LLVector3(0,1.0,0))); | ||
960 | LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_POSITION); | ||
961 | |||
962 | rez_agent_offset(LLVector3(1.0,0,0)); | ||
963 | } | ||
964 | else | ||
965 | { | ||
966 | llinfos << "All prims rezed, moving to build stage" <<llendl; | ||
967 | prim_import_iter=this_group.beginMap(); | ||
968 | LLSD prim_llsd=this_group[prim_import_iter->first]; | ||
969 | process_iter=toselect.begin(); | ||
970 | xmltoprim(prim_llsd,(*process_iter)); | ||
971 | } | ||
972 | } | ||
973 | |||
974 | return true; | ||
975 | } | ||
976 | |||
977 | void primbackup::update_map(LLUUID uploaded_asset) | ||
978 | { | ||
979 | if(current_asset.isNull()) | ||
980 | return; | ||
981 | |||
982 | assetmap.insert(std::pair<LLUUID,LLUUID>(current_asset,uploaded_asset)); | ||
983 | llinfos << "Mapping "<<current_asset<<" to "<<uploaded_asset<<llendl; | ||
984 | |||
985 | } | ||
986 | |||
987 | |||
988 | void myupload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_type, | ||
989 | std::string name, | ||
990 | std::string desc, S32 compression_info, | ||
991 | LLAssetType::EType destination_folder_type, | ||
992 | LLInventoryType::EType inv_type, | ||
993 | U32 next_owner_perm, | ||
994 | const std::string& display_name, | ||
995 | LLAssetStorage::LLStoreAssetCallback callback, | ||
996 | void *userdata) | ||
997 | { | ||
998 | if(gDisconnected) | ||
999 | { | ||
1000 | return ; | ||
1001 | } | ||
1002 | |||
1003 | LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID()); | ||
1004 | |||
1005 | // At this point, we're ready for the upload. | ||
1006 | std::string upload_message = "Uploading...\n\n"; | ||
1007 | upload_message.append(display_name); | ||
1008 | LLUploadDialog::modalUploadDialog(upload_message); | ||
1009 | |||
1010 | std::string url = gAgent.getRegion()->getCapability("NewFileAgentInventory"); | ||
1011 | if (!url.empty()) | ||
1012 | { | ||
1013 | LLSD body; | ||
1014 | body["folder_id"] = gInventory.findCategoryUUIDForType((destination_folder_type == LLAssetType::AT_NONE) ? asset_type : destination_folder_type); | ||
1015 | body["asset_type"] = LLAssetType::lookup(asset_type); | ||
1016 | body["inventory_type"] = LLInventoryType::lookup(inv_type); | ||
1017 | body["name"] = name; | ||
1018 | body["description"] = desc; | ||
1019 | |||
1020 | std::ostringstream llsdxml; | ||
1021 | LLSDSerialize::toXML(body, llsdxml); | ||
1022 | lldebugs << "posting body to capability: " << llsdxml.str() << llendl; | ||
1023 | //LLHTTPClient::post(url, body, new LLNewAgentInventoryResponder(body, uuid, asset_type)); | ||
1024 | LLHTTPClient::post(url, body, new importResponder(body, uuid, asset_type)); | ||
1025 | |||
1026 | } | ||
1027 | else | ||
1028 | { | ||
1029 | llinfos << "NewAgentInventory capability not found, FUCK!" << llendl; | ||
1030 | } | ||
1031 | } | ||
1032 | |||
1033 | |||
1034 | |||
1035 | void primbackup::upload_next_asset() | ||
1036 | { | ||
1037 | if(textures.empty()) | ||
1038 | { | ||
1039 | llinfos<<" Texture list is empty, moving to rez statge"<< llendl; | ||
1040 | current_asset=LLUUID::null; | ||
1041 | import_object1a(); | ||
1042 | return; | ||
1043 | } | ||
1044 | |||
1045 | this->updateimportnumbers(); | ||
1046 | |||
1047 | std::list<LLUUID>::iterator iter; | ||
1048 | iter=textures.begin(); | ||
1049 | LLUUID id=(*iter); | ||
1050 | textures.pop_front(); | ||
1051 | |||
1052 | llinfos<<"Got texture ID "<<id<< "trying to upload"<<llendl; | ||
1053 | |||
1054 | current_asset=id; | ||
1055 | std::string struid; | ||
1056 | id.toString(struid); | ||
1057 | std::string filename=folder+"//"+struid; | ||
1058 | |||
1059 | |||
1060 | LLAssetID uuid; | ||
1061 | LLTransactionID tid; | ||
1062 | |||
1063 | // gen a new transaction ID for this asset | ||
1064 | tid.generate(); | ||
1065 | uuid = tid.makeAssetID(gAgent.getSecureSessionID()); | ||
1066 | |||
1067 | S32 file_size; | ||
1068 | apr_file_t* fp = ll_apr_file_open(filename, LL_APR_RB, &file_size); | ||
1069 | if (fp) | ||
1070 | { | ||
1071 | const S32 buf_size = 65536; | ||
1072 | U8 copy_buf[buf_size]; | ||
1073 | LLVFile file(gVFS, uuid, LLAssetType::AT_TEXTURE, LLVFile::WRITE); | ||
1074 | file.setMaxSize(file_size); | ||
1075 | |||
1076 | while ((file_size = ll_apr_file_read(fp, copy_buf, buf_size))) | ||
1077 | { | ||
1078 | file.write(copy_buf, file_size); | ||
1079 | } | ||
1080 | apr_file_close(fp); | ||
1081 | } | ||
1082 | else | ||
1083 | { | ||
1084 | llwarns<<"Unable to access output file "<<filename<<llendl; | ||
1085 | upload_next_asset(); | ||
1086 | return; | ||
1087 | } | ||
1088 | |||
1089 | myupload_new_resource( | ||
1090 | tid, LLAssetType::AT_TEXTURE, struid, | ||
1091 | struid, 0, | ||
1092 | LLAssetType::AT_TEXTURE, | ||
1093 | LLInventoryType::defaultForAssetType(LLAssetType::AT_TEXTURE), | ||
1094 | 0x0, | ||
1095 | "Uploaded texture", | ||
1096 | NULL, | ||
1097 | NULL); | ||
1098 | |||
1099 | |||
1100 | } | ||
1101 | |||
diff --git a/linden/indra/newview/primbackup.h b/linden/indra/newview/primbackup.h new file mode 100644 index 0000000..ee4cb29 --- /dev/null +++ b/linden/indra/newview/primbackup.h | |||
@@ -0,0 +1,131 @@ | |||
1 | |||
2 | #include "llviewerinventory.h" | ||
3 | |||
4 | #define LL_GRID_PERMISSIONS 1 | ||
5 | |||
6 | enum export_states {EXPORT_INIT,EXPORT_STRUCTURE,EXPORT_TEXTURES,EXPORT_LLSD,EXPORT_DONE}; | ||
7 | |||
8 | class primbackup : public LLFloater | ||
9 | |||
10 | { | ||
11 | public: | ||
12 | //Export state machine | ||
13 | enum export_states export_state; | ||
14 | |||
15 | //Export idle callback | ||
16 | static void exportworker(void *userdata); | ||
17 | |||
18 | //Static accessor | ||
19 | static primbackup* getInstance(); | ||
20 | |||
21 | virtual ~primbackup(); | ||
22 | |||
23 | //Floater stuff | ||
24 | virtual void show(); | ||
25 | virtual void draw(); | ||
26 | virtual void onClose( bool app_quitting ); | ||
27 | |||
28 | //Import entry point | ||
29 | void import_object(bool upload=FALSE); | ||
30 | |||
31 | //Export entry point | ||
32 | void pre_export_object(); | ||
33 | |||
34 | //Update map from texture worker | ||
35 | void update_map(LLUUID uploaded_asset); | ||
36 | |||
37 | //Move to next texture upload | ||
38 | void upload_next_asset(); | ||
39 | |||
40 | // is ready for next texture? | ||
41 | bool m_nexttextureready; | ||
42 | |||
43 | //Folder public geter | ||
44 | std::string getfolder() {return folder;}; | ||
45 | |||
46 | //Prim updated callback | ||
47 | void prim_update(LLViewerObject* object); | ||
48 | |||
49 | //New prim call back | ||
50 | bool newprim(LLViewerObject * pobject); | ||
51 | |||
52 | private: | ||
53 | |||
54 | //Static singleton stuff | ||
55 | primbackup(); | ||
56 | static primbackup* sInstance; | ||
57 | |||
58 | // are we active flag | ||
59 | bool running; | ||
60 | |||
61 | //file and folder name control | ||
62 | std::string file_name; | ||
63 | std::string folder; | ||
64 | |||
65 | // True if we need to rebase the assets | ||
66 | bool m_retexture; | ||
67 | |||
68 | //Counts of import and export objects and textures and prims | ||
69 | int m_objects,m_curobject; | ||
70 | int m_prims,m_curprim; | ||
71 | int m_textures,m_curtexture; | ||
72 | |||
73 | // No prims rezed | ||
74 | int rezcount; | ||
75 | |||
76 | // Update the floater with status numbers | ||
77 | void updateimportnumbers(); | ||
78 | void updateexportnumbers(); | ||
79 | |||
80 | //Convert a selection list of objects to LLSD | ||
81 | LLSD prims_to_llsd(LLViewerObject::child_list_t child_list); | ||
82 | |||
83 | // Start the import process | ||
84 | void import_object1a(); | ||
85 | |||
86 | //Export the next texture in list | ||
87 | void export_next_texture(); | ||
88 | |||
89 | //apply LLSD to object | ||
90 | void xmltoprim(LLSD prim_llsd,LLViewerObject * pobject); | ||
91 | |||
92 | |||
93 | //rez a prim at a given position (note not agent offset X/Y screen for raycast) | ||
94 | void rez_agent_offset(LLVector3 offset); | ||
95 | |||
96 | //Move to the next import group | ||
97 | void import_next_object(); | ||
98 | |||
99 | //Get an offset from the agent based on rotation and current pos | ||
100 | LLVector3 offset_agent(LLVector3 offset); | ||
101 | |||
102 | // Rebase map | ||
103 | std::map<LLUUID,LLUUID> assetmap; | ||
104 | |||
105 | //Export texture list | ||
106 | std::list<LLUUID> textures; | ||
107 | |||
108 | //Import object tracking | ||
109 | std::vector<LLViewerObject *> toselect; | ||
110 | std::vector<LLViewerObject *>::iterator process_iter; | ||
111 | |||
112 | //Working LLSD holders | ||
113 | LLUUID current_asset; | ||
114 | LLSD llsd; | ||
115 | LLSD this_group; | ||
116 | LLUUID expecting_update; | ||
117 | |||
118 | //working llsd itterators for objects and linksets | ||
119 | LLSD::map_const_iterator prim_import_iter; | ||
120 | LLSD::array_const_iterator group_prim_import_iter; | ||
121 | |||
122 | // Root pos and central root pos for link set | ||
123 | LLVector3 root_pos; | ||
124 | LLVector3 root_root_pos; | ||
125 | LLVector3 group_offset; | ||
126 | |||
127 | //Agent inital pos and rot when starting import | ||
128 | LLQuaternion root_rot; | ||
129 | LLQuaternion agent_rot; | ||
130 | |||
131 | }; | ||
diff --git a/linden/indra/newview/skins/default/xui/en-us/menu_pie_object.xml b/linden/indra/newview/skins/default/xui/en-us/menu_pie_object.xml index 8d059c1..ac3d359 100644 --- a/linden/indra/newview/skins/default/xui/en-us/menu_pie_object.xml +++ b/linden/indra/newview/skins/default/xui/en-us/menu_pie_object.xml | |||
@@ -54,7 +54,11 @@ | |||
54 | <on_click function="Object.ReportAbuse" /> | 54 | <on_click function="Object.ReportAbuse" /> |
55 | <on_enable function="Object.EnableReportAbuse" /> | 55 | <on_enable function="Object.EnableReportAbuse" /> |
56 | </menu_item_call> | 56 | </menu_item_call> |
57 | <menu_item_separator /> | 57 | <menu_item_call label="Backup" enabled="false" hidden="false" |
58 | mouse_opaqu="true" name="Export"> | ||
59 | <on_click function="Object.Export" /> | ||
60 | <on_enable function="Object.EnableExport" /> | ||
61 | </menu_item_call> | ||
58 | <menu_item_call enabled="true" label="Inspect" mouse_opaque="true" name="Object Inspect"> | 62 | <menu_item_call enabled="true" label="Inspect" mouse_opaque="true" name="Object Inspect"> |
59 | <on_click function="Object.Inspect" /> | 63 | <on_click function="Object.Inspect" /> |
60 | <on_enable function="Object.EnableInspect" /> | 64 | <on_enable function="Object.EnableInspect" /> |
diff --git a/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml b/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml index f837029..c5f0331 100644 --- a/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml +++ b/linden/indra/newview/skins/default/xui/en-us/menu_viewer.xml | |||
@@ -8,6 +8,14 @@ | |||
8 | 8 | ||
9 | <menu name="File" create_jump_keys="true" label="File" | 9 | <menu name="File" create_jump_keys="true" label="File" |
10 | opaque="true" tear_off="true"> | 10 | opaque="true" tear_off="true"> |
11 | <menu_item_call enabled="false" hidden="false" label="Import" mouse_opaque="true" name="Import"> | ||
12 | <on_click function="Object.Import" /> | ||
13 | <on_enable function="Object.EnableImport" /> | ||
14 | </menu_item_call> | ||
15 | <menu_item_call enabled="false" hidden="false" label="Upload + Import" mouse_opaque="true" name="Import"> | ||
16 | <on_click function="Object.ImportUpload" /> | ||
17 | <on_enable function="Object.EnableImport" /> | ||
18 | </menu_item_call> | ||
11 | <menu_item_call name="Upload Image" | 19 | <menu_item_call name="Upload Image" |
12 | label="Upload Image (L$[COST])..." | 20 | label="Upload Image (L$[COST])..." |
13 | shortcut="control|U"> | 21 | shortcut="control|U"> |