1 /************************************************************************
2 **
3 **  Copyright (C) 2016-2021  Kevin B Hendricks, Stratford, Ontario, Canada
4 **  Copyright (C) 2012       John Schember <john@nachtimwald.com>
5 **  Copyright (C) 2012       Dave Heiland
6 **  Copyright (C) 2012       Grant Drake
7 **  Copyright (C) 2009, 2010, 2011  Strahinja Markovic  <strahinja.markovic@gmail.com>
8 **
9 **  This file is part of Sigil.
10 **
11 **  Sigil is free software: you can redistribute it and/or modify
12 **  it under the terms of the GNU General Public License as published by
13 **  the Free Software Foundation, either version 3 of the License, or
14 **  (at your option) any later version.
15 **
16 **  Sigil is distributed in the hope that it will be useful,
17 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 **  GNU General Public License for more details.
20 **
21 **  You should have received a copy of the GNU General Public License
22 **  along with Sigil.  If not, see <http://www.gnu.org/licenses/>.
23 **
24 *************************************************************************/
25 
26 #include <QtCore/QTimer>
27 #include <QtCore/QUrl>
28 #include <QtWidgets/QApplication>
29 #include <QtWidgets/QAction>
30 #include <QtWidgets/QDialog>
31 #include <QtWidgets/QLayout>
32 #include <QtPrintSupport/QPrinter>
33 #include <QtPrintSupport/QPrintDialog>
34 #include <QtPrintSupport/QPrintPreviewDialog>
35 #include <QDebug>
36 
37 #include "BookManipulation/CleanSource.h"
38 #include "MiscEditors/ClipEditorModel.h"
39 #include "Misc/SettingsStore.h"
40 #include "Misc/Utility.h"
41 #include "ResourceObjects/HTMLResource.h"
42 #include "sigil_constants.h"
43 #include "Tabs/FlowTab.h"
44 #include "Tabs/WellFormedCheckComponent.h"
45 #include "ViewEditors/CodeViewEditor.h"
46 
47 #define DBG if(0)
48 
49 static const QString SETTINGS_GROUP = "flowtab";
50 
FlowTab(HTMLResource * resource,const QUrl & fragment,int line_to_scroll_to,int position_to_scroll_to,QString caret_location_to_scroll_to,bool grab_focus,QWidget * parent)51 FlowTab::FlowTab(HTMLResource *resource,
52                  const QUrl &fragment,
53                  int line_to_scroll_to,
54                  int position_to_scroll_to,
55                  QString caret_location_to_scroll_to,
56                  bool grab_focus,
57                  QWidget *parent)
58     :
59     ContentTab(resource, parent),
60     m_FragmentToScroll(fragment),
61     m_LineToScrollTo(line_to_scroll_to),
62     m_PositionToScrollTo(position_to_scroll_to),
63     m_CaretLocationToScrollTo(caret_location_to_scroll_to),
64     m_HTMLResource(resource),
65     m_wCodeView(NULL),
66     m_WellFormedCheckComponent(new WellFormedCheckComponent(this, parent)),
67     m_safeToLoad(false),
68     m_initialLoad(true),
69     m_grabFocus(grab_focus),
70     m_suspendTabReloading(false),
71     m_defaultCaretLocationToTop(false),
72     m_LastPosition(-1)
73 {
74     // Loading a flow tab can take a while. We set the wait
75     // cursor and clear it at the end of the delayed initialization.
76     QApplication::setOverrideCursor(Qt::WaitCursor);
77     CreateCodeViewIfRequired(false);
78     m_Layout->addWidget(m_wCodeView);
79     LoadSettings();
80     setFocusProxy(m_wCodeView);
81     ConnectCodeViewSignalsToSlots();
82 
83     // We perform delayed initialization after the widget is on
84     // the screen. This way, the user perceives less load time.
85     QTimer::singleShot(0, this, SLOT(DelayedInitialization()));
86 }
87 
~FlowTab()88 FlowTab::~FlowTab()
89 {
90     // Tabs are destroyed later when they are closed with no impact on the
91     // the underlying resource.  But at the same time Tabs can be destroyed when
92     // the underlying resource emits its Destroyed signal.
93 
94     // This causes a potential race during MainWindow::SetNewBook when the
95     // previous book is destroyed at the same time the Tabmanager is closing all of its tabs.
96 
97     // Also disconnect signals from our underlying resource to prevent Modified
98     // from causing the ResourceModified method to be called on a defunct Tab after we delete
99     // other things later in this destructor but before completion.
100 
101     // Actually since we are being destroyed explicitly disconnect *all* future signals
102     // from our underlying resource including Deleted and Modified as they no
103     // longer needs to be delivered
104 
105     disconnect(this, 0, 0, 0);
106 
107     if (!GetResourceWasDeleted()) {
108         // was: disconnect(m_HTMLResource, SIGNAL(Modified()), this, SLOT(ResourceModified()));
109         disconnect(m_HTMLResource, 0, this , 0);
110     }
111 
112     m_WellFormedCheckComponent->deleteLater();
113 
114     if (m_wCodeView) {
115         delete m_wCodeView;
116         m_wCodeView = 0;
117     }
118 
119     m_HTMLResource = NULL;
120 
121 }
122 
CreateCodeViewIfRequired(bool is_delayed_load)123 void FlowTab::CreateCodeViewIfRequired(bool is_delayed_load)
124 {
125     if (m_wCodeView) {
126         return;
127     }
128 
129     QApplication::setOverrideCursor(Qt::WaitCursor);
130     m_wCodeView = new CodeViewEditor(CodeViewEditor::Highlight_XHTML, true, this);
131     m_wCodeView->SetReformatHTMLEnabled(true);
132     // m_views->addWidget(m_wCodeView);
133 
134     if (is_delayed_load) {
135         ConnectCodeViewSignalsToSlots();
136         // CodeView (if already loaded) will be directly hooked into the TextResource and
137         // will not need reloading. However if the tab was not in Code View at tab opening
138         // then we must populate it now.
139         m_wCodeView->CustomSetDocument(m_HTMLResource->GetTextDocumentForWriting());
140         // Zoom assignment only works after the document has been loaded
141         m_wCodeView->Zoom();
142     }
143 
144     QApplication::restoreOverrideCursor();
145 }
146 
DelayedInitialization()147 void FlowTab::DelayedInitialization()
148 {
149     if (m_wCodeView) {
150         m_wCodeView->CustomSetDocument(m_HTMLResource->GetTextDocumentForWriting());
151         // Zoom factor for CodeView can only be set when document has been loaded.
152         m_wCodeView->Zoom();
153     }
154 
155     CodeView();
156 
157     if (m_PositionToScrollTo > 0) {
158         m_wCodeView->ScrollToPosition(m_PositionToScrollTo);
159     } else if (m_LineToScrollTo > 0) {
160         m_wCodeView->ScrollToLine(m_LineToScrollTo);
161     } else {
162         m_wCodeView->ScrollToFragment(m_FragmentToScroll.toString());
163     }
164 
165     m_initialLoad = false;
166     // Only now will we wire up monitoring of ResourceChanged, to prevent
167     // unnecessary saving and marking of the resource for reloading.
168     DelayedConnectSignalsToSlots();
169 
170     // sync Preview to where CodeView is now
171     emit ScrollPreviewImmediately();
172 
173     // Cursor set in constructor
174     QApplication::restoreOverrideCursor();
175 }
176 
IsLoadingFinished()177 bool FlowTab::IsLoadingFinished()
178 {
179     bool is_finished = false;
180 
181     if (m_wCodeView) {
182         is_finished = m_wCodeView->IsLoadingFinished();
183     }
184 
185     return is_finished;
186 }
187 
IsModified()188 bool FlowTab::IsModified()
189 {
190     bool is_modified = false;
191 
192     if (m_wCodeView) {
193         is_modified = is_modified || m_wCodeView->document()->isModified();
194     }
195 
196     return is_modified;
197 }
198 
CodeView()199 void FlowTab::CodeView()
200 {
201     QApplication::setOverrideCursor(Qt::WaitCursor);
202     CreateCodeViewIfRequired();
203     m_wCodeView->SetDelayedCursorScreenCenteringRequired();
204     setFocusProxy(m_wCodeView);
205 
206     // We will usually want focus in the tab, except when splitting opens this as a preceding tab.
207     if (m_grabFocus) {
208         m_wCodeView->setFocus();
209     }
210 
211     m_grabFocus = true;
212 
213     m_wCodeView->ExecuteCaretUpdate();
214 
215     QApplication::restoreOverrideCursor();
216 }
217 
LoadTabContent()218 void FlowTab::LoadTabContent()
219 {
220     // In CV, this call has nothing to do, as the resource is connected to QTextDocument.
221     //        The only exception is initial load when control is created, done elsewhere.
222 }
223 
SaveTabContent()224 void FlowTab::SaveTabContent()
225 {
226     // In CV, the connection between QPlainTextEdit and the underlying QTextDocument
227     //        means the resource already is "saved". We just need to reset modified state.
228     m_HTMLResource->GetTextDocumentForWriting().setModified(false);
229 }
230 
ResourceModified()231 void FlowTab::ResourceModified()
232 {
233     // This slot tells us that the underlying HTML resource has been changed
234     // It could be the user has done a Replace All on underlying resource, so reset our well formed check.
235     m_safeToLoad = false;
236     // When the underlying resource has been modified, it replaces the whole QTextDocument which
237     // causes cursor position to move to bottom of the document. We will have captured the location
238     // of the caret prior to replacing in the ResourceTextChanging() slot, so now we can restore it.
239     // First try to get to the enclosing block and if possible the exact position
240     m_wCodeView->ExecuteCaretUpdate(m_defaultCaretLocationToTop);
241     m_defaultCaretLocationToTop = false;
242     if (m_LastPosition > 0) {
243         m_wCodeView->ScrollToPosition(m_LastPosition);
244         m_LastPosition = -1;
245     }
246 
247     if (IsLoadingFinished()) {
248         DBG qDebug() << "FlowTab in ResourceModified";
249         EmitUpdatePreview();
250     }
251 }
252 
HandleViewImage(const QUrl & url)253 void FlowTab::HandleViewImage(const QUrl &url)
254 {
255     if (url.toString().isEmpty()) {
256         return;
257     }
258     if (!url.isRelative()) return;
259 
260     // we have a relative url, so build an internal
261     // book: scheme url book:///bookpath
262     if (url.path().isEmpty()) return;
263 
264     QString startdir = m_HTMLResource->GetFolder();
265     QString dest_bookpath = Utility::buildBookPath(url.path(), startdir);
266     QString url_string = "book:///" + Utility::URLEncodePath(dest_bookpath);
267     emit ViewImageRequest(QUrl(url_string));
268 }
269 
LinkedResourceModified()270 void FlowTab::LinkedResourceModified()
271 {
272     // MainWindow::clearMemoryCaches();
273     ResourceModified();
274     ReloadTabIfPending();
275 }
276 
ResourceTextChanging()277 void FlowTab::ResourceTextChanging()
278 {
279     // We need to store caret (cursor) position so it can be restored later
280     // Store an exact position as well as the tag hierarchy
281     m_LastPosition = m_wCodeView->GetCursorPosition();
282     m_wCodeView->StoreCaretLocationUpdate(m_wCodeView->GetCaretLocation());
283     // If the caret happened to be at the very top of the document then our location
284     // will be empty. The problem is that when ResourceModified() fires next
285     // it will have effectively moved the caret to the bottom of the document.
286     // At which point we will call the CodeView to restore caret location.
287     // However the default behaviour of CodeView is to "do nothing" if no location
288     // is stored. So in this one situation we want to override that to force
289     // it to place the cursor at the top of the document.
290     m_defaultCaretLocationToTop = true;
291 }
292 
ReloadTabIfPending()293 void FlowTab::ReloadTabIfPending()
294 {
295     if (!isVisible()) {
296         return;
297     }
298 
299     if (m_suspendTabReloading) {
300         return;
301     }
302 
303     setFocus();
304 }
305 
LeaveEditor(QWidget * editor)306 void FlowTab::LeaveEditor(QWidget *editor)
307 {
308     SaveTabContent();
309 }
310 
LoadSettings()311 void FlowTab::LoadSettings()
312 {
313     UpdateDisplay();
314 
315     // SettingsChanged can fire for wanting the spelling highlighting to be refreshed on the tab.
316     if (m_wCodeView) {
317         m_wCodeView->RefreshSpellingHighlighting();
318     }
319 }
320 
UpdateDisplay()321 void FlowTab::UpdateDisplay()
322 {
323     if (m_wCodeView) {
324         m_wCodeView->UpdateDisplay();
325     }
326 }
327 
EmitContentChanged()328 void FlowTab::EmitContentChanged()
329 {
330     m_safeToLoad = false;
331     DBG qDebug() << "FlowTab emiting Content Changed";
332     emit ContentChanged();
333 }
334 
EmitUpdatePreview()335 void FlowTab::EmitUpdatePreview()
336 {
337     if (IsLoadingFinished()) {
338         DBG qDebug() << "FlowTab emiting UpdatePreviewRequest from EmitUpdatePreview";
339         emit UpdatePreview();
340     }
341 }
342 
EmitUpdatePreviewImmediately()343 void FlowTab::EmitUpdatePreviewImmediately()
344 {
345       DBG qDebug() << "FlowTab emiting UpdatePreviewImmediately from EmitUpdatePreviewImmediately";
346       emit UpdatePreviewImmediately();
347 }
348 
EmitScrollPreviewImmediately()349 void FlowTab::EmitScrollPreviewImmediately()
350 {
351       DBG qDebug() << "FlowTab emiting ScrollPreviewImmediately from EmitScrollPreviewImmediately";
352       emit ScrollPreviewImmediately();
353 }
354 
EmitUpdateCursorPosition()355 void FlowTab::EmitUpdateCursorPosition()
356 {
357     DBG qDebug() << "FlowTab emiting UpdateCursorPosition from EmitUpdateCursorPosition";
358     emit UpdateCursorPosition(GetCursorLine(), GetCursorColumn());
359 }
360 
HighlightWord(QString word,int pos)361 void FlowTab::HighlightWord(QString word, int pos)
362 {
363     if (m_wCodeView) {
364         m_wCodeView->HighlightWord(word, pos);
365     }
366 }
367 
RefreshSpellingHighlighting()368 void FlowTab::RefreshSpellingHighlighting()
369 {
370     if (m_wCodeView) {
371         m_wCodeView->RefreshSpellingHighlighting();
372     }
373 }
374 
375 
CutEnabled()376 bool FlowTab::CutEnabled()
377 {
378     if (m_wCodeView) {
379         return m_wCodeView->textCursor().hasSelection();
380     }
381     return false;
382 }
383 
CopyEnabled()384 bool FlowTab::CopyEnabled()
385 {
386     if (m_wCodeView) {
387         return m_wCodeView->textCursor().hasSelection();
388     }
389     return false;
390 }
391 
PasteEnabled()392 bool FlowTab::PasteEnabled()
393 {
394     if (m_wCodeView) {
395         return m_wCodeView->canPaste();
396     }
397     return false;
398 }
399 
DeleteLineEnabled()400 bool FlowTab::DeleteLineEnabled()
401 {
402     if (m_wCodeView) {
403         return !m_wCodeView->document()->isEmpty();
404     }
405     return false;
406 }
407 
RemoveFormattingEnabled()408 bool FlowTab::RemoveFormattingEnabled()
409 {
410     if (m_wCodeView) {
411         return m_wCodeView->IsCutCodeTagsAllowed();
412     }
413     return false;
414 }
415 
RemoveTagPairEnabled()416 bool FlowTab::RemoveTagPairEnabled()
417 {
418     if (m_wCodeView) {
419         return m_wCodeView->IsCutTagPairAllowed();
420     }
421     return false;
422 }
423 
InsertClosingTagEnabled()424 bool FlowTab::InsertClosingTagEnabled()
425 {
426     if (m_wCodeView) {
427         return m_wCodeView->IsInsertClosingTagAllowed();
428     }
429     return false;
430 }
431 
AddToIndexEnabled()432 bool FlowTab::AddToIndexEnabled()
433 {
434     if (m_wCodeView) {
435         return m_wCodeView->IsAddToIndexAllowed();
436     }
437     return false;
438 }
439 
MarkForIndexEnabled()440 bool FlowTab::MarkForIndexEnabled()
441 {
442     if (m_wCodeView) {
443         return m_wCodeView->textCursor().hasSelection();
444     }
445     return false;
446 }
447 
InsertIdEnabled()448 bool FlowTab::InsertIdEnabled()
449 {
450     if (m_wCodeView) {
451         return m_wCodeView->IsInsertIdAllowed();
452     }
453     return false;
454 }
455 
InsertHyperlinkEnabled()456 bool FlowTab::InsertHyperlinkEnabled()
457 {
458     if (m_wCodeView) {
459         return m_wCodeView->IsInsertHyperlinkAllowed();
460     }
461     return false;
462 }
463 
InsertSpecialCharacterEnabled()464 bool FlowTab::InsertSpecialCharacterEnabled()
465 {
466     if (m_wCodeView) {
467         return true;
468     }
469     return false;
470 }
471 
InsertFileEnabled()472 bool FlowTab::InsertFileEnabled()
473 {
474     if (m_wCodeView) {
475         return m_wCodeView->IsInsertFileAllowed();
476     }
477     return false;
478 }
479 
ToggleAutoSpellcheckEnabled()480 bool FlowTab::ToggleAutoSpellcheckEnabled()
481 {
482     if (m_wCodeView) {
483         return true;
484     }
485     return false;
486 }
487 
GoToCaretLocation(QList<ElementIndex> location)488 void FlowTab::GoToCaretLocation(QList<ElementIndex> location)
489 {
490     if (location.isEmpty()) {
491         return;
492     }
493     if (m_wCodeView) {
494         m_wCodeView->StoreCaretLocationUpdate(location);
495         m_wCodeView->ExecuteCaretUpdate();
496     }
497 }
498 
GetCaretLocation()499 QList<ElementIndex> FlowTab::GetCaretLocation()
500 {
501     if (m_wCodeView) {
502         return m_wCodeView->GetCaretLocation();
503     }
504     return QList<ElementIndex>();
505 }
506 
GetCaretLocationUpdate() const507 QString FlowTab::GetCaretLocationUpdate() const
508 {
509     return QString();
510 }
511 
GetDisplayedCharacters()512 QString FlowTab::GetDisplayedCharacters()
513 {
514     return "";
515 }
516 
GetText()517 QString FlowTab::GetText()
518 {
519     if (m_wCodeView) {
520         return m_wCodeView->toPlainText();
521     }
522     return "";
523 }
524 
GetCursorPosition() const525 int FlowTab::GetCursorPosition() const
526 {
527     if (m_wCodeView) {
528         return m_wCodeView->GetCursorPosition();
529     }
530     return -1;
531 }
532 
GetCursorLine() const533 int FlowTab::GetCursorLine() const
534 {
535     if (m_wCodeView) {
536         return m_wCodeView->GetCursorLine();
537     }
538     return -1;
539 }
540 
GetCursorColumn() const541 int FlowTab::GetCursorColumn() const
542 {
543     if (m_wCodeView) {
544         return m_wCodeView->GetCursorColumn();
545     }
546     return -1;
547 }
548 
GetZoomFactor() const549 float FlowTab::GetZoomFactor() const
550 {
551     if (m_wCodeView) {
552         return m_wCodeView->GetZoomFactor();
553     }
554     return 1;
555 }
556 
SetZoomFactor(float new_zoom_factor)557 void FlowTab::SetZoomFactor(float new_zoom_factor)
558 {
559     if (m_wCodeView) {
560         m_wCodeView->SetZoomFactor(new_zoom_factor);
561     }
562 }
563 
GetSearchableContent()564 Searchable *FlowTab::GetSearchableContent()
565 {
566     if (m_wCodeView) {
567         return m_wCodeView;
568     }
569     return NULL;
570 }
571 
572 
ScrollToFragment(const QString & fragment)573 void FlowTab::ScrollToFragment(const QString &fragment)
574 {
575     if (m_wCodeView) {
576         m_wCodeView->ScrollToFragment(fragment);
577     }
578 }
579 
ScrollToLine(int line)580 void FlowTab::ScrollToLine(int line)
581 {
582     if (m_wCodeView) {
583         m_wCodeView->ScrollToLine(line);
584     }
585 }
586 
ScrollToPosition(int cursor_position)587 void FlowTab::ScrollToPosition(int cursor_position)
588 {
589     if (m_wCodeView) {
590         m_wCodeView->ScrollToPosition(cursor_position);
591     }
592 }
593 
ScrollToCaretLocation(QString caret_location_update)594 void FlowTab::ScrollToCaretLocation(QString caret_location_update)
595 {
596 }
597 
ScrollToTop()598 void FlowTab::ScrollToTop()
599 {
600     if (m_wCodeView) {
601         m_wCodeView->ScrollToTop();
602     }
603 }
604 
AutoFixWellFormedErrors()605 void FlowTab::AutoFixWellFormedErrors()
606 {
607     if (m_wCodeView) {
608         int pos = m_wCodeView->GetCursorPosition();
609         QString version = m_HTMLResource->GetEpubVersion();
610         m_wCodeView->ReplaceDocumentText(CleanSource::ToValidXHTML(m_wCodeView->toPlainText(), version));
611         m_wCodeView->ScrollToPosition(pos);
612     }
613 }
614 
TakeControlOfUI()615 void FlowTab::TakeControlOfUI()
616 {
617     EmitCentralTabRequest();
618     setFocus();
619 }
620 
GetFilename()621 QString FlowTab::GetFilename()
622 {
623     return ContentTab::GetFilename();
624 }
625 
GetShortPathName()626 QString FlowTab::GetShortPathName()
627 {
628     return ContentTab::GetShortPathName();
629 }
630 
IsDataWellFormed()631 bool FlowTab::IsDataWellFormed()
632 {
633     // The content has been changed or was in a not well formed state when last checked.
634     QString version = m_HTMLResource->GetEpubVersion();
635 
636     // So lets play safe and have a fallback to use the resource text if CV is not loaded yet.
637     XhtmlDoc::WellFormedError error = (m_wCodeView != NULL)
638         ? XhtmlDoc::WellFormedErrorForSource(m_wCodeView->toPlainText(),version)
639         : XhtmlDoc::WellFormedErrorForSource(m_HTMLResource->GetText(),version);
640     m_safeToLoad = error.line == -1;
641     if (!m_safeToLoad) {
642           m_WellFormedCheckComponent->DemandAttentionIfAllowed(error);
643     }
644     return m_safeToLoad;
645 }
646 
Undo()647 void FlowTab::Undo()
648 {
649     if (m_wCodeView) {
650         m_wCodeView->undo();
651     }
652 }
653 
Redo()654 void FlowTab::Redo()
655 {
656     if (m_wCodeView) {
657         m_wCodeView->redo();
658     }
659 }
660 
Cut()661 void FlowTab::Cut()
662 {
663     if (m_wCodeView) {
664         m_wCodeView->cut();
665     }
666 }
667 
Copy()668 void FlowTab::Copy()
669 {
670     if (m_wCodeView) {
671         m_wCodeView->copy();
672     }
673 }
674 
Paste()675 void FlowTab::Paste()
676 {
677     if (m_wCodeView) {
678         m_wCodeView->paste();
679     }
680 }
681 
DeleteLine()682 void FlowTab::DeleteLine()
683 {
684     if (m_wCodeView) {
685         m_wCodeView->DeleteLine();
686     }
687 }
688 
MarkSelection()689 bool FlowTab::MarkSelection()
690 {
691     if (m_wCodeView) {
692         return m_wCodeView->MarkSelection();
693     }
694     return false;
695 }
696 
ClearMarkedText()697 bool FlowTab::ClearMarkedText()
698 {
699     if (m_wCodeView) {
700         return m_wCodeView->ClearMarkedText();
701     }
702     return false;
703 }
704 
SplitSection()705 void FlowTab::SplitSection()
706 {
707     if (!IsDataWellFormed()) {
708         return;
709     }
710 
711     QWidget *mainWindow_w = Utility::GetMainWindow();
712     MainWindow *mainWindow = qobject_cast<MainWindow *>(mainWindow_w);
713     if (!mainWindow) {
714         Utility::DisplayStdErrorDialog("Could not determine main window.");
715         return;
716     }
717     HTMLResource * nav_resource = mainWindow->GetCurrentBook()->GetConstOPF()->GetNavResource();
718     if (nav_resource && (nav_resource == m_HTMLResource)) {
719         Utility::DisplayStdErrorDialog("The Nav file can not be split");
720         return;
721     }
722 
723     // Handle warning the user about undefined url fragments.
724     if (!mainWindow->ProceedWithUndefinedUrlFragments()) {
725         return;
726     }
727 
728     if (m_wCodeView) {
729         emit OldTabRequest(m_wCodeView->SplitSection(), m_HTMLResource);
730     }
731 }
732 
InsertSGFSectionMarker()733 void FlowTab::InsertSGFSectionMarker()
734 {
735     if (!IsDataWellFormed()) {
736         return;
737     }
738 
739     QWidget *mainWindow_w = Utility::GetMainWindow();
740     MainWindow *mainWindow = qobject_cast<MainWindow *>(mainWindow_w);
741     if (!mainWindow) {
742         Utility::DisplayStdErrorDialog("Could not determine main window.");
743         return;
744     }
745     HTMLResource * nav_resource = mainWindow->GetCurrentBook()->GetConstOPF()->GetNavResource();
746     if (nav_resource && (nav_resource == m_HTMLResource)) {
747         Utility::DisplayStdErrorDialog("The Nav file can not be split");
748         return;
749     }
750 
751     if (m_wCodeView) {
752         m_wCodeView->InsertSGFSectionMarker();
753     }
754 }
755 
InsertClosingTag()756 void FlowTab::InsertClosingTag()
757 {
758     if (m_wCodeView) {
759         m_wCodeView->InsertClosingTag();
760     }
761 }
762 
AddToIndex()763 void FlowTab::AddToIndex()
764 {
765     if (m_wCodeView) {
766         m_wCodeView->AddToIndex();
767     }
768 }
769 
MarkForIndex(const QString & title)770 bool FlowTab::MarkForIndex(const QString &title)
771 {
772     if (m_wCodeView) {
773         return m_wCodeView->MarkForIndex(title);
774     }
775     return false;
776 }
777 
GetAttributeId()778 QString FlowTab::GetAttributeId()
779 {
780     QString attribute_value;
781 
782     if (m_wCodeView) {
783         // We are only interested in ids on <a> anchor elements
784         attribute_value = m_wCodeView->GetAttributeId();
785     }
786     return attribute_value;
787 }
788 
GetAttributeHref()789 QString FlowTab::GetAttributeHref()
790 {
791     QString attribute_value;
792 
793     if (m_wCodeView) {
794         attribute_value = m_wCodeView->GetAttribute("href", ANCHOR_TAGS, false, true);
795     }
796     return attribute_value;
797 }
798 
GetAttributeIndexTitle()799 QString FlowTab::GetAttributeIndexTitle()
800 {
801     QString attribute_value;
802 
803     if (m_wCodeView) {
804         attribute_value = m_wCodeView->GetAttribute("title", ANCHOR_TAGS, false, true);
805         if (attribute_value.isEmpty()) {
806             attribute_value = m_wCodeView->GetSelectedText();
807         }
808     }
809     return attribute_value;
810 }
811 
GetSelectedText()812 QString FlowTab::GetSelectedText()
813 {
814     if (m_wCodeView) {
815         return m_wCodeView->GetSelectedText();
816     }
817     return "";
818 }
819 
InsertId(const QString & id)820 bool FlowTab::InsertId(const QString &id)
821 {
822     if (m_wCodeView) {
823         return m_wCodeView->InsertId(id);
824     }
825     return false;
826 }
827 
InsertHyperlink(const QString & href)828 bool FlowTab::InsertHyperlink(const QString &href)
829 {
830     if (m_wCodeView) {
831         return m_wCodeView->InsertHyperlink(href);
832     }
833     return false;
834 }
835 
InsertFile(QString html)836 void FlowTab::InsertFile(QString html)
837 {
838     if (m_wCodeView) {
839         m_wCodeView->insertPlainText(html);
840     }
841 }
842 
PrintPreview()843 void FlowTab::PrintPreview()
844 {
845     QPrintPreviewDialog *print_preview = new QPrintPreviewDialog(this);
846 
847     if (m_wCodeView) {
848         connect(print_preview, SIGNAL(paintRequested(QPrinter *)), m_wCodeView, SLOT(print(QPrinter *)));
849     } else {
850         return;
851     }
852 
853     print_preview->exec();
854     print_preview->deleteLater();
855 }
856 
Print()857 void FlowTab::Print()
858 {
859     QPrinter printer;
860     QPrintDialog print_dialog(&printer, this);
861     print_dialog.setWindowTitle(tr("Print %1").arg(GetFilename()));
862 
863     if (print_dialog.exec() == QDialog::Accepted) {
864         if (m_wCodeView) {
865             m_wCodeView->print(&printer);
866         }
867     }
868 }
869 
Bold()870 void FlowTab::Bold()
871 {
872     if (m_wCodeView) {
873         m_wCodeView->ToggleFormatSelection("b", "font-weight", "bold");
874     }
875 }
876 
Italic()877 void FlowTab::Italic()
878 {
879     if (m_wCodeView) {
880         m_wCodeView->ToggleFormatSelection("i", "font-style", "italic");
881     }
882 }
883 
Underline()884 void FlowTab::Underline()
885 {
886     if (m_wCodeView) {
887         m_wCodeView->ToggleFormatSelection("u", "text-decoration", "underline");
888     }
889 }
890 
891 // the strike tag has been deprecated, the del tag is still okay
Strikethrough()892 void FlowTab::Strikethrough()
893 {
894     if (m_wCodeView) {
895         m_wCodeView->ToggleFormatSelection("del", "text-decoration", "line-through");
896     }
897 }
898 
Subscript()899 void FlowTab::Subscript()
900 {
901     if (m_wCodeView) {
902         m_wCodeView->ToggleFormatSelection("sub");
903     }
904 }
905 
Superscript()906 void FlowTab::Superscript()
907 {
908     if (m_wCodeView) {
909         m_wCodeView->ToggleFormatSelection("sup");
910     }
911 }
912 
AlignLeft()913 void FlowTab::AlignLeft()
914 {
915     if (m_wCodeView) {
916         m_wCodeView->FormatStyle("text-align", "left");
917     }
918 }
919 
AlignCenter()920 void FlowTab::AlignCenter()
921 {
922     if (m_wCodeView) {
923         m_wCodeView->FormatStyle("text-align", "center");
924     }
925 }
926 
AlignRight()927 void FlowTab::AlignRight()
928 {
929     if (m_wCodeView) {
930         m_wCodeView->FormatStyle("text-align", "right");
931     }
932 }
933 
AlignJustify()934 void FlowTab::AlignJustify()
935 {
936     if (m_wCodeView) {
937         m_wCodeView->FormatStyle("text-align", "justify");
938     }
939 }
940 
InsertBulletedList()941 void FlowTab::InsertBulletedList()
942 {
943     if (m_wCodeView) {
944         m_wCodeView->ApplyListToSelection("ul");
945     }
946 }
947 
InsertNumberedList()948 void FlowTab::InsertNumberedList()
949 {
950     if (m_wCodeView) {
951         m_wCodeView->ApplyListToSelection("ol");
952     }
953 }
954 
DecreaseIndent()955 void FlowTab::DecreaseIndent()
956 {
957     if (m_wCodeView) {
958         m_wCodeView->WrapSelectionInElement("blockquote", true);
959     }
960 }
961 
IncreaseIndent()962 void FlowTab::IncreaseIndent()
963 {
964     if (m_wCodeView) {
965         m_wCodeView->WrapSelectionInElement("blockquote", false);
966     }
967 }
968 
TextDirectionLeftToRight()969 void FlowTab::TextDirectionLeftToRight()
970 {
971     QString version = m_HTMLResource->GetEpubVersion();
972     if (m_wCodeView) {
973         if (version.startsWith("3")) {
974             m_wCodeView->FormatTextDir("ltr");
975         } else {
976             m_wCodeView->FormatStyle("direction", "ltr");
977         }
978    }
979 }
980 
TextDirectionRightToLeft()981 void FlowTab::TextDirectionRightToLeft()
982 {
983     QString version = m_HTMLResource->GetEpubVersion();
984     if (m_wCodeView) {
985         if (version.startsWith("3")) {
986             m_wCodeView->FormatTextDir("rtl");
987         } else {
988             m_wCodeView->FormatStyle("direction", "rtl");
989         }
990     }
991 }
992 
TextDirectionDefault()993 void FlowTab::TextDirectionDefault()
994 {
995     QString version = m_HTMLResource->GetEpubVersion();
996     if (m_wCodeView) {
997         if (version.startsWith("3")) {
998             m_wCodeView->FormatTextDir(QString());
999         } else {
1000             m_wCodeView->FormatStyle("direction", "inherit");
1001         }
1002     }
1003 }
1004 
RemoveFormatting()1005 void FlowTab::RemoveFormatting()
1006 {
1007     if (m_wCodeView) {
1008         m_wCodeView->CutCodeTags();
1009     }
1010 }
1011 
RemoveTagPair()1012 void FlowTab::RemoveTagPair()
1013 {
1014     if (m_wCodeView) {
1015         m_wCodeView->CutTagPair();
1016     }
1017 }
1018 
ChangeCasing(const Utility::Casing casing)1019 void FlowTab::ChangeCasing(const Utility::Casing casing)
1020 {
1021     if (m_wCodeView) {
1022         m_wCodeView->ApplyCaseChangeToSelection(casing);
1023     }
1024 }
1025 
HeadingStyle(const QString & heading_type,bool preserve_attributes)1026 void FlowTab::HeadingStyle(const QString &heading_type, bool preserve_attributes)
1027 {
1028     if (m_wCodeView) {
1029         QChar last_char = heading_type[ heading_type.count() - 1 ];
1030 
1031         // For heading_type == "Heading #"
1032         if (last_char.isDigit()) {
1033             m_wCodeView->FormatBlock("h" % QString(last_char), preserve_attributes);
1034         } else if (heading_type == "Normal") {
1035             m_wCodeView->FormatBlock("p", preserve_attributes);
1036         }
1037     }
1038 }
1039 
GoToLinkOrStyle()1040 void FlowTab::GoToLinkOrStyle()
1041 {
1042     if (m_wCodeView) {
1043         m_wCodeView->GoToLinkOrStyle();
1044     }
1045 }
1046 
AddMisspelledWord()1047 void FlowTab::AddMisspelledWord()
1048 {
1049     if (m_wCodeView) {
1050         m_wCodeView->AddMisspelledWord();
1051     }
1052 }
1053 
IgnoreMisspelledWord()1054 void FlowTab::IgnoreMisspelledWord()
1055 {
1056     if (m_wCodeView) {
1057         m_wCodeView->IgnoreMisspelledWord();
1058     }
1059 }
1060 
BoldChecked()1061 bool FlowTab::BoldChecked()
1062 {
1063     return ContentTab::BoldChecked();
1064 }
1065 
ItalicChecked()1066 bool FlowTab::ItalicChecked()
1067 {
1068     return ContentTab::ItalicChecked();
1069 }
1070 
UnderlineChecked()1071 bool FlowTab::UnderlineChecked()
1072 {
1073     return ContentTab::UnderlineChecked();
1074 }
1075 
StrikethroughChecked()1076 bool FlowTab::StrikethroughChecked()
1077 {
1078     return ContentTab::StrikethroughChecked();
1079 }
1080 
SubscriptChecked()1081 bool FlowTab::SubscriptChecked()
1082 {
1083     return ContentTab::SubscriptChecked();
1084 }
1085 
SuperscriptChecked()1086 bool FlowTab::SuperscriptChecked()
1087 {
1088     return ContentTab::SuperscriptChecked();
1089 }
1090 
AlignLeftChecked()1091 bool FlowTab::AlignLeftChecked()
1092 {
1093     return ContentTab::AlignLeftChecked();
1094 }
1095 
AlignRightChecked()1096 bool FlowTab::AlignRightChecked()
1097 {
1098     return ContentTab::AlignRightChecked();
1099 }
1100 
AlignCenterChecked()1101 bool FlowTab::AlignCenterChecked()
1102 {
1103     return ContentTab::AlignCenterChecked();
1104 }
1105 
AlignJustifyChecked()1106 bool FlowTab::AlignJustifyChecked()
1107 {
1108     return ContentTab::AlignJustifyChecked();
1109 }
1110 
BulletListChecked()1111 bool FlowTab::BulletListChecked()
1112 {
1113     return ContentTab::BulletListChecked();
1114 }
1115 
NumberListChecked()1116 bool FlowTab::NumberListChecked()
1117 {
1118     return ContentTab::NumberListChecked();
1119 }
1120 
PasteClipNumber(int clip_number)1121 bool FlowTab::PasteClipNumber(int clip_number)
1122 {
1123     if (m_wCodeView) {
1124         return m_wCodeView->PasteClipNumber(clip_number);
1125     }
1126     return false;
1127 }
1128 
PasteClipEntries(QList<ClipEditorModel::clipEntry * > clips)1129 bool FlowTab::PasteClipEntries(QList<ClipEditorModel::clipEntry *>clips)
1130 {
1131     if (m_wCodeView) {
1132         return m_wCodeView->PasteClipEntries(clips);
1133     }
1134     return false;
1135 }
1136 
GetCaretElementName()1137 QString FlowTab::GetCaretElementName()
1138 {
1139     if (m_wCodeView) {
1140         return m_wCodeView->GetCaretElementName();
1141     }
1142     return QString();
1143 }
1144 
SuspendTabReloading()1145 void FlowTab::SuspendTabReloading()
1146 {
1147     // Call this function to prevent the currently displayed PV from being
1148     // reloaded if a linked resource is changed. Automatic reloading can cause
1149     // issues if your code is attempting to manipulate the tab content concurrently.
1150     m_suspendTabReloading = true;
1151 }
1152 
ResumeTabReloading()1153 void FlowTab::ResumeTabReloading()
1154 {
1155     // Call this function to resume reloading of PV in response to linked
1156     // resources changing. If a reload tab request is pending it is executed now.
1157     m_suspendTabReloading = false;
1158 }
1159 
DelayedConnectSignalsToSlots()1160 void FlowTab::DelayedConnectSignalsToSlots()
1161 {
1162     connect(m_HTMLResource, SIGNAL(TextChanging()), this, SLOT(ResourceTextChanging()));
1163     connect(m_HTMLResource, SIGNAL(LinkedResourceUpdated()), this, SLOT(LinkedResourceModified()));
1164     connect(m_HTMLResource, SIGNAL(Modified()), this, SLOT(ResourceModified()));
1165     connect(m_HTMLResource, SIGNAL(LoadedFromDisk()), this, SLOT(ReloadTabIfPending()));
1166 }
1167 
ConnectCodeViewSignalsToSlots()1168 void FlowTab::ConnectCodeViewSignalsToSlots()
1169 {
1170     connect(m_wCodeView, SIGNAL(cursorPositionChanged()), this, SLOT(EmitUpdateCursorPosition()));
1171     connect(m_wCodeView, SIGNAL(ZoomFactorChanged(float)), this, SIGNAL(ZoomFactorChanged(float)));
1172     connect(m_wCodeView, SIGNAL(selectionChanged()), this, SIGNAL(SelectionChanged()));
1173     connect(m_wCodeView, SIGNAL(FocusLost(QWidget *)), this, SLOT(LeaveEditor(QWidget *)));
1174     connect(m_wCodeView, SIGNAL(LinkClicked(const QUrl &)), this, SIGNAL(LinkClicked(const QUrl &)));
1175     connect(m_wCodeView, SIGNAL(ViewImage(const QUrl &)), this, SLOT(HandleViewImage(const QUrl &)));
1176     connect(m_wCodeView, SIGNAL(OpenClipEditorRequest(ClipEditorModel::clipEntry *)), this, SIGNAL(OpenClipEditorRequest(ClipEditorModel::clipEntry *)));
1177     connect(m_wCodeView, SIGNAL(OpenIndexEditorRequest(IndexEditorModel::indexEntry *)), this, SIGNAL(OpenIndexEditorRequest(IndexEditorModel::indexEntry *)));
1178     connect(m_wCodeView, SIGNAL(GoToLinkedStyleDefinitionRequest(const QString &, const QString &)), this, SIGNAL(GoToLinkedStyleDefinitionRequest(const QString &, const QString &)));
1179     connect(m_wCodeView, SIGNAL(BookmarkLinkOrStyleLocationRequest()), this, SIGNAL(BookmarkLinkOrStyleLocationRequest()));
1180     connect(m_wCodeView, SIGNAL(SpellingHighlightRefreshRequest()), this, SIGNAL(SpellingHighlightRefreshRequest()));
1181     connect(m_wCodeView, SIGNAL(ShowStatusMessageRequest(const QString &)), this, SIGNAL(ShowStatusMessageRequest(const QString &)));
1182     connect(m_wCodeView, SIGNAL(FilteredTextChanged()), this, SLOT(EmitContentChanged()));
1183     //  This is needed to capture scroll from arrow keys and the like
1184     connect(m_wCodeView, SIGNAL(FilteredCursorMoved()), this, SLOT(EmitScrollPreviewImmediately()));
1185     connect(m_wCodeView, SIGNAL(PageUpdated()), this, SLOT(EmitUpdatePreview()));
1186     connect(m_wCodeView, SIGNAL(PageClicked()), this, SLOT(EmitScrollPreviewImmediately()));
1187     connect(m_wCodeView, SIGNAL(DocumentSet()), this, SLOT(EmitUpdatePreviewImmediately()));
1188     connect(m_wCodeView, SIGNAL(MarkSelectionRequest()), this, SIGNAL(MarkSelectionRequest()));
1189     connect(m_wCodeView, SIGNAL(ClearMarkedTextRequest()), this, SIGNAL(ClearMarkedTextRequest()));
1190 }
1191