SL-12186 WIP Updating UI for camera controls, including presets

master
Mnikolenko Productengine 2019-11-04 20:35:34 +02:00
parent b371c5a35d
commit c75d443c83
19 changed files with 443 additions and 504 deletions

View File

@ -37,10 +37,10 @@
<real>6</real>
</array>
</map>
<key>CameraOffsetRearView</key>
<key>CameraOffsetFrontView</key>
<map>
<key>Comment</key>
<string>Initial camera offset from avatar in Rear View</string>
<string>Initial camera offset from avatar in Front View</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -74,10 +74,10 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>FocusOffsetRearView</key>
<key>FocusOffsetFrontView</key>
<map>
<key>Comment</key>
<string>Initial focus point offset relative to avatar for the camera preset Rear View (x-axis is forward)</string>
<string>Initial focus point offset relative to avatar for the camera preset Front View (x-axis is forward)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>

View File

@ -1,115 +0,0 @@
<llsd>
<map>
<key>AppearanceCameraMovement</key>
<map>
<key>Comment</key>
<string>When entering appearance editing mode, camera zooms in on currently selected portion of avatar</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>CameraAngle</key>
<map>
<key>Comment</key>
<string>Camera field of view angle (Radians)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>1.047197551</real>
</map>
<key>CameraOffsetBuild</key>
<map>
<key>Comment</key>
<string>Default camera position relative to focus point when entering build mode</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>-6</real>
<real>0</real>
<real>6</real>
</array>
</map>
<key>CameraOffsetRearView</key>
<map>
<key>Comment</key>
<string>Initial camera offset from avatar in Rear View</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>-3.0</real>
<real>0.0</real>
<real>-0.2</real>
</array>
</map>
<key>CameraOffsetScale</key>
<map>
<key>Comment</key>
<string>Scales the default offset</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>1</real>
</map>
<key>EditCameraMovement</key>
<map>
<key>Comment</key>
<string>When entering build mode, camera moves up above avatar</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FocusOffsetRearView</key>
<map>
<key>Comment</key>
<string>Initial focus point offset relative to avatar for the camera preset Rear View (x-axis is forward)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3D</string>
<key>Value</key>
<array>
<real>0.9</real>
<real>0.0</real>
<real>0.2</real>
</array>
</map>
<key>PresetCameraActive</key>
<map>
<key>Comment</key>
<string>Name of currently selected preference</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>Default</string>
</map>
<key>TrackFocusObject</key>
<map>
<key>Comment</key>
<string>Camera tracks last object zoomed on</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
</map>
</llsd>

View File

@ -37,10 +37,10 @@
<real>6</real>
</array>
</map>
<key>CameraOffsetRearView</key>
<key>CameraOffsetGroupView</key>
<map>
<key>Comment</key>
<string>Initial camera offset from avatar in Rear View</string>
<string>Initial camera offset from avatar in Side View</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -74,10 +74,10 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>FocusOffsetRearView</key>
<key>FocusOffsetGroupView</key>
<map>
<key>Comment</key>
<string>Initial focus point offset relative to avatar for the camera preset Rear View (x-axis is forward)</string>
<string>Initial focus point offset relative to avatar for the camera preset Side View (x-axis is forward)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>

View File

@ -1499,6 +1499,21 @@
<real>0.5</real>
</array>
</map>
<key>CameraOffsetCustomPreset</key>
<map>
<key>Comment</key>
<string>Initial camera offset from avatar for the custom camera preset</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3</string>
<key>Value</key>
<array>
<real>-3.0</real>
<real>0.0</real>
<real>0.75</real>
</array>
</map>
<key>CameraOffsetScale</key>
<map>
<key>Comment</key>
@ -4356,6 +4371,21 @@
<real>1.0</real>
</array>
</map>
<key>FocusOffsetCustomPreset</key>
<map>
<key>Comment</key>
<string>Initial focus point offset relative to avatar for the custom camera preset (x-axis is forward)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector3D</string>
<key>Value</key>
<array>
<real>1.0</real>
<real>0.0</real>
<real>1.0</real>
</array>
</map>
<key>FocusPosOnLogout</key>
<map>
<key>Comment</key>

View File

@ -210,10 +210,12 @@ void LLAgentCamera::init()
mCameraOffsetInitial[CAMERA_PRESET_REAR_VIEW] = gSavedSettings.getControl("CameraOffsetRearView");
mCameraOffsetInitial[CAMERA_PRESET_FRONT_VIEW] = gSavedSettings.getControl("CameraOffsetFrontView");
mCameraOffsetInitial[CAMERA_PRESET_GROUP_VIEW] = gSavedSettings.getControl("CameraOffsetGroupView");
mCameraOffsetInitial[CAMERA_PRESET_CUSTOM] = gSavedSettings.getControl("CameraOffsetCustomPreset");
mFocusOffsetInitial[CAMERA_PRESET_REAR_VIEW] = gSavedSettings.getControl("FocusOffsetRearView");
mFocusOffsetInitial[CAMERA_PRESET_FRONT_VIEW] = gSavedSettings.getControl("FocusOffsetFrontView");
mFocusOffsetInitial[CAMERA_PRESET_GROUP_VIEW] = gSavedSettings.getControl("FocusOffsetGroupView");
mFocusOffsetInitial[CAMERA_PRESET_CUSTOM] = gSavedSettings.getControl("FocusOffsetCustomPreset");
mCameraCollidePlane.clearVec();
mCurrentCameraDistance = getCameraOffsetInitial().magVec() * gSavedSettings.getF32("CameraOffsetScale");
@ -1936,6 +1938,21 @@ LLVector3 LLAgentCamera::getCameraOffsetInitial()
return convert_from_llsd<LLVector3>(mCameraOffsetInitial[mCameraPreset]->get(), TYPE_VEC3, "");
}
LLVector3d LLAgentCamera::getFocusOffsetInitial()
{
return convert_from_llsd<LLVector3d>(mFocusOffsetInitial[mCameraPreset]->get(), TYPE_VEC3D, "");
}
std::string LLAgentCamera::getCameraOffsetCtrlName()
{
return mCameraOffsetInitial[mCameraPreset]->getName();
}
std::string LLAgentCamera::getFocusOffsetCtrlName()
{
return mFocusOffsetInitial[mCameraPreset]->getName();
}
F32 LLAgentCamera::getCameraMaxZoomDistance()
{
// Ignore "DisableCameraConstraints", we don't want to be out of draw range when we focus onto objects or avatars

View File

@ -59,7 +59,7 @@ enum ECameraPreset
CAMERA_PRESET_GROUP_VIEW,
/** Current view when a preset is saved */
CAMERA_PRESET_CUSTOM0
CAMERA_PRESET_CUSTOM
};
//------------------------------------------------------------------------
@ -112,9 +112,14 @@ private:
//--------------------------------------------------------------------
public:
void switchCameraPreset(ECameraPreset preset);
private:
/** Determines default camera offset depending on the current camera preset */
LLVector3 getCameraOffsetInitial();
/** Determines default focus offset depending on the current camera preset */
LLVector3d getFocusOffsetInitial();
std::string getCameraOffsetCtrlName();
std::string getFocusOffsetCtrlName();
private:
/** Determines maximum camera distance from target for mouselook, opposite to LAND_MIN_ZOOM */
F32 getCameraMaxZoomDistance();

View File

@ -34,6 +34,7 @@
// Viewer includes
#include "llagent.h"
#include "llagentcamera.h"
#include "llpresetsmanager.h"
#include "lljoystickbutton.h"
#include "llviewercontrol.h"
#include "llviewercamera.h"
@ -53,7 +54,6 @@ const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
#define ORBIT "cam_rotate_stick"
#define PAN "cam_track_stick"
#define ZOOM "zoom"
#define PRESETS "preset_views_list"
#define CONTROLS "controls"
bool LLFloaterCamera::sFreeCamera = false;
@ -270,13 +270,7 @@ void LLFloaterCamera::onAvatarEditingAppearance(bool editing)
void LLFloaterCamera::handleAvatarEditingAppearance(bool editing)
{
//camera presets (rear, front, etc.)
getChildView("preset_views_list")->setEnabled(!editing);
getChildView("presets_btn")->setEnabled(!editing);
//camera modes (object view, mouselook view)
getChildView("camera_modes_list")->setEnabled(!editing);
getChildView("avatarview_btn")->setEnabled(!editing);
}
void LLFloaterCamera::update()
@ -323,6 +317,8 @@ void LLFloaterCamera::onOpen(const LLSD& key)
else
toPrevMode();
mClosed = FALSE;
populatePresetCombo();
}
void LLFloaterCamera::onClose(bool app_quitting)
@ -354,7 +350,7 @@ LLFloaterCamera::LLFloaterCamera(const LLSD& val)
{
LLHints::registerHintTarget("view_popup", getHandle());
mCommitCallbackRegistrar.add("CameraPresets.ChangeView", boost::bind(&LLFloaterCamera::onClickCameraItem, _2));
mCommitCallbackRegistrar.add("Presets.GoViewPrefs", boost::bind(&LLFloaterCamera::onViewButtonClick, this, _2));
mCommitCallbackRegistrar.add("CameraPresets.Save", boost::bind(&LLFloaterCamera::onSavePreset, this));
}
// virtual
@ -366,9 +362,11 @@ BOOL LLFloaterCamera::postBuild()
mZoom = findChild<LLPanelCameraZoom>(ZOOM);
mTrack = getChild<LLJoystickCameraTrack>(PAN);
assignButton2Mode(CAMERA_CTRL_MODE_MODES, "avatarview_btn");
assignButton2Mode(CAMERA_CTRL_MODE_PAN, "pan_btn");
assignButton2Mode(CAMERA_CTRL_MODE_PRESETS, "presets_btn");
getChild<LLTextBox>("precise_ctrs_label")->setShowCursorHand(false);
getChild<LLTextBox>("precise_ctrs_label")->setSoundFlags(LLView::MOUSE_UP);
getChild<LLTextBox>("precise_ctrs_label")->setClickedCallback(boost::bind(&LLFloaterReg::showInstance, "prefs_view_advanced", LLSD(), FALSE));
getChild<LLComboBox>("preset_combo")->setCommitCallback(boost::bind(&LLFloaterCamera::onCustomPresetSelected, this));
LLPresetsManager::getInstance()->setPresetListChangeCameraCallback(boost::bind(&LLFloaterCamera::populatePresetCombo, this));
update();
@ -387,24 +385,6 @@ F32 LLFloaterCamera::getCurrentTransparency()
}
void LLFloaterCamera::onViewButtonClick(const LLSD& user_data)
{
// bring up the prefs floater
LLFloater* prefsfloater = LLFloaterReg::showInstance("preferences");
if (prefsfloater)
{
// grab the 'view' panel from the preferences floater and
// bring it the front!
LLTabContainer* tabcontainer = prefsfloater->getChild<LLTabContainer>("pref core");
LLPanel* graphicspanel = prefsfloater->getChild<LLPanel>("view");
if (tabcontainer && graphicspanel)
{
tabcontainer->selectTabPanel(graphicspanel);
}
}
}
void LLFloaterCamera::fillFlatlistFromPanel (LLFlatListView* list, LLPanel* panel)
{
// copying child list and then iterating over a copy, because list itself
@ -473,13 +453,6 @@ void LLFloaterCamera::switchMode(ECameraControlMode mode)
switch (mode)
{
case CAMERA_CTRL_MODE_MODES:
if(sFreeCamera)
{
switchMode(CAMERA_CTRL_MODE_FREE_CAMERA);
}
break;
case CAMERA_CTRL_MODE_PAN:
sFreeCamera = false;
clear_camera_tool();
@ -503,36 +476,8 @@ void LLFloaterCamera::switchMode(ECameraControlMode mode)
}
}
void LLFloaterCamera::onClickBtn(ECameraControlMode mode)
{
// check for a click on active button
if (mCurrMode == mode) mMode2Button[mode]->setToggleState(TRUE);
switchMode(mode);
}
void LLFloaterCamera::assignButton2Mode(ECameraControlMode mode, const std::string& button_name)
{
LLButton* button = getChild<LLButton>(button_name);
button->setClickedCallback(boost::bind(&LLFloaterCamera::onClickBtn, this, mode));
mMode2Button[mode] = button;
}
void LLFloaterCamera::updateState()
{
getChildView(ZOOM)->setVisible(CAMERA_CTRL_MODE_PAN == mCurrMode);
bool show_presets = (CAMERA_CTRL_MODE_PRESETS == mCurrMode) || (CAMERA_CTRL_MODE_FREE_CAMERA == mCurrMode
&& CAMERA_CTRL_MODE_PRESETS == mPrevMode);
getChildView(PRESETS)->setVisible(show_presets);
bool show_camera_modes = CAMERA_CTRL_MODE_MODES == mCurrMode || (CAMERA_CTRL_MODE_FREE_CAMERA == mCurrMode
&& CAMERA_CTRL_MODE_MODES == mPrevMode);
getChildView("camera_modes_list")->setVisible( show_camera_modes);
updateItemsSelection();
if (CAMERA_CTRL_MODE_FREE_CAMERA == mCurrMode)
@ -552,11 +497,11 @@ void LLFloaterCamera::updateItemsSelection()
{
ECameraPreset preset = (ECameraPreset) gSavedSettings.getU32("CameraPreset");
LLSD argument;
argument["selected"] = preset == CAMERA_PRESET_REAR_VIEW;
argument["selected"] = (preset == CAMERA_PRESET_REAR_VIEW) && !sFreeCamera;
getChild<LLPanelCameraItem>("rear_view")->setValue(argument);
argument["selected"] = preset == CAMERA_PRESET_GROUP_VIEW;
argument["selected"] = (preset == CAMERA_PRESET_GROUP_VIEW) && !sFreeCamera;
getChild<LLPanelCameraItem>("group_view")->setValue(argument);
argument["selected"] = preset == CAMERA_PRESET_FRONT_VIEW;
argument["selected"] = (preset == CAMERA_PRESET_FRONT_VIEW) && !sFreeCamera;
getChild<LLPanelCameraItem>("front_view")->setValue(argument);
argument["selected"] = gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK;
getChild<LLPanelCameraItem>("mouselook_view")->setValue(argument);
@ -580,6 +525,9 @@ void LLFloaterCamera::onClickCameraItem(const LLSD& param)
}
else
{
LLFloaterCamera* camera_floater = LLFloaterCamera::findInstance();
if (camera_floater)
camera_floater->switchMode(CAMERA_CTRL_MODE_PAN);
switchToPreset(name);
}
@ -599,14 +547,17 @@ void LLFloaterCamera::switchToPreset(const std::string& name)
if ("rear_view" == name)
{
gAgentCamera.switchCameraPreset(CAMERA_PRESET_REAR_VIEW);
LLPresetsManager::getInstance()->loadPreset(PRESETS_CAMERA, PRESETS_REAR);
}
else if ("group_view" == name)
{
gAgentCamera.switchCameraPreset(CAMERA_PRESET_GROUP_VIEW);
LLPresetsManager::getInstance()->loadPreset(PRESETS_CAMERA, PRESETS_SIDE);
}
else if ("front_view" == name)
{
gAgentCamera.switchCameraPreset(CAMERA_PRESET_FRONT_VIEW);
LLPresetsManager::getInstance()->loadPreset(PRESETS_CAMERA, PRESETS_FRONT);
}
}
@ -617,3 +568,36 @@ void LLFloaterCamera::fromFreeToPresets()
switchMode(CAMERA_CTRL_MODE_PRESETS);
}
}
void LLFloaterCamera::populatePresetCombo()
{
LLPresetsManager::getInstance()->setPresetNamesInComboBox(PRESETS_CAMERA, getChild<LLComboBox>("preset_combo"), EDefaultOptions::DEFAULT_VIEWS_HIDE);
if ((ECameraPreset)gSavedSettings.getU32("CameraPreset") == CAMERA_PRESET_CUSTOM)
{
getChild<LLComboBox>("preset_combo")->selectByValue(gSavedSettings.getString("PresetCameraActive"));
}
else
{
std::string inactive_text = getString("inactive_combo_text");
getChild<LLComboBox>("preset_combo")->setLabel(inactive_text);// add(inactive_text, inactive_text, ADD_TOP);
}
}
void LLFloaterCamera::onSavePreset()
{
LLFloaterReg::hideInstance("delete_pref_preset", PRESETS_CAMERA);
LLFloaterReg::hideInstance("load_pref_preset", PRESETS_CAMERA);
LLFloaterReg::showInstance("save_pref_preset", PRESETS_CAMERA);
}
void LLFloaterCamera::onCustomPresetSelected()
{
std::string selected_preset = getChild<LLComboBox>("preset_combo")->getSelectedItemLabel();
if (gSavedSettings.getString("PresetCameraActive") != selected_preset && getString("inactive_combo_text") != selected_preset)
{
gAgentCamera.switchCameraPreset(CAMERA_PRESET_CUSTOM);
updateItemsSelection();
fromFreeToPresets();
LLPresetsManager::getInstance()->loadPreset(PRESETS_CAMERA, selected_preset);
}
}

View File

@ -39,7 +39,6 @@ class LLPanelCameraZoom;
enum ECameraControlMode
{
CAMERA_CTRL_MODE_MODES,
CAMERA_CTRL_MODE_PAN,
CAMERA_CTRL_MODE_FREE_CAMERA,
CAMERA_CTRL_MODE_PRESETS
@ -50,7 +49,6 @@ class LLFloaterCamera : public LLFloater
friend class LLFloaterReg;
public:
/* whether in free camera mode */
static bool inFreeCameraMode();
/* callback for camera items selection changing */
@ -77,6 +75,11 @@ public:
virtual void onOpen(const LLSD& key);
virtual void onClose(bool app_quitting);
void onSavePreset();
void onCustomPresetSelected();
void populatePresetCombo();
LLJoystickCameraRotate* mRotate;
LLPanelCameraZoom* mZoom;
LLJoystickCameraTrack* mTrack;
@ -112,9 +115,6 @@ private:
/* update camera modes items selection and camera preset items selection according to the currently selected preset */
void updateItemsSelection();
void onClickBtn(ECameraControlMode mode);
void assignButton2Mode(ECameraControlMode mode, const std::string& button_name);
// fills flatlist with items from given panel
void fillFlatlistFromPanel (LLFlatListView* list, LLPanel* panel);

View File

@ -25,6 +25,7 @@
*/
#include "llviewerprecompiledheaders.h"
#include "llagentcamera.h"
#include "llfloaterpreferenceviewadvanced.h"
#include "llfloater.h"
#include "llfloaterreg.h"
@ -36,40 +37,12 @@
LLFloaterPreferenceViewAdvanced::LLFloaterPreferenceViewAdvanced(const LLSD& key)
: LLFloater(key)
{
mCommitCallbackRegistrar.add("Cancel", boost::bind(&LLFloaterPreferenceViewAdvanced::onClickCancel, this));
mCommitCallbackRegistrar.add("CommitSettings", boost::bind(&LLFloaterPreferenceViewAdvanced::onCommitSettings, this));
mCommitCallbackRegistrar.add("Ok", boost::bind(&LLFloaterPreferenceViewAdvanced::onClickOk, this));
}
LLFloaterPreferenceViewAdvanced::~LLFloaterPreferenceViewAdvanced()
{}
void LLFloaterPreferenceViewAdvanced::onClickOk()
{
closeFloater();
}
void LLFloaterPreferenceViewAdvanced::onClickCancel()
{
gSavedSettings.setVector3("CameraOffsetRearView", mCameraSaved);
gSavedSettings.setVector3d("FocusOffsetRearView", mFocusSaved);
updateCameraControl(mCameraSaved);
updateFocusControl(mFocusSaved);
}
BOOL LLFloaterPreferenceViewAdvanced::postBuild()
{
mCameraSaved = gSavedSettings.getVector3("CameraOffsetRearView");
mFocusSaved = gSavedSettings.getVector3d("FocusOffsetRearView");
updateCameraControl(mCameraSaved);
updateFocusControl(mFocusSaved);
return TRUE;
}
void LLFloaterPreferenceViewAdvanced::updateCameraControl(const LLVector3& vector)
{
getChild<LLSpinCtrl>("camera_x")->setValue(vector[VX]);
@ -86,11 +59,8 @@ void LLFloaterPreferenceViewAdvanced::updateFocusControl(const LLVector3d& vecto
void LLFloaterPreferenceViewAdvanced::draw()
{
static LLCachedControl<LLVector3> camera(gSavedSettings, "CameraOffsetRearView");
static LLCachedControl<LLVector3d> focus(gSavedSettings, "FocusOffsetRearView");
updateCameraControl(camera);
updateFocusControl(focus);
updateCameraControl(gAgentCamera.getCameraOffsetInitial());
updateFocusControl(gAgentCamera.getFocusOffsetInitial());
LLFloater::draw();
}
@ -103,10 +73,10 @@ void LLFloaterPreferenceViewAdvanced::onCommitSettings()
vector.mV[VX] = (F32)getChild<LLUICtrl>("camera_x")->getValue().asReal();
vector.mV[VY] = (F32)getChild<LLUICtrl>("camera_y")->getValue().asReal();
vector.mV[VZ] = (F32)getChild<LLUICtrl>("camera_z")->getValue().asReal();
gSavedSettings.setVector3("CameraOffsetRearView", vector);
gSavedSettings.setVector3(gAgentCamera.getCameraOffsetCtrlName(), vector);
vector3d.mdV[VX] = (F32)getChild<LLUICtrl>("focus_x")->getValue().asReal();
vector3d.mdV[VY] = (F32)getChild<LLUICtrl>("focus_y")->getValue().asReal();
vector3d.mdV[VZ] = (F32)getChild<LLUICtrl>("focus_z")->getValue().asReal();
gSavedSettings.setVector3d("FocusOffsetRearView", vector3d);
gSavedSettings.setVector3d(gAgentCamera.getFocusOffsetCtrlName(), vector3d);
}

View File

@ -37,21 +37,14 @@ class LLFloaterPreferenceViewAdvanced
public:
LLFloaterPreferenceViewAdvanced(const LLSD& key);
virtual BOOL postBuild();
virtual void draw();
void onCommitSettings();
void onClickCancel();
void onClickOk();
void updateCameraControl(const LLVector3& vector);
void updateFocusControl(const LLVector3d& vector3d);
private:
virtual ~LLFloaterPreferenceViewAdvanced();
LLVector3 mCameraSaved;
LLVector3d mFocusSaved;
};
#endif //LLFLOATERPREFERENCEVIEWADVANCED_H

View File

@ -32,12 +32,14 @@
#include "llcombobox.h"
#include "llfloaterpreference.h"
#include "llfloaterreg.h"
#include "lllineeditor.h"
#include "llnotificationsutil.h"
#include "llpresetsmanager.h"
#include "llradiogroup.h"
#include "lltrans.h"
LLFloaterSavePrefPreset::LLFloaterSavePrefPreset(const LLSD &key)
: LLFloater(key)
: LLModalDialog(key)
{
}
@ -49,29 +51,39 @@ BOOL LLFloaterSavePrefPreset::postBuild()
{
preferences->addDependentFloater(this);
}
getChild<LLComboBox>("preset_combo")->setTextEntryCallback(boost::bind(&LLFloaterSavePrefPreset::onPresetNameEdited, this));
getChild<LLComboBox>("preset_combo")->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onPresetNameEdited, this));
getChild<LLButton>("save")->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onBtnSave, this));
mPresetCombo = getChild<LLComboBox>("preset_combo");
//mPresetCombo->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onPresetNameEdited, this));
mNameEditor = getChild<LLLineEditor>("preset_txt_editor");
mNameEditor->setKeystrokeCallback(boost::bind(&LLFloaterSavePrefPreset::onPresetNameEdited, this), NULL);
mSaveButton = getChild<LLButton>("save");
mSaveButton->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onBtnSave, this));
mSaveRadioGroup = getChild<LLRadioGroup>("radio_save_preset");
mSaveRadioGroup->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onSwitchSaveReplace, this));
getChild<LLButton>("cancel")->setCommitCallback(boost::bind(&LLFloaterSavePrefPreset::onBtnCancel, this));
LLPresetsManager::instance().setPresetListChangeCallback(boost::bind(&LLFloaterSavePrefPreset::onPresetsListChange, this));
mSaveButton = getChild<LLButton>("save");
mPresetCombo = getChild<LLComboBox>("preset_combo");
return TRUE;
}
void LLFloaterSavePrefPreset::onPresetNameEdited()
{
// Disable saving a preset having empty name.
std::string name = mPresetCombo->getSimple();
mSaveButton->setEnabled(!name.empty());
if (mSaveRadioGroup->getSelectedIndex() == 0)
{
// Disable saving a preset having empty name.
std::string name = mNameEditor->getValue();
mSaveButton->setEnabled(!name.empty());
}
}
void LLFloaterSavePrefPreset::onOpen(const LLSD& key)
{
LLModalDialog::onOpen(key);
mSubdirectory = key.asString();
std::string floater_title = getString(std::string("title_") + mSubdirectory);
@ -81,22 +93,41 @@ void LLFloaterSavePrefPreset::onOpen(const LLSD& key)
EDefaultOptions option = DEFAULT_HIDE;
LLPresetsManager::getInstance()->setPresetNamesInComboBox(mSubdirectory, mPresetCombo, option);
mSaveRadioGroup->setSelectedIndex(0);
onPresetNameEdited();
onSwitchSaveReplace();
}
void LLFloaterSavePrefPreset::onBtnSave()
{
std::string name = mPresetCombo->getSimple();
bool is_saving_new = mSaveRadioGroup->getSelectedIndex() == 0;
std::string name = is_saving_new ? mNameEditor->getValue() : mPresetCombo->getSimple();
if ((name == LLTrans::getString(PRESETS_DEFAULT)) || (name == PRESETS_DEFAULT))
{
LLNotificationsUtil::add("DefaultPresetNotSaved");
}
else if (!LLPresetsManager::getInstance()->savePreset(mSubdirectory, name))
else
{
LLSD args;
args["NAME"] = name;
LLNotificationsUtil::add("PresetNotSaved", args);
if (is_saving_new)
{
std::list<std::string> preset_names;
std::string presets_dir = LLPresetsManager::getInstance()->getPresetsDir(mSubdirectory);
LLPresetsManager::getInstance()->loadPresetNamesFromDir(presets_dir, preset_names, DEFAULT_HIDE);
if (std::find(preset_names.begin(), preset_names.end(), name) != preset_names.end())
{
LLSD args;
args["NAME"] = name;
LLNotificationsUtil::add("PresetAlreadyExists", args);
return;
}
}
if (!LLPresetsManager::getInstance()->savePreset(mSubdirectory, name))
{
LLSD args;
args["NAME"] = name;
LLNotificationsUtil::add("PresetNotSaved", args);
}
}
closeFloater();
@ -112,3 +143,20 @@ void LLFloaterSavePrefPreset::onBtnCancel()
{
closeFloater();
}
void LLFloaterSavePrefPreset::onSwitchSaveReplace()
{
bool is_saving_new = mSaveRadioGroup->getSelectedIndex() == 0;
std::string label = is_saving_new ? getString("btn_label_save") : getString("btn_label_replace");
mSaveButton->setLabel(label);
mNameEditor->setEnabled(is_saving_new);
mPresetCombo->setEnabled(!is_saving_new);
if (is_saving_new)
{
onPresetNameEdited();
}
else
{
mSaveButton->setEnabled(true);
}
}

View File

@ -28,11 +28,13 @@
#ifndef LL_LLFLOATERSAVEPREFPRESET_H
#define LL_LLFLOATERSAVEPREFPRESET_H
#include "llfloater.h"
#include "llmodaldialog.h"
class LLComboBox;
class LLRadioGroup;
class LLLineEditor;
class LLFloaterSavePrefPreset : public LLFloater
class LLFloaterSavePrefPreset : public LLModalDialog
{
public:
@ -43,8 +45,11 @@ public:
void onBtnSave();
void onBtnCancel();
void onSwitchSaveReplace();
private:
LLRadioGroup* mSaveRadioGroup;
LLLineEditor* mNameEditor;
LLComboBox* mPresetCombo;
LLButton* mSaveButton;

View File

@ -65,22 +65,22 @@ void LLPresetsManager::createMissingDefault(const std::string& subdirectory)
{
if(gDirUtilp->getLindenUserDir().empty())
{
return;
return;
}
std::string default_file = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR,
subdirectory, PRESETS_DEFAULT + ".xml");
subdirectory, PRESETS_DEFAULT + ".xml");
if (!gDirUtilp->fileExists(default_file))
{
LL_INFOS() << "No default preset found -- creating one at " << default_file << LL_ENDL;
// Write current settings as the default
savePreset(subdirectory, PRESETS_DEFAULT, true);
savePreset(subdirectory, PRESETS_DEFAULT, true);
}
else
{
LL_DEBUGS() << "default preset exists; no-op" << LL_ENDL;
}
else
{
LL_DEBUGS() << "default preset exists; no-op" << LL_ENDL;
}
}
void LLPresetsManager::startWatching(const std::string& subdirectory)
@ -99,7 +99,7 @@ void LLPresetsManager::startWatching(const std::string& subdirectory)
if (cntrl_ptr.isNull())
{
LL_WARNS("Init") << "Unable to set signal on global setting '" << ctrl_name
<< "'" << LL_ENDL;
<< "'" << LL_ENDL;
}
else
{
@ -120,25 +120,25 @@ std::string LLPresetsManager::getPresetsDir(const std::string& subdirectory)
if (!gDirUtilp->fileExists(dest_path))
LLFile::mkdir(dest_path);
if (PRESETS_CAMERA == subdirectory)
if (PRESETS_CAMERA == subdirectory)
{
std::string source_dir = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, PRESETS_CAMERA);
LLDirIterator dir_iter(source_dir, "*.xml");
bool found = true;
while (found)
{
std::string source_dir = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, PRESETS_CAMERA);
LLDirIterator dir_iter(source_dir, "*.xml");
bool found = true;
while (found)
{
std::string file;
found = dir_iter.next(file);
std::string file;
found = dir_iter.next(file);
if (found)
{
std::string source = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, PRESETS_CAMERA, file);
file = LLURI::escape(file);
std::string dest = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR, PRESETS_CAMERA, file);
LLFile::copy(source, dest);
}
if (found)
{
std::string source = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, PRESETS_CAMERA, file);
file = LLURI::escape(file);
std::string dest = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, PRESETS_DIR, PRESETS_CAMERA, file);
LLFile::copy(source, dest);
}
}
}
return dest_path;
}
@ -160,8 +160,15 @@ void LLPresetsManager::loadPresetNamesFromDir(const std::string& dir, preset_nam
{
std::string path = gDirUtilp->add(dir, file);
std::string name = LLURI::unescape(gDirUtilp->getBaseFileName(path, /*strip_exten = */ true));
LL_DEBUGS() << " Found preset '" << name << "'" << LL_ENDL;
LL_DEBUGS() << " Found preset '" << name << "'" << LL_ENDL;
if (default_option == DEFAULT_VIEWS_HIDE)
{
if (name == PRESETS_REAR || name == PRESETS_SIDE || name == PRESETS_FRONT)
{
continue;
}
}
if (PRESETS_DEFAULT != name)
{
mPresetNames.push_back(name);
@ -205,11 +212,15 @@ void LLPresetsManager::settingChanged()
{
setCameraDirty(true);
gSavedSettings.setString("PresetCameraActive", "");
// Hack call because this is a static routine
LLPresetsManager::getInstance()->triggerChangeCameraSignal();
static LLCachedControl<std::string> preset_camera_active(gSavedSettings, "PresetCameraActive", "");
std::string preset_name = preset_camera_active;
if (!preset_name.empty())
{
gSavedSettings.setString("PresetCameraActive", "");
// Hack call because this is a static routine
LLPresetsManager::getInstance()->triggerChangeCameraSignal();
}
}
void LLPresetsManager::getControlNames(std::vector<std::string>& names)
@ -222,8 +233,6 @@ void LLPresetsManager::getControlNames(std::vector<std::string>& names)
("AppearanceCameraMovement")
// From llagentcamera.cpp
("CameraOffsetBuild")
("CameraOffsetRearView")
("FocusOffsetRearView")
("CameraOffsetScale")
("TrackFocusObject")
;
@ -286,9 +295,25 @@ bool LLPresetsManager::savePreset(const std::string& subdirectory, std::string n
}
else
{
bool custom_camera_offsets = false;
if (subdirectory == PRESETS_CAMERA)
{
name_list.push_back(gAgentCamera.getCameraOffsetCtrlName());
name_list.push_back(gAgentCamera.getFocusOffsetCtrlName());
custom_camera_offsets = (name != PRESETS_REAR && name != PRESETS_SIDE && name != PRESETS_FRONT);
}
for (std::vector<std::string>::iterator it = name_list.begin(); it != name_list.end(); ++it)
{
std::string ctrl_name = *it;
std::string dest_ctrl_name = ctrl_name;
if (custom_camera_offsets && ctrl_name == gAgentCamera.getCameraOffsetCtrlName())
{
dest_ctrl_name = "CameraOffsetCustomPreset";
}
if (custom_camera_offsets && ctrl_name == gAgentCamera.getFocusOffsetCtrlName())
{
dest_ctrl_name = "FocusOffsetCustomPreset";
}
LLControlVariable* ctrl = gSavedSettings.getControl(ctrl_name).get();
if (ctrl)
{
@ -296,10 +321,10 @@ bool LLPresetsManager::savePreset(const std::string& subdirectory, std::string n
std::string type = LLControlGroup::typeEnumToString(ctrl->type());
LLSD value = ctrl->getValue();
paramsData[ctrl_name]["Comment"] = comment;
paramsData[ctrl_name]["Persist"] = 1;
paramsData[ctrl_name]["Type"] = type;
paramsData[ctrl_name]["Value"] = value;
paramsData[dest_ctrl_name]["Comment"] = comment;
paramsData[dest_ctrl_name]["Persist"] = 1;
paramsData[dest_ctrl_name]["Type"] = type;
paramsData[dest_ctrl_name]["Value"] = value;
}
}
}
@ -354,6 +379,7 @@ bool LLPresetsManager::setPresetNamesInComboBox(const std::string& subdirectory,
combo->clearRows();
std::string presets_dir = getPresetsDir(subdirectory);
if (!presets_dir.empty())
@ -368,7 +394,7 @@ bool LLPresetsManager::setPresetNamesInComboBox(const std::string& subdirectory,
for (std::list<std::string>::const_iterator it = preset_names.begin(); it != preset_names.end(); ++it)
{
const std::string& name = *it;
combo->add(name, LLSD().with(0, name));
combo->add(name, name);
}
}
else

View File

@ -36,11 +36,15 @@ static const std::string PRESETS_DEFAULT = "Default";
static const std::string PRESETS_DIR = "presets";
static const std::string PRESETS_GRAPHIC = "graphic";
static const std::string PRESETS_CAMERA = "camera";
static const std::string PRESETS_REAR = "Rear";
static const std::string PRESETS_FRONT = "Front";
static const std::string PRESETS_SIDE = "Side";
enum EDefaultOptions
{
DEFAULT_SHOW,
DEFAULT_TOP,
DEFAULT_VIEWS_HIDE,
DEFAULT_HIDE // Do not display "Default" in a list
};

View File

@ -7,7 +7,7 @@
legacy_header_height="18"
can_minimize="true"
can_close="true"
height="164"
height="135"
layout="topleft"
name="camera_floater"
help_topic="camera_floater"
@ -16,7 +16,7 @@
title="CAMERA CONTROLS"
chrome="true"
save_rect="true"
width="228">
width="400">
<floater.string
name="rotate_tooltip">
Rotate Camera Around Focus
@ -33,6 +33,7 @@
name="free_mode_title">
View Object
</floater.string>
<string name="inactive_combo_text">Use preset</string>
<panel
border="false"
height="123"
@ -41,130 +42,18 @@
top="0"
mouse_opaque="false"
name="controls"
width="226">
<panel
follows="all"
height="102"
layout="topleft"
left="8"
name="preset_views_list"
top="24"
width="212"
visible="false">
<panel_camera_item
name="front_view">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="front_view" />
<panel_camera_item.picture
image_name="Cam_Preset_Front_Off" />
<panel_camera_item.selected_picture
image_name="Cam_Preset_Front_On" />
<panel_camera_item.text
name="front_view_text">
Front View
</panel_camera_item.text>
</panel_camera_item>
<panel_camera_item
name="group_view"
top_pad="4">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="group_view" />
<panel_camera_item.picture
image_name="Cam_Preset_Side_Off" />
<panel_camera_item.selected_picture
image_name="Cam_Preset_Side_On" />
<panel_camera_item.text
name="side_view_text">
Side View
</panel_camera_item.text>
</panel_camera_item>
<panel_camera_item
name="rear_view"
layout="topleft"
top_pad="4">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="rear_view" />
<panel_camera_item.picture
image_name="Cam_Preset_Back_Off" />
<panel_camera_item.selected_picture
image_name="Cam_Preset_Back_On" />
<panel_camera_item.text
name="rear_view_text">
Rear View
</panel_camera_item.text>
</panel_camera_item>
</panel>
<panel
follows="all"
height="68"
layout="topleft"
left="8"
name="camera_modes_list"
top="24"
width="212"
visible="false">
<panel_camera_item
name="object_view">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="object_view" />
<panel_camera_item.text
name="object_view_text">
Object View
</panel_camera_item.text>
<panel_camera_item.picture
image_name="Object_View_Off" />
<panel_camera_item.selected_picture
image_name="Object_View_On" />
</panel_camera_item>
<panel_camera_item
name="mouselook_view"
layout="topleft">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="mouselook_view" />
<panel_camera_item.text
name="mouselook_view_text">
Mouselook View
</panel_camera_item.text>
<panel_camera_item.picture
image_name="MouseLook_View_Off" />
<panel_camera_item.selected_picture
image_name="MouseLook_View_On" />
</panel_camera_item>
</panel>
width="220">
<!--TODO: replace + - images -->
<panel
border="false"
class="camera_zoom_panel"
height="114"
height="123"
layout="topleft"
left="0"
mouse_opaque="false"
name="zoom"
top="0"
width="226">
<slider
can_edit_text="true"
control_name="CameraAngle"
decimal_digits="2"
follows="left|top"
height="16"
top="20"
increment="0.025"
initial_value="1.57"
layout="topleft"
label_width="112"
label="View angle"
left="10"
max_val="1.6"
min_val="0.17"
name="camera_fov"
show_text="false"
width="200" />
width="220">
<joystick_rotate
follows="top|left"
height="78"
@ -175,8 +64,8 @@
sound_flags="3"
visible="true"
tool_tip="Orbit camera around focus"
top_delta="20"
width="78" />
top="25"
width="78" />
<button
follows="top|left"
height="18"
@ -187,7 +76,7 @@
left_pad="14"
name="zoom_plus_btn"
width="18"
top="38">
top="23">
<commit_callback
function="Zoom.plus" />
<mouse_held_callback
@ -232,53 +121,128 @@
scale_image="false"
sound_flags="3"
tool_tip="Move camera up and down, left and right"
top="40"
top="25"
width="78"/>
<text
type="string"
length="1"
follows="left|top"
height="15"
layout="topleft"
left="41"
top_pad="9"
name="precise_ctrs_label"
width="200">
Use precise controls
</text>
</panel>
</panel>
<panel
border="false"
height="42"
follows="all"
height="102"
layout="topleft"
left="2"
top_pad="0"
name="buttons"
width="226">
<button
name="open_prefs_btn"
image_overlay="Icon_Gear_Foreground"
tool_tip = "Open view preferences"
layout="topleft"
left="70"
height="23"
width="28">
<button.commit_callback
function="Presets.GoViewPrefs" />
</button>
<button
height="23"
label=""
layout="topleft"
left_pad="1"
is_toggle="true"
image_overlay="PanOrbit_Off"
image_selected="PushButton_Selected_Press"
name="pan_btn"
tab_stop="false"
tool_tip="Orbit Zoom Pan"
width="25">
</button>
<button
height="23"
label=""
layout="topleft"
left_pad="1"
image_overlay="Cam_FreeCam_Off"
image_selected="PushButton_Selected_Press"
name="avatarview_btn"
tab_stop="false"
tool_tip="Camera modes"
width="25">
</button>
</panel>
left_pad="2"
name="buttons_panel"
top="22"
width="212">
<panel_camera_item
name="front_view"
tool_tip="Front View"
width="30">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="front_view" />
<panel_camera_item.picture
image_name="Cam_Preset_Front_Off" />
<panel_camera_item.selected_picture
image_name="Cam_Preset_Front_On" />
</panel_camera_item>
<panel_camera_item
name="group_view"
tool_tip="Side View"
width="30"
left_pad="4">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="group_view" />
<panel_camera_item.picture
image_name="Cam_Preset_Side_Off" />
<panel_camera_item.selected_picture
image_name="Cam_Preset_Side_On" />
</panel_camera_item>
<panel_camera_item
name="rear_view"
tool_tip="Rear View"
width="30"
left_pad="4">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
tool_tip="Rear View"
parameter="rear_view" />
<panel_camera_item.picture
image_name="Cam_Preset_Back_Off" />
<panel_camera_item.selected_picture
image_name="Cam_Preset_Back_On" />
</panel_camera_item>
<panel_camera_item
name="object_view"
tool_tip="Object View"
width="30"
left_pad="4">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="object_view" />
<panel_camera_item.picture
image_name="Object_View_Off" />
<panel_camera_item.selected_picture
image_name="Object_View_On" />
</panel_camera_item>
<panel_camera_item
name="mouselook_view"
tool_tip="Mouselook View"
width="30"
left_pad="4">
<panel_camera_item.mousedown_callback
function="CameraPresets.ChangeView"
parameter="mouselook_view" />
<panel_camera_item.picture
image_name="MouseLook_View_Off" />
<panel_camera_item.selected_picture
image_name="MouseLook_View_On" />
</panel_camera_item>
<combo_box
height="23"
left="0"
mouse_opaque="true"
name="preset_combo"
top_pad="10"
width="136">
<combo_box.item
label="Use preset"
name="Use preset"
value="default" />
</combo_box>
<icon
height="28"
width="28"
image_name="Command_Preferences_Icon"
layout="topleft"
mouse_opaque="true"
name="icon_gear"
tool_tip="My Camera Presets"
top_delta="0"
left_pad="5" />
<button
follows="top|left"
height="25"
label="Save as preset..."
layout="topleft"
left="0"
name="save_preset_btn"
top_pad="8"
width="150">
<button.commit_callback
function="CameraPresets.Save"/>
</button>
</panel>
</floater>

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
height="170"
height="128"
layout="topleft"
name="floaterpreferencesviewadvanced"
help_topic="floaterviewadvanced"
title="ADJUST CAMERA VIEW"
title="CAMERA POSITION"
save_rect="true"
width="280">
@ -113,28 +113,4 @@
<spinner.commit_callback
function="CommitSettings" />
</spinner>
<button
follows="left|top"
height="23"
label="OK"
layout="topleft"
left="90"
name="ok"
top_pad="30"
width="90">
<button.commit_callback
function="Ok"/>
</button>
<button
follows="left|top"
height="23"
label="Cancel"
layout="topleft"
left_pad="5"
name="cancel"
width="90">
<button.commit_callback
function="Cancel"/>
</button>
</floater>

View File

@ -1,51 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<floater
legacy_header_height="18"
height="145"
height="185"
help_topic="floater_save_preset"
layout="topleft"
name="save_pref_preset"
save_rect="true"
title="SAVE PREF PRESET"
width="300">
width="280">
<string name="title_graphic">Save Graphic Preset</string>
<string name="title_camera">Save Camera Preset</string>
<text
follows="top|left|right"
height="32"
<string name="btn_label_save">Save</string>
<string name="btn_label_replace">Replace</string>
<radio_group
height="85"
layout="topleft"
word_wrap="true"
left="20"
name="Preset"
top="30"
width="200">
Type a name for the preset or choose an existing preset.
</text>
top="15"
width="150"
name="radio_save_preset">
<radio_item
label="Save as a new preset"
name="new_preset"
top="10"
layout="topleft"
height="16"
value="0"/>
<radio_item
label="Replace a preset"
name="replace_preset"
layout="topleft"
top="70"
height="16"
value="1"/>
</radio_group>
<line_editor
commit_on_focus_lost = "true"
follows="top|left"
height="23"
layout="topleft"
left="41"
name="preset_txt_editor"
width="200"
top="45"/>
<combo_box
follows="top|left"
layout="topleft"
left="20"
left="41"
name="preset_combo"
top_delta="35"
allow_text_entry="true"
top_pad="35"
width="200"/>
<button
follows="top|left"
height="23"
height="25"
label="Save"
layout="topleft"
top_delta="40"
left="20"
left="25"
name="save"
width="70"/>
width="110"/>
<button
follows="bottom|right"
height="23"
height="25"
label="Cancel"
layout="topleft"
left_pad="20"
name="cancel"
width="70"/>
width="110"/>
</floater>

View File

@ -8355,6 +8355,18 @@ Error saving preset [NAME].
Can not overwrite default preset.
</notification>
<notification
icon="alertmodal.tga"
name="PresetAlreadyExists"
type="alertmodal">
&apos;[NAME]&apos; is in use. You may replace
this preset or choose another name.
<tag>fail</tag>
<usetemplate
name="okbutton"
yestext="OK"/>
</notification>
<notification
icon="notifytip.tga"
name="PresetNotDeleted"

View File

@ -15,7 +15,7 @@
top="30"
scale_image="true"
visible="false"
width="212" />
width="30" />
<panel_camera_item.icon_selected
follows="top|left"
height="30"
@ -27,7 +27,7 @@
top="30"
scale_image="true"
visible="false"
width="212" />
width="30" />
<panel_camera_item.picture
follows="top|left"
height="30"