File indexing completed on 2024-04-28 05:49:30

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
0003    SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
0004    SPDX-FileCopyrightText: 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
0005 
0006    SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #pragma once
0010 
0011 #include "katemdi.h"
0012 #include "kateviewmanager.h"
0013 
0014 #include <ktexteditor/document.h>
0015 #include <ktexteditor/mainwindow.h>
0016 #include <ktexteditor/view.h>
0017 
0018 #include <KIO/UDSEntry>
0019 #include <KParts/Part>
0020 
0021 #include <QDragEnterEvent>
0022 #include <QDropEvent>
0023 #include <QEvent>
0024 #include <QHash>
0025 #include <QStackedLayout>
0026 #include <QUrl>
0027 
0028 class QMenu;
0029 
0030 namespace KIO
0031 {
0032 class UDSEntry;
0033 typedef class QList<UDSEntry> UDSEntryList;
0034 }
0035 
0036 class KFileItem;
0037 class KRecentFilesAction;
0038 
0039 class KateOutputView;
0040 class KateViewManager;
0041 class KateMwModOnHdDialog;
0042 
0043 // Helper layout class to always provide minimum size
0044 class KateContainerStackedLayout : public QStackedLayout
0045 {
0046     Q_OBJECT
0047 public:
0048     explicit KateContainerStackedLayout(QWidget *parent);
0049     QSize sizeHint() const override;
0050     QSize minimumSize() const override;
0051 };
0052 
0053 class KateMainWindow : public KateMDI::MainWindow, virtual public KParts::PartBase
0054 {
0055     Q_OBJECT
0056 
0057 public:
0058     /**
0059      * Construct the window and restore its state from given config if any
0060      * @param sconfig session config for this window, 0 if none
0061      * @param sgroup session config group to use
0062      * @param userTriggered is the user creating this window or is it part of session restore / startup
0063      */
0064     KateMainWindow(KConfig *sconfig, const QString &sgroup, bool userTriggered);
0065 
0066     /**
0067      * Destruct the nice window
0068      */
0069     ~KateMainWindow() override;
0070 
0071     /**
0072      * Accessor methodes for interface and child objects
0073      */
0074 public:
0075     KateViewManager *viewManager()
0076     {
0077         return m_viewManager;
0078     }
0079 
0080     /**
0081      * KTextEditor::MainWindow wrapper
0082      * @return KTextEditor::MainWindow wrapper.
0083      */
0084     KTextEditor::MainWindow *wrapper()
0085     {
0086         return m_wrapper;
0087     }
0088 
0089 public:
0090     /** Returns the URL of the current document.
0091      * anders: I add this for use from the file selector. */
0092     QUrl activeDocumentUrl();
0093 
0094     /** Enumeration to specify if files modified on disk should show up
0095      * in the reload dialog even if not edited in this instance. */
0096     enum ModOnDiskMode {
0097         PromptEdited, ///< Do not list files that have not been edited
0098         PromptAll, ///< Include all files modified on disk
0099     };
0100 
0101     /**
0102      * Prompts the user for what to do with files that are modified on disk if any.
0103      * This is optionally run when the window receives focus, and when the last
0104      * window is closed.
0105      * @return true if no documents are modified on disk, or all documents were
0106      * handled by the dialog; otherwise (the dialog was canceled) false.
0107      */
0108     bool showModOnDiskPrompt(ModOnDiskMode mode);
0109 
0110 public:
0111     /*reimp*/ void readProperties(const KConfigGroup &config) override;
0112     /*reimp*/ void saveProperties(KConfigGroup &config) override
0113     {
0114         saveProperties(config, true);
0115     }
0116     /*reimp*/ void saveGlobalProperties(KConfig *sessionConfig) override;
0117 
0118     void saveProperties(KConfigGroup &config, bool includeViewConfig);
0119 
0120     void saveOpenRecent(KConfig *config);
0121     void loadOpenRecent(const KConfig *config);
0122 
0123 public:
0124     bool queryClose_internal(KTextEditor::Document *doc = nullptr, KateMainWindow *win = nullptr);
0125 
0126     /**
0127      * save the settings, size and state of this window in
0128      * the provided config group
0129      */
0130     void saveWindowConfig(const KConfigGroup &);
0131     /**
0132      * restore the settings, size and state of this window from
0133      * the provided config group.
0134      */
0135     void restoreWindowConfig(const KConfigGroup &);
0136 
0137     /**
0138      * save some global options to katerc
0139      */
0140     void saveOptions();
0141 
0142     /**
0143      * Called if focus should go to the important part of the central widget.
0144      */
0145     void triggerFocusForCentralWidget() override
0146     {
0147         // just dispatch to view manager
0148         m_viewManager->triggerActiveViewFocus();
0149     }
0150 
0151 private:
0152     /**
0153      * Setup actions which pointers are needed already in setupMainWindow
0154      */
0155     void setupImportantActions();
0156 
0157     void setupMainWindow();
0158     void setupActions();
0159     bool queryClose() override;
0160 
0161     void setupDiagnosticsView(KConfig *);
0162 
0163     /**
0164      * read some global options from katerc
0165      */
0166     void readOptions();
0167 
0168     void dragEnterEvent(QDragEnterEvent *) override;
0169     void dropEvent(QDropEvent *) override;
0170 
0171 public Q_SLOTS:
0172     void slotFileClose();
0173     void slotFileQuit();
0174     void queueModifiedOnDisc(KTextEditor::Document *doc);
0175 
0176     void slotFocusPrevTab();
0177     void slotFocusNextTab();
0178 
0179     /**
0180      * Show quick open
0181      */
0182     void slotQuickOpen();
0183 
0184     /**
0185      * Overwrite size hint for better default window sizes
0186      * @return size hint
0187      */
0188     QSize sizeHint() const override;
0189 
0190     /**
0191      * Create a new window.
0192      * Inherit size from current one.
0193      * @return new window
0194      */
0195     KateMainWindow *newWindow() const;
0196 
0197     /**
0198      * slots used for actions in the menus/toolbars
0199      * or internal signal connections
0200      */
0201 private Q_SLOTS:
0202     void slotConfigure();
0203 
0204     void slotOpenWithMenuAction(QAction *a);
0205 
0206     void slotEditToolbars();
0207     void slotNewToolbarConfig();
0208     void slotUpdateActionsNeedingUrl();
0209     void slotOpenDocument(const QUrl &);
0210 
0211     void slotDropEvent(QDropEvent *);
0212     void editKeys();
0213     void mSlotFixOpenWithMenu();
0214     static void reloadXmlGui();
0215 
0216     /* to update the caption */
0217     void slotDocumentCreated(KTextEditor::Document *doc);
0218     void updateCaption(KTextEditor::Document *doc);
0219     // calls updateCaption(doc) with the current document
0220     void updateCaption();
0221 
0222     static void pluginHelp();
0223     void slotFullScreen(bool);
0224 
0225     void slotListRecursiveEntries(KIO::Job *job, const KIO::UDSEntryList &list);
0226 
0227     void onApplicationStateChanged(Qt::ApplicationState);
0228 
0229 private Q_SLOTS:
0230     void toggleShowMenuBar(bool showMessage = true);
0231     void toggleShowStatusBar();
0232     void toggleShowTabBar();
0233 
0234 public:
0235     bool showStatusBar();
0236     bool showTabBar();
0237 
0238 Q_SIGNALS:
0239     void tabBarToggled();
0240     void unhandledShortcutOverride(QEvent *e);
0241 
0242     void widgetAdded(QWidget *);
0243     void widgetRemoved(QWidget *);
0244 
0245 public:
0246     void openUrl(const QString &name = QString());
0247 
0248     QHash<KTextEditor::Plugin *, QObject *> &pluginViews()
0249     {
0250         return m_pluginViews;
0251     }
0252 
0253     QWidget *bottomViewBarContainer()
0254     {
0255         return m_bottomViewBarContainer;
0256     }
0257 
0258     void addToBottomViewBarContainer(KTextEditor::View *view, QWidget *bar)
0259     {
0260         m_bottomContainerStack->addWidget(bar);
0261         m_bottomViewBarMapping[view] = BarState(bar);
0262     }
0263 
0264     void hideBottomViewBarForView(KTextEditor::View *view)
0265     {
0266         BarState &state = m_bottomViewBarMapping[view];
0267         if (state.bar()) {
0268             m_bottomContainerStack->setCurrentWidget(state.bar());
0269             state.bar()->hide();
0270             state.setState(false);
0271         }
0272         m_bottomViewBarContainer->hide();
0273     }
0274 
0275     void showBottomViewBarForView(KTextEditor::View *view)
0276     {
0277         BarState &state = m_bottomViewBarMapping[view];
0278         if (state.bar()) {
0279             m_bottomContainerStack->setCurrentWidget(state.bar());
0280             state.bar()->show();
0281             state.setState(true);
0282             m_bottomViewBarContainer->show();
0283         }
0284     }
0285 
0286     void deleteBottomViewBarForView(KTextEditor::View *view)
0287     {
0288         BarState state = m_bottomViewBarMapping.take(view);
0289         if (state.bar()) {
0290             if (m_bottomContainerStack->currentWidget() == state.bar()) {
0291                 m_bottomViewBarContainer->hide();
0292             }
0293             delete state.bar();
0294             delete state.statusBar();
0295         }
0296     }
0297 
0298     bool modNotificationEnabled() const
0299     {
0300         return m_modNotification;
0301     }
0302 
0303     void setModNotificationEnabled(bool e)
0304     {
0305         m_modNotification = e;
0306     }
0307 
0308     bool modCloseAfterLast() const
0309     {
0310         return m_modCloseAfterLast;
0311     }
0312 
0313     void setModCloseAfterLast(bool e)
0314     {
0315         m_modCloseAfterLast = e;
0316     }
0317 
0318     /**
0319      * add given url to list of recently opened files
0320      * @param url url that got opened
0321      */
0322     void addRecentOpenedFile(const QUrl &url);
0323 
0324     KRecentFilesAction *recentFilesAction()
0325     {
0326         return m_fileOpenRecent;
0327     }
0328 
0329     //
0330     // KTextEditor::MainWindow interface, get called by invokeMethod from our wrapper object!
0331     //
0332 public Q_SLOTS:
0333     /**
0334      * get the toplevel widget.
0335      * \return the real main window widget.
0336      */
0337     QWidget *window()
0338     {
0339         return this;
0340     }
0341 
0342     /**
0343      * Accessor to the XMLGUIFactory.
0344      * \return the mainwindow's KXMLGUIFactory.
0345      */
0346     KXMLGUIFactory *guiFactory() override
0347     {
0348         return KateMDI::MainWindow::guiFactory();
0349     }
0350 
0351     /**
0352      * Get a list of all views for this main window.
0353      * @return all views
0354      */
0355     QList<KTextEditor::View *> views()
0356     {
0357         return viewManager()->views();
0358     }
0359 
0360     /**
0361      * Access the active view.
0362      * \return active view
0363      */
0364     KTextEditor::View *activeView()
0365     {
0366         return viewManager()->activeView();
0367     }
0368 
0369     /**
0370      * Activate the view with the corresponding \p document.
0371      * If none exist for this document, create one
0372      * \param document the document
0373      * \return activated view of this document
0374      */
0375     KTextEditor::View *activateView(KTextEditor::Document *document)
0376     {
0377         return viewManager()->activateView(document);
0378     }
0379 
0380     /**
0381      * Open the document \p url with the given \p encoding.
0382      * \param url the document's url
0383      * \param encoding the preferred encoding. If encoding is QString() the
0384      *        encoding will be guessed or the default encoding will be used.
0385      * \return a pointer to the created view for the new document, if a document
0386      *         with this url is already existing, its view will be activated
0387      */
0388     KTextEditor::View *openUrl(const QUrl &url, const QString &encoding = QString())
0389     {
0390         return viewManager()->openUrlWithView(url, encoding);
0391     }
0392 
0393     /**
0394      * Close selected view
0395      * \param view the view
0396      * \return true if view was closed
0397      */
0398     bool closeView(KTextEditor::View *view)
0399     {
0400         m_viewManager->closeView(view);
0401         return true;
0402     }
0403 
0404     /**
0405      * Close the split view where the given view is contained.
0406      * \param view the view.
0407      * \return true if the split view was closed.
0408      */
0409     bool closeSplitView(KTextEditor::View *view)
0410     {
0411         m_viewManager->closeViewSpace(view);
0412         return true;
0413     }
0414 
0415     /**
0416      * @returns true if the two given views share the same split view,
0417      * false otherwise.
0418      */
0419     bool viewsInSameSplitView(KTextEditor::View *view1, KTextEditor::View *view2)
0420     {
0421         return m_viewManager->viewsInSameViewSpace(view1, view2);
0422     }
0423 
0424     /**
0425      * Split current view space according to \p orientation
0426      * \param orientation in which line split the view
0427      */
0428     void splitView(Qt::Orientation orientation)
0429     {
0430         m_viewManager->splitViewSpace(nullptr, orientation);
0431     }
0432 
0433     /**
0434      * Try to create a view bar for the given view.
0435      * Its parameter is the view for which we want a view bar
0436      * @return suitable widget that can host view bars widgets or nullptr
0437      */
0438     QWidget *createViewBar(KTextEditor::View *)
0439     {
0440         return bottomViewBarContainer();
0441     }
0442 
0443     /**
0444      * Delete the view bar for the given view.
0445      * @param view view for which we want an view bar
0446      */
0447     void deleteViewBar(KTextEditor::View *view)
0448     {
0449         deleteBottomViewBarForView(view);
0450     }
0451 
0452     /**
0453      * Add a widget to the view bar.
0454      * @param view view for which the view bar is used
0455      * @param bar bar widget, shall have the viewBarParent() as parent widget
0456      */
0457     void addWidgetToViewBar(KTextEditor::View *view, QWidget *bar)
0458     {
0459         addToBottomViewBarContainer(view, bar);
0460     }
0461 
0462     /**
0463      * Show the view bar for the given view
0464      * @param view view for which the view bar is used
0465      */
0466     void showViewBar(KTextEditor::View *view)
0467     {
0468         showBottomViewBarForView(view);
0469     }
0470 
0471     /**
0472      * Hide the view bar for the given view
0473      * @param view view for which the view bar is used
0474      */
0475     void hideViewBar(KTextEditor::View *view)
0476     {
0477         hideBottomViewBarForView(view);
0478     }
0479 
0480     void insertWidgetInStatusbar(QWidget *w)
0481     {
0482         insertWidgetBeforeStatusbar(w);
0483     }
0484 
0485     /**
0486      * Create a new toolview with unique \p identifier at side \p pos
0487      * with \p icon and caption \p text. Use the returned widget to embed
0488      * your widgets.
0489      * \param plugin which owns this tool view
0490      * \param identifier unique identifier for this toolview
0491      * \param pos position for the toolview, if we are in session restore,
0492      *        this is only a preference
0493      * \param icon icon to use in the sidebar for the toolview
0494      * \param text translated text (i18n()) to use in addition to icon
0495      * \return created toolview on success, otherwise NULL
0496      */
0497     QWidget *createToolView(KTextEditor::Plugin *plugin,
0498                             const QString &identifier,
0499                             KTextEditor::MainWindow::ToolViewPosition pos,
0500                             const QIcon &icon,
0501                             const QString &text);
0502 
0503     /**
0504      * Find the toolview with the given \p identifier
0505      * \param identifier identifier as specified in createToolView()
0506      * \return pointer to the toolview, if found, otherwise NULL
0507      */
0508     QWidget *toolviewForName(const QString &identifier)
0509     {
0510         return toolView(identifier);
0511     }
0512 
0513     /**
0514      * Move the toolview \p widget to position \p pos.
0515      * \param widget the toolview to move, where the widget was constructed
0516      *        by createToolView().
0517      * \param pos new position to move widget to
0518      * \return \e true on success, otherwise \e false
0519      */
0520     bool moveToolView(QWidget *widget, KTextEditor::MainWindow::ToolViewPosition pos);
0521 
0522     /**
0523      * Show the toolview \p widget.
0524      * \param widget the toolview to show, where the widget was constructed
0525      *        by createToolView().
0526      * \return \e true on success, otherwise \e false
0527      * \todo add focus parameter: bool showToolView (QWidget *widget, bool giveFocus );
0528      */
0529     bool showToolView(QWidget *widget);
0530 
0531     /**
0532      * Hide the toolview \p widget.
0533      * \param widget the toolview to hide, where the widget was constructed
0534      *        by createToolView().
0535      * \return \e true on success, otherwise \e false
0536      */
0537     bool hideToolView(QWidget *widget);
0538 
0539     /**
0540      * Shows the @p plugin's config page. The @p page specifies which
0541      * config page will be shown, see KTextEditor::Plugin::configPages().
0542      *
0543      * \return \e true on success, otherwise \e false
0544      * \since 5.63
0545      */
0546     bool showPluginConfigPage(KTextEditor::Plugin *configpageinterface, int id);
0547 
0548     /**
0549      * Get a plugin view for the plugin with with identifier \p name.
0550      * \param name the plugin's name
0551      * \return pointer to the plugin view if a plugin with \p name is loaded and has a view for this mainwindow,
0552      *         otherwise NULL
0553      */
0554     QObject *pluginView(const QString &name);
0555 
0556     /**
0557      * Add a widget to the main window.
0558      * This is useful to show non-KTextEditor::View widgets in the main window.
0559      * The host application should try to manage this like some KTextEditor::View (e.g. as a tab) and provide
0560      * the means to close it.
0561      * \param widget widget to add
0562      * \return success, if false, the plugin needs to take care to show the widget itself, otherwise
0563      *         the main window will take ownership of the widget
0564      */
0565     bool addWidget(QWidget *widget);
0566 
0567     /**
0568      * \brief returns the list of non-KTextEditor::View widgets in this main window.
0569      * \see addWidget
0570      */
0571     QWidgetList widgets() const;
0572 
0573     /**
0574      * \brief remove this \p widget from this mainwindow. The widget will be deleted afterwards
0575      * \param widget the widget to be removed
0576      * \return true on success
0577      */
0578     bool removeWidget(QWidget *widget);
0579 
0580     /**
0581      * \brief returns the currently active widget. It can be a
0582      * non-KTextEditor::View widget or a KTextEditor::View
0583      */
0584     QWidget *activeWidget();
0585 
0586     /**
0587      * \brief activate @p widget. If the widget is not present in the window, it will be added to the window
0588      * \param widget the widget to activate
0589      */
0590     void activateWidget(QWidget *widget);
0591 
0592     /**
0593      * @brief show a message in the output view. See KTextEditor::MainWindow::showMessage for details
0594      */
0595     void showMessage(const QVariantMap &map);
0596 
0597     void addPositionToHistory(const QUrl &url, KTextEditor::Cursor c);
0598 
0599 private Q_SLOTS:
0600     void slotUpdateBottomViewBar();
0601     void slotDocumentCloseAll();
0602     void slotDocumentCloseOther();
0603     void slotDocumentCloseOther(KTextEditor::Document *document);
0604     void slotDocumentCloseSelected(const QList<KTextEditor::Document *> &);
0605 
0606 private:
0607     void ensureHamburgerBarSize();
0608 
0609     /**
0610      * Wrapper of main window for KTextEditor
0611      */
0612     KTextEditor::MainWindow *m_wrapper;
0613 
0614     /**
0615      * Notify about file modifications from other processes?
0616      */
0617     bool m_modNotification = false;
0618 
0619     /**
0620      * Shutdown Kate after last file is closed
0621      */
0622     bool m_modCloseAfterLast = false;
0623 
0624     /**
0625      * keeps track of views
0626      */
0627     KateViewManager *m_viewManager = nullptr;
0628 
0629     KRecentFilesAction *m_fileOpenRecent = nullptr;
0630 
0631     KActionMenu *documentOpenWith = nullptr;
0632 
0633     KToggleAction *settingsShowFileselector = nullptr;
0634 
0635     KToggleAction *m_showFullScreenAction = nullptr;
0636 
0637     bool m_modignore = false;
0638 
0639     // all plugin views for this mainwindow, used by the pluginmanager
0640     QHash<KTextEditor::Plugin *, QObject *> m_pluginViews;
0641 
0642     // options: show statusbar + show path
0643     KToggleAction *m_paShowPath = nullptr;
0644     KToggleAction *m_paShowMenuBar = nullptr;
0645     KToggleAction *m_paShowStatusBar = nullptr;
0646     KToggleAction *m_paShowTabBar = nullptr;
0647     KToggleAction *m_paShowUrlNavBar = nullptr;
0648 
0649     QWidget *m_bottomViewBarContainer = nullptr;
0650     KateContainerStackedLayout *m_bottomContainerStack = nullptr;
0651 
0652     class BarState
0653     {
0654     public:
0655         BarState() = default;
0656         explicit BarState(QWidget *bar)
0657             : m_bar(bar)
0658             , m_state(false)
0659         {
0660         }
0661         ~BarState()
0662         {
0663         }
0664         QWidget *bar()
0665         {
0666             return m_bar;
0667         }
0668         bool state()
0669         {
0670             return m_state;
0671         }
0672         void setState(bool state)
0673         {
0674             m_state = state;
0675         }
0676         QWidget *statusBar()
0677         {
0678             return m_statusBar;
0679         }
0680         void setStatusBar(QWidget *statusBar)
0681         {
0682             Q_ASSERT(statusBar);
0683             Q_ASSERT(!m_statusBar);
0684             m_statusBar = statusBar;
0685         }
0686 
0687     private:
0688         QWidget *m_bar = nullptr;
0689         bool m_state = false;
0690 
0691         /**
0692          * status bar widget
0693          * if already extracted from the bar itself
0694          * we can't extract this on creation of the bar widget, but do this lazy
0695          * later, as the construction order in KateView doesn't allow for that.
0696          */
0697         QWidget *m_statusBar = nullptr;
0698     };
0699     QHash<KTextEditor::View *, BarState> m_bottomViewBarMapping;
0700 
0701     /**
0702      * generic output tool view
0703      */
0704     QWidget *m_toolViewOutput = nullptr;
0705 
0706     /**
0707      * output widget contained in above tool view
0708      */
0709     KateOutputView *m_outputView = nullptr;
0710 
0711     /**
0712      * Diagnostics view at the bottom
0713      */
0714     class DiagnosticsView *m_diagView = nullptr;
0715 
0716 public:
0717     /**
0718      * The available actions for the mouse back button, used as indexing
0719      * for the comboboxes of the config window
0720      */
0721     enum MouseBackButtonAction { PreviousTab = 0, HistoryBack };
0722 
0723     /**
0724      * The available actions for the mouse forward button, used as indexing
0725      * for the comboboxes of the config window
0726      */
0727     enum MouseForwardButtonAction { NextTab = 0, HistoryForward };
0728 
0729 private:
0730     MouseBackButtonAction m_mouseButtonBackAction = PreviousTab;
0731     MouseForwardButtonAction m_mouseButtonForwardAction = NextTab;
0732     void handleBackButtonAction();
0733     void handleForwardButtonAction();
0734 
0735 public:
0736     /**
0737      * Accessor for unique output view per main window.
0738      * @return our output view, will always exist!
0739      */
0740     KateOutputView *outputView()
0741     {
0742         return m_outputView;
0743     }
0744 
0745 public:
0746     static void unsetModifiedOnDiscDialogIfIf(KateMwModOnHdDialog *diag)
0747     {
0748         if (s_modOnHdDialog == diag) {
0749             s_modOnHdDialog = nullptr;
0750         }
0751     }
0752 
0753 private:
0754     static KateMwModOnHdDialog *s_modOnHdDialog;
0755 
0756 public Q_SLOTS:
0757     void slotWindowActivated();
0758 
0759 protected:
0760     bool event(QEvent *e) override;
0761     void mousePressEvent(QMouseEvent *e) override;
0762 };