From 53e00bab4d44d644c2be0df82b5e5b8c2db34beb Mon Sep 17 00:00:00 2001 From: vsv Date: Mon, 4 Dec 2017 16:31:19 +0300 Subject: [PATCH] Provide a Choice control --- src/ModuleBase/CMakeLists.txt | 3 + src/ModuleBase/ModuleBase_ChoiceCtrl.cpp | 175 +++++++++++++++++++++ src/ModuleBase/ModuleBase_ChoiceCtrl.h | 77 +++++++++ src/ModuleBase/ModuleBase_WidgetChoice.cpp | 139 +++++----------- src/ModuleBase/ModuleBase_WidgetChoice.h | 11 +- src/XGUI/pictures/edge32.png | Bin 0 -> 412 bytes src/XGUI/pictures/face32.png | Bin 0 -> 772 bytes src/XGUI/pictures/solid32.png | Bin 0 -> 772 bytes src/XGUI/pictures/vertex32.png | Bin 0 -> 990 bytes 9 files changed, 297 insertions(+), 108 deletions(-) create mode 100644 src/ModuleBase/ModuleBase_ChoiceCtrl.cpp create mode 100644 src/ModuleBase/ModuleBase_ChoiceCtrl.h create mode 100644 src/XGUI/pictures/edge32.png create mode 100644 src/XGUI/pictures/face32.png create mode 100644 src/XGUI/pictures/solid32.png create mode 100644 src/XGUI/pictures/vertex32.png diff --git a/src/ModuleBase/CMakeLists.txt b/src/ModuleBase/CMakeLists.txt index cb40f7222..d536ef23d 100644 --- a/src/ModuleBase/CMakeLists.txt +++ b/src/ModuleBase/CMakeLists.txt @@ -94,6 +94,7 @@ SET(PROJECT_HEADERS ModuleBase_IconFactory.h ModuleBase_Dialog.h ModuleBase_ModelDialogWidget.h + ModuleBase_ChoiceCtrl.h ) SET(PROJECT_MOC_HEADERS @@ -138,6 +139,7 @@ SET(PROJECT_MOC_HEADERS ModuleBase_WidgetSwitch.h ModuleBase_WidgetToolbox.h ModuleBase_WidgetValidated.h + ModuleBase_ChoiceCtrl.h ) SET(PROJECT_SOURCES @@ -200,6 +202,7 @@ SET(PROJECT_SOURCES ModuleBase_IconFactory.cpp ModuleBase_SelectionValidator.cpp ModuleBase_Dialog.cpp + ModuleBase_ChoiceCtrl.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/ModuleBase/ModuleBase_ChoiceCtrl.cpp b/src/ModuleBase/ModuleBase_ChoiceCtrl.cpp new file mode 100644 index 000000000..4d915b801 --- /dev/null +++ b/src/ModuleBase/ModuleBase_ChoiceCtrl.cpp @@ -0,0 +1,175 @@ +// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or +// email : webmaster.salome@opencascade.com +// + +#include "ModuleBase_ChoiceCtrl.h" +#include "ModuleBase_Tools.h" +#include "ModuleBase_IconFactory.h" + +#include +#include +#include +#include +#include +#include +#include + +ModuleBase_ChoiceCtrl::ModuleBase_ChoiceCtrl(QWidget* theParent, + const QStringList& theChoiceList, + const QStringList& theIconsList, + ControlType theType, + Qt::Orientation theButtonsDir) + : QWidget(theParent), myType(theType) +{ + QHBoxLayout* aLayout = new QHBoxLayout(this); + ModuleBase_Tools::adjustMargins(aLayout); + + switch (myType) { + case RadioButtons: + { + myButtons = new QButtonGroup(this); + myGroupBox = new QGroupBox("", this); + aLayout->addWidget(myGroupBox); + + QLayout* aBtnLayout = 0; + switch (theButtonsDir) { + case Qt::Horizontal: + aBtnLayout = new QHBoxLayout(myGroupBox); + break; + case Qt::Vertical: + aBtnLayout = new QVBoxLayout(myGroupBox); + break; + } + ModuleBase_Tools::adjustMargins(aBtnLayout); + + if (theIconsList.length() == theChoiceList.length()) { + int aId = 0; + foreach(QString aBtnTxt, theChoiceList) { + QToolButton* aBtn = new QToolButton(myGroupBox); + aBtn->setFocusPolicy(Qt::StrongFocus); + aBtn->setCheckable(true); + aBtn->setToolTip(aBtnTxt); + + QPixmap aIcon = ModuleBase_IconFactory::loadPixmap(theIconsList.at(aId)); + aBtn->setIcon(aIcon); + aBtn->setIconSize(aIcon.size()); + + aBtnLayout->addWidget(aBtn); + myButtons->addButton(aBtn, aId++); + } + } else { + int aId = 0; + foreach(QString aBtnTxt, theChoiceList) { + QRadioButton* aBtn = new QRadioButton(aBtnTxt, myGroupBox); + aBtnLayout->addWidget(aBtn); + myButtons->addButton(aBtn, aId++); + } + } + connect(myButtons, SIGNAL(buttonClicked(int)), this, SIGNAL(valueChanged(int))); + } + break; + case ComboBox: + myLabel = new QLabel("", this); + aLayout->addWidget(myLabel); + + //std::string aToolstr = theData->widgetTooltip(); + //if (!aToolstr.empty()) { + // myLabel->setToolTip(QString::fromStdString(aToolstr)); + //} + + myCombo = new QComboBox(this); + aLayout->addWidget(myCombo, 1); + + myCombo->addItems(theChoiceList); + connect(myCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(valueChanged(int))); + break; + } +} + +void ModuleBase_ChoiceCtrl::setLabel(const QString& theText) +{ + switch (myType) { + case RadioButtons: + myGroupBox->setTitle(theText); + break; + case ComboBox: + myLabel->setText(theText); + break; + } +} + +void ModuleBase_ChoiceCtrl::setLabelIcon(const QString& theIcon) +{ + if (myType == ComboBox) + myLabel->setPixmap(ModuleBase_IconFactory::loadPixmap(theIcon)); +} + +void ModuleBase_ChoiceCtrl::setValue(int theVal) +{ + switch (myType) { + case RadioButtons: + myButtons->button(theVal)->setChecked(true); + break; + case ComboBox: + myCombo->setCurrentIndex(theVal); + break; + } +} + +void ModuleBase_ChoiceCtrl::setTooltip(QString theTip) +{ + if (myType == ComboBox) + myLabel->setToolTip(theTip); +} + +int ModuleBase_ChoiceCtrl::value() const +{ + switch (myType) { + case RadioButtons: + return myButtons->checkedId(); + case ComboBox: + return myCombo->currentIndex(); + } + return -1; +} + +bool ModuleBase_ChoiceCtrl::focusTo() +{ + if (myType == ComboBox) + ModuleBase_Tools::setFocus(myCombo, "ModuleBase_WidgetChoice::focusTo()"); + else + return false; + return true; +} + +QList ModuleBase_ChoiceCtrl::getControls() const +{ + QList aControls; + if (myType == ComboBox) + aControls.append(myCombo); + return aControls; +} + +void ModuleBase_ChoiceCtrl::setChoiceList(const QStringList& theChoiceList) +{ + if (myType == ComboBox) { + myCombo->clear(); + myCombo->addItems(theChoiceList); + } +} diff --git a/src/ModuleBase/ModuleBase_ChoiceCtrl.h b/src/ModuleBase/ModuleBase_ChoiceCtrl.h new file mode 100644 index 000000000..3f9c5de9b --- /dev/null +++ b/src/ModuleBase/ModuleBase_ChoiceCtrl.h @@ -0,0 +1,77 @@ +// Copyright (C) 2014-2017 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or +// email : webmaster.salome@opencascade.com +// + +#ifndef ModuleBase_ChoiceCtrl_H +#define ModuleBase_ChoiceCtrl_H + +#include "ModuleBase.h" + +#include +#include +#include + +class QLabel; +class QComboBox; +class QGroupBox; +class QButtonGroup; + +class MODULEBASE_EXPORT ModuleBase_ChoiceCtrl: public QWidget +{ +Q_OBJECT +public: + enum ControlType { + RadioButtons, + ComboBox + }; + + ModuleBase_ChoiceCtrl(QWidget* theParent, + const QStringList& theChoiceList, + const QStringList& theIconsList, + ControlType theType = RadioButtons, + Qt::Orientation theButtonsDir = Qt::Horizontal); + + void setLabel(const QString& theText); + + void setLabelIcon(const QString& theIcon); + + void setValue(int theVal); + + void setTooltip(QString theTip); + + int value() const; + + bool focusTo(); + + QList getControls() const; + + void setChoiceList(const QStringList& theChoiceList); + +signals: + void valueChanged(int theVal); + +private: + ControlType myType; + QLabel* myLabel; + QComboBox* myCombo; + QGroupBox* myGroupBox; + QButtonGroup* myButtons; +}; + +#endif \ No newline at end of file diff --git a/src/ModuleBase/ModuleBase_WidgetChoice.cpp b/src/ModuleBase/ModuleBase_WidgetChoice.cpp index 7ef0cfb54..c259ed324 100644 --- a/src/ModuleBase/ModuleBase_WidgetChoice.cpp +++ b/src/ModuleBase/ModuleBase_WidgetChoice.cpp @@ -21,6 +21,7 @@ #include "ModuleBase_WidgetChoice.h" #include "ModuleBase_Tools.h" #include "ModuleBase_IconFactory.h" +#include "ModuleBase_ChoiceCtrl.h" #include #include @@ -39,11 +40,8 @@ ModuleBase_WidgetChoice::ModuleBase_WidgetChoice(QWidget* theParent, const Config_WidgetAPI* theData) -: ModuleBase_ModelWidget(theParent, theData), myCombo(0), myButtons(0) +: ModuleBase_ModelWidget(theParent, theData)//, myCombo(0), myButtons(0) { - QHBoxLayout* aLayout = new QHBoxLayout(this); - ModuleBase_Tools::adjustMargins(aLayout); - QString aLabelText = translate(theData->widgetLabel()); QString aLabelIcon = QString::fromStdString(theData->widgetIcon()); std::string aTypes = theData->getProperty("string_list"); @@ -62,72 +60,28 @@ ModuleBase_WidgetChoice::ModuleBase_WidgetChoice(QWidget* theParent, bool aHasDefaultValue; int aDefaultVal = QString::fromStdString(getDefaultValue()).toInt(&aHasDefaultValue); + // Widget type can be combobox or radiobuttons std::string aWgtType = theData->getProperty("widget_type"); - if ((aWgtType.length() > 0) && (aWgtType == "radiobuttons")) { - myButtons = new QButtonGroup(this); - QGroupBox* aGroupBox = new QGroupBox(aLabelText, this); - aLayout->addWidget(aGroupBox); - - - QLayout* aBtnLayout = 0; - std::string aWgtDir = theData->getProperty("buttons_dir"); - if (aWgtDir == "horizontal") - aBtnLayout = new QHBoxLayout(aGroupBox); - else - aBtnLayout = new QVBoxLayout(aGroupBox); - ModuleBase_Tools::adjustMargins(aBtnLayout); - - std::string aIcons = theData->getProperty("icons_list"); - QStringList aIconList = QString(aIcons.c_str()).split(' '); - if (aIconList.length() == aList.length()) { - int aId = 0; - foreach(QString aBtnTxt, aList) { - QToolButton* aBtn = new QToolButton(aGroupBox); - aBtn->setFocusPolicy(Qt::StrongFocus); - aBtn->setCheckable(true); - aBtn->setToolTip(aBtnTxt); - - QPixmap aIcon = ModuleBase_IconFactory::loadPixmap(aIconList.at(aId)); - aBtn->setIcon(aIcon); - aBtn->setIconSize(aIcon.size()); - - aBtnLayout->addWidget(aBtn); - myButtons->addButton(aBtn, aId++); - } + std::string aIcons = theData->getProperty("icons_list"); + QStringList aIconList = QString(aIcons.c_str()).split(' '); - } else { - int aId = 0; - foreach(QString aBtnTxt, aList) { - QRadioButton* aBtn = new QRadioButton(aBtnTxt, aGroupBox); - aBtnLayout->addWidget(aBtn); - myButtons->addButton(aBtn, aId++); - } - } - int aCheckedId = aHasDefaultValue ? aDefaultVal : 0; - myButtons->button(aDefaultVal)->setChecked(true); - connect(myButtons, SIGNAL(buttonClicked(int)), this, SLOT(onCurrentIndexChanged(int))); - } else { - myLabel = new QLabel(aLabelText, this); - if (!aLabelIcon.isEmpty()) - myLabel->setPixmap(ModuleBase_IconFactory::loadPixmap(aLabelIcon)); - aLayout->addWidget(myLabel); - - std::string aToolstr = theData->widgetTooltip(); - if (!aToolstr.empty()) { - myLabel->setToolTip(QString::fromStdString(aToolstr)); - } + std::string aWgtDir = theData->getProperty("buttons_dir"); - myCombo = new QComboBox(this); - aLayout->addWidget(myCombo, 1); + QHBoxLayout* aLayout = new QHBoxLayout(this); + myChoiceCtrl = new ModuleBase_ChoiceCtrl(this, aList, aIconList, + (aWgtType == "radiobuttons")? ModuleBase_ChoiceCtrl::RadioButtons : ModuleBase_ChoiceCtrl::ComboBox, + (aWgtDir == "horizontal")? Qt::Horizontal : Qt::Vertical); + myChoiceCtrl->setLabel(aLabelText); - myCombo->addItems(aList); + if (!aLabelIcon.isEmpty()) + myChoiceCtrl->setLabelIcon(aLabelIcon); - if (aHasDefaultValue && aDefaultVal < aList.size()) - myCombo->setCurrentIndex(aDefaultVal); + connect(myChoiceCtrl, SIGNAL(valueChanged(int)), this, SLOT(onCurrentIndexChanged(int))); - connect(myCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); - } + int aCheckedId = aHasDefaultValue ? aDefaultVal : 0; + myChoiceCtrl->setValue(aCheckedId); + aLayout->addWidget(myChoiceCtrl); } ModuleBase_WidgetChoice::~ModuleBase_WidgetChoice() @@ -139,10 +93,7 @@ bool ModuleBase_WidgetChoice::storeValueCustom() DataPtr aData = myFeature->data(); std::shared_ptr aIntAttr = aData->integer(attributeID()); - if (myCombo) - aIntAttr->setValue(myCombo->currentIndex()); - else - aIntAttr->setValue(myButtons->checkedId()); + aIntAttr->setValue(myChoiceCtrl->value()); updateObject(myFeature); return true; } @@ -153,54 +104,38 @@ bool ModuleBase_WidgetChoice::restoreValueCustom() std::shared_ptr aIntAttr = aData->integer(attributeID()); if (aIntAttr->value() != -1) { - if (myCombo) { - bool isBlocked = myCombo->blockSignals(true); - if (myCombo->count() == 0 && !myStringListAttribute.empty()) { - AttributeStringArrayPtr aStrAttr = aData->stringArray(myStringListAttribute); - if (aStrAttr) { - for (int i = 0; i < aStrAttr->size(); i++) { - myCombo->insertItem(i, aStrAttr->value(i).c_str()); - } + bool isBlocked = myChoiceCtrl->blockSignals(true); + if (!myStringListAttribute.empty()) { + AttributeStringArrayPtr aStrAttr = aData->stringArray(myStringListAttribute); + QStringList aChoiceList; + if (aStrAttr) { + for (int i = 0; i < aStrAttr->size(); i++) { + aChoiceList << aStrAttr->value(i).c_str(); } + myChoiceCtrl->setChoiceList(aChoiceList); } - myCombo->setCurrentIndex(aIntAttr->value()); - myCombo->blockSignals(isBlocked); - } else { - bool isBlocked = myButtons->blockSignals(true); - if (aIntAttr->isInitialized()) - myButtons->button(aIntAttr->value())->setChecked(true); - else { - bool aHasDefaultValue; - int aDefaultVal = QString::fromStdString(getDefaultValue()).toInt(&aHasDefaultValue); - myButtons->button(aHasDefaultValue ? aDefaultVal : 0)->setChecked(true); - } - myButtons->blockSignals(isBlocked); - emit itemSelected(this, aIntAttr->value()); } + if (aIntAttr->isInitialized()) + myChoiceCtrl->setValue(aIntAttr->value()); + else { + bool aHasDefaultValue; + int aDefaultVal = QString::fromStdString(getDefaultValue()).toInt(&aHasDefaultValue); + myChoiceCtrl->setValue(aHasDefaultValue ? aDefaultVal : 0); + } + myChoiceCtrl->blockSignals(isBlocked); + emit itemSelected(this, aIntAttr->value()); } return true; } bool ModuleBase_WidgetChoice::focusTo() { - if (myCombo) - ModuleBase_Tools::setFocus(myCombo, "ModuleBase_WidgetChoice::focusTo()"); - else - return false; - return true; + return myChoiceCtrl->focusTo(); } QList ModuleBase_WidgetChoice::getControls() const { - QList aControls; - if (myCombo) - aControls.append(myCombo); - //else { - // //foreach(QAbstractButton* aBtn, myButtons->buttons()) - // //if (myButtons->checkedId() != -1) - // // aControls.append(myButtons->button(myButtons->checkedId())); - //} - return aControls; + return myChoiceCtrl->getControls(); } QString ModuleBase_WidgetChoice::getPropertyPanelTitle(int theIndex) diff --git a/src/ModuleBase/ModuleBase_WidgetChoice.h b/src/ModuleBase/ModuleBase_WidgetChoice.h index a6ac35cfc..de5b0e565 100644 --- a/src/ModuleBase/ModuleBase_WidgetChoice.h +++ b/src/ModuleBase/ModuleBase_WidgetChoice.h @@ -25,9 +25,7 @@ #include "ModuleBase_ModelWidget.h" class QWidget; -class QLabel; -class QComboBox; -class QButtonGroup; +class ModuleBase_ChoiceCtrl; /** * \ingroup GUI @@ -92,11 +90,12 @@ private slots: private: /// The label - QLabel* myLabel; + //QLabel* myLabel; /// The control - QComboBox* myCombo; - QButtonGroup* myButtons; + //QComboBox* myCombo; + //QButtonGroup* myButtons; + ModuleBase_ChoiceCtrl* myChoiceCtrl; // XML definition of titles QStringList myButtonTitles; diff --git a/src/XGUI/pictures/edge32.png b/src/XGUI/pictures/edge32.png new file mode 100644 index 0000000000000000000000000000000000000000..cb8bc1de836165b2f5f946360905d56687820056 GIT binary patch literal 412 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NS%G}c0*}aI zp!%yI%xHTp;v-N{vcxr_Bsf2KqO9WoGbt!CIRmj77k=5pzBy}c_HzOX3e?QMMEmic$4pZ6uTGGk7TpC2Z+ zbpHPwFk{uVXPo=9Qty4(dd>HJbQ@upi`5sR^oPLezR83l=>>h=Kk3TuxXm+}L zfPqtP`gIW@Ek>c}T)k^Hdiv@H9!w9n+px~LaP;P+8}eq?{`mOC-4KvaTR1Cu7SkVd z>B9}XTi-7f+F;Y^(I{44>3ODgg6tu`g+c|p?jO<6Vo+W)K|{qOWWj`n6%LAwfsIZ~ ziN2bZe?^J zG%heMF*(%MvSa`N0)a_HK~z{r?UYYw(_k3Jqem|)=$3X%8EXfMH^GDVv{{?5CjFP9 zU8geeAQnUg5u|ucA0>s02^+!okhYB_NxZ*+lO`wo+E$jNga z^%;$27&wz47+26_UB2?Q`%ksO^lR<*%v(daI+4*j`tFc$io){pb{Y>m=jG#Yvw&uk z(zb}Hmp?}L4R1XZfVaLIO(;>iJp91$0=?sDoL$y4d>2_EaFE6`4Chv# z&~WWQXLsq=<2M?zGI(VNLyu-xaRZKrka|VvpU&z$d`Y-I^@0%XxRl?awwEvi{w=dK zwjwadxx0^^E+6aZGrRyFJ4NPW4(ACE)7Xl@D8cS?tV=0a1YZHZfF;Bt1oDWr*}te0 z_=?mw2NwvQ&=-VrhjK{0sD4J^)B1$ir0@s$i++UpMJ%wD_r^Ag;$|9K5$6e}fN2aj zx#Y;Xa?s}@*mK>-IsFHM;;W$IrO!+ZlA6HB5?j7&ZN0000 CyIwW` literal 0 HcmV?d00001 diff --git a/src/XGUI/pictures/solid32.png b/src/XGUI/pictures/solid32.png new file mode 100644 index 0000000000000000000000000000000000000000..6db2ffdd6d94ec70e87806b466d44c18241c08fb GIT binary patch literal 772 zcmV+f1N;1mP)N2bZe?^J zG%heMF*(%MvSa`N0)a_HK~z{rV;BX47Kph72mrAIluJLL=o=v}=^kVu?i%!C2aGyk&^rKB5drIkpLBP?I$&`;Yu~xQz)})iOw-)~ z6Sp4ys}dLsE~Y__pt%EJmDY^iXMPvWTmfopfPDX7+$G?KtY>Hm-5ik8J?k&9+6Vhw z);s(&(DHmaudrO2IRI1hOY#9x5TzJ(ymu{wa30BE@f5X(ThqhK%s1_l6uB@1N~8Py~J0000N2bZe?^J zG%heMF*(%MvSa`N16xT%K~z{r?UqYOR8bhmCnD%E<>)*W%U+9U>n7Sv(>XI~>YOv9 zQxI62sE~>Z0wEzNh|&tN0yl0f3NdS9s_+~ z1Wfq3AVx9v1e^dKHGOdd!n1k7M6GLfcA1haEFf@03t3`Zeho(I zw&yb7+byoZz!yi(gLxS+0!&W%n7pgo0WL-YJl;~uQzb-I_|Qrs$i)5t69-Hl7k|KC z`&zau+57@Y>A#-IK^VBK+cx5T8E^DvF!0LcanTQ6+Sl?2e%(qwclg68H;Nusi62VB zVj}^uW^gfGKn{Zo!D!+n;pm_CaoMBK1>KJ)<4k}hH>)z)Lm9ZI#JcAgXf}fO_&Zg6 zQ1_vDA2^@Ez{9)@Krny=tg|W-ZrDmx?r2&?plB;FkEIhI6E8~Y>PiXRNQgk;+cbuo z8uLfw7F8yDC<8yWXchyldfd!o;tdSk%wXV>J&sc|9|ILkdXe-?iMDG4*3y6=!Zfdmh&nO#Ue$3fJg3#h(P|E@+4nf30BuO(pS zI2bl_xabAeL8K_`H4OvTEFt?S?$ttQe+K9Do^TjwArodE7r%jZ6cb9eut1{q45ty_ zLriX_Z!K&_Hlc;HQU70_GlKO@<2rBcf+7Be7Bujv-h zkLAGKiV4LiHXQC)oWj`Be3a^-cZH4pMO1-LazgI~HLV!M$TM87e^kIL^zGDSrvLx| M07*qoM6N<$f~63(p#T5? literal 0 HcmV?d00001 -- 2.39.2