1 /************************************************************************
2 **
3 ** Copyright (C) 2015-2021 Kevin B. Hendricks, Stratford, Ontario Canada
4 ** Copyright (C) 2012 John Schember <john@nachtimwald.com>
5 ** Copyright (C) 2012 Dave Heiland
6 **
7 ** This file is part of Sigil.
8 **
9 ** Sigil is free software: you can redistribute it and/or modify
10 ** it under the terms of the GNU General Public License as published by
11 ** the Free Software Foundation, either version 3 of the License, or
12 ** (at your option) any later version.
13 **
14 ** Sigil is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with Sigil. If not, see <http://www.gnu.org/licenses/>.
21 **
22 *************************************************************************/
23
24 #include "Dialogs/SelectHyperlink.h"
25 #include "ResourceObjects/HTMLResource.h"
26 #include "ResourceObjects/Resource.h"
27 #include "Misc/SettingsStore.h"
28 #include "sigil_constants.h"
29
30 static QString SETTINGS_GROUP = "select_hyperlink";
31
SelectHyperlink(QString default_href,Resource * base_resource,const QString & restype,QList<Resource * > resources,QSharedPointer<Book> book,QWidget * parent)32 SelectHyperlink::SelectHyperlink(QString default_href,
33 Resource *base_resource,
34 const QString & restype,
35 QList<Resource *> resources,
36 QSharedPointer<Book> book,
37 QWidget *parent)
38 :
39 QDialog(parent),
40 m_CurrentResource(base_resource),
41 m_restype(restype),
42 m_DefaultTarget(default_href),
43 m_SavedTarget(QString()),
44 m_Resources(resources),
45 m_Book(book),
46 m_SelectHyperlinkModel(new QStandardItemModel)
47 {
48 ui.setupUi(this);
49 connectSignalsSlots();
50 ReadSettings();
51 SetList();
52 // Set default href name
53 ui.href->setText(m_DefaultTarget);
54
55 if (!m_DefaultTarget.isEmpty()) {
56 SelectText(m_DefaultTarget);
57 } else {
58 SelectText(m_SavedTarget);
59 }
60
61 // Default entry on the target url not the filter
62 ui.href->setFocus();
63 }
64
SetList()65 void SelectHyperlink::SetList()
66 {
67 m_SelectHyperlinkModel->clear();
68 QStringList header;
69 header.append(tr("Targets in the Book"));
70 m_SelectHyperlinkModel->setHorizontalHeaderLabels(header);
71 ui.list->setSelectionBehavior(QAbstractItemView::SelectRows);
72 ui.list->setModel(m_SelectHyperlinkModel);
73 // Get the complete list of valid targets
74 // Key is the book path of the html file
75 m_IDNames = m_Book->GetIdsInHTMLFiles();
76 if (m_restype == "html") {
77 // Display in-file targets first, then in order
78 AddEntry(m_CurrentResource);
79 }
80 foreach(Resource * resource, m_Resources) {
81 if (resource != m_CurrentResource) {
82 AddEntry(resource);
83 }
84 }
85 }
86
AddEntry(Resource * resource)87 void SelectHyperlink::AddEntry(Resource *resource)
88 {
89 if (!resource) {
90 return;
91 }
92
93 QString filename = resource->ShortPathName();
94 QString bkpath = resource->GetRelativePath();
95 QStringList ids = QStringList() << "" << m_IDNames[bkpath];
96 foreach(QString id, ids) {
97 // Do not allow linking to index entries because they can be regenerated
98 // and because they can take up a lot of room.
99 if (id.startsWith(SIGIL_INDEX_ID_PREFIX)) {
100 continue;
101 }
102
103 // filepath is a relative link from m_CurrentResource to resource
104 // target is a short unique name for this resource for use in table only
105 QString target;
106 QString filepath;
107 if (!(m_CurrentResource == resource)) {
108 filepath = resource->GetRelativePathFromResource(m_CurrentResource);
109 target = filename;
110 }
111
112 if (!id.isEmpty()) {
113 QString fragment = "#" + id;
114 target = target + fragment;
115 filepath = filepath + fragment;
116 }
117
118 if (target.isEmpty()) target = filename;
119 if (filepath.isEmpty()) filepath = "#";
120
121 QList<QStandardItem *> rowItems;
122 QStandardItem *target_item = new QStandardItem();
123 target_item->setText(target);
124 target_item->setData(filepath);
125 rowItems << target_item;
126 m_SelectHyperlinkModel->appendRow(rowItems);
127 rowItems[0]->setEditable(false);
128 }
129 }
130
GetSelectedText()131 QString SelectHyperlink::GetSelectedText()
132 {
133 QString text;
134
135 if (ui.list->selectionModel()->hasSelection()) {
136 QModelIndexList selected_indexes = ui.list->selectionModel()->selectedRows(0);
137
138 if (!selected_indexes.isEmpty()) {
139 QStandardItem *item = m_SelectHyperlinkModel->itemFromIndex(selected_indexes.last());
140
141 if (item) {
142 text = item->text();
143 }
144 }
145 }
146
147 return text;
148 }
149
SelectText(QString & text)150 void SelectHyperlink::SelectText(QString &text)
151 {
152 if (!text.isEmpty()) {
153 QModelIndex parent_index;
154 QStandardItem *root_item = m_SelectHyperlinkModel->invisibleRootItem();
155 // Convert search text to filename#fragment
156 QString target = text;
157
158 if (target.startsWith("#") && m_CurrentResource && m_restype == "html") {
159 target = m_CurrentResource->ShortPathName() + text;
160 }
161
162 for (int row = 0; row < root_item->rowCount(); row++) {
163 QStandardItem *child = root_item->child(row, 0);
164 // Convert selection text to proper href
165 QString selection = child->data().toString();
166
167 if (target == selection) {
168 ui.list->selectionModel()->select(m_SelectHyperlinkModel->index(row, 0, parent_index), QItemSelectionModel::Select | QItemSelectionModel::Rows);
169 ui.list->setFocus();
170 ui.list->setCurrentIndex(child->index());
171 }
172 }
173 }
174 }
175
FilterEditTextChangedSlot(const QString & text)176 void SelectHyperlink::FilterEditTextChangedSlot(const QString &text)
177 {
178 const QString lowercaseText = text.toLower();
179 QStandardItem *root_item = m_SelectHyperlinkModel->invisibleRootItem();
180 QModelIndex parent_index;
181 // Hide rows that don't contain the filter text
182 int first_visible_row = -1;
183
184 for (int row = 0; row < root_item->rowCount(); row++) {
185 if (text.isEmpty() || root_item->child(row, 0)->text().toLower().contains(lowercaseText)) {
186 ui.list->setRowHidden(row, parent_index, false);
187
188 if (first_visible_row == -1) {
189 first_visible_row = row;
190 }
191 } else {
192 ui.list->setRowHidden(row, parent_index, true);
193 }
194 }
195
196 if (!text.isEmpty() && first_visible_row != -1) {
197 // Select the first non-hidden row
198 ui.list->setCurrentIndex(root_item->child(first_visible_row, 0)->index());
199 } else {
200 // Clear current and selection, which clears preview image
201 ui.list->setCurrentIndex(QModelIndex());
202 }
203 }
204
GetTarget()205 QString SelectHyperlink::GetTarget()
206 {
207 QString target;
208
209 target = ui.href->text();
210
211 return target;
212 }
213
DoubleClicked(const QModelIndex & index)214 void SelectHyperlink::DoubleClicked(const QModelIndex &index)
215 {
216 Clicked(index);
217 accept();
218 }
219
Clicked(const QModelIndex & index)220 void SelectHyperlink::Clicked(const QModelIndex &index)
221 {
222 QStandardItem *item = m_SelectHyperlinkModel->itemFromIndex(index);
223 ui.href->setText(item->data().toString());
224 }
225
ReadSettings()226 void SelectHyperlink::ReadSettings()
227 {
228 SettingsStore settings;
229 settings.beginGroup(SETTINGS_GROUP);
230 // The size of the window and it's full screen status
231 QByteArray geometry = settings.value("geometry").toByteArray();
232
233 if (!geometry.isNull()) {
234 restoreGeometry(geometry);
235 }
236
237 m_SavedTarget = settings.value("lastselectedentry").toString();
238 settings.endGroup();
239 }
240
WriteSettings()241 void SelectHyperlink::WriteSettings()
242 {
243 SettingsStore settings;
244 settings.beginGroup(SETTINGS_GROUP);
245 // The size of the window and it's full screen status
246 settings.setValue("geometry", saveGeometry());
247 settings.setValue("lastselectedentry", GetSelectedText());
248 settings.endGroup();
249 }
250
connectSignalsSlots()251 void SelectHyperlink::connectSignalsSlots()
252 {
253 connect(this, SIGNAL(accepted()),
254 this, SLOT(WriteSettings()));
255 connect(ui.filter, SIGNAL(textChanged(QString)),
256 this, SLOT(FilterEditTextChangedSlot(QString)));
257 connect(ui.list, SIGNAL(doubleClicked(const QModelIndex &)),
258 this, SLOT(DoubleClicked(const QModelIndex &)));
259 connect(ui.list, SIGNAL(clicked(const QModelIndex &)),
260 this, SLOT(Clicked(const QModelIndex &)));
261 }
262