From a4ebe8b9d5412d07bb80a1ab52288548610e9399 Mon Sep 17 00:00:00 2001 From: Igor Borovkov Date: Tue, 9 Mar 2010 15:34:09 +0200 Subject: possible defensive fix for EXT-6092 [crashhunters] Crash in LLFlatListView::selectItemPair, bug reason is unknown --HG-- branch : product-engine --- indra/llui/llflatlistview.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index 2481249f91..2e5aeec41d 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -492,6 +492,12 @@ void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask) { if (!item_pair) return; + if (!item_pair->first) + { + llwarning("Attempt to selet an item pair containing null panel item", 0); + return; + } + setFocus(TRUE); bool select_item = !isSelected(item_pair); -- cgit v1.2.3 From 60e3ba16a55747165608696b0c0a6a5de97e1e8a Mon Sep 17 00:00:00 2001 From: Mike Antipov Date: Tue, 9 Mar 2010 16:29:52 +0200 Subject: Fixed major bug EXT-5939 (UI slider tracks turn light grey when in focus) - All horizontal sliders are configured to have no highlighted track. --HG-- branch : product-engine --- indra/newview/skins/default/xui/en/widgets/slider_bar.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/widgets/slider_bar.xml b/indra/newview/skins/default/xui/en/widgets/slider_bar.xml index 89d5950e98..ea63cac790 100644 --- a/indra/newview/skins/default/xui/en/widgets/slider_bar.xml +++ b/indra/newview/skins/default/xui/en/widgets/slider_bar.xml @@ -1,4 +1,5 @@ + -- cgit v1.2.3 From b513811c7be5e4d71dfc2c2a8d07e738c990293a Mon Sep 17 00:00:00 2001 From: Dmitry Zaporozhan Date: Tue, 9 Mar 2010 15:20:42 +0200 Subject: Update for commit 10217 : 158e0438a11b (WIP on EXT-5687 (Classifieds are missing the clickthrough data)). Updated parameter name. --HG-- branch : product-engine --- indra/newview/llpanelpicks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index f0860e7027..ada770f78b 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -945,7 +945,7 @@ void LLPanelPicks::onPanelClassifiedEdit() LLSD params; params["classified_id"] = c_item->getClassifiedId(); - params["avatar_id"] = c_item->getAvatarId(); + params["classified_creator_id"] = c_item->getAvatarId(); params["snapshot_id"] = c_item->getSnapshotId(); params["name"] = c_item->getClassifiedName(); params["desc"] = c_item->getDescription(); -- cgit v1.2.3 From a45544f9a0adca7bd765929ffe321756ffb09609 Mon Sep 17 00:00:00 2001 From: Andrew Dyukov Date: Tue, 9 Mar 2010 22:54:45 +0200 Subject: Implemented nice-to have sub-task EXT-4706 (Add maturity icons to About Land). - Added maturity icons to Options and Access tabs. - Created auxiliary local class to get full access to checkboxes' textboxes. --HG-- branch : product-engine --- indra/newview/llfloaterland.cpp | 69 +++++++++++++++++----- .../skins/default/xui/en/floater_about_land.xml | 5 +- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 7051447409..26a179074d 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -86,6 +86,7 @@ static std::string OWNER_ONLINE = "0"; static std::string OWNER_OFFLINE = "1"; static std::string OWNER_GROUP = "2"; +static std::string MATURITY = "[MATURITY]"; // constants used in callbacks below - syntactic sugar. static const BOOL BUY_GROUP_LAND = TRUE; @@ -102,9 +103,21 @@ public: virtual void changed() { LLFloaterLand::refreshAll(); } }; -// fills target textbox with maturity info(icon and text) +// class needed to get full access to textbox inside checkbox, because LLCheckBoxCtrl::setLabel() has string as its argument. +// It was introduced while implementing EXT-4706 +class LLCheckBoxWithTBAcess : public LLCheckBoxCtrl +{ +public: + LLTextBox* getTextBox() + { + return mLabel; + } +}; + +// inserts maturity info(icon and text) into target textbox // names_floater - pointer to floater which contains strings with maturity icons filenames -void FillMaturityTextBox(LLTextBox* target_textbox, LLFloater* names_floater); +// str_to_parse is string in format "txt1[MATURITY]txt2" where maturity icon and text will be inserted instead of [MATURITY] +void insert_maturity_into_textbox(LLTextBox* target_textbox, LLFloater* names_floater, std::string str_to_parse); //--------------------------------------------------------------------------- // LLFloaterLand @@ -558,7 +571,7 @@ void LLPanelLandGeneral::refresh() if (regionp) { - FillMaturityTextBox(mContentRating, gFloaterView->getParentFloater(this)); + insert_maturity_into_textbox(mContentRating, gFloaterView->getParentFloater(this), MATURITY); mLandType->setText(regionp->getSimProductName()); } @@ -2062,8 +2075,14 @@ void LLPanelLandOptions::refresh() { // not teen so fill in the data for the maturity control mMatureCtrl->setVisible(TRUE); - mMatureCtrl->setLabel(getString("mature_check_mature")); - mMatureCtrl->setToolTip(getString("mature_check_mature_tooltip")); + LLStyle::Params style; + style.image(LLUI::getUIImage(gFloaterView->getParentFloater(this)->getString("maturity_icon_moderate"))); + LLCheckBoxWithTBAcess* fullaccess_mature_ctrl = (LLCheckBoxWithTBAcess*)mMatureCtrl; + fullaccess_mature_ctrl->getTextBox()->setText(std::string("icon"),style); + fullaccess_mature_ctrl->getTextBox()->appendText(getString("mature_check_mature"), false); + fullaccess_mature_ctrl->setToolTip(getString("mature_check_mature_tooltip")); + fullaccess_mature_ctrl->reshape(fullaccess_mature_ctrl->getRect().getWidth(), fullaccess_mature_ctrl->getRect().getHeight(), FALSE); + // they can see the checkbox, but its disposition depends on the // state of the region LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion(); @@ -2464,19 +2483,26 @@ void LLPanelLandAccess::refresh() } } + LLCheckBoxWithTBAcess* maturity_checkbox = (LLCheckBoxWithTBAcess*) getChild( "public_access"); LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); if(region) { - std::string region_access = "("; - region_access += region->getSimAccessString(); - region_access += ")"; - childSetLabelArg( "public_access", "[MATURITY]", region_access ); + LLTextBox* maturity_textbox = maturity_checkbox->getTextBox(); + insert_maturity_into_textbox(maturity_textbox, gFloaterView->getParentFloater(this), getString("allow_public_access")); + maturity_checkbox->reshape(maturity_checkbox->getRect().getWidth(), maturity_checkbox->getRect().getHeight(), FALSE); } else { - childSetLabelArg( "public_access", "[MATURITY]", std::string() ); - } + std::string maturity_string = getString("allow_public_access"); + size_t maturity_pos = maturity_string.find(MATURITY); + if (maturity_pos != std::string::npos) + { + maturity_string.replace(maturity_pos, MATURITY.length(), std::string("")); + } + + maturity_checkbox->setLabel(maturity_string); + } if(parcel->getRegionDenyAnonymousOverride()) { @@ -2862,7 +2888,7 @@ void LLPanelLandCovenant::refresh() LLTextBox* region_maturity = getChild("region_maturity_text"); if (region_maturity) { - FillMaturityTextBox(region_maturity, gFloaterView->getParentFloater(this)); + insert_maturity_into_textbox(region_maturity, gFloaterView->getParentFloater(this), MATURITY); } LLTextBox* resellable_clause = getChild("resellable_clause"); @@ -2944,9 +2970,10 @@ void LLPanelLandCovenant::updateEstateOwnerName(const std::string& name) } } -// fills target textbox with maturity info(icon and text) +// inserts maturity info(icon and text) into target textbox // names_floater - pointer to floater which contains strings with maturity icons filenames -void FillMaturityTextBox(LLTextBox* target_textbox, LLFloater* names_floater) +// str_to_parse is string in format "txt1[MATURITY]txt2" where maturity icon and text will be inserted instead of [MATURITY] +void insert_maturity_into_textbox(LLTextBox* target_textbox, LLFloater* names_floater, std::string str_to_parse) { LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); if (!region) @@ -2974,7 +3001,19 @@ void FillMaturityTextBox(LLTextBox* target_textbox, LLFloater* names_floater) break; } + size_t maturity_pos = str_to_parse.find(MATURITY); + + if (maturity_pos == std::string::npos) + { + return; + } + + std::string text_before_rating = str_to_parse.substr(0, maturity_pos); + std::string text_after_rating = str_to_parse.substr(maturity_pos + MATURITY.length()); + + target_textbox->setText(text_before_rating); // any text may be here instead of "icon" except "" - target_textbox->setText(std::string("icon"),style); + target_textbox->appendText(std::string("icon"), false, style); target_textbox->appendText(LLViewerParcelMgr::getInstance()->getSelectionRegion()->getSimAccessString(), false); + target_textbox->appendText(text_after_rating, false); } diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 2b0cb66f61..7613e81c3b 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -1886,6 +1886,10 @@ Only large parcels can be listed in search. name="access_estate_defined"> (Defined by the Estate) + + Allow Public Access ([MATURITY]) + One or more of these options is set at the estate level @@ -1906,7 +1910,6 @@ Only large parcels can be listed in search. Date: Tue, 9 Mar 2010 16:16:07 -0800 Subject: EXT-6030 - [ Assert Failure] Assert in LLFolderViewFolder when select inventory folder after moving item between folders reviewed by Leyla --- indra/newview/llfolderview.cpp | 9 ++-- indra/newview/llfolderviewitem.cpp | 82 -------------------------------- indra/newview/llfolderviewitem.h | 10 +--- indra/newview/llpanelobjectinventory.cpp | 2 +- 4 files changed, 9 insertions(+), 94 deletions(-) diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index cc807c2370..23062bafec 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -837,11 +837,14 @@ void LLFolderView::sanitizeSelection() void LLFolderView::clearSelection() { - if (mSelectedItems.size() > 0) + for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); + item_it != mSelectedItems.end(); + ++item_it) { - recursiveDeselect(FALSE); - mSelectedItems.clear(); + (*item_it)->setUnselected(); } + + mSelectedItems.clear(); mSelectThisID.setNull(); } diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 76607e4874..d3e3d2b57b 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -121,7 +121,6 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mHasVisibleChildren(FALSE), mIndentation(0), mItemHeight(p.item_height), - mNumDescendantsSelected(0), mPassedFilter(FALSE), mLastFilterGeneration(-1), mStringMatchOffset(std::string::npos), @@ -497,22 +496,6 @@ BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selecte return FALSE; } -void LLFolderViewItem::recursiveDeselect(BOOL deselect_self) -{ - if (mIsSelected && deselect_self) - { - mIsSelected = FALSE; - - // update ancestors' count of selected descendents - LLFolderViewFolder* parent_folder = getParentFolder(); - while(parent_folder) - { - parent_folder->mNumDescendantsSelected--; - parent_folder = parent_folder->getParentFolder(); - } - } -} - BOOL LLFolderViewItem::isMovable() { @@ -1459,7 +1442,6 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem { rv = TRUE; child_selected = TRUE; - mNumDescendantsSelected++; } } for (items_t::iterator iter = mItems.begin(); @@ -1470,7 +1452,6 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem { rv = TRUE; child_selected = TRUE; - mNumDescendantsSelected++; } } if(openitem && child_selected) @@ -1503,14 +1484,6 @@ BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, folders_t::iterator fit = iter++; if((*fit)->changeSelection(selection, selected)) { - if (selected) - { - mNumDescendantsSelected++; - } - else - { - mNumDescendantsSelected--; - } rv = TRUE; } } @@ -1520,14 +1493,6 @@ BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, items_t::iterator iit = iter++; if((*iit)->changeSelection(selection, selected)) { - if (selected) - { - mNumDescendantsSelected++; - } - else - { - mNumDescendantsSelected--; - } rv = TRUE; } } @@ -1544,7 +1509,6 @@ S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderVie { folders_t::iterator fit = iter++; num_selected += (*fit)->extendSelection(selection, last_selected, selected_items); - mNumDescendantsSelected += num_selected; } // handle selection of our immediate children... @@ -1637,7 +1601,6 @@ S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderVie if (item->changeSelection(item, TRUE)) { selected_items.put(item); - mNumDescendantsSelected++; num_selected++; } } @@ -1648,7 +1611,6 @@ S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderVie if (selection->changeSelection(selection, TRUE)) { selected_items.put(selection); - mNumDescendantsSelected++; num_selected++; } } @@ -1656,47 +1618,6 @@ S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderVie return num_selected; } -void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self) -{ - // make sure we don't have negative values - llassert(mNumDescendantsSelected >= 0); - - if (mIsSelected && deselect_self) - { - mIsSelected = FALSE; - - // update ancestors' count of selected descendents - LLFolderViewFolder* parent_folder = getParentFolder(); - while(parent_folder) - { - parent_folder->mNumDescendantsSelected--; - parent_folder = parent_folder->getParentFolder(); - } - } - - if (0 == mNumDescendantsSelected) - { - return; - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* item = (*iit); - item->recursiveDeselect(TRUE); - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - LLFolderViewFolder* folder = (*fit); - folder->recursiveDeselect(TRUE); - } - -} - void LLFolderViewFolder::destroyView() { for (items_t::iterator iter = mItems.begin(); @@ -1730,8 +1651,6 @@ BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item) { if(item->remove()) { - //RN: this seem unneccessary as remove() moves to trash - //removeView(item); return TRUE; } return FALSE; @@ -1746,7 +1665,6 @@ void LLFolderViewFolder::removeView(LLFolderViewItem* item) return; } // deselect without traversing hierarchy - item->recursiveDeselect(TRUE); getRoot()->removeFromSelectionList(item); extractItem(item); delete item; diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index be8e73a5a9..655ad89e99 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -150,7 +150,6 @@ protected: BOOL mHasVisibleChildren; S32 mIndentation; S32 mItemHeight; - S32 mNumDescendantsSelected; BOOL mPassedFilter; S32 mLastFilterGeneration; std::string::size_type mStringMatchOffset; @@ -231,9 +230,6 @@ public: // this method is used to group select items virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items){ return FALSE; } - // this method is used to group select items - virtual void recursiveDeselect(BOOL deselect_self); - // gets multiple-element selection virtual BOOL getSelectionList(std::set &selection) const {return TRUE;} @@ -246,10 +242,10 @@ public: // destroys this item recursively virtual void destroyView(); - S32 getNumSelectedDescendants() { return mNumDescendantsSelected; } - BOOL isSelected() { return mIsSelected; } + void setUnselected() { mIsSelected = FALSE; } + void setIsCurSelection(BOOL select) { mIsCurSelection = select; } BOOL getIsCurSelection() { return mIsCurSelection; } @@ -441,8 +437,6 @@ public: // this method is used to group select items virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray& items); - virtual void recursiveDeselect(BOOL deselect_self); - // Returns true is this object and all of its children can be removed. virtual BOOL isRemovable(); diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 6fcba80845..dedd1afcde 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -1642,7 +1642,7 @@ void LLPanelObjectInventory::updateInventory() // We're still interested in this task's inventory. std::set selected_items; BOOL inventory_has_focus = FALSE; - if (mHaveInventory && mFolders->getNumSelectedDescendants()) + if (mHaveInventory) { mFolders->getSelectionList(selected_items); inventory_has_focus = gFocusMgr.childHasKeyboardFocus(mFolders); -- cgit v1.2.3 From b4180ae2f612776dcf6a9d2b4e721de91ee443de Mon Sep 17 00:00:00 2001 From: Eli Linden Date: Tue, 9 Mar 2010 18:00:59 -0800 Subject: VWR-17514 en_xui_change --- indra/newview/skins/default/xui/en/floater_about_land.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 2b0cb66f61..e048c092ec 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -1119,7 +1119,7 @@ Leyla Linden label="Type" name="type" sort_column="online_status" - width="24" /> + width="50" /> -- cgit v1.2.3 From 239250fdd20796b4210162304afad3fd1b9721c5 Mon Sep 17 00:00:00 2001 From: Eli Linden Date: Tue, 9 Mar 2010 18:22:29 -0800 Subject: VWR-17485 en_xui_change --- indra/newview/skins/default/xui/en/floater_tools.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index 3b1ecb4c20..12d169b70a 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -290,11 +290,11 @@ width="134" /> Date: Tue, 9 Mar 2010 18:30:04 -0800 Subject: VWR-18034 en_xui_change --- indra/newview/skins/default/xui/en/panel_edit_classified.xml | 2 +- indra/newview/skins/default/xui/en/panel_edit_pick.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/skins/default/xui/en/panel_edit_classified.xml b/indra/newview/skins/default/xui/en/panel_edit_classified.xml index 74d63ab328..7383edf63d 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_classified.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_classified.xml @@ -186,7 +186,7 @@ left="10" top_pad="5" name="set_to_curr_location_btn" - width="156" /> + width="200" /> + width="200" /> Date: Wed, 10 Mar 2010 10:21:19 +0200 Subject: Fixed major bug EXT-5942 - Entering a Classified with a banned keyword doesn't publish, and doesn't let you edit. Changed the way panels handle classified publishing - each classified panel will keep listening to callbacks and will know when classified is published successfuly. If publish failed, panel will allow user to edit classified and try publishing again. All unpublished classifieds will be lost on Viewer restart. --HG-- branch : product-engine --- indra/newview/llpanelclassified.cpp | 131 +++++++++++++++++----- indra/newview/llpanelclassified.h | 23 +++- indra/newview/llpanelpicks.cpp | 91 ++++++++++++--- indra/newview/llpanelpicks.h | 41 ++++++- indra/newview/skins/default/xui/en/menu_picks.xml | 15 +++ 5 files changed, 246 insertions(+), 55 deletions(-) diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 115c7a1aa5..021e1f5159 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -1410,6 +1410,11 @@ void LLPanelClassifiedInfo::setClassifiedLocation(const std::string& location) childSetValue("classified_location", location); } +std::string LLPanelClassifiedInfo::getClassifiedLocation() +{ + return childGetValue("classified_location").asString(); +} + void LLPanelClassifiedInfo::setSnapshotId(const LLUUID& id) { mSnapshotCtrl->setValue(id); @@ -1651,6 +1656,7 @@ static const S32 CB_ITEM_PG = 1; LLPanelClassifiedEdit::LLPanelClassifiedEdit() : LLPanelClassifiedInfo() , mIsNew(false) + , mIsNewWithErrors(false) , mCanClose(false) , mPublishFloater(NULL) { @@ -1709,21 +1715,12 @@ BOOL LLPanelClassifiedEdit::postBuild() return TRUE; } -void LLPanelClassifiedEdit::onOpen(const LLSD& key) +void LLPanelClassifiedEdit::fillIn(const LLSD& key) { - LLUUID classified_id = key["classified_id"]; + setAvatarId(gAgent.getID()); - mIsNew = classified_id.isNull(); - - scrollToTop(); - - if(mIsNew) + if(key.isUndefined()) { - setAvatarId(gAgent.getID()); - - resetData(); - resetControls(); - setPosGlobal(gAgent.getPositionGlobal()); LLUUID snapshot_id = LLUUID::null; @@ -1746,25 +1743,55 @@ void LLPanelClassifiedEdit::onOpen(const LLSD& key) childSetValue("classified_name", makeClassifiedName()); childSetValue("classified_desc", desc); setSnapshotId(snapshot_id); - setClassifiedLocation(createLocationText(getLocationNotice(), region_name, getPosGlobal())); - // server will set valid parcel id setParcelId(LLUUID::null); + } + else + { + setClassifiedId(key["classified_id"]); + setClassifiedName(key["name"]); + setDescription(key["desc"]); + setSnapshotId(key["snapshot_id"]); + setCategory((U32)key["category"].asInteger()); + setContentType((U32)key["content_type"].asInteger()); + setClassifiedLocation(key["location_text"]); + childSetValue("auto_renew", key["auto_renew"]); + childSetValue("price_for_listing", key["price_for_listing"].asInteger()); + } +} + +void LLPanelClassifiedEdit::onOpen(const LLSD& key) +{ + mIsNew = key.isUndefined(); + + scrollToTop(); + + // classified is not created yet + bool is_new = isNew() || isNewWithErrors(); + + if(is_new) + { + resetData(); + resetControls(); + + fillIn(key); - enableVerbs(true); - enableEditing(true); + if(isNew()) + { + LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this); + } } else { LLPanelClassifiedInfo::onOpen(key); - enableVerbs(false); - enableEditing(false); } - std::string save_btn_label = isNew() ? getString("publish_label") : getString("save_label"); + std::string save_btn_label = is_new ? getString("publish_label") : getString("save_label"); childSetLabelArg("save_changes_btn", "[LABEL]", save_btn_label); + enableVerbs(is_new); + enableEditing(is_new); resetDirty(); setInfoLoaded(false); } @@ -1776,6 +1803,14 @@ void LLPanelClassifiedEdit::processProperties(void* data, EAvatarProcessorType t LLAvatarClassifiedInfo* c_info = static_cast(data); if(c_info && getClassifiedId() == c_info->classified_id) { + // see LLPanelClassifiedEdit::sendUpdate() for notes + mIsNewWithErrors = false; + // for just created classified - panel will probably be closed when we get here. + if(!getVisible()) + { + return; + } + enableEditing(true); setClassifiedName(c_info->name); @@ -1785,19 +1820,22 @@ void LLPanelClassifiedEdit::processProperties(void* data, EAvatarProcessorType t setClassifiedLocation(createLocationText(c_info->parcel_name, c_info->sim_name, c_info->pos_global)); // *HACK see LLPanelClassifiedEdit::sendUpdate() - getChild("category")->setCurrentByIndex(c_info->category - 1); - getChild("category")->resetDirty(); + setCategory(c_info->category - 1); bool mature = is_cf_mature(c_info->flags); bool auto_renew = is_cf_auto_renew(c_info->flags); - getChild("content_type")->setCurrentByIndex(mature ? CB_ITEM_MATURE : CB_ITEM_PG); + setContentType(mature ? CB_ITEM_MATURE : CB_ITEM_PG); childSetValue("auto_renew", auto_renew); childSetValue("price_for_listing", c_info->price_for_listing); childSetEnabled("price_for_listing", isNew()); resetDirty(); setInfoLoaded(true); + enableVerbs(false); + + // for just created classified - in case user opened edit panel before processProperties() callback + childSetLabelArg("save_changes_btn", "[LABEL]", getString("save_label")); } } } @@ -1828,7 +1866,12 @@ void LLPanelClassifiedEdit::resetDirty() LLPanelClassifiedInfo::resetDirty(); getChild("classified_snapshot")->resetDirty(); getChild("classified_name")->resetDirty(); - getChild("classified_desc")->resetDirty(); + + LLTextEditor* desc = getChild("classified_desc"); + // call blockUndo() to really reset dirty(and make isDirty work as intended) + desc->blockUndo(); + desc->resetDirty(); + getChild("category")->resetDirty(); getChild("content_type")->resetDirty(); getChild("auto_renew")->resetDirty(); @@ -1877,15 +1920,31 @@ void LLPanelClassifiedEdit::stretchSnapshot() getChild("edit_icon")->setShape(mSnapshotCtrl->getRect()); } +U32 LLPanelClassifiedEdit::getContentType() +{ + LLComboBox* ct_cb = getChild("content_type"); + return ct_cb->getCurrentIndex(); +} + +void LLPanelClassifiedEdit::setContentType(U32 content_type) +{ + LLIconsComboBox* ct_cb = getChild("content_type"); + ct_cb->setCurrentByIndex(content_type); + ct_cb->resetDirty(); +} + +bool LLPanelClassifiedEdit::getAutoRenew() +{ + return childGetValue("auto_renew").asBoolean(); +} + void LLPanelClassifiedEdit::sendUpdate() { LLAvatarClassifiedInfo c_data; if(getClassifiedId().isNull()) { - LLUUID id; - id.generate(); - setClassifiedId(id); + setClassifiedId(LLUUID::generateNewID()); } c_data.agent_id = gAgent.getID(); @@ -1902,6 +1961,14 @@ void LLPanelClassifiedEdit::sendUpdate() c_data.price_for_listing = getPriceForListing(); LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoUpdate(&c_data); + + if(isNew()) + { + // Lets assume there will be some error. + // Successful sendClassifiedInfoUpdate will trigger processProperties and + // let us know there was no error. + mIsNewWithErrors = true; + } } U32 LLPanelClassifiedEdit::getCategory() @@ -1910,6 +1977,13 @@ U32 LLPanelClassifiedEdit::getCategory() return cat_cb->getCurrentIndex(); } +void LLPanelClassifiedEdit::setCategory(U32 category) +{ + LLComboBox* cat_cb = getChild("category"); + cat_cb->setCurrentByIndex(category); + cat_cb->resetDirty(); +} + U8 LLPanelClassifiedEdit::getFlags() { bool auto_renew = childGetValue("auto_renew").asBoolean(); @@ -2005,17 +2079,14 @@ void LLPanelClassifiedEdit::onSaveClick() notifyInvalidName(); return; } - if(isNew()) + if(isNew() || isNewWithErrors()) { if(gStatusBar->getBalance() < getPriceForListing()) { LLNotificationsUtil::add("ClassifiedInsufficientFunds"); return; } - } - if(isNew()) - { mPublishFloater = LLFloaterReg::findTypedInstance( "publish_classified", LLSD()); diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index 58e7c9a4b4..1157649a16 100644 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -256,6 +256,8 @@ public: void setClassifiedLocation(const std::string& location); + std::string getClassifiedLocation(); + void setPosGlobal(const LLVector3d& pos) { mPosGlobal = pos; } LLVector3d& getPosGlobal() { return mPosGlobal; } @@ -366,6 +368,8 @@ public: /*virtual*/ BOOL postBuild(); + void fillIn(const LLSD& key); + /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); @@ -382,28 +386,38 @@ public: bool isNew() { return mIsNew; } + bool isNewWithErrors() { return mIsNewWithErrors; } + bool canClose(); void draw(); void stretchSnapshot(); + U32 getCategory(); + + void setCategory(U32 category); + + U32 getContentType(); + + void setContentType(U32 content_type); + + bool getAutoRenew(); + + S32 getPriceForListing(); + protected: LLPanelClassifiedEdit(); void sendUpdate(); - U32 getCategory(); - void enableVerbs(bool enable); void enableEditing(bool enable); std::string makeClassifiedName(); - S32 getPriceForListing(); - void setPriceForListing(S32 price); U8 getFlags(); @@ -429,6 +443,7 @@ protected: private: bool mIsNew; + bool mIsNewWithErrors; bool mCanClose; LLPublishClassifiedFloater* mPublishFloater; diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index ada770f78b..287896bf65 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -192,7 +192,6 @@ LLPanelPicks::LLPanelPicks() mPicksAccTab(NULL), mClassifiedsAccTab(NULL), mPanelClassifiedInfo(NULL), - mPanelClassifiedEdit(NULL), mNoClassifieds(false), mNoPicks(false) { @@ -385,6 +384,9 @@ BOOL LLPanelPicks::postBuild() registar.add("Pick.Teleport", boost::bind(&LLPanelPicks::onClickTeleport, this)); registar.add("Pick.Map", boost::bind(&LLPanelPicks::onClickMap, this)); registar.add("Pick.Delete", boost::bind(&LLPanelPicks::onClickDelete, this)); + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registar; + enable_registar.add("Pick.Enable", boost::bind(&LLPanelPicks::onEnableMenuItem, this, _2)); + mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_picks.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); LLUICtrl::CommitCallbackRegistry::ScopedRegistrar plus_registar; @@ -693,9 +695,10 @@ void LLPanelPicks::createNewPick() void LLPanelPicks::createNewClassified() { - createClassifiedEditPanel(); + LLPanelClassifiedEdit* panel = NULL; + createClassifiedEditPanel(&panel); - getProfilePanel()->openPanel(mPanelClassifiedEdit, LLSD()); + getProfilePanel()->openPanel(panel, LLSD()); } void LLPanelPicks::onClickInfo() @@ -780,11 +783,10 @@ void LLPanelPicks::onPanelClassifiedSave(LLPanelClassifiedEdit* panel) if(panel->isNew()) { + mEditClassifiedPanels[panel->getClassifiedId()] = panel; + LLClassifiedItem* c_item = new LLClassifiedItem(getAvatarId(), panel->getClassifiedId()); - - c_item->setClassifiedName(panel->getClassifiedName()); - c_item->setDescription(panel->getDescription()); - c_item->setSnapshotId(panel->getSnapshotId()); + c_item->fillIn(panel); LLSD c_value; c_value.insert(CLASSIFIED_ID, c_item->getClassifiedId()); @@ -800,6 +802,11 @@ void LLPanelPicks::onPanelClassifiedSave(LLPanelClassifiedEdit* panel) mClassifiedsAccTab->changeOpenClose(false); showAccordion("tab_classifieds", true); } + else if(panel->isNewWithErrors()) + { + LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); + c_item->fillIn(panel); + } else { onPanelClassifiedClose(panel); @@ -860,15 +867,16 @@ void LLPanelPicks::createClassifiedInfoPanel() } } -void LLPanelPicks::createClassifiedEditPanel() +void LLPanelPicks::createClassifiedEditPanel(LLPanelClassifiedEdit** panel) { - if(!mPanelClassifiedEdit) + if(panel) { - mPanelClassifiedEdit = LLPanelClassifiedEdit::create(); - mPanelClassifiedEdit->setExitCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, mPanelClassifiedEdit)); - mPanelClassifiedEdit->setSaveCallback(boost::bind(&LLPanelPicks::onPanelClassifiedSave, this, mPanelClassifiedEdit)); - mPanelClassifiedEdit->setCancelCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, mPanelClassifiedEdit)); - mPanelClassifiedEdit->setVisible(FALSE); + LLPanelClassifiedEdit* new_panel = LLPanelClassifiedEdit::create(); + new_panel->setExitCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, new_panel)); + new_panel->setSaveCallback(boost::bind(&LLPanelPicks::onPanelClassifiedSave, this, new_panel)); + new_panel->setCancelCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, new_panel)); + new_panel->setVisible(FALSE); + *panel = new_panel; } } @@ -941,16 +949,26 @@ void LLPanelPicks::onPanelClassifiedEdit() LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); - createClassifiedEditPanel(); - LLSD params; params["classified_id"] = c_item->getClassifiedId(); params["classified_creator_id"] = c_item->getAvatarId(); params["snapshot_id"] = c_item->getSnapshotId(); params["name"] = c_item->getClassifiedName(); params["desc"] = c_item->getDescription(); - - getProfilePanel()->openPanel(mPanelClassifiedEdit, params); + params["category"] = (S32)c_item->getCategory(); + params["content_type"] = (S32)c_item->getContentType(); + params["auto_renew"] = c_item->getAutoRenew(); + params["price_for_listing"] = c_item->getPriceForListing(); + params["location_text"] = c_item->getLocationText(); + + LLPanelClassifiedEdit* panel = mEditClassifiedPanels[c_item->getClassifiedId()]; + if(!panel) + { + createClassifiedEditPanel(&panel); + mEditClassifiedPanels[c_item->getClassifiedId()] = panel; + } + getProfilePanel()->openPanel(panel, params); + panel->setPosGlobal(c_item->getPosGlobal()); } void LLPanelPicks::onClickMenuEdit() @@ -965,6 +983,25 @@ void LLPanelPicks::onClickMenuEdit() } } +bool LLPanelPicks::onEnableMenuItem(const LLSD& user_data) +{ + std::string param = user_data.asString(); + + LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); + if(c_item) + { + LLPanelClassifiedEdit* panel = mEditClassifiedPanels[c_item->getClassifiedId()]; + + if(panel && "info" == param) + { + // dont show Info panel if classified was not created + return ! panel->isNewWithErrors(); + } + } + + return true; +} + inline LLPanelProfile* LLPanelPicks::getProfilePanel() { llassert_always(NULL != mProfilePanel); @@ -1153,6 +1190,24 @@ void LLClassifiedItem::setValue(const LLSD& value) childSetVisible("selected_icon", value["selected"]); } +void LLClassifiedItem::fillIn(LLPanelClassifiedEdit* panel) +{ + if(!panel) + { + return; + } + + setClassifiedName(panel->getClassifiedName()); + setDescription(panel->getDescription()); + setSnapshotId(panel->getSnapshotId()); + setCategory(panel->getCategory()); + setContentType(panel->getContentType()); + setAutoRenew(panel->getAutoRenew()); + setPriceForListing(panel->getPriceForListing()); + setPosGlobal(panel->getPosGlobal()); + setLocationText(panel->getClassifiedLocation()); +} + void LLClassifiedItem::setClassifiedName(const std::string& name) { childSetValue("name", name); diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h index 11e811275b..6d013035a2 100644 --- a/indra/newview/llpanelpicks.h +++ b/indra/newview/llpanelpicks.h @@ -114,6 +114,8 @@ private: void onPanelClassifiedEdit(); void onClickMenuEdit(); + bool onEnableMenuItem(const LLSD& user_data); + void createNewPick(); void createNewClassified(); @@ -140,7 +142,7 @@ private: void createPickInfoPanel(); void createPickEditPanel(); void createClassifiedInfoPanel(); - void createClassifiedEditPanel(); + void createClassifiedEditPanel(LLPanelClassifiedEdit** panel); LLMenuGL* mPopupMenu; LLPanelProfile* mProfilePanel; @@ -149,10 +151,16 @@ private: LLFlatListView* mClassifiedsList; LLPanelPickInfo* mPanelPickInfo; LLPanelClassifiedInfo* mPanelClassifiedInfo; - LLPanelClassifiedEdit* mPanelClassifiedEdit; LLPanelPickEdit* mPanelPickEdit; LLToggleableMenu* mPlusMenu; + // + typedef std::map panel_classified_edit_map_t; + + // This map is needed for newly created classifieds. The purpose of panel is to + // sit in this map and listen to LLPanelClassifiedEdit::processProperties callback. + panel_classified_edit_map_t mEditClassifiedPanels; + LLAccordionCtrlTab* mPicksAccTab; LLAccordionCtrlTab* mClassifiedsAccTab; @@ -245,6 +253,8 @@ public: /*virtual*/ void setValue(const LLSD& value); + void fillIn(LLPanelClassifiedEdit* panel); + LLUUID getAvatarId() {return mAvatarId;} void setAvatarId(const LLUUID& avatar_id) {mAvatarId = avatar_id;} @@ -255,7 +265,11 @@ public: void setPosGlobal(const LLVector3d& pos) { mPosGlobal = pos; } - const LLVector3d& getPosGlobal() { return mPosGlobal; } + const LLVector3d getPosGlobal() { return mPosGlobal; } + + void setLocationText(const std::string location) { mLocationText = location; } + + std::string getLocationText() { return mLocationText; } void setClassifiedName (const std::string& name); @@ -269,10 +283,31 @@ public: LLUUID getSnapshotId(); + void setCategory(U32 cat) { mCategory = cat; } + + U32 getCategory() { return mCategory; } + + void setContentType(U32 ct) { mContentType = ct; } + + U32 getContentType() { return mContentType; } + + void setAutoRenew(U32 renew) { mAutoRenew = renew; } + + bool getAutoRenew() { return mAutoRenew; } + + void setPriceForListing(S32 price) { mPriceForListing = price; } + + S32 getPriceForListing() { return mPriceForListing; } + private: LLUUID mAvatarId; LLUUID mClassifiedId; LLVector3d mPosGlobal; + std::string mLocationText; + U32 mCategory; + U32 mContentType; + bool mAutoRenew; + S32 mPriceForListing; }; #endif // LL_LLPANELPICKS_H diff --git a/indra/newview/skins/default/xui/en/menu_picks.xml b/indra/newview/skins/default/xui/en/menu_picks.xml index 7e07a97016..ebb49c9004 100644 --- a/indra/newview/skins/default/xui/en/menu_picks.xml +++ b/indra/newview/skins/default/xui/en/menu_picks.xml @@ -8,6 +8,9 @@ name="pick_info"> + + + + + -- cgit v1.2.3 From d6b32cd0a83385c10fa6a450337379aae5a04dd4 Mon Sep 17 00:00:00 2001 From: Dmitry Zaporozhan Date: Wed, 10 Mar 2010 11:24:22 +0200 Subject: Update for major bug EXT-5942 - Entering a Classified with a banned keyword doesn't publish, and doesn't let you edit. Updated code for disabling verbs for unpublished classified. --HG-- branch : product-engine --- indra/newview/llpanelpicks.cpp | 33 +++++++++++++++++++++++++-------- indra/newview/llpanelpicks.h | 2 ++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index 287896bf65..0a7c39db46 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -423,6 +423,22 @@ bool LLPanelPicks::isActionEnabled(const LLSD& userdata) const return true; } +bool LLPanelPicks::isClassifiedPublished(LLClassifiedItem* c_item) +{ + if(c_item) + { + LLPanelClassifiedEdit* panel = mEditClassifiedPanels[c_item->getClassifiedId()]; + if(panel) + { + return !panel->isNewWithErrors(); + } + + // we've got this classified from server - it's published + return true; + } + return false; +} + void LLPanelPicks::onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab) { if(!mPicksAccTab->getDisplayChildren()) @@ -659,6 +675,12 @@ void LLPanelPicks::updateButtons() childSetEnabled(XML_BTN_INFO, has_selected); childSetEnabled(XML_BTN_TELEPORT, has_selected); childSetEnabled(XML_BTN_SHOW_ON_MAP, has_selected); + + LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); + if(c_item) + { + childSetEnabled(XML_BTN_INFO, isClassifiedPublished(c_item)); + } } void LLPanelPicks::setProfilePanel(LLPanelProfile* profile_panel) @@ -988,15 +1010,10 @@ bool LLPanelPicks::onEnableMenuItem(const LLSD& user_data) std::string param = user_data.asString(); LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); - if(c_item) + if(c_item && "info" == param) { - LLPanelClassifiedEdit* panel = mEditClassifiedPanels[c_item->getClassifiedId()]; - - if(panel && "info" == param) - { - // dont show Info panel if classified was not created - return ! panel->isNewWithErrors(); - } + // dont show Info panel if classified was not created + return isClassifiedPublished(c_item); } return true; diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h index 6d013035a2..a98b8c413e 100644 --- a/indra/newview/llpanelpicks.h +++ b/indra/newview/llpanelpicks.h @@ -98,6 +98,8 @@ private: void onPlusMenuItemClicked(const LLSD& param); bool isActionEnabled(const LLSD& userdata) const; + bool isClassifiedPublished(LLClassifiedItem* c_item); + void onListCommit(const LLFlatListView* f_list); void onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab); -- cgit v1.2.3 From 983d633a3c2d41cef1e6a85a1d6a7432f091012b Mon Sep 17 00:00:00 2001 From: Lynx Linden Date: Wed, 10 Mar 2010 09:27:49 +0000 Subject: EXT-6114: Ignore secondlife:///app/login SLapps if logged in. If you were already logged in, clicking on these SLapps would crash your viewer (crash was in experimental reg-in-client code). We now ignore these SLapps if the user is already logged in, and a one-time warning is emitted in this case. --- indra/newview/llloginhandler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index 1be3430e07..e3817eecc4 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -35,6 +35,7 @@ #include "llloginhandler.h" // viewer includes +#include "lllogininstance.h" // to check if logged in yet #include "llpanellogin.h" // save_password_to_disk() #include "llstartup.h" // getStartupState() #include "llurlsimstring.h" @@ -169,6 +170,13 @@ bool LLLoginHandler::handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { + // do nothing if we are already logged in + if (LLLoginInstance::getInstance()->authSuccess()) + { + LL_WARNS_ONCE("SLURL") << "Already logged in! Ignoring login SLapp." << LL_ENDL; + return true; + } + if (tokens.size() == 1 && tokens[0].asString() == "show") { -- cgit v1.2.3 From e349c33fa91505f944bd0e24a5e3a26a5f51197f Mon Sep 17 00:00:00 2001 From: Eli Linden Date: Wed, 10 Mar 2010 10:53:17 -0800 Subject: linguistic change --- indra/newview/skins/default/xui/fr/floater_about_land.xml | 2 +- .../newview/skins/default/xui/fr/floater_preview_texture.xml | 2 +- indra/newview/skins/default/xui/fr/floater_tools.xml | 4 ++-- indra/newview/skins/default/xui/fr/menu_land.xml | 2 +- indra/newview/skins/default/xui/fr/panel_places.xml | 2 +- .../newview/skins/default/xui/fr/panel_preferences_chat.xml | 4 ++-- .../skins/default/xui/fr/panel_preferences_general.xml | 2 +- indra/newview/skins/default/xui/fr/role_actions.xml | 12 ++++++------ 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/indra/newview/skins/default/xui/fr/floater_about_land.xml b/indra/newview/skins/default/xui/fr/floater_about_land.xml index ac3bd17c85..b5acd6299a 100644 --- a/indra/newview/skins/default/xui/fr/floater_about_land.xml +++ b/indra/newview/skins/default/xui/fr/floater_about_land.xml @@ -1,5 +1,5 @@ - + "Parcel_PG_Dark" diff --git a/indra/newview/skins/default/xui/fr/floater_preview_texture.xml b/indra/newview/skins/default/xui/fr/floater_preview_texture.xml index 433dc944cf..9fc9d14026 100644 --- a/indra/newview/skins/default/xui/fr/floater_preview_texture.xml +++ b/indra/newview/skins/default/xui/fr/floater_preview_texture.xml @@ -28,7 +28,7 @@ 10:7 - + 3:2 diff --git a/indra/newview/skins/default/xui/fr/floater_tools.xml b/indra/newview/skins/default/xui/fr/floater_tools.xml index 64722ff1a7..40765b9e06 100644 --- a/indra/newview/skins/default/xui/fr/floater_tools.xml +++ b/indra/newview/skins/default/xui/fr/floater_tools.xml @@ -1,5 +1,5 @@ - + Pour faire tourner l'objet, faîtes glisser les bandes de couleur. @@ -474,7 +474,7 @@ Surface : [AREA] m² -