23#include <QTextDocument>
28#include <QPainterPath>
33#include <QApplication>
34#include <QSslCertificate>
39#include <QFileIconProvider>
40#include <QTemporaryFile>
46#include <QtGuiVersion>
59#include <CoreServices/CoreServices.h>
65 QBuffer buffer(&bytes);
66 buffer.open(QIODevice::WriteOnly);
67 if (pix.save(&buffer,
"PNG")) {
68 return buffer.buffer().toBase64();
77 QByteArray bArray = QByteArray::fromBase64(data);
78 image.loadFromData(bArray);
86 return data.isEmpty() ? QUrl() : QUrl(
QSL(
"data:image/png;base64,") + data);
91 const QIcon icon(path);
92 if (icon.availableSizes().isEmpty()) {
95 return icon.pixmap(icon.availableSizes().at(0));
105 QFile file(filename);
107 if (!filename.isEmpty() && file.open(QFile::ReadOnly)) {
108 const QByteArray a = file.readAll();
118 QRect screen = w->screen()->geometry();
119 const QRect size = w->geometry();
120 w->move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2);
131 parent = parent->window();
132 QPoint pp = parent->mapToGlobal(QPoint(0, 0));
133 p = QPoint(pp.x() + parent->width() / 2, pp.y() + parent->height() / 2);
134 p = QPoint(p.x() - w->width() / 2, p.y() - w->height() / 2 - 20);
141 const QFileInfo fileInfo(filePath);
142 if (!fileInfo.exists() && !fileInfo.isSymLink()) {
145 if (fileInfo.isDir() && !fileInfo.isSymLink()) {
147 dir.setPath(dir.canonicalPath());
148 if (dir.isRoot() || dir.path() == QDir::home().canonicalPath()) {
149 qCritical() <<
"Attempt to remove root/home directory" << dir;
152 const QStringList fileNames = dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
153 for (
const QString &fileName : fileNames) {
158 if (!QDir::root().rmdir(dir.path())) {
161 }
else if (!QFile::remove(filePath)) {
169 const QFileInfo srcFileInfo(sourcePath);
170 if (srcFileInfo.isDir() && !srcFileInfo.isSymLink()) {
171 QDir targetDir(targetPath);
173 if (!targetDir.mkdir(QFileInfo(targetPath).fileName())) {
176 const QStringList fileNames = QDir(sourcePath).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System);
177 for (
const QString &fileName : fileNames) {
178 const QString newSourcePath = sourcePath +
QL1C(
'/') + fileName;
179 const QString newTargetPath = targetPath +
QL1C(
'/') + fileName;
185 }
else if (srcFileInfo.isSymLink()) {
186 const QByteArray pathData = sourcePath.toLocal8Bit();
188 ssize_t len = readlink(pathData.constData(), buf,
sizeof(buf) - 1);
190 qWarning() <<
"Error getting symlink path" << pathData;
194 return QFile::link(QString::fromLocal8Bit(buf), targetPath);
196 }
else if (!QFile::copy(sourcePath, targetPath)) {
205 int maxSize = qMin(one.size(), other.size());
211 while (one.at(
i) == other.at(
i)) {
222 QString returnString = url.toString(QUrl::RemoveQuery | QUrl::RemoveFragment);
224 if (url.hasQuery()) {
225 returnString += QLatin1Char(
'?') + url.query(QUrl::FullyEncoded);
228 if (url.hasFragment()) {
229 returnString += QLatin1Char(
'#') + url.fragment(QUrl::FullyEncoded);
232 returnString.replace(QLatin1Char(
' '), QLatin1String(
"%20"));
239 if (!str.startsWith(
QL1S(
"xn--")))
243 const QString decoded = QUrl::fromAce(str.toUtf8() + QByteArray(
".org"));
244 return decoded.left(decoded.size() - 4);
249 urlString.replace(
QL1C(
'['), QStringLiteral(
"[["));
250 urlString.replace(
QL1C(
']'), QStringLiteral(
"[]]"));
251 urlString.replace(QStringLiteral(
"[["), QStringLiteral(
"[[]"));
252 urlString.replace(
QL1C(
'*'), QStringLiteral(
"[*]"));
253 urlString.replace(
QL1C(
'?'), QStringLiteral(
"[?]"));
259 Q_ASSERT(appendFormat.contains(
QL1S(
"%1")));
261 QFileInfo info(name);
266 const QDir dir = info.absoluteDir();
267 const QString fileName = info.fileName();
271 while (info.exists()) {
272 QString file = fileName;
273 int index = file.lastIndexOf(
QL1C(
'.'));
274 const QString appendString = appendFormat.arg(
i);
276 file.append(appendString);
278 file = file.left(index) + appendString + file.mid(index);
279 info.setFile(dir, file);
283 return info.absoluteFilePath();
288 QString fileName = url.toString(QUrl::RemoveFragment | QUrl::RemoveQuery | QUrl::RemoveScheme | QUrl::RemovePort);
290 if (fileName.endsWith(QLatin1Char(
'/'))) {
291 fileName = fileName.mid(0, fileName.length() - 1);
294 if (fileName.indexOf(QLatin1Char(
'/')) != -1) {
295 int pos = fileName.lastIndexOf(QLatin1Char(
'/'));
296 fileName = fileName.mid(pos);
297 fileName.remove(QLatin1Char(
'/'));
302 if (fileName.isEmpty()) {
311 QString
value = name;
313 value.replace(QLatin1Char(
'/'), QLatin1Char(
'-'));
314 value.remove(QLatin1Char(
'\\'));
315 value.remove(QLatin1Char(
':'));
316 value.remove(QLatin1Char(
'*'));
317 value.remove(QLatin1Char(
'?'));
318 value.remove(QLatin1Char(
'"'));
319 value.remove(QLatin1Char(
'<'));
320 value.remove(QLatin1Char(
'>'));
321 value.remove(QLatin1Char(
'|'));
330 QString path = settings.
value(
QSL(
"FileDialogs/") + dialogName).toString();
333 return path.isEmpty() ? fallbackPath : path;
338 if (path.isEmpty()) {
344 settings.
setValue(dialogName, path);
351 QString returnString;
353 while (pos <=
string.size()) {
354 QString part =
string.mid(pos);
355 QString elidedLine = metrics.elidedText(part, Qt::ElideRight, width);
357 if (elidedLine.isEmpty()) {
361 if (elidedLine.size() != part.size()) {
362 elidedLine = elidedLine.left(elidedLine.size() - 3);
365 if (!returnString.isEmpty()) {
366 returnString += text;
369 returnString += elidedLine;
370 pos += elidedLine.size();
379 return QObject::tr(
"Unknown size");
384 double _size = size / 1024.0;
386 return QObject::tr(
"%1 kB").arg(
locale.toString(_size > 1 ? _size : 1,
'f', 0));
391 return QObject::tr(
"%1 MB").arg(
locale.toString(_size,
'f', 1));
395 return QObject::tr(
"%1 GB").arg(
locale.toString(_size,
'f', 2));
400 const QFontMetricsF fontMetrics(QApplication::font());
401 const int padding = 4;
402 const int maxWidth = fontMetrics.horizontalAdvance(title.length() > url.length() ? title : url) + 3 * padding + 16;
403 const int width = qMin(maxWidth, 150);
404 const int height = fontMetrics.height() * 2 + fontMetrics.leading() + 2 * padding;
406 QPixmap pixmap(width * qApp->devicePixelRatio(), height * qApp->devicePixelRatio());
407 pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
409 QPainter painter(&pixmap);
410 painter.setRenderHint(QPainter::Antialiasing);
418 path.addRect(QRect(0, 0, width, height));
420 painter.fillPath(path, Qt::white);
421 painter.drawPath(path);
424 QRect iconRect(padding, 0, 16, height);
425 icon.paint(&painter, iconRect);
428 QRect titleRect(iconRect.right() + padding, padding, width - padding - iconRect.right(), fontMetrics.height());
429 painter.drawText(titleRect, fontMetrics.elidedText(title, Qt::ElideRight, titleRect.width()));
432 QRect urlRect(titleRect.x(), titleRect.bottom() + fontMetrics.leading(), titleRect.width(), titleRect.height());
433 painter.setPen(QApplication::palette().color(QPalette::Link));
434 painter.drawText(urlRect, fontMetrics.elidedText(url, Qt::ElideRight, urlRect.width()));
441 QString direction = QLatin1String(
"ltr");
442 QString right_str = QLatin1String(
"right");
443 QString left_str = QLatin1String(
"left");
445 if (QApplication::isRightToLeft()) {
446 direction = QLatin1String(
"rtl");
447 right_str = QLatin1String(
"left");
448 left_str = QLatin1String(
"right");
451 pageContents.replace(QLatin1String(
"%DIRECTION%"), direction);
452 pageContents.replace(QLatin1String(
"%RIGHT_STR%"), right_str);
453 pageContents.replace(QLatin1String(
"%LEFT_STR%"), left_str);
460 if (text.length() > size) {
461 return text.left(size) +
QL1S(
"..");
472 region += rect.adjusted(radius, 0, -radius, 0);
473 region += rect.adjusted(0, radius, 0, -radius);
476 QRect corner(rect.topLeft(), QSize(radius * 2, radius * 2));
477 region += QRegion(corner, QRegion::Ellipse);
480 corner.moveTopRight(rect.topRight());
481 region += QRegion(corner, QRegion::Ellipse);
484 corner.moveBottomLeft(rect.bottomLeft());
485 region += QRegion(corner, QRegion::Ellipse);
488 corner.moveBottomRight(rect.bottomRight());
489 region += QRegion(corner, QRegion::Ellipse);
496 static QHash<QString, QIcon> iconCache;
498 QFileInfo tempInfo(fileName);
499 if (iconCache.contains(tempInfo.suffix())) {
500 return iconCache.value(tempInfo.suffix());
503 QFileIconProvider iconProvider;
506 tempInfo.setFile(tempFile.fileName());
508 QIcon icon(iconProvider.icon(tempInfo));
509 iconCache.insert(tempInfo.suffix(), icon);
516 const QString path = QString::fromUtf8(qgetenv(
"PATH").trimmed());
518 if (path.isEmpty()) {
522 const QStringList dirs = path.split(QLatin1Char(
':'), Qt::SkipEmptyParts);
524 for (
const QString &dir : dirs) {
526 if (d.exists(name)) {
527 return d.absoluteFilePath(name);
541 const unsigned char* bytes = (
const unsigned char*)
string;
547 (0x20 <= bytes[0] && bytes[0] <= 0x7F)
555 (0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
556 (0x80 <= bytes[1] && bytes[1] <= 0xBF)
565 (0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
566 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
569 ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
572 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
573 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
577 (0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
578 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
587 (0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
588 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
589 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
592 (0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
593 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
594 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
595 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
599 (0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
600 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
601 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
616 for (
const QChar &c : str) {
628 QString lastDir = settings.
value(name, dir).toString();
630 QString path = QFileDialog::getExistingDirectory(parent, caption, lastDir, options);
632 if (!path.isEmpty()) {
633 settings.
setValue(name, QFileInfo(path).absolutePath());
640static QString getFilename(
const QString &path)
642 QFileInfo info(path);
645 return info.fileName();
652 if (info.dir().exists()) {
653 return info.fileName();
659QString
QzTools::getOpenFileName(
const QString &name, QWidget* parent,
const QString &caption,
const QString &dir,
const QString &filter, QString* selectedFilter, QFileDialog::Options options)
664 QString lastDir = settings.
value(name, QString()).toString();
665 QString fileName = getFilename(dir);
667 if (lastDir.isEmpty()) {
671 lastDir.append(QDir::separator() + fileName);
674 QString path = QFileDialog::getOpenFileName(parent, caption, lastDir, filter, selectedFilter, options);
676 if (!path.isEmpty()) {
677 settings.
setValue(name, QFileInfo(path).absolutePath());
684QStringList
QzTools::getOpenFileNames(
const QString &name, QWidget* parent,
const QString &caption,
const QString &dir,
const QString &filter, QString* selectedFilter, QFileDialog::Options options)
689 QString lastDir = settings.
value(name, QString()).toString();
690 QString fileName = getFilename(dir);
692 if (lastDir.isEmpty()) {
696 lastDir.append(QDir::separator() + fileName);
699 QStringList paths = QFileDialog::getOpenFileNames(parent, caption, lastDir, filter, selectedFilter, options);
701 if (!paths.isEmpty()) {
702 settings.
setValue(name, QFileInfo(paths.at(0)).absolutePath());
709QString
QzTools::getSaveFileName(
const QString &name, QWidget* parent,
const QString &caption,
const QString &dir,
const QString &filter, QString* selectedFilter, QFileDialog::Options options)
714 QString lastDir = settings.
value(name, QString()).toString();
715 QString fileName = getFilename(dir);
717 if (lastDir.isEmpty()) {
721 lastDir.append(QDir::separator() + fileName);
724 QString path = QFileDialog::getSaveFileName(parent, caption, lastDir, filter, selectedFilter, options);
726 if (!path.isEmpty()) {
727 settings.
setValue(name, QFileInfo(path).absolutePath());
739 if (pattern == domain) {
743 if (!domain.endsWith(pattern)) {
747 int index = domain.indexOf(pattern);
749 return index > 0 && domain[index - 1] == QLatin1Char(
'.');
752QKeySequence
QzTools::actionShortcut(
const QKeySequence &shortcut,
const QKeySequence &fallBack,
const QKeySequence &shortcutRtl,
const QKeySequence &fallbackRtl)
754 if (QApplication::isRightToLeft() && (!shortcutRtl.isEmpty() || !fallbackRtl.isEmpty()))
755 return shortcutRtl.isEmpty() ? fallbackRtl : shortcutRtl;
757 return shortcut.isEmpty() ? fallBack : shortcut;
760static inline bool isQuote(
const QChar &c)
762 return (c == QLatin1Char(
'"') || c == QLatin1Char(
'\''));
770 QString line = command.trimmed();
772 if (line.isEmpty()) {
776 QChar SPACE(
QL1C(
' '));
777 QChar EQUAL(
QL1C(
'='));
778 QChar BSLASH(
QL1C(
'\\'));
779 QChar QUOTE(
QL1C(
'"'));
783 int startPos = isQuote(line.at(0)) ? 1 : 0;
784 bool inWord = !isQuote(line.at(0));
785 bool inQuote = !inWord;
791 const int strlen = line.length();
792 for (
int i = 0;
i < strlen; ++
i) {
793 const QChar c = line.at(
i);
795 if (inQuote && c == QUOTE &&
i > 0 && line.at(
i - 1) != BSLASH) {
796 QString str = line.mid(startPos,
i - startPos);
798 str.remove(equalPos - startPos + 1, 1);
802 if (!str.isEmpty()) {
807 else if (!inQuote && isQuote(c)) {
814 else if (
i > 0 && line.at(
i - 1) == EQUAL) {
823 if (inWord && (c == SPACE ||
i == strlen - 1)) {
824 int len = (
i == strlen - 1) ? -1 :
i - startPos;
825 const QString str = line.mid(startPos, len);
828 if (!str.isEmpty()) {
832 else if (!inWord && c != SPACE) {
850 bool success = QProcess::startDetached(executable, arguments);
853 QString info =
QSL(
"<ul><li><b>%1</b>%2</li><li><b>%3</b>%4</li></ul>");
854 info = info.arg(QObject::tr(
"Executable: "), executable,
855 QObject::tr(
"Arguments: "), arguments.join(QLatin1Char(
' ')));
857 QMessageBox::critical(
nullptr, QObject::tr(
"Cannot start external program"),
858 QObject::tr(
"Cannot start external program! %1").arg(info));
865static xcb_connection_t *getXcbConnection()
867 const QNativeInterface::QX11Application *x11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
868 if (x11App ==
nullptr)
870 return x11App->connection();
877 if (QGuiApplication::platformName() !=
QL1S(
"xcb"))
880 xcb_connection_t *connection = getXcbConnection();
881 if (connection ==
nullptr)
884 const QByteArray nameData = name.toUtf8();
885 const QByteArray classData =
mApp->wmClass().isEmpty() ? QByteArrayLiteral(
"Falkon") :
mApp->wmClass();
887 uint32_t class_len = nameData.length() + 1 + classData.length() + 1;
888 char *class_hint = (
char*) malloc(class_len);
890 qstrcpy(class_hint, nameData.constData());
891 qstrcpy(class_hint + nameData.length() + 1, classData.constData());
893 xcb_change_property(connection, XCB_PROP_MODE_REPLACE, widget->winId(),
894 XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, class_len, class_hint);
907 QString str =
QSL(
"Mac OS X");
912 if (Gestalt(gestaltSystemVersionMajor, &majorVersion) == noErr && Gestalt(gestaltSystemVersionMinor, &minorVersion) == noErr) {
913 str.append(
QSL(
" %1.%2").arg(majorVersion).arg(minorVersion));
922 return QSL(
"BSD 4.4");
925 return QSL(
"BSD/OS");
928 return QSL(
"FreeBSD");
934 return QSL(
"GNU Hurd");
937 return QSL(
"LynxOS");
940 return QSL(
"NetBSD");
946 return QSL(
"OpenBSD");
949 return QSL(
"HP Tru64 UNIX");
952 return QSL(
"Sun Solaris");
955 return QSL(
"UnixWare 7 / Open UNIX 8");
964 QString str =
QSL(
"Windows");
966 switch (QSysInfo::windowsVersion()) {
967 case QSysInfo::WV_NT:
968 str.append(
QSL(
" NT"));
971 case QSysInfo::WV_2000:
972 str.append(
QSL(
" 2000"));
975 case QSysInfo::WV_XP:
976 str.append(
QSL(
" XP"));
978 case QSysInfo::WV_2003:
979 str.append(
QSL(
" XP Pro x64"));
982 case QSysInfo::WV_VISTA:
983 str.append(
QSL(
" Vista"));
986 case QSysInfo::WV_WINDOWS7:
987 str.append(
QSL(
" 7"));
990 case QSysInfo::WV_WINDOWS8:
991 str.append(
QSL(
" 8"));
994 case QSysInfo::WV_WINDOWS8_1:
995 str.append(
QSL(
" 8.1"));
998 case QSysInfo::WV_WINDOWS10:
999 str.append(
QSL(
" 10"));
1012 return QSysInfo::currentCpuArchitecture();
1026 QColor color = widget->palette().brush(QPalette::Normal, QPalette::Highlight).color();
1027 const int x = (r.left() + r.right()) / 2;
1028 const int thickness = qRound(r.width() / 2.0);
1030 const int alphaDec = alpha / (thickness + 1);
1031 QStylePainter p(widget);
1032 for (
int i = 0;
i < thickness;
i++) {
1033 color.setAlpha(alpha);
1036 p.drawLine(x -
i, r.top(), x -
i, r.bottom());
1037 p.drawLine(x +
i, r.top(), x +
i, r.bottom());
static QString path(Path type)
void beginGroup(const QString &prefix)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant())
void setValue(const QString &key, const QVariant &defaultValue=QVariant())
int value(const QColor &c)