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