KTextEditor

appcommands.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Erlend Hamberg <ehamberg@gmail.com>
3 SPDX-FileCopyrightText: 2011 Svyatoslav Kuzmich <svatoslav1@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include <QDir>
9#include <QTimer>
10
11#include <KLocalizedString>
12#include <KTextEditor/Application>
13#include <KTextEditor/Document>
14#include <KTextEditor/Editor>
15#include <KTextEditor/MainWindow>
16#include <KTextEditor/View>
17
18#include <vimode/appcommands.h>
19
20using namespace KateVi;
21
22// BEGIN AppCommands
23AppCommands *AppCommands::m_instance = nullptr;
24
25AppCommands::AppCommands()
26 : KTextEditor::Command({QStringLiteral("q"), QStringLiteral("qa"), QStringLiteral("qall"), QStringLiteral("q!"), QStringLiteral("qa!"),
27 QStringLiteral("qall!"), QStringLiteral("w"), QStringLiteral("wq"), QStringLiteral("wa"), QStringLiteral("wqa"),
28 QStringLiteral("x"), QStringLiteral("xa"), QStringLiteral("new"), QStringLiteral("vnew"), QStringLiteral("e"),
29 QStringLiteral("edit"), QStringLiteral("enew"), QStringLiteral("sp"), QStringLiteral("split"), QStringLiteral("vs"),
30 QStringLiteral("vsplit"), QStringLiteral("only"), QStringLiteral("tabe"), QStringLiteral("tabedit"), QStringLiteral("tabnew"),
31 QStringLiteral("bd"), QStringLiteral("bdelete"), QStringLiteral("tabc"), QStringLiteral("tabclose"), QStringLiteral("clo"),
32 QStringLiteral("close")})
33 , re_write(QStringLiteral("^w(a)?$"))
34 , re_close(QStringLiteral("^bd(elete)?|tabc(lose)?$"))
35 , re_quit(QStringLiteral("^(w)?q(a|all)?(!)?$"))
36 , re_exit(QStringLiteral("^x(a)?$"))
37 , re_edit(QStringLiteral("^e(dit)?|tabe(dit)?|tabnew$"))
38 , re_tabedit(QStringLiteral("^tabe(dit)?|tabnew$"))
39 , re_new(QStringLiteral("^(v)?new$"))
40 , re_split(QStringLiteral("^sp(lit)?$"))
41 , re_vsplit(QStringLiteral("^vs(plit)?$"))
42 , re_vclose(QStringLiteral("^clo(se)?$"))
43 , re_only(QStringLiteral("^on(ly)?$"))
44{
45}
46
47AppCommands::~AppCommands()
48{
49 m_instance = nullptr;
50}
51
52bool AppCommands::exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &)
53{
54 QStringList args(cmd.split(QRegularExpression(QStringLiteral("\\s+")), Qt::SkipEmptyParts));
55 QString command(args.takeFirst());
56
57 KTextEditor::MainWindow *mainWin = view->mainWindow();
59
61 if ((match = re_write.match(command)).hasMatch()) { // TODO: handle writing to specific file
62 if (!match.captured(1).isEmpty()) { // [a]ll
63 const auto docs = app->documents();
64 for (KTextEditor::Document *doc : docs) {
65 doc->save();
66 }
67 msg = i18n("All documents written to disk");
68 } else {
69 view->document()->documentSave();
70 msg = i18n("Document written to disk");
71 }
72 }
73 // Other buffer commands are implemented by the KateFileTree plugin
74 else if ((match = re_close.match(command)).hasMatch()) {
75 QTimer::singleShot(0, view, [app, view]() {
76 app->closeDocument(view->document());
77 });
78 } else if ((match = re_quit.match(command)).hasMatch()) {
79 const bool save = !match.captured(1).isEmpty(); // :[w]q
80 const bool allDocuments = !match.captured(2).isEmpty(); // :q[all]
81 const bool doNotPromptForSave = !match.captured(3).isEmpty(); // :q[!]
82
83 if (allDocuments) {
84 if (save) {
85 const auto docs = app->documents();
86 for (KTextEditor::Document *doc : docs) {
87 doc->save();
88 }
89 }
90
91 if (doNotPromptForSave) {
92 const auto docs = app->documents();
93 for (KTextEditor::Document *doc : docs) {
94 if (doc->isModified()) {
95 doc->setModified(false);
96 }
97 }
98 }
99
100 QTimer::singleShot(0, this, [this, app]() {
101 closeDocuments(app->documents());
102 });
103 } else {
104 if (save && view->document()->isModified()) {
105 view->document()->documentSave();
106 }
107
108 if (doNotPromptForSave) {
109 view->document()->setModified(false);
110 }
111
112 if (mainWin->views().size() > 1) {
113 QTimer::singleShot(0, this, &AppCommands::closeCurrentView);
114 } else {
115 Q_ASSERT(app->documents().size() > 0);
116 QTimer::singleShot(0, this, &AppCommands::closeCurrentDocument);
117 }
118 }
119 } else if ((match = re_exit.match(command)).hasMatch()) {
120 if (!match.captured(1).isEmpty()) { // a[ll]
121 const auto docs = app->documents();
122 for (KTextEditor::Document *doc : docs) {
123 doc->save();
124 }
125 QTimer::singleShot(0, this, &AppCommands::quit);
126 } else {
127 if (view->document()->isModified()) {
128 view->document()->documentSave();
129 }
130
131 if (app->documents().size() > 1) {
132 QTimer::singleShot(0, this, &AppCommands::closeCurrentDocument);
133 } else {
134 QTimer::singleShot(0, this, &AppCommands::quit);
135 }
136 }
137 } else if ((match = re_edit.match(command)).hasMatch()) {
138 QString argument = args.join(QLatin1Char(' '));
139 if (argument.isEmpty() || argument == QLatin1String("!")) {
140 if ((match = re_tabedit.match(command)).hasMatch()) {
141 if (auto doc = app->openUrl(QUrl())) {
142 QTimer::singleShot(0, [mainWin, doc]() {
143 mainWin->activateView(doc);
144 });
145 }
146 } else {
147 view->document()->documentReload();
148 }
149 } else {
150 QUrl base = view->document()->url();
151 QUrl url;
152 QUrl arg2path(argument);
153 if (base.isValid()) { // first try to use the same path as the current open document has
154 url =
155 QUrl(base.resolved(arg2path)); // resolved handles the case where the args is a relative path, and is the same as using QUrl(args) elsewise
156 } else { // else use the cwd
158 .resolved(arg2path)); // + "/" is needed because of https://lists.qt-project.org/pipermail/qt-interest-old/2011-May/033913.html
159 }
160
161 // either find existing document or just open it, openUrl will take care of non-existing files, too
162 KTextEditor::Document *doc = app->findUrl(url);
163 if (!doc) {
164 doc = app->openUrl(url);
165 }
166 if (doc) {
167 QTimer::singleShot(0, [mainWin, doc]() {
168 mainWin->activateView(doc);
169 });
170 }
171 }
172 // splitView() orientations are reversed from the usual editor convention.
173 // 'vsplit' and 'vnew' use Qt::Horizontal to match vi and the Kate UI actions.
174 } else if ((match = re_new.match(command)).hasMatch()) {
175 if (match.captured(1) == QLatin1String("v")) { // vertical split
176 mainWin->splitView(Qt::Horizontal);
177 } else { // horizontal split
178 mainWin->splitView(Qt::Vertical);
179 }
180 mainWin->openUrl(QUrl());
181 } else if (command == QLatin1String("enew")) {
182 mainWin->openUrl(QUrl());
183 } else if ((match = re_split.match(command)).hasMatch()) {
184 mainWin->splitView(Qt::Vertical); // see above
185 } else if ((match = re_vsplit.match(command)).hasMatch()) {
186 mainWin->splitView(Qt::Horizontal);
187 } else if ((match = re_vclose.match(command)).hasMatch()) {
188 QTimer::singleShot(0, this, &AppCommands::closeCurrentSplitView);
189 } else if ((match = re_only.match(command)).hasMatch()) {
190 QTimer::singleShot(0, this, &AppCommands::closeOtherSplitViews);
191 }
192
193 return true;
194}
195
196bool AppCommands::help(KTextEditor::View *view, const QString &cmd, QString &msg)
197{
198 Q_UNUSED(view);
199
200 if (re_write.match(cmd).hasMatch()) {
201 msg = i18n(
202 "<p><b>w/wa &mdash; write document(s) to disk</b></p>"
203 "<p>Usage: <tt><b>w[a]</b></tt></p>"
204 "<p>Writes the current document(s) to disk. "
205 "It can be called in two ways:<br />"
206 " <tt>w</tt> &mdash; writes the current document to disk<br />"
207 " <tt>wa</tt> &mdash; writes all documents to disk.</p>"
208 "<p>If no file name is associated with the document, "
209 "a file dialog will be shown.</p>");
210 return true;
211 } else if (re_quit.match(cmd).hasMatch()) {
212 msg = i18n(
213 "<p><b>q/qa/wq/wqa &mdash; [write and] quit</b></p>"
214 "<p>Usage: <tt><b>[w]q[a]</b></tt></p>"
215 "<p>Quits the application. If <tt>w</tt> is prepended, it also writes"
216 " the document(s) to disk. This command "
217 "can be called in several ways:<br />"
218 " <tt>q</tt> &mdash; closes the current view.<br />"
219 " <tt>qa</tt> &mdash; closes all views, effectively quitting the application.<br />"
220 " <tt>wq</tt> &mdash; writes the current document to disk and closes its view.<br />"
221 " <tt>wqa</tt> &mdash; writes all documents to disk and quits.</p>"
222 "<p>In all cases, if the view being closed is the last view, the application quits. "
223 "If no file name is associated with the document and it should be written to disk, "
224 "a file dialog will be shown.</p>");
225 return true;
226 } else if (re_exit.match(cmd).hasMatch()) {
227 msg = i18n(
228 "<p><b>x/xa &mdash; write and quit</b></p>"
229 "<p>Usage: <tt><b>x[a]</b></tt></p>"
230 "<p>Saves document(s) and quits (e<b>x</b>its). This command "
231 "can be called in two ways:<br />"
232 " <tt>x</tt> &mdash; closes the current view.<br />"
233 " <tt>xa</tt> &mdash; closes all views, effectively quitting the application.</p>"
234 "<p>In all cases, if the view being closed is the last view, the application quits. "
235 "If no file name is associated with the document and it should be written to disk, "
236 "a file dialog will be shown.</p>"
237 "<p>Unlike the 'w' commands, this command only writes the document if it is modified."
238 "</p>");
239 return true;
240 } else if (re_split.match(cmd).hasMatch()) {
241 msg = i18n(
242 "<p><b>sp,split&mdash; Split horizontally the current view into two</b></p>"
243 "<p>Usage: <tt><b>sp[lit]</b></tt></p>"
244 "<p>The result is two views on the same document.</p>");
245 return true;
246 } else if (re_vsplit.match(cmd).hasMatch()) {
247 msg = i18n(
248 "<p><b>vs,vsplit&mdash; Split vertically the current view into two</b></p>"
249 "<p>Usage: <tt><b>vs[plit]</b></tt></p>"
250 "<p>The result is two views on the same document.</p>");
251 return true;
252 } else if (re_vclose.match(cmd).hasMatch()) {
253 msg = i18n(
254 "<p><b>clo[se]&mdash; Close the current view</b></p>"
255 "<p>Usage: <tt><b>clo[se]</b></tt></p>"
256 "<p>After executing it, the current view will be closed.</p>");
257 return true;
258 } else if (re_new.match(cmd).hasMatch()) {
259 msg = i18n(
260 "<p><b>[v]new &mdash; split view and create new document</b></p>"
261 "<p>Usage: <tt><b>[v]new</b></tt></p>"
262 "<p>Splits the current view and opens a new document in the new view."
263 " This command can be called in two ways:<br />"
264 " <tt>new</tt> &mdash; splits the view horizontally and opens a new document.<br />"
265 " <tt>vnew</tt> &mdash; splits the view vertically and opens a new document.<br />"
266 "</p>");
267 return true;
268 } else if (re_edit.match(cmd).hasMatch()) {
269 msg = i18n(
270 "<p><b>e[dit] &mdash; reload current document</b></p>"
271 "<p>Usage: <tt><b>e[dit]</b></tt></p>"
272 "<p>Starts <b>e</b>diting the current document again. This is useful to re-edit"
273 " the current file, when it was modified on disk.</p>");
274 return true;
275 }
276
277 return false;
278}
279
280KTextEditor::View *AppCommands::findViewInDifferentSplitView(KTextEditor::MainWindow *window, KTextEditor::View *view)
281{
282 const auto views = window->views();
283 for (KTextEditor::View *it : views) {
284 if (!window->viewsInSameSplitView(it, view)) {
285 return it;
286 }
287 }
288 return nullptr;
289}
290
291void AppCommands::closeDocuments(const QList<KTextEditor::Document *> &documents)
292{
294 QTimer::singleShot(0, app, [app, documents]() {
295 app->closeDocuments(documents);
296 });
297}
298
299void AppCommands::closeCurrentDocument()
300{
303 QTimer::singleShot(0, doc, [app, doc]() {
304 app->closeDocument(doc);
305 });
306}
307
308void AppCommands::closeCurrentView()
309{
312 mw->closeView(mw->activeView());
313}
314
315void AppCommands::closeCurrentSplitView()
316{
319 mw->closeSplitView(mw->activeView());
320}
321
322void AppCommands::closeOtherSplitViews()
323{
326 KTextEditor::View *view = mw->activeView();
327 KTextEditor::View *viewToRemove = nullptr;
328
329 while ((viewToRemove = findViewInDifferentSplitView(mw, view))) {
330 mw->closeSplitView(viewToRemove);
331 }
332}
333
334void AppCommands::quit()
335{
337}
338
339// END AppCommands
340
341// BEGIN KateViBufferCommand
342BufferCommands *BufferCommands::m_instance = nullptr;
343
344BufferCommands::BufferCommands()
345 : KTextEditor::Command({QStringLiteral("ls"),
346 QStringLiteral("b"),
347 QStringLiteral("buffer"),
348 QStringLiteral("bn"),
349 QStringLiteral("bnext"),
350 QStringLiteral("bp"),
351 QStringLiteral("bprevious"),
352 QStringLiteral("tabn"),
353 QStringLiteral("tabnext"),
354 QStringLiteral("tabp"),
355 QStringLiteral("tabprevious"),
356 QStringLiteral("bf"),
357 QStringLiteral("bfirst"),
358 QStringLiteral("bl"),
359 QStringLiteral("blast"),
360 QStringLiteral("tabf"),
361 QStringLiteral("tabfirst"),
362 QStringLiteral("tabl"),
363 QStringLiteral("tablast")})
364{
365}
366
367BufferCommands::~BufferCommands()
368{
369 m_instance = nullptr;
370}
371
372bool BufferCommands::exec(KTextEditor::View *view, const QString &cmd, QString &, const KTextEditor::Range &)
373{
374 // create list of args
376 QString command = args.takeFirst(); // same as cmd if split failed
377 QString argument = args.join(QLatin1Char(' '));
378
379 if (command == QLatin1String("ls")) {
380 // TODO: open quickview
381 } else if (command == QLatin1String("b") || command == QLatin1String("buffer")) {
382 switchDocument(view, argument);
383 } else if (command == QLatin1String("bp") || command == QLatin1String("bprevious")) {
384 prevBuffer(view);
385 } else if (command == QLatin1String("bn") || command == QLatin1String("bnext")) {
386 nextBuffer(view);
387 } else if (command == QLatin1String("bf") || command == QLatin1String("bfirst")) {
388 firstBuffer(view);
389 } else if (command == QLatin1String("bl") || command == QLatin1String("blast")) {
390 lastBuffer(view);
391 } else if (command == QLatin1String("tabn") || command == QLatin1String("tabnext")) {
392 nextTab(view);
393 } else if (command == QLatin1String("tabp") || command == QLatin1String("tabprevious")) {
394 prevTab(view);
395 } else if (command == QLatin1String("tabf") || command == QLatin1String("tabfirst")) {
396 firstTab(view);
397 } else if (command == QLatin1String("tabl") || command == QLatin1String("tablast")) {
398 lastTab(view);
399 }
400 return true;
401}
402
403void BufferCommands::switchDocument(KTextEditor::View *view, const QString &address)
404{
405 if (address.isEmpty()) {
406 // no argument: switch to the previous document
407 prevBuffer(view);
408 return;
409 }
410
411 const int idx = address.toInt();
412 QList<KTextEditor::Document *> docs = documents();
413
414 if (idx > 0 && idx <= docs.size()) {
415 // numerical argument: switch to the nth document
416 activateDocument(view, docs.at(idx - 1));
417 } else {
418 // string argument: switch to the given file
419 KTextEditor::Document *doc = nullptr;
420
421 for (KTextEditor::Document *it : docs) {
422 if (it->documentName() == address) {
423 doc = it;
424 break;
425 }
426 }
427
428 if (doc) {
429 activateDocument(view, doc);
430 }
431 }
432}
433
434void BufferCommands::prevBuffer(KTextEditor::View *view)
435{
436 const QList<KTextEditor::Document *> docs = documents();
437 const int idx = docs.indexOf(view->document());
438
439 if (idx > 0) {
440 activateDocument(view, docs.at(idx - 1));
441 } else if (!docs.isEmpty()) { // wrap
442 activateDocument(view, docs.last());
443 }
444}
445
446void BufferCommands::nextBuffer(KTextEditor::View *view)
447{
448 QList<KTextEditor::Document *> docs = documents();
449 const int idx = docs.indexOf(view->document());
450
451 if (idx + 1 < docs.size()) {
452 activateDocument(view, docs.at(idx + 1));
453 } else if (!docs.isEmpty()) { // wrap
454 activateDocument(view, docs.first());
455 }
456}
457
458void BufferCommands::firstBuffer(KTextEditor::View *view)
459{
460 auto docs = documents();
461 if (!docs.isEmpty()) {
462 activateDocument(view, documents().at(0));
463 }
464}
465
466void BufferCommands::lastBuffer(KTextEditor::View *view)
467{
468 auto docs = documents();
469 if (!docs.isEmpty()) {
470 activateDocument(view, documents().last());
471 }
472}
473
474void BufferCommands::prevTab(KTextEditor::View *view)
475{
476 prevBuffer(view); // TODO: implement properly, when interface is added
477}
478
479void BufferCommands::nextTab(KTextEditor::View *view)
480{
481 nextBuffer(view); // TODO: implement properly, when interface is added
482}
483
484void BufferCommands::firstTab(KTextEditor::View *view)
485{
486 firstBuffer(view); // TODO: implement properly, when interface is added
487}
488
489void BufferCommands::lastTab(KTextEditor::View *view)
490{
491 lastBuffer(view); // TODO: implement properly, when interface is added
492}
493
494void BufferCommands::activateDocument(KTextEditor::View *view, KTextEditor::Document *doc)
495{
496 KTextEditor::MainWindow *mainWindow = view->mainWindow();
497 QTimer::singleShot(0, [mainWindow, doc]() {
498 mainWindow->activateView(doc);
499 });
500}
501
502QList<KTextEditor::Document *> BufferCommands::documents()
503{
505 return app->documents();
506}
507
508bool BufferCommands::help(KTextEditor::View * /*view*/, const QString &cmd, QString &msg)
509{
510 if (cmd == QLatin1String("b") || cmd == QLatin1String("buffer")) {
511 msg = i18n(
512 "<p><b>b,buffer &mdash; Edit document N from the document list</b></p>"
513 "<p>Usage: <tt><b>b[uffer] [N]</b></tt></p>");
514 return true;
515 } else if (cmd == QLatin1String("bp") || cmd == QLatin1String("bprevious") || cmd == QLatin1String("tabp") || cmd == QLatin1String("tabprevious")) {
516 msg = i18n(
517 "<p><b>bp,bprev &mdash; previous buffer</b></p>"
518 "<p>Usage: <tt><b>bp[revious] [N]</b></tt></p>"
519 "<p>Goes to <b>[N]</b>th previous document (\"<b>b</b>uffer\") in document list. </p>"
520 "<p> <b>[N]</b> defaults to one. </p>"
521 "<p>Wraps around the start of the document list.</p>");
522 return true;
523 } else if (cmd == QLatin1String("bn") || cmd == QLatin1String("bnext") || cmd == QLatin1String("tabn") || cmd == QLatin1String("tabnext")) {
524 msg = i18n(
525 "<p><b>bn,bnext &mdash; switch to next document</b></p>"
526 "<p>Usage: <tt><b>bn[ext] [N]</b></tt></p>"
527 "<p>Goes to <b>[N]</b>th next document (\"<b>b</b>uffer\") in document list."
528 "<b>[N]</b> defaults to one. </p>"
529 "<p>Wraps around the end of the document list.</p>");
530 return true;
531 } else if (cmd == QLatin1String("bf") || cmd == QLatin1String("bfirst") || cmd == QLatin1String("tabf") || cmd == QLatin1String("tabfirst")) {
532 msg = i18n(
533 "<p><b>bf,bfirst &mdash; first document</b></p>"
534 "<p>Usage: <tt><b>bf[irst]</b></tt></p>"
535 "<p>Goes to the <b>f</b>irst document (\"<b>b</b>uffer\") in document list.</p>");
536 return true;
537 } else if (cmd == QLatin1String("bl") || cmd == QLatin1String("blast") || cmd == QLatin1String("tabl") || cmd == QLatin1String("tablast")) {
538 msg = i18n(
539 "<p><b>bl,blast &mdash; last document</b></p>"
540 "<p>Usage: <tt><b>bl[ast]</b></tt></p>"
541 "<p>Goes to the <b>l</b>ast document (\"<b>b</b>uffer\") in document list.</p>");
542 return true;
543 } else if (cmd == QLatin1String("ls")) {
544 msg = i18n(
545 "<p><b>ls</b></p>"
546 "<p>list current buffers<p>");
547 }
548
549 return false;
550}
551// END KateViBufferCommand
bool isModified() const
This class allows the application that embeds the KTextEditor component to allow it access to applica...
Definition application.h:44
KTextEditor::Document * openUrl(const QUrl &url, const QString &encoding=QString())
Open the document url with the given encoding.
bool closeDocuments(const QList< KTextEditor::Document * > &documents)
Close a list of documents.
KTextEditor::Document * findUrl(const QUrl &url)
Get the document with the URL url.
bool closeDocument(KTextEditor::Document *document)
Close the given document.
KTextEditor::MainWindow * activeMainWindow()
Accessor to the active main window.
bool quit()
Ask app to quit.
QList< KTextEditor::Document * > documents()
Get a list of all documents that are managed by the application.
A KParts derived class representing a text document.
Definition document.h:284
virtual bool documentReload()=0
Reload the current file.
virtual bool documentSave()=0
Save the current file.
static Editor * instance()
Accessor to get the Editor instance.
virtual KTextEditor::Application * application() const =0
Current hosting application, if any set.
This class allows the application that embeds the KTextEditor component to allow it to access parts o...
Definition mainwindow.h:47
KTextEditor::View * openUrl(const QUrl &url, const QString &encoding=QString())
Open the document url with the given encoding.
QList< KTextEditor::View * > views()
Get a list of all views for this main window.
KTextEditor::View * activeView()
Access the active view.
bool closeView(KTextEditor::View *view)
Close selected view.
bool closeSplitView(KTextEditor::View *view)
Close the split view that contains the given view.
KTextEditor::View * activateView(KTextEditor::Document *document)
Activate the view with the corresponding document.
void splitView(Qt::Orientation orientation)
Split current view space according to orientation.
An object representing a section of text, from one Cursor to another.
A text widget with KXMLGUIClient that represents a Document.
Definition view.h:244
virtual Document * document() const =0
Get the view's document, that means the view is a view of the returned document.
virtual KTextEditor::MainWindow * mainWindow() const =0
Get the view's main window, if any.
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
PostalAddress address(const QVariant &location)
KGUIADDONS_EXPORT QWindow * window(QObject *job)
QAction * save(const QObject *recvr, const char *slot, QObject *parent)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
QString currentPath()
const T & at(int i) const const
T & first()
int indexOf(const T &value, int from) const const
bool isEmpty() const const
T & last()
int size() const const
QRegularExpressionMatch match(const QString &subject, int offset, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions) const const
bool hasMatch() const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
Horizontal
SkipEmptyParts
QUrl fromLocalFile(const QString &localFile)
bool isValid() const const
QUrl resolved(const QUrl &relative) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Feb 24 2024 20:00:58 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.