1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#include "editorlist.h"
18#include "editor.h"
19#include <QMessageBox>
20#include <QVariant>
21#include <mainwindow.h>
22#include <QFileInfo>
23#include "settings.h"
24#include "project.h"
25#include "systemconsts.h"
26#include <QApplication>
27
28EditorList::EditorList(QTabWidget* leftPageWidget,
29 QTabWidget* rightPageWidget,
30 QSplitter* splitter,
31 QWidget* panel,
32 QObject* parent):
33 QObject(parent),
34 mLayout(LayoutShowType::lstLeft),
35 mLeftPageWidget(leftPageWidget),
36 mRightPageWidget(rightPageWidget),
37 mSplitter(splitter),
38 mPanel(panel),
39 mUpdateCount(0)
40{
41
42}
43
44Editor* EditorList::newEditor(const QString& filename, const QByteArray& encoding,
45 bool inProject, bool newFile,
46 QTabWidget* page) {
47 QTabWidget * parentPageControl = nullptr;
48 if (page == nullptr)
49 parentPageControl = getNewEditorPageControl();
50 else
51 parentPageControl = page;
52 if (fileExists(filename)) {
53 pMainWindow->fileSystemWatcher()->addPath(filename);
54 }
55 Editor * e = new Editor(parentPageControl,filename,encoding,inProject,newFile,parentPageControl);
56 connect(e, &Editor::renamed, this, &EditorList::onEditorRenamed);
57 updateLayout();
58 if (pMainWindow->project()){
59 PProjectUnit unit = pMainWindow->project()->findUnitByFilename(filename);
60 if (unit) {
61 pMainWindow->project()->associateEditorToUnit(e,unit);
62 e->setInProject(true);
63 }
64 }
65 connect(e,&Editor::fileSaved,
66 pMainWindow, &MainWindow::onFileSaved);
67 return e;
68}
69
70QTabWidget* EditorList::getNewEditorPageControl() const {
71 return getFocusedPageControl();
72}
73
74QTabWidget* EditorList::getFocusedPageControl() const {
75 //todo:
76 switch(mLayout) {
77 case LayoutShowType::lstLeft:
78 return mLeftPageWidget;
79 case LayoutShowType::lstRight:
80 return mRightPageWidget;
81 case LayoutShowType::lstBoth: {
82 Editor* editor = dynamic_cast<Editor*>(mRightPageWidget->currentWidget());
83 if (editor && editor->hasFocus())
84 return mRightPageWidget;
85 return mLeftPageWidget;
86 }
87 default:
88 return nullptr;
89 }
90}
91
92void EditorList::showLayout(LayoutShowType layout)
93{
94 if (layout == mLayout)
95 return;
96 mLayout = layout;
97 // Apply widths if layout does not change
98 switch(mLayout) {
99 case LayoutShowType::lstLeft:
100 mLeftPageWidget->setVisible(true);
101 mRightPageWidget->setVisible(false);
102 break;
103 case LayoutShowType::lstRight:
104 mLeftPageWidget->setVisible(false);
105 mRightPageWidget->setVisible(true);
106 break;
107 case LayoutShowType::lstBoth:
108 mLeftPageWidget->setVisible(true);
109 mRightPageWidget->setVisible(true);
110 }
111}
112
113void EditorList::onEditorRenamed(const QString &oldFilename, const QString &newFilename, bool firstSave)
114{
115 emit editorRenamed(oldFilename, newFilename, firstSave);
116}
117
118QTabWidget *EditorList::rightPageWidget() const
119{
120 return mRightPageWidget;
121}
122
123QTabWidget *EditorList::leftPageWidget() const
124{
125 return mLeftPageWidget;
126}
127
128Editor* EditorList::getEditor(int index, QTabWidget* tabsWidget) const {
129 QTabWidget* selectedWidget;
130 if (tabsWidget == nullptr) {
131 selectedWidget = getFocusedPageControl();
132 } else {
133 selectedWidget = tabsWidget;
134 }
135 if (!selectedWidget)
136 return nullptr;
137 if (index == -1) {
138 index = selectedWidget->currentIndex();
139 }
140 if (index<0 || index >= selectedWidget->count()) {
141 return nullptr;
142 }
143 return (Editor*)selectedWidget->widget(index);
144}
145
146bool EditorList::closeEditor(Editor* editor, bool transferFocus, bool force) {
147 if (editor == NULL)
148 return false;
149 if (force) {
150 editor->save(true,false);
151 } else if ( (editor->modified()) && (!editor->empty())) {
152 // ask user if he wants to save
153 QMessageBox::StandardButton reply;
154 reply = QMessageBox::question(editor,QObject::tr("Save"),
155 QString(QObject::tr("Save changes to %1?")).arg(editor->filename()),
156 QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel);
157 if (reply == QMessageBox::Cancel) {
158 return false;
159 } else if (reply == QMessageBox::Yes) {
160 if (!editor->save(false,false)) {
161 return false;
162 }
163 }
164 }
165
166 beginUpdate();
167// if (transferFocus && (editor->pageControl()->currentWidget()==editor)) {
168// //todo: activate & focus the previous editor
169// }
170
171 if (editor->inProject() && pMainWindow->project()) {
172 int projIndex = pMainWindow->project()->indexInUnits(editor);
173 if (projIndex>=0) {
174 pMainWindow->project()->closeUnit(projIndex);
175 }
176 } else {
177 if (pSettings->history().addToOpenedFiles(editor->filename())) {
178 pMainWindow->rebuildOpenedFileHisotryMenu();
179 }
180 delete editor;
181 }
182 updateLayout();
183 if (!force && transferFocus) {
184 editor = getEditor();
185 if (editor) {
186 editor->activate();
187 pMainWindow->updateClassBrowserForEditor(editor);
188 } else {
189 pMainWindow->updateClassBrowserForEditor(nullptr);
190 }
191 }
192 emit editorClosed();
193 endUpdate();
194 return true;
195}
196
197bool EditorList::swapEditor(Editor *editor)
198{
199 Q_ASSERT(editor!=nullptr);
200 beginUpdate();
201 auto action = finally([this](){
202 endUpdate();
203 });
204 //remember old index
205 QTabWidget* fromPageControl = editor->pageControl();
206 if (fromPageControl == mLeftPageWidget) {
207 editor->setPageControl(mRightPageWidget);
208 } else {
209 editor->setPageControl(mLeftPageWidget);
210 }
211 updateLayout();
212 editor->activate();
213 return true;
214}
215
216void EditorList::beginUpdate() {
217 if (mUpdateCount==0) {
218 mPanel->setUpdatesEnabled(false);
219 }
220 mUpdateCount++;
221}
222
223void EditorList::endUpdate() {
224 mUpdateCount--;
225 if (mUpdateCount==0) {
226 mPanel->setUpdatesEnabled(true);
227 mPanel->update();
228 }
229}
230
231void EditorList::applySettings()
232{
233 for (int i=0;i<mLeftPageWidget->count();i++) {
234 Editor* e = static_cast<Editor*>(mLeftPageWidget->widget(i));
235 e->applySettings();
236 }
237 for (int i=0;i<mRightPageWidget->count();i++) {
238 Editor* e = static_cast<Editor*>(mRightPageWidget->widget(i));
239 e->applySettings();
240 }
241}
242
243void EditorList::applyColorSchemes(const QString& name)
244{
245 for (int i=0;i<mLeftPageWidget->count();i++) {
246 Editor* e = static_cast<Editor*>(mLeftPageWidget->widget(i));
247 e->applyColorScheme(name);
248 }
249 for (int i=0;i<mRightPageWidget->count();i++) {
250 Editor* e = static_cast<Editor*>(mRightPageWidget->widget(i));
251 e->applyColorScheme(name);
252 }
253}
254
255bool EditorList::isFileOpened(const QString &name)
256{
257 QFileInfo fileInfo(name);
258 QString filename = fileInfo.absoluteFilePath();
259 for (int i=0;i<mLeftPageWidget->count();i++) {
260 Editor* e = static_cast<Editor*>(mLeftPageWidget->widget(i));
261 if (e->filename().compare(filename)==0 || e->filename().compare(name)==0)
262 return true;
263 }
264 for (int i=0;i<mRightPageWidget->count();i++) {
265 Editor* e = static_cast<Editor*>(mRightPageWidget->widget(i));
266 if (e->filename().compare(filename)==0 || e->filename().compare(name)==0)
267 return true;
268 }
269 return false;
270}
271
272int EditorList::pageCount()
273{
274 return mLeftPageWidget->count()+mRightPageWidget->count();
275}
276
277void EditorList::selectNextPage()
278{
279 QTabWidget * pageControl = getFocusedPageControl();
280 if (pageControl && pageControl->count()>0) {
281 pageControl->setCurrentIndex(
282 (pageControl->currentIndex()+1) % pageControl->count()
283 );
284 }
285}
286
287void EditorList::selectPreviousPage()
288{
289 QTabWidget * pageControl = getFocusedPageControl();
290 if (pageControl && pageControl->count()>0) {
291 pageControl->setCurrentIndex(
292 (pageControl->currentIndex()+pageControl->count()-1) % pageControl->count()
293 );
294 }
295}
296
297Editor *EditorList::operator[](int index)
298{
299 if (index>=0 && index<mLeftPageWidget->count()) {
300 return static_cast<Editor*>(mLeftPageWidget->widget(index));
301 }
302 index -= mLeftPageWidget->count();
303 if (index>=0 && index<mRightPageWidget->count()) {
304 return static_cast<Editor*>(mRightPageWidget->widget(index));
305 }
306 return nullptr;
307}
308
309bool EditorList::closeAll(bool force) {
310// beginUpdate();
311// auto end = finally([this] {
312// this->endUpdate();
313// });
314 while (mLeftPageWidget->count()>0) {
315 if (!closeEditor(getEditor(0,mLeftPageWidget),false,force)) {
316 return false;
317 }
318 }
319 while (mRightPageWidget->count()>0) {
320 if (!closeEditor(getEditor(0,mRightPageWidget),false,force)) {
321 return false;
322 }
323 }
324 return true;
325}
326
327void EditorList::forceCloseEditor(Editor *editor)
328{
329 beginUpdate();
330 delete editor;
331 // Force layout update when creating, destroying or moving editors
332 updateLayout();
333 endUpdate();
334 emit editorClosed();
335}
336
337Editor* EditorList::getOpenedEditorByFilename(QString filename)
338{
339 if (filename.isEmpty())
340 return nullptr;
341 QFileInfo fileInfo(filename);
342 QString fullname = fileInfo.absoluteFilePath();
343 for (int i=0;i<mLeftPageWidget->count();i++) {
344 Editor* e = static_cast<Editor*>(mLeftPageWidget->widget(i));
345 if (e->filename().compare(filename, PATH_SENSITIVITY)==0 ||
346 e->filename().compare(fullname, PATH_SENSITIVITY)==0) {
347 return e;
348 }
349 }
350 for (int i=0;i<mRightPageWidget->count();i++) {
351 Editor* e = static_cast<Editor*>(mRightPageWidget->widget(i));
352 if (e->filename().compare(filename)==0 || e->filename().compare(fullname)==0) {
353 return e;
354 }
355 }
356 return nullptr;
357}
358
359Editor *EditorList::getEditorByFilename(QString filename)
360{
361 if (filename.isEmpty())
362 return nullptr;
363 //check if an editor is already openned
364 Editor* e=getOpenedEditorByFilename(filename);
365 if (e!=nullptr)
366 return e;
367 //Todo: check if is in the project
368
369 //Create a new editor
370 QFileInfo fileInfo(filename);
371 QString fullname = fileInfo.absoluteFilePath();
372 if (fileInfo.exists() && fileInfo.isFile())
373 return newEditor(fullname,pSettings->editor().autoDetectFileEncoding()?ENCODING_AUTO_DETECT:pSettings->editor().defaultEncoding(),false,false);
374 return nullptr;
375}
376
377bool EditorList::getContentFromOpenedEditor(const QString &filename, QStringList &buffer)
378{
379 Editor * e= getOpenedEditorByFilename(filename);
380 if (!e)
381 return false;
382 buffer = e->contents();
383 return true;
384}
385
386void EditorList::getVisibleEditors(Editor *&left, Editor *&right)
387{
388 switch(mLayout) {
389 case LayoutShowType::lstLeft:
390 left = getEditor(-1,mLeftPageWidget);
391 right = nullptr;
392 break;
393 case LayoutShowType::lstRight:
394 left = nullptr;
395 right = getEditor(-1,mRightPageWidget);
396 break;
397 case LayoutShowType::lstBoth:
398 left = getEditor(-1,mLeftPageWidget);
399 right = getEditor(-1,mRightPageWidget);
400 break;
401 }
402}
403
404void EditorList::updateLayout()
405{
406 if (mRightPageWidget->count() == 0)
407 showLayout(LayoutShowType::lstLeft);
408 else if (mLeftPageWidget->count() ==0)
409 showLayout(LayoutShowType::lstRight);
410 else
411 showLayout(LayoutShowType::lstBoth);
412}
413