1 /*
2  * Copyright (C) 2019-2021 Ashar Khan <ashar786khan@gmail.com>
3  *
4  * This file is part of CP Editor.
5  *
6  * CP Editor is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * I will not be responsible if CP Editor behaves in unexpected way and
12  * causes your ratings to go down and or lose any important contest.
13  *
14  * Believe Software is "Software" and it isn't immune to bugs.
15  *
16  */
17 
18 #ifndef MAINWINDOW_HPP
19 #define MAINWINDOW_HPP
20 
21 #include <QMainWindow>
22 
23 class AppWindow;
24 class MessageLogger;
25 class QCodeEditor;
26 class QFileSystemWatcher;
27 class QPushButton;
28 class QSplitter;
29 class QTemporaryDir;
30 
31 QT_BEGIN_NAMESPACE namespace Ui
32 {
33     class MainWindow;
34 }
35 QT_END_NAMESPACE
36 
37 namespace Core
38 {
39 class Checker;
40 class Compiler;
41 class Runner;
42 } // namespace Core
43 
44 namespace Extensions
45 {
46 class CFTool;
47 struct CompanionData;
48 } // namespace Extensions
49 
50 namespace Widgets
51 {
52 class TestCases;
53 }
54 
55 class MainWindow : public QMainWindow
56 {
57     Q_OBJECT
58 
59   public:
60     struct EditorStatus
61     {
62         bool isLanguageSet{};
63         QString filePath, savedText, problemURL, editorText, language, customCompileCommand;
64         int editorCursor{}, editorAnchor{}, horizontalScrollBarValue{}, verticalScrollbarValue{}, untitledIndex{},
65             checkerIndex{}, customTimeLimit{};
66         QStringList input, expected, customCheckers;
67         QVariantList testcasesIsShow; // This can't be renamed to "isChecked" because that's not compatible
68         QVariantList testCaseSplitterStates;
69 
70         EditorStatus() = default;
71 
72         explicit EditorStatus(const QMap<QString, QVariant> &status);
73 
74         QMap<QString, QVariant> toMap() const;
75     };
76 
77     explicit MainWindow(int index, AppWindow *parent);
78     explicit MainWindow(const QString &fileOpen, int index, AppWindow *parent);
79     explicit MainWindow(const EditorStatus &status, bool duplicate, int index, AppWindow *parent);
80     ~MainWindow() override;
81 
82     int getUntitledIndex() const;
83     QString getFileName() const;
84     QString getFilePath() const;
85     QString getProblemURL() const;
86     QString getCompleteTitle() const;
87     QString getTabTitle(bool complete, bool star, int removeLength = 0);
88     QCodeEditor *getEditor() const;
89     bool isUntitled() const;
90 
91     void setProblemURL(const QString &url);
92     void setUntitledIndex(int index);
93 
94     EditorStatus toStatus() const;
95     void loadStatus(const EditorStatus &status, bool duplicate = false);
96 
97     bool save(bool force, const QString &head, bool safe = true);
98     void saveAs();
99 
100     bool isTextChanged() const;
101     bool closeConfirm();
102 
103     void killProcesses();
104     void detachedExecution();
105     void compileOnly();
106     void runOnly();
107     void compileAndRun();
108     void formatSource(bool selectionOnly, bool logOnNoChange);
109 
110     void applyCompanion(const Extensions::CompanionData &data);
111 
112     void setLanguage(const QString &lang);
113     QString getLanguage();
114     void applySettings(const QString &pagePath);
115 
116     MessageLogger *getLogger();
117     QSplitter *getSplitter();
118     QSplitter *getRightSplitter();
119 
120     void insertText(const QString &text);
121 
122     void setViewMode(const QString &mode);
123     QString tmpPath();
124 
125     /**
126      * @brief get the file path of a titled path, get the tmp path of an untitled path
127      */
128     QString filePathOrTmpPath();
129 
130     /**
131      * @brief ask the user for the new compile command for this tab
132      */
133     void updateCompileCommand();
134 
135     /**
136      * @brief ask the user for the new time limit for this tab
137      */
138     void updateTimeLimit();
139 
140   private slots:
141     void onCompilationStarted();
142     void onCompilationFinished(const QString &warning);
143     void onCompilationErrorOccurred(const QString &error);
144     void onCompilationFailed(const QString &reason);
145     void onCompilationKilled();
146 
147     void onRunStarted(int index);
148     void onRunFinished(int index, const QString &out, const QString &err, int exitCode, int timeUsed, bool tle);
149     void onFailedToStartRun(int index, const QString &error);
150     void onRunOutputLimitExceeded(int index, const QString &type);
151     void onRunKilled(int index);
152 
153     void onFileWatcherChanged(const QString &);
154     void onEditorFontChanged(const QFont &newFont);
155     void onTextChanged();
156     void updateCursorInfo();
157     void updateChecker();
158     void runTestCase(int index);
159 
160     // UI Slots
161 
162     void on_compile_clicked();
163 
164     void on_runOnly_clicked();
165 
166     void on_run_clicked();
167 
168     void on_clearMessagesButton_clicked();
169 
170     void on_changeLanguageButton_clicked();
171 
172   signals:
173     void editorFileChanged();
174     void requestUpdateLanguageServerFilePath(MainWindow *window, const QString &path);
175     void editorTextChanged(MainWindow *window);
176     void editorFontChanged();
177     void confirmTriggered(MainWindow *widget);
178     void requestToastMessage(const QString &head, const QString &body);
179     void editorLanguageChanged(MainWindow *window);
180     void compileOrRunTriggered();
181 
182   private:
183     enum SaveMode
184     {
185         IgnoreUntitled, // save only when filePath is not empty
186         AutoSave,       // basically the same as IgnoreUntitled, only different in auto-format
187         AlwaysSave,     // save to filePath if it's not empty, otherwise ask for new path
188         SaveAs,         // ask for new path no matter filePath is empty or not
189     };
190     enum AfterCompile
191     {
192         Nothing,
193         Run,
194         RunDetached
195     };
196 
197     Ui::MainWindow *ui;
198     QCodeEditor *editor;
199     QString language;
200     bool isLanguageSet = false;
201 
202     Core::Compiler *compiler = nullptr;
203     QVector<Core::Runner *> runner;
204     Core::Checker *checker = nullptr;
205     Core::Runner *detachedRunner = nullptr;
206     QTemporaryDir *tmpDir = nullptr;
207     AfterCompile afterCompile = Nothing;
208 
209     MessageLogger *log = nullptr;
210 
211     AppWindow *appWindow = nullptr;
212 
213     int untitledIndex;
214     QString problemURL;
215     QString filePath;
216     QString savedText;
217     QString cftoolPath;
218     QFileSystemWatcher *fileWatcher;
219 
220     std::atomic<bool> reloading;
221     std::atomic<bool> killingProcesses;
222 
223     QPushButton *submitToCodeforces = nullptr;
224     Extensions::CFTool *cftool = nullptr;
225 
226     Widgets::TestCases *testcases = nullptr;
227 
228     QTimer *autoSaveTimer = nullptr;
229 
230     int customTimeLimit = -1;     // the custom time limit for this tab, -1 represents for the same as settings
231     QString customCompileCommand; // the custom compile command for this tab, empty represents for the same as settings
232 
233     void setEditor();
234     void compile();
235     void run();
236     void run(int index);
237     void loadTests();
238     void saveTests(bool safe);
239     void setCFToolUI();
240     void setFilePath(QString path, bool updateBinder = true);
241     void setText(const QString &text, bool keep = false);
242     void updateWatcher();
243     void loadFile(const QString &loadPath);
244     bool saveFile(SaveMode mode, const QString &head, bool safe);
245     void performCompileAndRunDiagonistics();
246     static QString getRunnerHead(int index);
247     QString compileCommand() const;
248     int timeLimit() const;
249     void updateCompileAndRunButtons() const;
250 };
251 #endif // MAINWINDOW_HPP
252