Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc
Commits
1e80fd26
Commit
1e80fd26
authored
Nov 19, 2012
by
Francois Cartegnie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Qt: add fingerprinting ui
parent
0248a7fc
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
488 additions
and
2 deletions
+488
-2
modules/gui/qt4/Modules.am
modules/gui/qt4/Modules.am
+8
-0
modules/gui/qt4/adapters/chromaprint.cpp
modules/gui/qt4/adapters/chromaprint.cpp
+77
-0
modules/gui/qt4/adapters/chromaprint.hpp
modules/gui/qt4/adapters/chromaprint.hpp
+55
-0
modules/gui/qt4/components/info_panels.cpp
modules/gui/qt4/components/info_panels.cpp
+28
-2
modules/gui/qt4/components/info_panels.hpp
modules/gui/qt4/components/info_panels.hpp
+5
-0
modules/gui/qt4/dialogs/fingerprintdialog.cpp
modules/gui/qt4/dialogs/fingerprintdialog.cpp
+116
-0
modules/gui/qt4/dialogs/fingerprintdialog.hpp
modules/gui/qt4/dialogs/fingerprintdialog.hpp
+61
-0
modules/gui/qt4/ui/fingerprintdialog.ui
modules/gui/qt4/ui/fingerprintdialog.ui
+138
-0
No files found.
modules/gui/qt4/Modules.am
View file @
1e80fd26
...
...
@@ -23,6 +23,7 @@ nodist_SOURCES_qt4 = \
extensions_manager.moc.cpp \
recents.moc.cpp \
adapters/seekpoints.moc.cpp \
adapters/chromaprint.moc.cpp \
variables.moc.cpp \
dialogs/playlist.moc.cpp \
dialogs/bookmarks.moc.cpp \
...
...
@@ -45,6 +46,7 @@ nodist_SOURCES_qt4 = \
dialogs/vlm.moc.cpp \
dialogs/firstrun.moc.cpp \
dialogs/extensions.moc.cpp \
dialogs/fingerprintdialog.moc.cpp \
components/extended_panels.moc.cpp \
components/info_panels.moc.cpp \
components/info_widgets.moc.cpp \
...
...
@@ -98,6 +100,7 @@ nodist_SOURCES_qt4 = \
ui/messages_panel.h \
ui/about.h \
ui/update.h \
ui/fingerprintdialog.h \
styles/seekstyle.moc.cpp \
dialogs/ml_configuration.moc.cpp \
components/playlist/ml_model.moc.cpp \
...
...
@@ -282,6 +285,7 @@ SOURCES_qt4 = qt4.cpp \
extensions_manager.cpp \
recents.cpp \
adapters/seekpoints.cpp \
adapters/chromaprint.cpp \
variables.cpp \
dialogs/playlist.cpp \
dialogs/bookmarks.cpp \
...
...
@@ -305,6 +309,7 @@ SOURCES_qt4 = qt4.cpp \
dialogs/firstrun.cpp \
dialogs/podcast_configuration.cpp \
dialogs/extensions.cpp \
dialogs/fingerprintdialog.cpp \
components/extended_panels.cpp \
components/info_panels.cpp \
components/info_widgets.cpp \
...
...
@@ -359,6 +364,7 @@ noinst_HEADERS = \
extensions_manager.hpp \
recents.hpp \
adapters/seekpoints.hpp \
adapters/chromaprint.hpp \
variables.hpp \
dialogs/playlist.hpp \
dialogs/bookmarks.hpp \
...
...
@@ -382,6 +388,7 @@ noinst_HEADERS = \
dialogs/firstrun.hpp \
dialogs/podcast_configuration.hpp \
dialogs/extensions.hpp \
dialogs/fingerprintdialog.hpp \
components/extended_panels.hpp \
components/info_panels.hpp \
components/info_widgets.hpp \
...
...
@@ -450,4 +457,5 @@ EXTRA_DIST += \
ui/update.ui \
ui/sout.ui \
ui/vlm.ui \
ui/fingerprintdialog.ui \
$(DEPS_res)
modules/gui/qt4/adapters/chromaprint.cpp
0 → 100644
View file @
1e80fd26
/*****************************************************************************
* chromaprint.cpp: Fingerprinter helper class
*****************************************************************************
* Copyright (C) 2012 VLC authors and VideoLAN
*
* This program 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 program 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 program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "chromaprint.hpp"
#include <vlc_fingerprinter.h>
#include <vlc_modules.h>
Chromaprint
::
Chromaprint
(
intf_thread_t
*
_p_intf
)
:
p_intf
(
_p_intf
)
{
Q_ASSERT
(
p_intf
);
p_fingerprinter
=
fingerprinter_Create
(
VLC_OBJECT
(
p_intf
)
);
if
(
p_fingerprinter
)
var_AddCallback
(
p_fingerprinter
,
"results-available"
,
results_available
,
this
);
}
int
Chromaprint
::
results_available
(
vlc_object_t
*
,
const
char
*
,
vlc_value_t
,
vlc_value_t
,
void
*
param
)
{
Chromaprint
*
me
=
(
Chromaprint
*
)
param
;
me
->
finish
();
return
0
;
}
fingerprint_request_t
*
Chromaprint
::
fetchResults
()
{
return
p_fingerprinter
->
pf_getresults
(
p_fingerprinter
);
}
void
Chromaprint
::
apply
(
fingerprint_request_t
*
p_r
,
int
i_id
)
{
p_fingerprinter
->
pf_apply
(
p_r
,
i_id
);
}
bool
Chromaprint
::
enqueue
(
input_item_t
*
p_item
)
{
if
(
!
p_fingerprinter
)
return
false
;
fingerprint_request_t
*
p_r
=
fingerprint_request_New
(
p_item
);
if
(
!
p_r
)
return
false
;
mtime_t
t
=
input_item_GetDuration
(
p_item
);
if
(
t
)
p_r
->
i_duration
=
(
unsigned
int
)
(
t
/
1000000
);
p_fingerprinter
->
pf_enqueue
(
p_fingerprinter
,
p_r
);
return
true
;
}
bool
Chromaprint
::
isSupported
(
QString
uri
)
{
if
(
!
module_exists
(
"stream_out_chromaprint"
)
)
return
false
;
else
return
(
uri
.
startsWith
(
"file://"
)
||
uri
.
startsWith
(
"/"
)
);
}
Chromaprint
::~
Chromaprint
()
{
if
(
p_fingerprinter
)
fingerprinter_Destroy
(
p_fingerprinter
);
}
modules/gui/qt4/adapters/chromaprint.hpp
0 → 100644
View file @
1e80fd26
/*****************************************************************************
* chromaprint.hpp: Fingerprinter helper class
*****************************************************************************
* Copyright (C) 2012 VLC authors and VideoLAN
*
* This program 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 program 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 program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef CHROMAPRINT_HPP
#define CHROMAPRINT_HPP
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <QObject>
#include <QString>
#include <vlc_fingerprinter.h>
#include <vlc_interface.h>
class
Chromaprint
:
public
QObject
{
Q_OBJECT
public:
Chromaprint
(
intf_thread_t
*
p_intf
=
NULL
);
virtual
~
Chromaprint
();
bool
enqueue
(
input_item_t
*
p_item
);
static
int
results_available
(
vlc_object_t
*
p_this
,
const
char
*
,
vlc_value_t
,
vlc_value_t
newval
,
void
*
param
);
fingerprint_request_t
*
fetchResults
();
void
apply
(
fingerprint_request_t
*
,
int
i_id
);
static
bool
isSupported
(
QString
uri
);
signals:
void
finished
();
private:
void
finish
()
{
emit
finished
();
}
intf_thread_t
*
p_intf
;
fingerprinter_thread_t
*
p_fingerprinter
;
};
#endif // CHROMAPRINT_HPP
modules/gui/qt4/components/info_panels.cpp
View file @
1e80fd26
...
...
@@ -34,6 +34,8 @@
#include "components/info_panels.hpp"
#include "components/interface_widgets.hpp"
#include "info_widgets.hpp"
#include "dialogs/fingerprintdialog.hpp"
#include "adapters/chromaprint.hpp"
#include <assert.h>
#include <vlc_url.h>
...
...
@@ -129,7 +131,15 @@ MetaPanel::MetaPanel( QWidget *parent,
/* Language on the same line */
ADD_META
(
VLC_META_LANGUAGE
,
language_text
,
7
,
-
1
);
line
++
;
ADD_META
(
VLC_META_PUBLISHER
,
publisher_text
,
0
,
7
);
line
++
;
ADD_META
(
VLC_META_PUBLISHER
,
publisher_text
,
0
,
7
);
fingerprintButton
=
new
QPushButton
(
qtr
(
"&Fingerprint"
)
);
fingerprintButton
->
setToolTip
(
qtr
(
"Find meta data using audio fingerprinting"
)
);
fingerprintButton
->
setVisible
(
false
);
metaLayout
->
addWidget
(
fingerprintButton
,
line
,
7
,
3
,
-
1
);
CONNECT
(
fingerprintButton
,
clicked
(),
this
,
fingerprint
()
);
line
++
;
lblURL
=
new
QLabel
;
lblURL
->
setOpenExternalLinks
(
true
);
...
...
@@ -212,7 +222,8 @@ void MetaPanel::update( input_item_t *p_item )
/* URL / URI */
psz_meta
=
input_item_GetURI
(
p_item
);
if
(
!
EMPTY_STR
(
psz_meta
)
)
emit
uriSet
(
qfu
(
psz_meta
)
);
emit
uriSet
(
qfu
(
psz_meta
)
);
fingerprintButton
->
setVisible
(
Chromaprint
::
isSupported
(
QString
(
psz_meta
)
)
);
free
(
psz_meta
);
/* Other classic though */
...
...
@@ -337,11 +348,26 @@ void MetaPanel::clear()
publisher_text
->
clear
();
encodedby_text
->
clear
();
art_cover
->
clear
();
fingerprintButton
->
setVisible
(
false
);
setEditMode
(
false
);
emit
uriSet
(
""
);
}
void
MetaPanel
::
fingerprint
()
{
FingerprintDialog
*
dialog
=
new
FingerprintDialog
(
this
,
p_intf
,
p_input
);
CONNECT
(
dialog
,
metaApplied
(
input_item_t
*
),
this
,
fingerprintUpdate
(
input_item_t
*
)
);
dialog
->
setAttribute
(
Qt
::
WA_DeleteOnClose
,
true
);
dialog
->
show
();
}
void
MetaPanel
::
fingerprintUpdate
(
input_item_t
*
p_item
)
{
update
(
p_item
);
setEditMode
(
true
);
}
/**
* Second Panel - Shows the extra metadata in a tree, non editable.
**/
...
...
modules/gui/qt4/components/info_panels.hpp
View file @
1e80fd26
...
...
@@ -52,6 +52,7 @@ class CoverArtLabel;
class
QTextEdit
;
class
QLabel
;
class
VLCStatsView
;
class
QPushButton
;
class
MetaPanel
:
public
QWidget
{
...
...
@@ -88,9 +89,13 @@ private:
QLabel
*
lblURL
;
QString
currentURL
;
QPushButton
*
fingerprintButton
;
public
slots
:
void
update
(
input_item_t
*
);
void
clear
();
void
fingerprint
();
void
fingerprintUpdate
(
input_item_t
*
);
private
slots
:
void
enterEditMode
();
...
...
modules/gui/qt4/dialogs/fingerprintdialog.cpp
0 → 100644
View file @
1e80fd26
/*****************************************************************************
* fingerprintdialog.cpp: Fingerprinter Dialog
*****************************************************************************
* Copyright (C) 2012 VLC authors and VideoLAN
*
* This program 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 program 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 program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "fingerprintdialog.hpp"
#include "ui/fingerprintdialog.h"
#include "adapters/chromaprint.hpp"
#include <vlc_url.h>
#include <QLabel>
#include <QListWidgetItem>
FingerprintDialog
::
FingerprintDialog
(
QWidget
*
parent
,
intf_thread_t
*
p_intf
,
input_item_t
*
p_item
)
:
QDialog
(
parent
),
ui
(
new
Ui
::
FingerprintDialog
),
p_r
(
NULL
)
{
ui
->
setupUi
(
this
);
ui
->
stackedWidget
->
setCurrentWidget
(
ui
->
wait
);
ui
->
buttonBox
->
addButton
(
"&Close"
,
QDialogButtonBox
::
RejectRole
);
ui
->
buttonsBox
->
addButton
(
"&Apply this identity to the file"
,
QDialogButtonBox
::
AcceptRole
);
ui
->
buttonsBox
->
addButton
(
"&Discard all identities"
,
QDialogButtonBox
::
RejectRole
);
CONNECT
(
ui
->
buttonsBox
,
accepted
(),
this
,
applyIdentity
()
);
CONNECT
(
ui
->
buttonBox
,
rejected
(),
this
,
close
()
);
CONNECT
(
ui
->
buttonsBox
,
rejected
(),
this
,
close
()
);
t
=
new
Chromaprint
(
p_intf
);
if
(
t
)
{
CONNECT
(
t
,
finished
(),
this
,
handleResults
()
);
t
->
enqueue
(
p_item
);
}
}
void
FingerprintDialog
::
applyIdentity
()
{
Q_ASSERT
(
p_r
);
if
(
ui
->
recordsList
->
currentIndex
().
isValid
()
)
t
->
apply
(
p_r
,
ui
->
recordsList
->
currentIndex
().
row
()
);
emit
metaApplied
(
p_r
->
p_item
);
close
();
}
void
FingerprintDialog
::
handleResults
()
{
p_r
=
t
->
fetchResults
();
if
(
!
p_r
)
{
ui
->
stackedWidget
->
setCurrentWidget
(
ui
->
error
);
return
;
}
if
(
vlc_array_count
(
&
p_r
->
results
.
metas_array
)
==
0
)
{
fingerprint_request_Delete
(
p_r
);
p_r
=
NULL
;
ui
->
stackedWidget
->
setCurrentWidget
(
ui
->
error
);
return
;
}
ui
->
stackedWidget
->
setCurrentWidget
(
ui
->
results
);
for
(
int
i
=
0
;
i
<
vlc_array_count
(
&
p_r
->
results
.
metas_array
)
;
i
++
)
{
vlc_meta_t
*
p_meta
=
(
vlc_meta_t
*
)
vlc_array_item_at_index
(
&
p_r
->
results
.
metas_array
,
i
);
QListWidgetItem
*
item
=
new
QListWidgetItem
();
ui
->
recordsList
->
addItem
(
item
);
QString
mb_id
(
vlc_meta_GetExtra
(
p_meta
,
"musicbrainz-id"
)
);
QLabel
*
label
=
new
QLabel
(
QString
(
"<h3 style=
\"
margin: 0
\"
><a style=
\"
text-decoration:none
\"
href=
\"
%1
\"
>%2</a></h3>"
"<span style=
\"
padding-left:20px
\"
>%3</span>"
)
.
arg
(
QString
(
"http://mb.videolan.org/recording/%1"
).
arg
(
mb_id
)
)
.
arg
(
qfu
(
vlc_meta_Get
(
p_meta
,
vlc_meta_Title
)
)
)
.
arg
(
qfu
(
vlc_meta_Get
(
p_meta
,
vlc_meta_Artist
)
)
)
);
label
->
setSizePolicy
(
QSizePolicy
::
Expanding
,
QSizePolicy
::
Preferred
);
label
->
setOpenExternalLinks
(
true
);
item
->
setSizeHint
(
label
->
sizeHint
()
);
ui
->
recordsList
->
setItemWidget
(
item
,
label
);
}
ui
->
recordsList
->
setCurrentIndex
(
ui
->
recordsList
->
model
()
->
index
(
0
,
0
)
);
}
FingerprintDialog
::~
FingerprintDialog
()
{
if
(
t
)
delete
t
;
if
(
p_r
)
fingerprint_request_Delete
(
p_r
);
delete
ui
;
}
modules/gui/qt4/dialogs/fingerprintdialog.hpp
0 → 100644
View file @
1e80fd26
/*****************************************************************************
* fingerprintdialog.hpp: Fingerprinter Dialog
*****************************************************************************
* Copyright (C) 2012 VLC authors and VideoLAN
*
* This program 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 program 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 program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef FINGERPRINTDIALOG_HPP
#define FINGERPRINTDIALOG_HPP
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "qt4.hpp"
#include <QDialog>
#include <vlc_interface.h>
#include <vlc_fingerprinter.h>
namespace
Ui
{
class
FingerprintDialog
;
}
class
Chromaprint
;
class
FingerprintDialog
:
public
QDialog
{
Q_OBJECT
public:
FingerprintDialog
(
QWidget
*
parent
,
intf_thread_t
*
p_intf
,
input_item_t
*
p_item
);
~
FingerprintDialog
();
private:
Ui
::
FingerprintDialog
*
ui
;
Chromaprint
*
t
;
fingerprint_request_t
*
p_r
;
private
slots
:
void
handleResults
();
void
applyIdentity
();
signals:
void
metaApplied
(
input_item_t
*
);
};
#endif // FINGERPRINTDIALOG_HPP
modules/gui/qt4/ui/fingerprintdialog.ui
0 → 100644
View file @
1e80fd26
<?xml version="1.0" encoding="UTF-8"?>
<ui
version=
"4.0"
>
<class>
FingerprintDialog
</class>
<widget
class=
"QDialog"
name=
"FingerprintDialog"
>
<property
name=
"windowModality"
>
<enum>
Qt::WindowModal
</enum>
</property>
<property
name=
"geometry"
>
<rect>
<x>
0
</x>
<y>
0
</y>
<width>
499
</width>
<height>
257
</height>
</rect>
</property>
<property
name=
"windowTitle"
>
<string>
Audio Fingerprinting
</string>
</property>
<layout
class=
"QVBoxLayout"
name=
"verticalLayout_3"
>
<item>
<widget
class=
"QStackedWidget"
name=
"stackedWidget"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Preferred"
vsizetype=
"Minimum"
>
<horstretch>
0
</horstretch>
<verstretch>
0
</verstretch>
</sizepolicy>
</property>
<property
name=
"currentIndex"
>
<number>
0
</number>
</property>
<widget
class=
"QWidget"
name=
"results"
>
<layout
class=
"QVBoxLayout"
name=
"verticalLayout_2"
>
<item>
<widget
class=
"QLabel"
name=
"label_3"
>
<property
name=
"text"
>
<string>
Select a matching identity
</string>
</property>
</widget>
</item>
<item>
<widget
class=
"QListWidget"
name=
"recordsList"
/>
</item>
<item>
<widget
class=
"QDialogButtonBox"
name=
"buttonsBox"
>
<property
name=
"standardButtons"
>
<set>
QDialogButtonBox::NoButton
</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget
class=
"QWidget"
name=
"error"
>
<layout
class=
"QVBoxLayout"
name=
"verticalLayout_4"
>
<item>
<widget
class=
"QLabel"
name=
"label_2"
>
<property
name=
"text"
>
<string>
No fingerprint has been found
</string>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignCenter
</set>
</property>
</widget>
</item>
<item>
<widget
class=
"QDialogButtonBox"
name=
"buttonBox"
>
<property
name=
"standardButtons"
>
<set>
QDialogButtonBox::NoButton
</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget
class=
"QWidget"
name=
"wait"
>
<property
name=
"sizePolicy"
>
<sizepolicy
hsizetype=
"Preferred"
vsizetype=
"Preferred"
>
<horstretch>
0
</horstretch>
<verstretch>
0
</verstretch>
</sizepolicy>
</property>
<layout
class=
"QVBoxLayout"
name=
"verticalLayout"
>
<item>
<spacer
name=
"verticalSpacer"
>
<property
name=
"orientation"
>
<enum>
Qt::Vertical
</enum>
</property>
<property
name=
"sizeHint"
stdset=
"0"
>
<size>
<width>
20
</width>
<height>
40
</height>
</size>
</property>
</spacer>
</item>
<item>
<widget
class=
"QProgressBar"
name=
"progressBar"
>
<property
name=
"minimum"
>
<number>
0
</number>
</property>
<property
name=
"maximum"
>
<number>
0
</number>
</property>
<property
name=
"value"
>
<number>
-1
</number>
</property>
</widget>
</item>
<item>
<widget
class=
"QLabel"
name=
"label"
>
<property
name=
"text"
>
<string>
Fingerprinting track...
</string>
</property>
<property
name=
"alignment"
>
<set>
Qt::AlignCenter
</set>
</property>
</widget>
</item>
<item>
<spacer
name=
"verticalSpacer_2"
>
<property
name=
"orientation"
>
<enum>
Qt::Vertical
</enum>
</property>
<property
name=
"sizeHint"
stdset=
"0"
>
<size>
<width>
20
</width>
<height>
40
</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment