forked from len0rd/rockbox
Theme Editor: Got save/save-as functionality working and added Tango icons to the toolbar
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26593 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
45ab395c2f
commit
47181b8b9b
11 changed files with 242 additions and 31 deletions
|
@ -96,9 +96,22 @@ void EditorWindow::setupMenus()
|
||||||
QObject::connect(ui->actionPreview_Panel, SIGNAL(triggered()),
|
QObject::connect(ui->actionPreview_Panel, SIGNAL(triggered()),
|
||||||
this, SLOT(showPanel()));
|
this, SLOT(showPanel()));
|
||||||
|
|
||||||
/* Connecting the document opening/closing actions */
|
/* Connecting the document management actions */
|
||||||
QObject::connect(ui->actionNew_Document, SIGNAL(triggered()),
|
QObject::connect(ui->actionNew_Document, SIGNAL(triggered()),
|
||||||
this, SLOT(newTab()));
|
this, SLOT(newTab()));
|
||||||
|
QObject::connect(ui->actionToolbarNew, SIGNAL(triggered()),
|
||||||
|
this, SLOT(newTab()));
|
||||||
|
|
||||||
|
QObject::connect(ui->actionClose_Document, SIGNAL(triggered()),
|
||||||
|
this, SLOT(closeCurrent()));
|
||||||
|
|
||||||
|
QObject::connect(ui->actionSave_Document, SIGNAL(triggered()),
|
||||||
|
this, SLOT(saveCurrent()));
|
||||||
|
QObject::connect(ui->actionSave_Document_As, SIGNAL(triggered()),
|
||||||
|
this, SLOT(saveCurrentAs()));
|
||||||
|
QObject::connect(ui->actionToolbarSave, SIGNAL(triggered()),
|
||||||
|
this, SLOT(saveCurrent()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,13 +128,23 @@ void EditorWindow::newTab()
|
||||||
void EditorWindow::shiftTab(int index)
|
void EditorWindow::shiftTab(int index)
|
||||||
{
|
{
|
||||||
if(index < 0)
|
if(index < 0)
|
||||||
|
{
|
||||||
ui->parseTree->setModel(0);
|
ui->parseTree->setModel(0);
|
||||||
|
ui->actionSave_Document->setEnabled(false);
|
||||||
|
ui->actionSave_Document_As->setEnabled(false);
|
||||||
|
ui->actionClose_Document->setEnabled(false);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
ui->parseTree->setModel(dynamic_cast<SkinDocument*>
|
ui->parseTree->setModel(dynamic_cast<SkinDocument*>
|
||||||
(ui->editorTabs->currentWidget())->getModel());
|
(ui->editorTabs->currentWidget())->getModel());
|
||||||
|
ui->actionSave_Document->setEnabled(true);
|
||||||
|
ui->actionSave_Document_As->setEnabled(true);
|
||||||
|
ui->actionClose_Document->setEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorWindow::closeTab(int index)
|
bool EditorWindow::closeTab(int index)
|
||||||
{
|
{
|
||||||
SkinDocument* widget = dynamic_cast<SkinDocument*>
|
SkinDocument* widget = dynamic_cast<SkinDocument*>
|
||||||
(ui->editorTabs->widget(index));
|
(ui->editorTabs->widget(index));
|
||||||
|
@ -129,9 +152,30 @@ void EditorWindow::closeTab(int index)
|
||||||
{
|
{
|
||||||
ui->editorTabs->removeTab(index);
|
ui->editorTabs->removeTab(index);
|
||||||
widget->deleteLater();
|
widget->deleteLater();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorWindow::closeCurrent()
|
||||||
|
{
|
||||||
|
closeTab(ui->editorTabs->currentIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorWindow::saveCurrent()
|
||||||
|
{
|
||||||
|
if(ui->editorTabs->currentIndex() >= 0)
|
||||||
|
dynamic_cast<SkinDocument*>(ui->editorTabs->currentWidget())->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorWindow::saveCurrentAs()
|
||||||
|
{
|
||||||
|
if(ui->editorTabs->currentIndex() >= 0)
|
||||||
|
dynamic_cast<SkinDocument*>(ui->editorTabs->currentWidget())->saveAs();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void EditorWindow::tabTitleChanged(QString title)
|
void EditorWindow::tabTitleChanged(QString title)
|
||||||
{
|
{
|
||||||
SkinDocument* sender = dynamic_cast<SkinDocument*>(QObject::sender());
|
SkinDocument* sender = dynamic_cast<SkinDocument*>(QObject::sender());
|
||||||
|
@ -150,7 +194,20 @@ void EditorWindow::showPanel()
|
||||||
|
|
||||||
void EditorWindow::closeEvent(QCloseEvent* event)
|
void EditorWindow::closeEvent(QCloseEvent* event)
|
||||||
{
|
{
|
||||||
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
|
/* Closing all the tabs */
|
||||||
|
for(int i = 0; i < ui->editorTabs->count(); i++)
|
||||||
|
{
|
||||||
|
if(!dynamic_cast<SkinDocument*>
|
||||||
|
(ui->editorTabs->widget(i))->requestClose())
|
||||||
|
{
|
||||||
|
event->ignore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
event->accept();
|
event->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,10 @@ private slots:
|
||||||
void showPanel();
|
void showPanel();
|
||||||
void newTab();
|
void newTab();
|
||||||
void shiftTab(int index);
|
void shiftTab(int index);
|
||||||
void closeTab(int index);
|
bool closeTab(int index);
|
||||||
|
void closeCurrent();
|
||||||
|
void saveCurrent();
|
||||||
|
void saveCurrentAs();
|
||||||
void tabTitleChanged(QString title);
|
void tabTitleChanged(QString title);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<string>Rockbox Theme Editor</string>
|
<string>Rockbox Theme Editor</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset>
|
<iconset resource="resources.qrc">
|
||||||
<normaloff>:/resources/resources/windowicon.png</normaloff>:/resources/resources/windowicon.png</iconset>
|
<normaloff>:/resources/resources/windowicon.png</normaloff>:/resources/resources/windowicon.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
@ -50,6 +50,11 @@
|
||||||
<addaction name="actionNew_Document"/>
|
<addaction name="actionNew_Document"/>
|
||||||
<addaction name="actionOpen_Document"/>
|
<addaction name="actionOpen_Document"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionClose_Document"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionSave_Document"/>
|
||||||
|
<addaction name="actionSave_Document_As"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
<addaction name="actionPreferences"/>
|
<addaction name="actionPreferences"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionQuit"/>
|
<addaction name="actionQuit"/>
|
||||||
|
@ -91,6 +96,9 @@
|
||||||
<attribute name="toolBarBreak">
|
<attribute name="toolBarBreak">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</attribute>
|
</attribute>
|
||||||
|
<addaction name="actionToolbarNew"/>
|
||||||
|
<addaction name="actionToolbarOpen"/>
|
||||||
|
<addaction name="actionToolbarSave"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QDockWidget" name="fileDock">
|
<widget class="QDockWidget" name="fileDock">
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -147,14 +155,14 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Parse &Tree Panel</string>
|
<string>Parse &Tree Panel</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
|
||||||
<string>Ctrl+D</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
</action>
|
||||||
<action name="actionPreferences">
|
<action name="actionPreferences">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Preferences</string>
|
<string>&Preferences</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+P</string>
|
||||||
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionFile_Panel">
|
<action name="actionFile_Panel">
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
|
@ -194,8 +202,79 @@
|
||||||
<string>Ctrl+O</string>
|
<string>Ctrl+O</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionSave_Document">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Save Document</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+S</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionClose_Document">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Close Document</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+W</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSave_Document_As">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save Document &As</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+Shift+S</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionToolbarNew">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="resources.qrc">
|
||||||
|
<normaloff>:/resources/resources/document-new.png</normaloff>:/resources/resources/document-new.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>ToolbarNew</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>New</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionToolbarOpen">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="resources.qrc">
|
||||||
|
<normaloff>:/resources/resources/document-open.png</normaloff>:/resources/resources/document-open.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>ToolbarOpen</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Open</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionToolbarSave">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="resources.qrc">
|
||||||
|
<normaloff>:/resources/resources/document-save.png</normaloff>:/resources/resources/document-save.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>ToolbarSave</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Save</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources>
|
||||||
|
<include location="resources.qrc"/>
|
||||||
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
<sender>actionQuit</sender>
|
<sender>actionQuit</sender>
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/resources">
|
<qresource prefix="/resources">
|
||||||
<file>resources/windowicon.png</file>
|
<file>resources/windowicon.png</file>
|
||||||
|
<file>resources/document-new.png</file>
|
||||||
|
<file>resources/document-open.png</file>
|
||||||
|
<file>resources/document-save.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
6
utils/themeeditor/resources/COPYING
Normal file
6
utils/themeeditor/resources/COPYING
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
The files appicon.xcf and windowicon.png are authored by Robert Bieber, and
|
||||||
|
made available in the public domain.
|
||||||
|
|
||||||
|
The files document-new.png, document-open.png, and document-save.png came from
|
||||||
|
the Tango Desktop Project (http://www.tango.freedesktop.org) and are also in
|
||||||
|
the public domain.
|
BIN
utils/themeeditor/resources/document-new.png
Normal file
BIN
utils/themeeditor/resources/document-new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1,008 B |
BIN
utils/themeeditor/resources/document-open.png
Normal file
BIN
utils/themeeditor/resources/document-open.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
utils/themeeditor/resources/document-save.png
Normal file
BIN
utils/themeeditor/resources/document-save.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
|
@ -28,13 +28,18 @@
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
|
||||||
SkinDocument::SkinDocument(QWidget *parent) :
|
SkinDocument::SkinDocument(QWidget *parent) :
|
||||||
QWidget(parent)
|
QWidget(parent), fileFilter(tr("WPS Files (*.wps *.rwps);;"
|
||||||
|
"SBS Files (*.sbs *.rsbs);;"
|
||||||
|
"FMS Files (*.fms *.rfms);;"
|
||||||
|
"All Skin Files (*.wps *.rwps *.sbs "
|
||||||
|
"*.rsbs *.fms *.rfms);;"
|
||||||
|
"All Files (*.*)"))
|
||||||
{
|
{
|
||||||
setupUI();
|
setupUI();
|
||||||
|
|
||||||
title = "Untitled";
|
title = "Untitled";
|
||||||
fileName = "";
|
fileName = "";
|
||||||
saved = true;
|
saved = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
SkinDocument::~SkinDocument()
|
SkinDocument::~SkinDocument()
|
||||||
|
@ -45,7 +50,35 @@ SkinDocument::~SkinDocument()
|
||||||
|
|
||||||
bool SkinDocument::requestClose()
|
bool SkinDocument::requestClose()
|
||||||
{
|
{
|
||||||
saveAs();
|
if(editor->document()->toPlainText() != saved)
|
||||||
|
{
|
||||||
|
/* Spawning the "Are you sure?" dialog */
|
||||||
|
QMessageBox confirm(this);
|
||||||
|
confirm.setText(title + tr(" has been modified."));
|
||||||
|
confirm.setInformativeText(tr("Do you want to save your changes?"));
|
||||||
|
confirm.setStandardButtons(QMessageBox::Save | QMessageBox::Discard
|
||||||
|
| QMessageBox::Cancel);
|
||||||
|
confirm.setDefaultButton(QMessageBox::Save);
|
||||||
|
int confirmation = confirm.exec();
|
||||||
|
|
||||||
|
switch(confirmation)
|
||||||
|
{
|
||||||
|
case QMessageBox::Save:
|
||||||
|
save();
|
||||||
|
/* After calling save, make sure the user actually went through */
|
||||||
|
if(editor->document()->toPlainText() != saved)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case QMessageBox::Discard:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case QMessageBox::Cancel:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,12 +107,23 @@ void SkinDocument::setupUI()
|
||||||
void SkinDocument::codeChanged()
|
void SkinDocument::codeChanged()
|
||||||
{
|
{
|
||||||
model->changeTree(editor->document()->toPlainText().toAscii());
|
model->changeTree(editor->document()->toPlainText().toAscii());
|
||||||
if(saved == true)
|
|
||||||
|
if(editor->document()->toPlainText() != saved)
|
||||||
{
|
{
|
||||||
saved = false;
|
if(title.length() > 0 && title[title.length() - 1] != '*')
|
||||||
title.append(tr("*"));
|
{
|
||||||
|
title.append('*');
|
||||||
emit titleChanged(title);
|
emit titleChanged(title);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(title.length() > 0 && title[title.length() - 1] == '*')
|
||||||
|
{
|
||||||
|
title.chop(1);
|
||||||
|
emit titleChanged(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinDocument::save()
|
void SkinDocument::save()
|
||||||
|
@ -88,7 +132,7 @@ void SkinDocument::save()
|
||||||
|
|
||||||
if(!fout.exists())
|
if(!fout.exists())
|
||||||
{
|
{
|
||||||
QTimer::singleShot(0, this, SLOT(saveAs()));
|
saveAs();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,22 +140,31 @@ void SkinDocument::save()
|
||||||
fout.write(editor->document()->toPlainText().toAscii());
|
fout.write(editor->document()->toPlainText().toAscii());
|
||||||
fout.close();
|
fout.close();
|
||||||
|
|
||||||
saved = true;
|
saved = editor->document()->toPlainText();
|
||||||
|
QStringList decompose = fileName.split('/');
|
||||||
|
title = decompose[decompose.count() - 1];
|
||||||
|
emit titleChanged(title);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinDocument::saveAs()
|
void SkinDocument::saveAs()
|
||||||
{
|
{
|
||||||
/* Determining the directory to open */
|
/* Determining the directory to open */
|
||||||
QSettings settings;
|
|
||||||
|
|
||||||
settings.beginGroup("SkinDocument");
|
|
||||||
QString openDir = settings.value("defaultDirectory", "").toString();
|
|
||||||
|
|
||||||
fileName = QFileDialog::getSaveFileName(this, tr("Save File"), openDir,"");
|
|
||||||
QString directory = fileName;
|
QString directory = fileName;
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("SkinDocument");
|
||||||
|
if(directory == "")
|
||||||
|
directory = settings.value("defaultDirectory", "").toString();
|
||||||
|
|
||||||
|
fileName = QFileDialog::getSaveFileName(this, tr("Save Document"),
|
||||||
|
directory, fileFilter);
|
||||||
|
directory = fileName;
|
||||||
|
if(fileName == "")
|
||||||
|
return;
|
||||||
|
|
||||||
directory.chop(fileName.length() - fileName.lastIndexOf('/') - 1);
|
directory.chop(fileName.length() - fileName.lastIndexOf('/') - 1);
|
||||||
settings.setValue("defaultDirectory", directory);
|
settings.setValue("defaultDirectory", directory);
|
||||||
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
QFile fout(fileName);
|
QFile fout(fileName);
|
||||||
|
@ -119,5 +172,9 @@ void SkinDocument::saveAs()
|
||||||
fout.write(editor->document()->toPlainText().toAscii());
|
fout.write(editor->document()->toPlainText().toAscii());
|
||||||
fout.close();
|
fout.close();
|
||||||
|
|
||||||
saved = true;
|
saved = editor->document()->toPlainText();
|
||||||
|
QStringList decompose = fileName.split('/');
|
||||||
|
title = decompose[decompose.count() - 1];
|
||||||
|
emit titleChanged(title);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,21 +33,23 @@ class SkinDocument : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
const QString fileFilter;
|
||||||
|
|
||||||
|
|
||||||
SkinDocument(QWidget *parent = 0);
|
SkinDocument(QWidget *parent = 0);
|
||||||
virtual ~SkinDocument();
|
virtual ~SkinDocument();
|
||||||
|
|
||||||
ParseTreeModel* getModel(){ return model; }
|
ParseTreeModel* getModel(){ return model; }
|
||||||
QString getTitle(){ return title; }
|
QString getTitle(){ return title; }
|
||||||
|
|
||||||
|
void save();
|
||||||
|
void saveAs();
|
||||||
|
|
||||||
bool requestClose();
|
bool requestClose();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void titleChanged(QString);
|
void titleChanged(QString);
|
||||||
|
|
||||||
public slots:
|
|
||||||
void save();
|
|
||||||
void saveAs();
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void codeChanged();
|
void codeChanged();
|
||||||
|
|
||||||
|
@ -56,7 +58,7 @@ private:
|
||||||
|
|
||||||
QString title;
|
QString title;
|
||||||
QString fileName;
|
QString fileName;
|
||||||
bool saved;
|
QString saved;
|
||||||
|
|
||||||
QLayout* layout;
|
QLayout* layout;
|
||||||
QPlainTextEdit* editor;
|
QPlainTextEdit* editor;
|
||||||
|
|
|
@ -26,6 +26,10 @@ SOURCES += tag_table.c \
|
||||||
skindocument.cpp
|
skindocument.cpp
|
||||||
OTHER_FILES += README \
|
OTHER_FILES += README \
|
||||||
resources/windowicon.png \
|
resources/windowicon.png \
|
||||||
resources/appicon.xcf
|
resources/appicon.xcf \
|
||||||
|
resources/COPYING \
|
||||||
|
resources/document-save.png \
|
||||||
|
resources/document-open.png \
|
||||||
|
resources/document-new.png
|
||||||
FORMS += editorwindow.ui
|
FORMS += editorwindow.ui
|
||||||
RESOURCES += resources.qrc
|
RESOURCES += resources.qrc
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue