1 /************************************************************************
2 **
3 **  Copyright (C) 2015-2019 Kevin B. Hendricks, Stratford Ontario Canada
4 **  Copyright (C) 2009-2011 Strahinja Markovic  <strahinja.markovic@gmail.com>
5 **
6 **  This file is part of Sigil.
7 **
8 **  Sigil is free software: you can redistribute it and/or modify
9 **  it under the terms of the GNU General Public License as published by
10 **  the Free Software Foundation, either version 3 of the License, or
11 **  (at your option) any later version.
12 **
13 **  Sigil is distributed in the hope that it will be useful,
14 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 **  GNU General Public License for more details.
17 **
18 **  You should have received a copy of the GNU General Public License
19 **  along with Sigil.  If not, see <http://www.gnu.org/licenses/>.
20 **
21 *************************************************************************/
22 
23 #include <QtCore/QTimer>
24 #include <QtWidgets/QLayout>
25 #include <QtPrintSupport/QPrinter>
26 #include <QtPrintSupport/QPrintDialog>
27 #include <QtPrintSupport/QPrintPreviewDialog>
28 
29 #include "ResourceObjects/TextResource.h"
30 #include "Tabs/TextTab.h"
31 
TextTab(TextResource * resource,CodeViewEditor::HighlighterType type,int line_to_scroll_to,int position_to_scroll_to,QWidget * parent)32 TextTab::TextTab(TextResource *resource,
33                  CodeViewEditor::HighlighterType type,
34                  int line_to_scroll_to,
35                  int position_to_scroll_to,
36                  QWidget *parent)
37     :
38     ContentTab(resource, parent),
39     m_wCodeView(new CodeViewEditor(type, false, this)),
40     m_TextResource(resource),
41     m_LineToScrollTo(line_to_scroll_to),
42     m_PositionToScrollTo(position_to_scroll_to)
43 {
44     m_Layout->addWidget(m_wCodeView);
45     setFocusProxy(m_wCodeView);
46     ConnectSignalsToSlots();
47     // Make sure the resource is loaded as its file doesn't seem
48     // to exist when the resource tries to do an initial load.
49     m_TextResource->InitialLoad();
50     // We perform delayed initialization after the widget is on
51     // the screen. This way, the user perceives less load time.
52     QTimer::singleShot(0, this, SLOT(DelayedInitialization()));
53 }
54 
~TextTab()55 TextTab::~TextTab()
56 {
57     if (m_wCodeView) {
58         delete m_wCodeView;
59         m_wCodeView = 0;
60     }
61 }
62 
63 
ScrollToLine(int line)64 void TextTab::ScrollToLine(int line)
65 {
66     m_wCodeView->ScrollToLine(line);
67 }
68 
ScrollToPosition(int cursor_position)69 void TextTab::ScrollToPosition(int cursor_position)
70 {
71     m_wCodeView->ScrollToPosition(cursor_position);
72 }
73 
IsModified()74 bool TextTab::IsModified()
75 {
76     return m_wCodeView->document()->isModified();
77 }
78 
79 
CutEnabled()80 bool TextTab::CutEnabled()
81 {
82     return m_wCodeView->textCursor().hasSelection();
83 }
84 
85 
CopyEnabled()86 bool TextTab::CopyEnabled()
87 {
88     return m_wCodeView->textCursor().hasSelection();
89 }
90 
91 
PasteEnabled()92 bool TextTab::PasteEnabled()
93 {
94     return m_wCodeView->canPaste();
95 }
96 
DeleteLineEnabled()97 bool TextTab::DeleteLineEnabled()
98 {
99     return !m_wCodeView->document()->isEmpty();
100 }
101 
CutCodeTagsEnabled()102 bool TextTab::CutCodeTagsEnabled()
103 {
104     return false;
105 }
106 
107 
GetCursorLine() const108 int TextTab::GetCursorLine() const
109 {
110     return m_wCodeView->GetCursorLine();
111 }
112 
GetCursorPosition() const113 int TextTab::GetCursorPosition() const
114 {
115     return m_wCodeView->GetCursorPosition();
116 }
117 
GetCursorColumn() const118 int TextTab::GetCursorColumn() const
119 {
120     return m_wCodeView->GetCursorColumn();
121 }
122 
123 
GetZoomFactor() const124 float TextTab::GetZoomFactor() const
125 {
126     return m_wCodeView->GetZoomFactor();
127 }
128 
129 
SetZoomFactor(float new_zoom_factor)130 void TextTab::SetZoomFactor(float new_zoom_factor)
131 {
132     m_wCodeView->SetZoomFactor(new_zoom_factor);
133 }
134 
135 
UpdateDisplay()136 void TextTab::UpdateDisplay()
137 {
138     m_wCodeView->UpdateDisplay();
139 }
140 
141 
GetSearchableContent()142 Searchable *TextTab::GetSearchableContent()
143 {
144     return m_wCodeView;
145 }
146 
147 
Undo()148 void TextTab::Undo()
149 {
150     if (m_wCodeView->hasFocus()) {
151         m_wCodeView->undo();
152     }
153 }
154 
155 
Redo()156 void TextTab::Redo()
157 {
158     if (m_wCodeView->hasFocus()) {
159         m_wCodeView->redo();
160     }
161 }
162 
163 
Cut()164 void TextTab::Cut()
165 {
166     if (m_wCodeView->hasFocus()) {
167         m_wCodeView->cut();
168     }
169 }
170 
171 
Copy()172 void TextTab::Copy()
173 {
174     if (m_wCodeView->hasFocus()) {
175         m_wCodeView->copy();
176     }
177 }
178 
179 
Paste()180 void TextTab::Paste()
181 {
182     m_wCodeView->paste();
183 }
184 
185 
DeleteLine()186 void TextTab::DeleteLine()
187 {
188     if (m_wCodeView->hasFocus()) {
189         m_wCodeView->DeleteLine();
190     }
191 }
192 
MarkSelection()193 bool TextTab::MarkSelection()
194 {
195     return m_wCodeView->MarkSelection();
196 }
197 
ClearMarkedText()198 bool TextTab::ClearMarkedText()
199 {
200     return m_wCodeView->ClearMarkedText();
201 }
202 
CutCodeTags()203 void TextTab::CutCodeTags()
204 {
205 }
206 
ChangeCasing(const Utility::Casing casing)207 void TextTab::ChangeCasing(const Utility::Casing casing)
208 {
209     if (m_wCodeView->hasFocus()) {
210         m_wCodeView->ApplyCaseChangeToSelection(casing);
211     }
212 }
213 
214 
SaveTabContent()215 void TextTab::SaveTabContent()
216 {
217     // We can't perform the document modified check
218     // here because that causes problems with epub export
219     // when the user has not changed the text file.
220     // (some text files have placeholder text on disk)
221     if (!m_wCodeView->document()->isModified()) {
222         ContentTab::SaveTabContent();
223         return;
224     }
225 
226     // Losing focus will invoke SaveTabContent
227     // which may run SaveToDisk on the underlying resource
228     // that will reset the cursor to the end of file
229     // so save the cursor position and then put it
230     // back after the SaveToDisk
231     int cursorPosition = m_wCodeView->GetCursorPosition();
232 
233     // The call to SaveToDisk **IS** needed here even though the
234     // QTextEdit is tied directly to the QtQPlainTextDocument.
235     // Webkit is passed in an xhtml file but all supporting files
236     // such as CSS, svg, and etc are only ever read from Disk,
237     // so any time focus is lost and the contents of the TextTab
238     // have changed, we should save them to disk
239     m_TextResource->SaveToDisk();
240 
241     // Some systems (Linux/Windows) may lose text highlighting when
242     // ScrollToPosition is called unnecessarily. Only reposition
243     // if the cursor has actually moved.
244     int newcursorPosition = m_wCodeView->GetCursorPosition();
245     if (newcursorPosition != cursorPosition) m_wCodeView->ScrollToPosition(cursorPosition, false);
246     ContentTab::SaveTabContent();
247 }
248 
249 
SaveTabContent(QWidget * editor)250 void TextTab::SaveTabContent(QWidget *editor)
251 {
252     Q_UNUSED(editor);
253     SaveTabContent();
254 }
255 
256 
LoadTabContent()257 void TextTab::LoadTabContent()
258 {
259 }
260 
261 
LoadTabContent(QWidget * editor)262 void TextTab::LoadTabContent(QWidget *editor)
263 {
264     Q_UNUSED(editor);
265     LoadTabContent();
266 }
267 
268 
EmitUpdateCursorPosition()269 void TextTab::EmitUpdateCursorPosition()
270 {
271     emit UpdateCursorPosition(GetCursorLine(), GetCursorColumn());
272 }
273 
DelayedInitialization()274 void TextTab::DelayedInitialization()
275 {
276     m_wCodeView->CustomSetDocument(m_TextResource->GetTextDocumentForWriting());
277     m_wCodeView->Zoom();
278     if (m_PositionToScrollTo > 0) {
279         m_wCodeView->ScrollToPosition(m_PositionToScrollTo);
280     } else {
281         m_wCodeView->ScrollToLine(m_LineToScrollTo);
282     }
283 }
284 
285 
ConnectSignalsToSlots()286 void TextTab::ConnectSignalsToSlots()
287 {
288     // We set the Code View as the focus proxy for the tab,
289     // so the ContentTab focusIn/Out handlers are not called.
290     connect(m_wCodeView, SIGNAL(FocusGained(QWidget *)),    this, SLOT(LoadTabContent(QWidget *)));
291     connect(m_wCodeView, SIGNAL(FocusLost(QWidget *)),      this, SLOT(SaveTabContent(QWidget *)));
292     connect(m_wCodeView, SIGNAL(FilteredTextChanged()),      this, SIGNAL(ContentChanged()));
293     connect(m_wCodeView, SIGNAL(cursorPositionChanged()),     this, SLOT(EmitUpdateCursorPosition()));
294     connect(m_wCodeView, SIGNAL(ZoomFactorChanged(float)), this, SIGNAL(ZoomFactorChanged(float)));
295     connect(m_wCodeView, SIGNAL(selectionChanged()),         this, SIGNAL(SelectionChanged()));
296     connect(m_wCodeView, SIGNAL(OpenClipEditorRequest(ClipEditorModel::clipEntry *)), this, SIGNAL(OpenClipEditorRequest(ClipEditorModel::clipEntry *)));
297     connect(m_wCodeView, SIGNAL(MarkSelectionRequest()),         this, SIGNAL(MarkSelectionRequest()));
298     connect(m_wCodeView, SIGNAL(ClearMarkedTextRequest()),              this, SIGNAL(ClearMarkedTextRequest()));
299 }
300 
301 
PrintPreview()302 void TextTab::PrintPreview()
303 {
304     QPrintPreviewDialog *print_preview = new QPrintPreviewDialog(this);
305     connect(print_preview, SIGNAL(paintRequested(QPrinter *)), m_wCodeView, SLOT(print(QPrinter *)));
306     print_preview->exec();
307     print_preview->deleteLater();
308 }
309 
Print()310 void TextTab::Print()
311 {
312     QPrinter printer;
313     QPrintDialog print_dialog(&printer, this);
314     print_dialog.setWindowTitle(tr("Print %1").arg(GetFilename()));
315 
316     if (print_dialog.exec() == QDialog::Accepted) {
317         m_wCodeView->print(&printer);
318     }
319 }
320 
321