File indexing completed on 2024-04-28 17:06:20
0001 /* 0002 SPDX-FileCopyrightText: 2010 Jan Lepper <krusader@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2010-2022 Krusader Krew <https://krusader.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "krlayoutfactory.h" 0009 0010 #include "../compat.h" 0011 #include "../krglobal.h" 0012 #include "listpanel.h" 0013 #include "listpanelframe.h" 0014 0015 // QtCore 0016 #include <QDebug> 0017 #include <QFile> 0018 #include <QMetaEnum> 0019 #include <QResource> 0020 #include <QStandardPaths> 0021 // QtWidgets 0022 #include <QHBoxLayout> 0023 #include <QVBoxLayout> 0024 // QtXml 0025 #include <QDomDocument> 0026 0027 #include <KConfigCore/KSharedConfig> 0028 #include <KI18n/KLocalizedString> 0029 0030 #define XMLFILE_VERSION "1.0" 0031 #define MAIN_FILE "layout.xml" 0032 #define MAIN_FILE_RC_PATH ":/" MAIN_FILE 0033 #define EXTRA_FILE_MASK "layouts/*.xml" 0034 #define DEFAULT_LAYOUT "krusader:default" 0035 0036 bool KrLayoutFactory::_parsed = false; 0037 QDomDocument KrLayoutFactory::_mainDoc; 0038 QList<QDomDocument> KrLayoutFactory::_extraDocs; 0039 0040 QString KrLayoutFactory::layoutDescription(const QString &layoutName) 0041 { 0042 if (layoutName == DEFAULT_LAYOUT) 0043 return i18nc("Default layout", "Default"); 0044 else if (layoutName == "krusader:compact") 0045 return i18n("Compact"); 0046 else if (layoutName == "krusader:classic") 0047 return i18n("Classic"); 0048 else 0049 return i18n("Custom layout: \"%1\"", layoutName); 0050 } 0051 0052 bool KrLayoutFactory::parseFiles() 0053 { 0054 if (_parsed) 0055 return true; 0056 0057 _parsed = parseResource(MAIN_FILE_RC_PATH, _mainDoc); 0058 if (!_parsed) { 0059 return false; 0060 } 0061 0062 QStringList extraFilePaths = QStandardPaths::locateAll(QStandardPaths::DataLocation, EXTRA_FILE_MASK); 0063 0064 foreach (const QString &path, extraFilePaths) { 0065 qWarning() << "extra file: " << path; 0066 QDomDocument doc; 0067 if (parseFile(path, doc)) 0068 _extraDocs << doc; 0069 } 0070 0071 return true; 0072 } 0073 0074 bool KrLayoutFactory::parseFile(const QString &path, QDomDocument &doc) 0075 { 0076 bool success = false; 0077 0078 QFile file(path); 0079 0080 if (file.open(QIODevice::ReadOnly)) 0081 return parseContent(file.readAll(), path, doc); 0082 else 0083 qWarning() << "can't open" << path; 0084 0085 return success; 0086 } 0087 0088 bool KrLayoutFactory::parseResource(const QString &path, QDomDocument &doc) 0089 { 0090 QResource res(path); 0091 if (res.isValid()) { 0092 QByteArray data; 0093 if (QRESOURCE_ISCOMPRESSED(res)) 0094 data = qUncompress(res.data(), static_cast<int>(res.size())); 0095 else 0096 data = QByteArray(reinterpret_cast<const char *>(res.data()), static_cast<int>(res.size())); 0097 return parseContent(data, path, doc); 0098 } else { 0099 qWarning() << "resource does not exist:" << path; 0100 return false; 0101 } 0102 } 0103 0104 bool KrLayoutFactory::parseContent(const QByteArray &content, const QString &fileName, QDomDocument &doc) 0105 { 0106 bool success = false; 0107 0108 QString errorMsg; 0109 if (doc.setContent(content, &errorMsg)) { 0110 QDomElement root = doc.documentElement(); 0111 if (root.tagName() == "KrusaderLayout") { 0112 QString version = root.attribute("version"); 0113 if (version == XMLFILE_VERSION) 0114 success = true; 0115 else 0116 qWarning() << fileName << "has wrong version" << version << "- required is" << XMLFILE_VERSION; 0117 } else 0118 qWarning() << "root.tagName() != \"KrusaderLayout\""; 0119 } else 0120 qWarning() << "error parsing" << fileName << ":" << errorMsg; 0121 0122 return success; 0123 } 0124 0125 void KrLayoutFactory::getLayoutNames(const QDomDocument &doc, QStringList &names) 0126 { 0127 QDomElement root = doc.documentElement(); 0128 0129 for (QDomElement e = root.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { 0130 if (e.tagName() == "layout") { 0131 QString name(e.attribute("name")); 0132 if (!name.isEmpty() && (name != DEFAULT_LAYOUT)) 0133 names << name; 0134 } 0135 } 0136 } 0137 0138 QStringList KrLayoutFactory::layoutNames() 0139 { 0140 QStringList names; 0141 names << DEFAULT_LAYOUT; 0142 0143 if (parseFiles()) { 0144 getLayoutNames(_mainDoc, names); 0145 0146 foreach (const QDomDocument &doc, _extraDocs) 0147 getLayoutNames(doc, names); 0148 } 0149 0150 return names; 0151 } 0152 0153 QDomElement KrLayoutFactory::findLayout(const QDomDocument &doc, const QString &layoutName) 0154 { 0155 QDomElement root = doc.documentElement(); 0156 0157 for (QDomElement e = root.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { 0158 if (e.tagName() == "layout" && e.attribute("name") == layoutName) 0159 return e; 0160 } 0161 0162 return QDomElement(); 0163 } 0164 0165 QLayout *KrLayoutFactory::createLayout(QString layoutName) 0166 { 0167 if (layoutName.isEmpty()) { 0168 KConfigGroup cg(krConfig, "PanelLayout"); 0169 layoutName = cg.readEntry("Layout", DEFAULT_LAYOUT); 0170 } 0171 QLayout *layout = nullptr; 0172 0173 if (parseFiles()) { 0174 QDomElement layoutRoot; 0175 0176 layoutRoot = findLayout(_mainDoc, layoutName); 0177 if (layoutRoot.isNull()) { 0178 foreach (const QDomDocument &doc, _extraDocs) { 0179 layoutRoot = findLayout(doc, layoutName); 0180 if (!layoutRoot.isNull()) 0181 break; 0182 } 0183 } 0184 if (layoutRoot.isNull()) { 0185 qWarning() << "no layout with name" << layoutName << "found"; 0186 if (layoutName != DEFAULT_LAYOUT) 0187 return createLayout(DEFAULT_LAYOUT); 0188 } else { 0189 layout = createLayout(layoutRoot, panel); 0190 } 0191 } 0192 0193 if (layout) { 0194 for (auto it = widgets.constBegin(), end = widgets.constEnd(); it != end; ++it) { 0195 qWarning() << "widget" << it.key() << "was not added to the layout"; 0196 it.value()->hide(); 0197 } 0198 } else 0199 qWarning() << "couldn't load layout" << layoutName; 0200 0201 return layout; 0202 } 0203 0204 QBoxLayout *KrLayoutFactory::createLayout(const QDomElement &e, QWidget *parent) 0205 { 0206 QBoxLayout *l = nullptr; 0207 bool horizontal = false; 0208 0209 if (e.attribute("type") == "horizontal") { 0210 horizontal = true; 0211 l = new QHBoxLayout(); 0212 } else if (e.attribute("type") == "vertical") 0213 l = new QVBoxLayout(); 0214 else { 0215 qWarning() << "unknown layout type:" << e.attribute("type"); 0216 return nullptr; 0217 } 0218 0219 l->setSpacing(0); 0220 l->setContentsMargins(0, 0, 0, 0); 0221 0222 for (QDomElement child = e.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { 0223 if (child.tagName() == "layout") { 0224 if (QLayout *childLayout = createLayout(child, parent)) 0225 l->addLayout(childLayout); 0226 } else if (child.tagName() == "frame") { 0227 QWidget *frame = createFrame(child, parent); 0228 l->addWidget(frame); 0229 } else if (child.tagName() == "widget") { 0230 if (QWidget *w = widgets.take(child.attribute("name"))) 0231 l->addWidget(w); 0232 else 0233 qWarning() << "layout: no such widget:" << child.attribute("name"); 0234 } else if (child.tagName() == "hide_widget") { 0235 if (QWidget *w = widgets.take(child.attribute("name"))) 0236 w->hide(); 0237 else 0238 qWarning() << "layout: no such widget:" << child.attribute("name"); 0239 } else if (child.tagName() == "spacer") { 0240 if (horizontal) 0241 l->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); 0242 else 0243 l->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding)); 0244 } 0245 } 0246 0247 return l; 0248 } 0249 0250 QWidget *KrLayoutFactory::createFrame(const QDomElement &e, QWidget *parent) 0251 { 0252 KConfigGroup cg(krConfig, "PanelLayout"); 0253 0254 QString color = cg.readEntry("FrameColor", "default"); 0255 if (color == "default") 0256 color = e.attribute("color"); 0257 else if (color == "none") 0258 color.clear(); 0259 0260 int shadow = -1, shape = -1; 0261 0262 QMetaEnum shadowEnum = QFrame::staticMetaObject.enumerator(QFrame::staticMetaObject.indexOfEnumerator("Shadow")); 0263 QString cfgShadow = cg.readEntry("FrameShadow", "default"); 0264 if (cfgShadow != "default") 0265 shadow = shadowEnum.keyToValue(cfgShadow.toLatin1().data()); 0266 if (shadow < 0) 0267 shadow = shadowEnum.keyToValue(e.attribute("shadow").toLatin1().data()); 0268 0269 QMetaEnum shapeEnum = QFrame::staticMetaObject.enumerator(QFrame::staticMetaObject.indexOfEnumerator("Shape")); 0270 QString cfgShape = cg.readEntry("FrameShape", "default"); 0271 if (cfgShape != "default") 0272 shape = shapeEnum.keyToValue(cfgShape.toLatin1().data()); 0273 if (shape < 0) 0274 shape = shapeEnum.keyToValue(e.attribute("shape").toLatin1().data()); 0275 0276 ListPanelFrame *frame = new ListPanelFrame(parent, color); 0277 frame->setFrameStyle(shape | shadow); 0278 frame->setAcceptDrops(true); 0279 0280 if (QLayout *l = createLayout(e, frame)) { 0281 l->setContentsMargins(frame->frameWidth(), frame->frameWidth(), frame->frameWidth(), frame->frameWidth()); 0282 frame->setLayout(l); 0283 } 0284 0285 QObject::connect(frame, &ListPanelFrame::dropped, panel, [=](QDropEvent *event) { 0286 qobject_cast<ListPanel *>(panel)->handleDrop(event); 0287 }); 0288 if (!color.isEmpty()) 0289 QObject::connect(panel, &ListPanel::signalRefreshColors, frame, &ListPanelFrame::refreshColors); 0290 0291 return frame; 0292 }