Fixed html encoding in native linux notifications. #2532

Also use case-insensitive filters in GTK file chooser.
This commit is contained in:
John Preston 2016-10-22 16:57:13 +03:00
parent c773bffec6
commit 77df38b4fd
2 changed files with 217 additions and 178 deletions

View File

@ -74,7 +74,7 @@ bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption,
auto parent = App::wnd() ? App::wnd()->filedialogParent() : nullptr; auto parent = App::wnd() ? App::wnd()->filedialogParent() : nullptr;
internal::GtkFileDialog dialog(parent, caption, QString(), filter); internal::GtkFileDialog dialog(parent, caption, QString(), filter);
dialog.setModal(true); dialog.setModal(true);
if (type == Type::ReadFile || type == Type::ReadFiles) { if (type == Type::ReadFile || type == Type::ReadFiles) {
dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile); dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile);
dialog.setAcceptMode(QFileDialog::AcceptOpen); dialog.setAcceptMode(QFileDialog::AcceptOpen);
@ -116,17 +116,17 @@ bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption,
namespace internal { namespace internal {
QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) { QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) {
Libs::g_signal_connect_swapped_helper(Libs::g_object_cast(gtkWidget), "response", GCallback(onResponse), this); Libs::g_signal_connect_swapped_helper(Libs::g_object_cast(gtkWidget), "response", GCallback(onResponse), this);
Libs::g_signal_connect_helper(Libs::g_object_cast(gtkWidget), "delete-event", GCallback(Libs::gtk_widget_hide_on_delete), NULL); Libs::g_signal_connect_helper(Libs::g_object_cast(gtkWidget), "delete-event", GCallback(Libs::gtk_widget_hide_on_delete), NULL);
} }
QGtkDialog::~QGtkDialog() { QGtkDialog::~QGtkDialog() {
Libs::gtk_clipboard_store(Libs::gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); Libs::gtk_clipboard_store(Libs::gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
Libs::gtk_widget_destroy(gtkWidget); Libs::gtk_widget_destroy(gtkWidget);
} }
GtkDialog *QGtkDialog::gtkDialog() const { GtkDialog *QGtkDialog::gtkDialog() const {
return Libs::gtk_dialog_cast(gtkWidget); return Libs::gtk_dialog_cast(gtkWidget);
} }
void QGtkDialog::exec() { void QGtkDialog::exec() {
@ -147,42 +147,42 @@ void QGtkDialog::exec() {
} }
void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
connect(parent, &QWindow::destroyed, this, &QGtkDialog::onParentWindowDestroyed, connect(parent, &QWindow::destroyed, this, &QGtkDialog::onParentWindowDestroyed,
Qt::UniqueConnection); Qt::UniqueConnection);
setParent(parent); setParent(parent);
setFlags(flags); setFlags(flags);
setModality(modality); setModality(modality);
Libs::gtk_widget_realize(gtkWidget); // creates X window Libs::gtk_widget_realize(gtkWidget); // creates X window
if (parent) { if (parent) {
Platform::internal::XSetTransientForHint(Libs::gtk_widget_get_window(gtkWidget), parent->winId()); Platform::internal::XSetTransientForHint(Libs::gtk_widget_get_window(gtkWidget), parent->winId());
} }
if (modality != Qt::NonModal) { if (modality != Qt::NonModal) {
Libs::gdk_window_set_modal_hint(Libs::gtk_widget_get_window(gtkWidget), true); Libs::gdk_window_set_modal_hint(Libs::gtk_widget_get_window(gtkWidget), true);
QGuiApplicationPrivate::showModalWindow(this); QGuiApplicationPrivate::showModalWindow(this);
} }
Libs::gtk_widget_show(gtkWidget); Libs::gtk_widget_show(gtkWidget);
Libs::gdk_window_focus(Libs::gtk_widget_get_window(gtkWidget), 0); Libs::gdk_window_focus(Libs::gtk_widget_get_window(gtkWidget), 0);
} }
void QGtkDialog::hide() { void QGtkDialog::hide() {
QGuiApplicationPrivate::hideModalWindow(this); QGuiApplicationPrivate::hideModalWindow(this);
Libs::gtk_widget_hide(gtkWidget); Libs::gtk_widget_hide(gtkWidget);
} }
void QGtkDialog::onResponse(QGtkDialog *dialog, int response) { void QGtkDialog::onResponse(QGtkDialog *dialog, int response) {
if (response == GTK_RESPONSE_OK) if (response == GTK_RESPONSE_OK)
emit dialog->accept(); emit dialog->accept();
else else
emit dialog->reject(); emit dialog->reject();
} }
void QGtkDialog::onParentWindowDestroyed() { void QGtkDialog::onParentWindowDestroyed() {
// The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it. // The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it.
setParent(nullptr); setParent(nullptr);
} }
namespace { namespace {
@ -192,13 +192,13 @@ const char *filterRegExp =
// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)" // Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)"
QStringList cleanFilterList(const QString &filter) { QStringList cleanFilterList(const QString &filter) {
QRegExp regexp(QString::fromLatin1(filterRegExp)); QRegExp regexp(QString::fromLatin1(filterRegExp));
Q_ASSERT(regexp.isValid()); Q_ASSERT(regexp.isValid());
QString f = filter; QString f = filter;
int i = regexp.indexIn(f); int i = regexp.indexIn(f);
if (i >= 0) if (i >= 0)
f = regexp.cap(2); f = regexp.cap(2);
return f.split(QLatin1Char(' '), QString::SkipEmptyParts); return f.split(QLatin1Char(' '), QString::SkipEmptyParts);
} }
} // namespace } // namespace
@ -213,35 +213,35 @@ GtkFileDialog::GtkFileDialog(QWidget *parent, const QString &caption, const QStr
_nameFilters << filters[i].simplified(); _nameFilters << filters[i].simplified();
} }
d.reset(new QGtkDialog(Libs::gtk_file_chooser_dialog_new("", nullptr, d.reset(new QGtkDialog(Libs::gtk_file_chooser_dialog_new("", nullptr,
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK, NULL))); GTK_STOCK_OK, GTK_RESPONSE_OK, NULL)));
connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted()));
connect(d.data(), SIGNAL(reject()), this, SLOT(onRejected())); connect(d.data(), SIGNAL(reject()), this, SLOT(onRejected()));
Libs::g_signal_connect_helper(Libs::gtk_file_chooser_cast(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this); Libs::g_signal_connect_helper(Libs::gtk_file_chooser_cast(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
Libs::g_signal_connect_swapped_helper(Libs::gtk_file_chooser_cast(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this); Libs::g_signal_connect_swapped_helper(Libs::gtk_file_chooser_cast(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
} }
GtkFileDialog::~GtkFileDialog() { GtkFileDialog::~GtkFileDialog() {
} }
void GtkFileDialog::showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { void GtkFileDialog::showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
_dir.clear(); _dir.clear();
_selection.clear(); _selection.clear();
applyOptions(); applyOptions();
return d->show(flags, modality, parent); return d->show(flags, modality, parent);
} }
void GtkFileDialog::setVisible(bool visible) { void GtkFileDialog::setVisible(bool visible) {
if (visible) { if (visible) {
if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) { if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) {
return; return;
} }
} else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) { } else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) {
return; return;
} }
if (visible) { if (visible) {
@ -254,64 +254,64 @@ void GtkFileDialog::setVisible(bool visible) {
// updates the state correctly, but skips showing the non-native version: // updates the state correctly, but skips showing the non-native version:
setAttribute(Qt::WA_DontShowOnScreen); setAttribute(Qt::WA_DontShowOnScreen);
QDialog::setVisible(visible); QDialog::setVisible(visible);
} }
int GtkFileDialog::exec() { int GtkFileDialog::exec() {
d->setModality(windowModality()); d->setModality(windowModality());
bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose); bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
setAttribute(Qt::WA_DeleteOnClose, false); setAttribute(Qt::WA_DeleteOnClose, false);
bool wasShowModal = testAttribute(Qt::WA_ShowModal); bool wasShowModal = testAttribute(Qt::WA_ShowModal);
setAttribute(Qt::WA_ShowModal, true); setAttribute(Qt::WA_ShowModal, true);
setResult(0); setResult(0);
show(); show();
QPointer<QDialog> guard = this; QPointer<QDialog> guard = this;
d->exec(); d->exec();
if (guard.isNull()) if (guard.isNull())
return QDialog::Rejected; return QDialog::Rejected;
setAttribute(Qt::WA_ShowModal, wasShowModal); setAttribute(Qt::WA_ShowModal, wasShowModal);
return result(); return result();
} }
void GtkFileDialog::hideHelper() { void GtkFileDialog::hideHelper() {
// After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder() // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder()
// & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual
// values before hiding the dialog // values before hiding the dialog
_dir = directory().absolutePath(); _dir = directory().absolutePath();
_selection = selectedFiles(); _selection = selectedFiles();
d->hide(); d->hide();
} }
bool GtkFileDialog::defaultNameFilterDisables() const { bool GtkFileDialog::defaultNameFilterDisables() const {
return false; return false;
} }
void GtkFileDialog::setDirectory(const QString &directory) { void GtkFileDialog::setDirectory(const QString &directory) {
GtkDialog *gtkDialog = d->gtkDialog(); GtkDialog *gtkDialog = d->gtkDialog();
Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), directory.toUtf8()); Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), directory.toUtf8());
} }
QDir GtkFileDialog::directory() const { QDir GtkFileDialog::directory() const {
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder() // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder()
// returns a bogus value -> return the cached value before hiding // returns a bogus value -> return the cached value before hiding
if (!_dir.isEmpty()) if (!_dir.isEmpty())
return _dir; return _dir;
QString ret; QString ret;
GtkDialog *gtkDialog = d->gtkDialog(); GtkDialog *gtkDialog = d->gtkDialog();
gchar *folder = Libs::gtk_file_chooser_get_current_folder(Libs::gtk_file_chooser_cast(gtkDialog)); gchar *folder = Libs::gtk_file_chooser_get_current_folder(Libs::gtk_file_chooser_cast(gtkDialog));
if (folder) { if (folder) {
ret = QString::fromUtf8(folder); ret = QString::fromUtf8(folder);
Libs::g_free(folder); Libs::g_free(folder);
} }
return QDir(ret); return QDir(ret);
} }
void GtkFileDialog::selectFile(const QString &filename) { void GtkFileDialog::selectFile(const QString &filename) {
@ -320,49 +320,49 @@ void GtkFileDialog::selectFile(const QString &filename) {
} }
QStringList GtkFileDialog::selectedFiles() const { QStringList GtkFileDialog::selectedFiles() const {
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames() // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames()
// returns a bogus value -> return the cached value before hiding // returns a bogus value -> return the cached value before hiding
if (!_selection.isEmpty()) if (!_selection.isEmpty())
return _selection; return _selection;
QStringList selection; QStringList selection;
GtkDialog *gtkDialog = d->gtkDialog(); GtkDialog *gtkDialog = d->gtkDialog();
GSList *filenames = Libs::gtk_file_chooser_get_filenames(Libs::gtk_file_chooser_cast(gtkDialog)); GSList *filenames = Libs::gtk_file_chooser_get_filenames(Libs::gtk_file_chooser_cast(gtkDialog));
for (GSList *it = filenames; it; it = it->next) for (GSList *it = filenames; it; it = it->next)
selection += QString::fromUtf8((const char*)it->data); selection += QString::fromUtf8((const char*)it->data);
Libs::g_slist_free(filenames); Libs::g_slist_free(filenames);
return selection; return selection;
} }
void GtkFileDialog::setFilter() { void GtkFileDialog::setFilter() {
applyOptions(); applyOptions();
} }
void GtkFileDialog::selectNameFilter(const QString &filter) { void GtkFileDialog::selectNameFilter(const QString &filter) {
GtkFileFilter *gtkFilter = _filters.value(filter); GtkFileFilter *gtkFilter = _filters.value(filter);
if (gtkFilter) { if (gtkFilter) {
GtkDialog *gtkDialog = d->gtkDialog(); GtkDialog *gtkDialog = d->gtkDialog();
Libs::gtk_file_chooser_set_filter(Libs::gtk_file_chooser_cast(gtkDialog), gtkFilter); Libs::gtk_file_chooser_set_filter(Libs::gtk_file_chooser_cast(gtkDialog), gtkFilter);
} }
} }
QString GtkFileDialog::selectedNameFilter() const { QString GtkFileDialog::selectedNameFilter() const {
GtkDialog *gtkDialog = d->gtkDialog(); GtkDialog *gtkDialog = d->gtkDialog();
GtkFileFilter *gtkFilter = Libs::gtk_file_chooser_get_filter(Libs::gtk_file_chooser_cast(gtkDialog)); GtkFileFilter *gtkFilter = Libs::gtk_file_chooser_get_filter(Libs::gtk_file_chooser_cast(gtkDialog));
return _filterNames.value(gtkFilter); return _filterNames.value(gtkFilter);
} }
void GtkFileDialog::onAccepted() { void GtkFileDialog::onAccepted() {
emit accept(); emit accept();
// QString filter = selectedNameFilter(); // QString filter = selectedNameFilter();
// if (filter.isEmpty()) // if (filter.isEmpty())
// emit filterSelected(filter); // emit filterSelected(filter);
// QList<QUrl> files = selectedFiles(); // QList<QUrl> files = selectedFiles();
// emit filesSelected(files); // emit filesSelected(files);
// if (files.count() == 1) // if (files.count() == 1)
// emit fileSelected(files.first()); // emit fileSelected(files.first());
} }
void GtkFileDialog::onRejected() { void GtkFileDialog::onRejected() {
@ -372,36 +372,36 @@ void GtkFileDialog::onRejected() {
} }
void GtkFileDialog::onSelectionChanged(GtkDialog *gtkDialog, GtkFileDialog *helper) { void GtkFileDialog::onSelectionChanged(GtkDialog *gtkDialog, GtkFileDialog *helper) {
// QString selection; // QString selection;
// gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog)); // gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog));
// if (filename) { // if (filename) {
// selection = QString::fromUtf8(filename); // selection = QString::fromUtf8(filename);
// g_free(filename); // g_free(filename);
// } // }
// emit helper->currentChanged(QUrl::fromLocalFile(selection)); // emit helper->currentChanged(QUrl::fromLocalFile(selection));
} }
void GtkFileDialog::onCurrentFolderChanged(GtkFileDialog *dialog) { void GtkFileDialog::onCurrentFolderChanged(GtkFileDialog *dialog) {
// emit dialog->directoryEntered(dialog->directory()); // emit dialog->directoryEntered(dialog->directory());
} }
GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileDialog::AcceptMode acceptMode) { GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileDialog::AcceptMode acceptMode) {
switch (fileMode) { switch (fileMode) {
case QFileDialog::AnyFile: case QFileDialog::AnyFile:
case QFileDialog::ExistingFile: case QFileDialog::ExistingFile:
case QFileDialog::ExistingFiles: case QFileDialog::ExistingFiles:
if (acceptMode == QFileDialog::AcceptOpen) if (acceptMode == QFileDialog::AcceptOpen)
return GTK_FILE_CHOOSER_ACTION_OPEN; return GTK_FILE_CHOOSER_ACTION_OPEN;
else else
return GTK_FILE_CHOOSER_ACTION_SAVE; return GTK_FILE_CHOOSER_ACTION_SAVE;
case QFileDialog::Directory: case QFileDialog::Directory:
case QFileDialog::DirectoryOnly: case QFileDialog::DirectoryOnly:
default: default:
if (acceptMode == QFileDialog::AcceptOpen) if (acceptMode == QFileDialog::AcceptOpen)
return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
else else
return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
} }
} }
bool CustomButtonsSupported() { bool CustomButtonsSupported() {
@ -411,41 +411,41 @@ bool CustomButtonsSupported() {
} }
void GtkFileDialog::applyOptions() { void GtkFileDialog::applyOptions() {
GtkDialog *gtkDialog = d->gtkDialog(); GtkDialog *gtkDialog = d->gtkDialog();
Libs::gtk_window_set_title(Libs::gtk_window_cast(gtkDialog), _windowTitle.toUtf8()); Libs::gtk_window_set_title(Libs::gtk_window_cast(gtkDialog), _windowTitle.toUtf8());
Libs::gtk_file_chooser_set_local_only(Libs::gtk_file_chooser_cast(gtkDialog), true); Libs::gtk_file_chooser_set_local_only(Libs::gtk_file_chooser_cast(gtkDialog), true);
const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode); const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode);
Libs::gtk_file_chooser_set_action(Libs::gtk_file_chooser_cast(gtkDialog), action); Libs::gtk_file_chooser_set_action(Libs::gtk_file_chooser_cast(gtkDialog), action);
const bool selectMultiple = (_fileMode == QFileDialog::ExistingFiles); const bool selectMultiple = (_fileMode == QFileDialog::ExistingFiles);
Libs::gtk_file_chooser_set_select_multiple(Libs::gtk_file_chooser_cast(gtkDialog), selectMultiple); Libs::gtk_file_chooser_set_select_multiple(Libs::gtk_file_chooser_cast(gtkDialog), selectMultiple);
const bool confirmOverwrite = !_options.testFlag(QFileDialog::DontConfirmOverwrite); const bool confirmOverwrite = !_options.testFlag(QFileDialog::DontConfirmOverwrite);
Libs::gtk_file_chooser_set_do_overwrite_confirmation(Libs::gtk_file_chooser_cast(gtkDialog), confirmOverwrite); Libs::gtk_file_chooser_set_do_overwrite_confirmation(Libs::gtk_file_chooser_cast(gtkDialog), confirmOverwrite);
if (!_nameFilters.isEmpty()) if (!_nameFilters.isEmpty())
setNameFilters(_nameFilters); setNameFilters(_nameFilters);
if (!_initialDirectory.isEmpty()) if (!_initialDirectory.isEmpty())
setDirectory(_initialDirectory); setDirectory(_initialDirectory);
for_const (const auto &filename, _initialFiles) { for_const (const auto &filename, _initialFiles) {
if (_acceptMode == QFileDialog::AcceptSave) { if (_acceptMode == QFileDialog::AcceptSave) {
QFileInfo fi(filename); QFileInfo fi(filename);
Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8()); Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8());
Libs::gtk_file_chooser_set_current_name(Libs::gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8()); Libs::gtk_file_chooser_set_current_name(Libs::gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8());
} else if (filename.endsWith('/')) { } else if (filename.endsWith('/')) {
Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), filename.toUtf8()); Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), filename.toUtf8());
} else { } else {
Libs::gtk_file_chooser_select_filename(Libs::gtk_file_chooser_cast(gtkDialog), filename.toUtf8()); Libs::gtk_file_chooser_select_filename(Libs::gtk_file_chooser_cast(gtkDialog), filename.toUtf8());
} }
} }
const QString initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front(); const QString initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front();
if (!initialNameFilter.isEmpty()) if (!initialNameFilter.isEmpty())
selectNameFilter(initialNameFilter); selectNameFilter(initialNameFilter);
if (CustomButtonsSupported()) { if (CustomButtonsSupported()) {
GtkWidget *acceptButton = Libs::gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); GtkWidget *acceptButton = Libs::gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK);
@ -469,27 +469,40 @@ void GtkFileDialog::applyOptions() {
} }
void GtkFileDialog::setNameFilters(const QStringList &filters) { void GtkFileDialog::setNameFilters(const QStringList &filters) {
GtkDialog *gtkDialog = d->gtkDialog(); GtkDialog *gtkDialog = d->gtkDialog();
foreach (GtkFileFilter *filter, _filters) foreach (GtkFileFilter *filter, _filters)
Libs::gtk_file_chooser_remove_filter(Libs::gtk_file_chooser_cast(gtkDialog), filter); Libs::gtk_file_chooser_remove_filter(Libs::gtk_file_chooser_cast(gtkDialog), filter);
_filters.clear(); _filters.clear();
_filterNames.clear(); _filterNames.clear();
foreach (const QString &filter, filters) { for_const (auto &filter, filters) {
GtkFileFilter *gtkFilter = Libs::gtk_file_filter_new(); GtkFileFilter *gtkFilter = Libs::gtk_file_filter_new();
const QString name = filter.left(filter.indexOf(QLatin1Char('('))); auto name = filter;//.left(filter.indexOf(QLatin1Char('(')));
const QStringList extensions = cleanFilterList(filter); auto extensions = cleanFilterList(filter);
Libs::gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8()); Libs::gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8());
foreach (const QString &ext, extensions) for_const (auto &ext, extensions) {
Libs::gtk_file_filter_add_pattern(gtkFilter, ext.toUtf8()); auto caseInsensitiveExt = QString();
caseInsensitiveExt.reserve(4 * ext.size());
for_const (auto ch, ext) {
auto chLower = ch.toLower();
auto chUpper = ch.toUpper();
if (chLower != chUpper) {
caseInsensitiveExt.append('[').append(chLower).append(chUpper).append(']');
} else {
caseInsensitiveExt.append(ch);
}
}
Libs::gtk_file_chooser_add_filter(Libs::gtk_file_chooser_cast(gtkDialog), gtkFilter); Libs::gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8());
}
_filters.insert(filter, gtkFilter); Libs::gtk_file_chooser_add_filter(Libs::gtk_file_chooser_cast(gtkDialog), gtkFilter);
_filterNames.insert(gtkFilter, filter);
} _filters.insert(filter, gtkFilter);
_filterNames.insert(gtkFilter, filter);
}
} }
} // namespace internal } // namespace internal

View File

@ -71,10 +71,31 @@ bool LibNotifyLoaded() {
&& (Libs::gdk_pixbuf_new_from_file != nullptr); && (Libs::gdk_pixbuf_new_from_file != nullptr);
} }
QString escapeNotificationHtml(QString text) { QString escapeHtml(const QString &text) {
text = text.replace(QChar('<'), qstr("&lt;")); auto result = QString();
text = text.replace(QChar('>'), qstr("&gt;")); auto copyFrom = 0, textSize = text.size();
text = text.replace(QChar('&'), qstr("&amp;")); auto data = text.constData();
for (auto i = 0; i != textSize; ++i) {
auto ch = data[i];
if (ch == '<' || ch == '>' || ch == '&') {
if (!copyFrom) {
result.reserve(textSize * 5);
}
if (i > copyFrom) {
result.append(data + copyFrom, i - copyFrom);
}
switch (ch.unicode()) {
case '<': result.append(qstr("&lt;")); break;
case '>': result.append(qstr("&gt;")); break;
case '&': result.append(qstr("&amp;")); break;
}
copyFrom = i + 1;
}
}
if (copyFrom > 0) {
result.append(data + copyFrom, textSize - copyFrom);
return result;
}
return text; return text;
} }
@ -260,6 +281,7 @@ public:
} }
private: private:
QString escapeNotificationText(const QString &text) const;
void showNextNotification(); void showNextNotification();
struct QueuedNotification { struct QueuedNotification {
@ -327,10 +349,14 @@ bool Manager::Impl::init() {
return !_serverName.isEmpty(); return !_serverName.isEmpty();
} }
QString Manager::Impl::escapeNotificationText(const QString &text) const {
return _markupSupported ? escapeHtml(text) : text;
}
void Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { void Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) {
auto titleText = escapeNotificationHtml(title); auto titleText = escapeNotificationText(title);
auto subtitleText = escapeNotificationHtml(subtitle); auto subtitleText = escapeNotificationText(subtitle);
auto msgText = escapeNotificationHtml(msg); auto msgText = escapeNotificationText(msg);
if (_markupSupported && !subtitleText.isEmpty()) { if (_markupSupported && !subtitleText.isEmpty()) {
subtitleText = qstr("<b>") + subtitleText + qstr("</b>"); subtitleText = qstr("<b>") + subtitleText + qstr("</b>");
} }