From 70956fb2dbc42501d27a474e3f80003d591ee646 Mon Sep 17 00:00:00 2001
From: Alexander Gavriliuk <alexandrgproductengine@lindenlab.com>
Date: Sat, 14 Oct 2023 00:11:06 +0200
Subject: SL-20163 Allow residents to hide exact join date on profiles

---
 indra/newview/llavatarpropertiesprocessor.h        |   1 +
 indra/newview/llpanelavatar.cpp                    |  42 ++++
 indra/newview/llpanelavatar.h                      |   4 +-
 indra/newview/llpanelprofile.cpp                   | 225 ++++++++-------------
 indra/newview/llpanelprofile.h                     |   5 +-
 .../default/xui/en/panel_profile_secondlife.xml    |  37 +++-
 6 files changed, 165 insertions(+), 149 deletions(-)

diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h
index f4e4252e61..c2347b9e35 100644
--- a/indra/newview/llavatarpropertiesprocessor.h
+++ b/indra/newview/llavatarpropertiesprocessor.h
@@ -86,6 +86,7 @@ struct LLAvatarData
 	U8			caption_index;
 	std::string	caption_text;
     std::string	customer_type;
+    bool		hide_sl_age;
 	U32			flags;
 };
 
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index ff33efe4aa..626978f9fb 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -127,6 +127,48 @@ void LLPanelProfileTab::setApplyProgress(bool started)
     }
 }
 
+static void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data)
+{
+    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("put_avatar_properties_coro", httpPolicy));
+    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+    LLCore::HttpHeaders::ptr_t httpHeaders;
+
+    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
+    httpOpts->setFollowRedirects(true);
+
+    std::string finalUrl = cap_url + "/" + agent_id.asString();
+
+    LLSD result = httpAdapter->putAndSuspend(httpRequest, finalUrl, data, httpOpts, httpHeaders);
+
+    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+    if (!status)
+    {
+        LL_WARNS("AvatarProperties") << "Failed to put agent information " << data << " for id " << agent_id << LL_ENDL;
+        return;
+    }
+
+    LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Data: " << data << " Result: " << httpResults << LL_ENDL;
+}
+
+bool LLPanelProfileTab::saveAgentUserInfoCoro(std::string name, LLSD value) const
+{
+    std::string cap_url = gAgent.getRegionCapability("AgentProfile");
+    if (cap_url.empty())
+    {
+        LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
+        return false;
+    }
+
+    LLCoros::instance().launch("putAgentUserInfoCoro",
+        boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with(name, value)));
+
+    return true;
+}
+
 LLPanelProfilePropertiesProcessorTab::LLPanelProfilePropertiesProcessorTab()
     : LLPanelProfileTab()
 {
diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h
index f182660c8e..572e1eb029 100644
--- a/indra/newview/llpanelavatar.h
+++ b/indra/newview/llpanelavatar.h
@@ -92,7 +92,7 @@ public:
     /**
      * Returns avatar ID.
      */
-    virtual const LLUUID& getAvatarId() { return mAvatarId; }
+    virtual const LLUUID& getAvatarId() const { return mAvatarId; }
 
     /**
      * Sends update data request to server.
@@ -133,6 +133,8 @@ protected:
 
     const bool getSelfProfile() const { return mSelfProfile; }
 
+    bool saveAgentUserInfoCoro(std::string name, LLSD value) const;
+
 public:
     void setIsLoading() { mLoadingState = PROFILE_LOADING; }
     void resetLoading() { mLoadingState = PROFILE_INIT; }
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 3333c832d2..1d81586c15 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -191,6 +191,8 @@ void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id)
         avatar_data->caption_text = result["caption"].asString();
     }
 
+    avatar_data->hide_sl_age = result["hide_sl_age"].asBoolean();
+
     panel = floater_profile->findChild<LLPanel>(PANEL_SECONDLIFE, TRUE);
     LLPanelProfileSecondLife *panel_sl = dynamic_cast<LLPanelProfileSecondLife*>(panel);
     if (panel_sl)
@@ -274,38 +276,6 @@ void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id)
     }
 }
 
-//TODO: changes take two minutes to propagate!
-// Add some storage that holds updated data for two minutes
-// for new instances to reuse the data
-// Profile data is only relevant to won avatar, but notes
-// are for everybody
-void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data)
-{
-    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
-    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
-        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("put_avatar_properties_coro", httpPolicy));
-    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
-    LLCore::HttpHeaders::ptr_t httpHeaders;
-
-    LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
-    httpOpts->setFollowRedirects(true);
-
-    std::string finalUrl = cap_url + "/" + agent_id.asString();
-
-    LLSD result = httpAdapter->putAndSuspend(httpRequest, finalUrl, data, httpOpts, httpHeaders);
-
-    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
-    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
-
-    if (!status)
-    {
-        LL_WARNS("AvatarProperties") << "Failed to put agent information " << data << " for id " << agent_id << LL_ENDL;
-        return;
-    }
-
-    LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Data: " << data << " Result: " << httpResults << LL_ENDL;
-}
-
 LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::string path_to_image, LLHandle<LLPanel> *handle)
 {
     LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
@@ -889,6 +859,7 @@ LLPanelProfileSecondLife::LLPanelProfileSecondLife()
     , mHasUnsavedDescriptionChanges(false)
     , mWaitingForImageUpload(false)
     , mAllowPublish(false)
+    , mHideSLAge(false)
 {
 }
 
@@ -914,6 +885,7 @@ BOOL LLPanelProfileSecondLife::postBuild()
 {
     mGroupList              = getChild<LLGroupList>("group_list");
     mShowInSearchCombo      = getChild<LLComboBox>("show_in_search");
+    mHideSLAgeCombo         = getChild<LLComboBox>("hide_sl_age");
     mSecondLifePic          = getChild<LLThumbnailCtrl>("2nd_life_pic");
     mSecondLifePicLayout    = getChild<LLPanel>("image_panel");
     mDescriptionEdit        = getChild<LLTextEditor>("sl_description_edit");
@@ -928,6 +900,7 @@ BOOL LLPanelProfileSecondLife::postBuild()
     mCantEditObjectsIcon    = getChild<LLIconCtrl>("cant_edit_objects");
 
     mShowInSearchCombo->setCommitCallback([this](LLUICtrl*, void*) { onShowInSearchCallback(); }, nullptr);
+    mHideSLAgeCombo->setCommitCallback([this](LLUICtrl*, void*) { onHideSLAgeCallback(); }, nullptr);
     mGroupList->setDoubleClickCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { LLPanelProfileSecondLife::openGroupProfile(); });
     mGroupList->setReturnCallback([this](LLUICtrl*, const LLSD&) { LLPanelProfileSecondLife::openGroupProfile(); });
     mSaveDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr);
@@ -1202,7 +1175,7 @@ void LLPanelProfileSecondLife::fillCommonData(const LLAvatarData* avatar_data)
     // and to make sure icons in text will be up to date
     LLAvatarIconIDCache::getInstance()->add(avatar_data->avatar_id, avatar_data->image_id);
 
-    fillAgeData(avatar_data->born_on);
+    fillAgeData(avatar_data);
 
     setDescriptionText(avatar_data->about_text);
 
@@ -1237,7 +1210,7 @@ void LLPanelProfileSecondLife::fillCommonData(const LLAvatarData* avatar_data)
     if (getSelfProfile())
     {
         mAllowPublish = avatar_data->flags & AVATAR_ALLOW_PUBLISH;
-        mShowInSearchCombo->setValue((BOOL)mAllowPublish);
+        mShowInSearchCombo->setValue(mAllowPublish ? TRUE : FALSE);
     }
 }
 
@@ -1362,21 +1335,44 @@ void LLPanelProfileSecondLife::fillRightsData()
     }
 }
 
-void LLPanelProfileSecondLife::fillAgeData(const LLDate &born_on)
+void LLPanelProfileSecondLife::fillAgeData(const LLAvatarData* avatar_data)
 {
     // Date from server comes already converted to stl timezone,
     // so display it as an UTC + 0
-    std::string name_and_date = getString("date_format");
+    std::string name_and_date = getString(avatar_data->hide_sl_age ? "date_format_short" : "date_format_full");
     LLSD args_name;
-    args_name["datetime"] = (S32)born_on.secondsSinceEpoch();
+    args_name["datetime"] = (S32)avatar_data->born_on.secondsSinceEpoch();
     LLStringUtil::format(name_and_date, args_name);
     getChild<LLUICtrl>("sl_birth_date")->setValue(name_and_date);
 
-    std::string register_date = getString("age_format");
-    LLSD args_age;
-    args_age["[AGE]"] = LLDateUtil::ageFromDate(born_on, LLDate::now());
-    LLStringUtil::format(register_date, args_age);
-    getChild<LLUICtrl>("user_age")->setValue(register_date);
+    LLUICtrl* userAgeCtrl = getChild<LLUICtrl>("user_age");
+    if (avatar_data->hide_sl_age)
+    {
+        userAgeCtrl->setVisible(FALSE);
+    }
+    else
+    {
+        std::string register_date = getString("age_format");
+        LLSD args_age;
+        args_age["[AGE]"] = LLDateUtil::ageFromDate(avatar_data->born_on, LLDate::now());
+        LLStringUtil::format(register_date, args_age);
+        userAgeCtrl->setValue(register_date);
+    }
+
+    if (getSelfProfile())
+    {
+        F64 birth = avatar_data->born_on.secondsSinceEpoch();
+        F64 now = LLDate::now().secondsSinceEpoch();
+        if (now - birth > 365 * 24 * 60 * 60)
+        {
+            mHideSLAge = avatar_data->hide_sl_age;
+            mHideSLAgeCombo->setValue(mHideSLAge ? TRUE : FALSE);
+        }
+        else
+        {
+            mHideSLAgeCombo->setVisible(FALSE);
+        }
+    }
 }
 
 void LLPanelProfileSecondLife::onImageLoaded(BOOL success, LLViewerFetchedTexture *imagep)
@@ -1493,6 +1489,10 @@ void LLPanelProfileSecondLife::setLoaded()
     if (getSelfProfile())
     {
         mShowInSearchCombo->setEnabled(TRUE);
+        if (mHideSLAgeCombo->getVisible())
+        {
+            mHideSLAgeCombo->setEnabled(TRUE);
+        }
         mDescriptionEdit->setEnabled(TRUE);
     }
 }
@@ -1820,39 +1820,28 @@ void LLPanelProfileSecondLife::onSetDescriptionDirty()
 
 void LLPanelProfileSecondLife::onShowInSearchCallback()
 {
-    S32 value = mShowInSearchCombo->getValue().asInteger();
-    if (mAllowPublish == (bool)value)
-    {
+    bool value = mShowInSearchCombo->getValue().asInteger();
+    if (value == mAllowPublish)
         return;
-    }
-    std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
-    if (!cap_url.empty())
-    {
-        mAllowPublish = value;
-        LLSD data;
-        data["allow_publish"] = mAllowPublish;
-        LLCoros::instance().launch("putAgentUserInfoCoro",
-            boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), data));
-    }
-    else
-    {
-        LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
-    }
+
+    mAllowPublish = value;
+    saveAgentUserInfoCoro("allow_publish", value);
+}
+
+void LLPanelProfileSecondLife::onHideSLAgeCallback()
+{
+    bool value = mHideSLAgeCombo->getValue().asInteger();
+    if (value == mHideSLAge)
+        return;
+
+    mHideSLAge = value;
+    saveAgentUserInfoCoro("hide_sl_age", value);
 }
 
 void LLPanelProfileSecondLife::onSaveDescriptionChanges()
 {
     mDescriptionText = mDescriptionEdit->getValue().asString();
-    std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
-    if (!cap_url.empty())
-    {
-        LLCoros::instance().launch("putAgentUserInfoCoro",
-            boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("sl_about_text", mDescriptionText)));
-    }
-    else
-    {
-        LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
-    }
+    saveAgentUserInfoCoro("sl_about_text", mDescriptionText);
 
     mSaveDescriptionChanges->setEnabled(FALSE);
     mDiscardDescriptionChanges->setEnabled(FALSE);
@@ -1987,46 +1976,34 @@ void LLPanelProfileSecondLife::onShowTexturePicker()
 void LLPanelProfileSecondLife::onCommitProfileImage(const LLUUID& id)
 {
     if (mImageId == id)
-    {
         return;
-    }
 
-    std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
-    if (!cap_url.empty())
+    if (!saveAgentUserInfoCoro("sl_image_id", id))
+        return;
+
+    mImageId = id;
+    if (mImageId == LLUUID::null)
+    {
+        mSecondLifePic->setValue("Generic_Person_Large");
+    }
+    else
     {
-        LLSD params;
-        params["sl_image_id"] = id;
-        LLCoros::instance().launch("putAgentUserInfoCoro",
-            boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params));
+        mSecondLifePic->setValue(mImageId);
+    }
 
-        mImageId = id;
+    LLFloater *floater = mFloaterProfileTextureHandle.get();
+    if (floater)
+    {
+        LLFloaterProfileTexture * texture_view = dynamic_cast<LLFloaterProfileTexture*>(floater);
         if (mImageId == LLUUID::null)
         {
-            mSecondLifePic->setValue("Generic_Person_Large");
+            texture_view->resetAsset();
         }
         else
         {
-            mSecondLifePic->setValue(mImageId);
-        }
-
-        LLFloater *floater = mFloaterProfileTextureHandle.get();
-        if (floater)
-        {
-            LLFloaterProfileTexture * texture_view = dynamic_cast<LLFloaterProfileTexture*>(floater);
-            if (mImageId == LLUUID::null)
-            {
-                texture_view->resetAsset();
-            }
-            else
-            {
-                texture_view->loadAsset(mImageId);
-            }
+            texture_view->loadAsset(mImageId);
         }
     }
-    else
-    {
-        LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
-    }
 }
 
 //////////////////////////////////////////////////////////////////////////
@@ -2324,34 +2301,22 @@ void LLPanelProfileFirstLife::onRemovePhoto()
 void LLPanelProfileFirstLife::onCommitPhoto(const LLUUID& id)
 {
     if (mImageId == id)
-    {
         return;
-    }
 
-    std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
-    if (!cap_url.empty())
-    {
-        LLSD params;
-        params["fl_image_id"] = id;
-        LLCoros::instance().launch("putAgentUserInfoCoro",
-            boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params));
-
-        mImageId = id;
-        if (mImageId.notNull())
-        {
-            mPicture->setValue(mImageId);
-        }
-        else
-        {
-            mPicture->setValue("Generic_Person_Large");
-        }
+    if (!saveAgentUserInfoCoro("fl_image_id", id))
+        return;
 
-        mRemovePhoto->setEnabled(mImageId.notNull());
+    mImageId = id;
+    if (mImageId.notNull())
+    {
+        mPicture->setValue(mImageId);
     }
     else
     {
-        LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
+        mPicture->setValue("Generic_Person_Large");
     }
+
+    mRemovePhoto->setEnabled(mImageId.notNull());
 }
 
 void LLPanelProfileFirstLife::setDescriptionText(const std::string &text)
@@ -2374,16 +2339,7 @@ void LLPanelProfileFirstLife::onSetDescriptionDirty()
 void LLPanelProfileFirstLife::onSaveDescriptionChanges()
 {
     mCurrentDescription = mDescriptionEdit->getValue().asString();
-    std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
-    if (!cap_url.empty())
-    {
-        LLCoros::instance().launch("putAgentUserInfoCoro",
-            boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("fl_about_text", mCurrentDescription)));
-    }
-    else
-    {
-        LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
-    }
+    saveAgentUserInfoCoro("fl_about_text", mCurrentDescription);
 
     mSaveChanges->setEnabled(FALSE);
     mDiscardChanges->setEnabled(FALSE);
@@ -2517,16 +2473,7 @@ void LLPanelProfileNotes::onSetNotesDirty()
 void LLPanelProfileNotes::onSaveNotesChanges()
 {
     mCurrentNotes = mNotesEditor->getValue().asString();
-    std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP);
-    if (!cap_url.empty())
-    {
-        LLCoros::instance().launch("putAgentUserInfoCoro",
-            boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("notes", mCurrentNotes)));
-    }
-    else
-    {
-        LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL;
-    }
+    saveAgentUserInfoCoro("notes", mCurrentNotes);
 
     mSaveChanges->setEnabled(FALSE);
     mDiscardChanges->setEnabled(FALSE);
diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h
index 11632a10ae..61ced27db5 100644
--- a/indra/newview/llpanelprofile.h
+++ b/indra/newview/llpanelprofile.h
@@ -143,7 +143,7 @@ protected:
     /**
      * Fills user name, display name, age.
      */
-    void fillAgeData(const LLDate &born_on);
+    void fillAgeData(const LLAvatarData* avatar_data);
 
     void onImageLoaded(BOOL success, LLViewerFetchedTexture *imagep);
     static void onImageLoaded(BOOL success,
@@ -179,6 +179,7 @@ private:
     void setDescriptionText(const std::string &text);
     void onSetDescriptionDirty();
     void onShowInSearchCallback();
+    void onHideSLAgeCallback();
     void onSaveDescriptionChanges();
     void onDiscardDescriptionChanges();
     void onShowAgentPermissionsDialog();
@@ -193,6 +194,7 @@ private:
 
 	LLGroupList*		mGroupList;
     LLComboBox*			mShowInSearchCombo;
+    LLComboBox*			mHideSLAgeCombo;
     LLThumbnailCtrl*	mSecondLifePic;
 	LLPanel*			mSecondLifePicLayout;
     LLTextEditor*		mDescriptionEdit;
@@ -214,6 +216,7 @@ private:
 	bool				mVoiceStatus;
     bool				mWaitingForImageUpload;
     bool				mAllowPublish;
+    bool				mHideSLAge;
     std::string			mDescriptionText;
     LLUUID				mImageId;
 
diff --git a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
index d50a223dd0..26cc04e6af 100644
--- a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
@@ -5,7 +5,7 @@
  top="0"
  left="0"
  height="480"
- width="420"
+ width="440"
  follows="all"
  layout="topleft"
 >
@@ -14,8 +14,11 @@
   so display it as an UTC+0
   -->
    <string 
-    name="date_format"
+    name="date_format_full"
     value="SL birthdate: [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]" />
+   <string 
+    name="date_format_short"
+    value="SL birthdate: [mth,datetime,utc] [day,datetime,utc]" />
    <string
     name="age_format"
     value="[AGE]" />
@@ -53,7 +56,7 @@ Account: [ACCTTYPE]
    top="8"
    left="6"
    bottom="-1"
-   width="160"
+   width="180"
    border_size="0"
    follows="left|top|bottom"
    layout="topleft"
@@ -335,7 +338,7 @@ Account: [ACCTTYPE]
        left="1"
        top="25"
        height="25"
-       width="140"
+       width="176"
        label="Actions"
        halign="left"
        image_unselected="DropDown_Off"
@@ -349,7 +352,7 @@ Account: [ACCTTYPE]
      name="settings_panel"
      follows="all"
      layout="topleft"
-     height="50"
+     height="80"
      auto_resize="false"
      user_resize="false">
       <!-- only for self -->
@@ -359,10 +362,9 @@ Account: [ACCTTYPE]
        left="1"
        top="18"
        height="23"
-       width="140"
+       width="176"
        follows="left|top"
        layout="topleft"
-       visible="true"
        enabled="false">
         <combo_box.item
            name="Show"
@@ -373,13 +375,32 @@ Account: [ACCTTYPE]
            label="Don't show me in search"
            value="0" />
       </combo_box>
+      <combo_box
+       name="hide_sl_age"
+       tool_tip="Let people see your SL age"
+       left="1"
+       top="48"
+       height="23"
+       width="176"
+       follows="left|top"
+       layout="topleft"
+       enabled="false">
+        <combo_box.item
+           name="Show"
+           label="Show birthdate + SL age"
+           value="0"/>
+        <combo_box.item
+           name="Hide"
+           label="Show month + day only"
+           value="1"/>
+      </combo_box>
     </layout_panel>
   </layout_stack>
 
   <layout_stack
    name="main_stack"
    top="8"
-   left="168"
+   left="188"
    bottom="-1"
    right="-1"
    follows="all"
-- 
cgit v1.2.3