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