1 /*
2 SPDX-FileCopyrightText: 2009-2020 Graeme Gott <graeme@gottcode.org>
3
4 SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7 #include "word_tree.h"
8
9 #include "solver.h"
10 #include "trie.h"
11
12 #include <QDesktopServices>
13 #include <QHeaderView>
14 #include <QMenu>
15 #include <QMouseEvent>
16 #include <QUrl>
17
18 //-----------------------------------------------------------------------------
19
WordTree(QWidget * parent)20 WordTree::WordTree(QWidget* parent)
21 : QTreeWidget(parent)
22 , m_active_item(nullptr)
23 , m_hebrew(false)
24 , m_trie(nullptr)
25 {
26 setColumnCount(3);
27 hideColumn(2);
28 header()->setStretchLastSection(false);
29 header()->setSectionResizeMode(0, QHeaderView::Stretch);
30 header()->setSectionResizeMode(1, QHeaderView::Fixed);
31 header()->hide();
32 setColumnWidth(1, 22);
33
34 setIconSize(QSize(16, 16));
35 setMouseTracking(true);
36 setRootIsDecorated(false);
37 setSortingEnabled(true);
38 sortByColumn(0, Qt::AscendingOrder);
39 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
40 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
41
42 connect(this, &QTreeWidget::itemClicked, this, &WordTree::onItemClicked);
43 }
44
45 //-----------------------------------------------------------------------------
46
addWord(const QString & word)47 QTreeWidgetItem* WordTree::addWord(const QString& word)
48 {
49 QTreeWidgetItem* item = new QTreeWidgetItem(this);
50 item->setText(2, word);
51 if (!m_hebrew) {
52 item->setText(0, word);
53 } else {
54 QString copy = word;
55 int end = copy.length() - 1;
56 switch (copy.at(end).unicode()) {
57 case 0x05db:
58 copy[end] = QChar(0x05da);
59 break;
60 case 0x05de:
61 copy[end] = QChar(0x05dd);
62 break;
63 case 0x05e0:
64 copy[end] = QChar(0x05df);
65 break;
66 case 0x05e4:
67 copy[end] = QChar(0x05e3);
68 break;
69 case 0x05e6:
70 copy[end] = QChar(0x05e5);
71 break;
72 default:
73 break;
74 }
75 item->setText(0, copy);
76 }
77
78 QStringList spellings = m_trie->spellings(word, QStringList(item->text(0).toLower()));
79 item->setData(1, Qt::UserRole, spellings);
80
81 item->setIcon(1, QIcon(":/empty.png"));
82 int score = Solver::score(word);
83 item->setData(0, Qt::UserRole, score);
84
85 spellings.append(tr("%n point(s)", "", score));
86 item->setToolTip(0, spellings.join("\n"));
87
88 return item;
89 }
90
91 //-----------------------------------------------------------------------------
92
removeAll()93 void WordTree::removeAll()
94 {
95 m_active_item = nullptr;
96 clear();
97 scrollToTop();
98 }
99
100 //-----------------------------------------------------------------------------
101
setDictionary(const QString & url)102 void WordTree::setDictionary(const QString& url)
103 {
104 m_url = QUrl::toPercentEncoding(url, "#$%&+,/:;=?@~");
105 }
106
107 //-----------------------------------------------------------------------------
108
setHebrew(bool hebrew)109 void WordTree::setHebrew(bool hebrew)
110 {
111 m_hebrew = hebrew;
112 }
113
114 //-----------------------------------------------------------------------------
115
setTrie(const Trie * trie)116 void WordTree::setTrie(const Trie* trie)
117 {
118 m_trie = trie;
119 }
120
121 //-----------------------------------------------------------------------------
122
leaveEvent(QEvent * event)123 void WordTree::leaveEvent(QEvent* event)
124 {
125 QTreeWidget::leaveEvent(event);
126 enterItem(nullptr);
127 }
128
129 //-----------------------------------------------------------------------------
130
mouseMoveEvent(QMouseEvent * event)131 void WordTree::mouseMoveEvent(QMouseEvent* event)
132 {
133 QTreeWidget::mouseMoveEvent(event);
134 enterItem(itemAt(event->pos()));
135 }
136
137 //-----------------------------------------------------------------------------
138
wheelEvent(QWheelEvent * event)139 void WordTree::wheelEvent(QWheelEvent* event)
140 {
141 QTreeWidget::wheelEvent(event);
142 #if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
143 enterItem(itemAt(event->position().toPoint()));
144 #else
145 enterItem(itemAt(event->pos()));
146 #endif
147 }
148
149 //-----------------------------------------------------------------------------
150
onItemClicked(QTreeWidgetItem * item,int column)151 void WordTree::onItemClicked(QTreeWidgetItem* item, int column)
152 {
153 if (item && column == 1) {
154 const QStringList spellings = item->data(1, Qt::UserRole).toStringList();
155 QString word = spellings.first();
156
157 if (spellings.count() > 1) {
158 QMenu menu;
159 for (const QString& spelling : spellings) {
160 menu.addAction(spelling);
161 }
162 QAction* action = menu.exec(QCursor::pos());
163 if (action) {
164 word = action->text();
165 } else {
166 return;
167 }
168 }
169
170 QByteArray url(m_url);
171 url.replace("%s", QUrl::toPercentEncoding(word));
172 QDesktopServices::openUrl(QUrl::fromEncoded(url));
173 }
174 }
175
176 //-----------------------------------------------------------------------------
177
enterItem(QTreeWidgetItem * item)178 void WordTree::enterItem(QTreeWidgetItem* item)
179 {
180 if (m_active_item) {
181 m_active_item->setIcon(1, QIcon(":/empty.png"));
182 }
183 m_active_item = item;
184 if (m_active_item) {
185 m_active_item->setIcon(1, QIcon::fromTheme("accessories-dictionary", QIcon(":/help-browser.png")));
186 }
187 }
188
189 //-----------------------------------------------------------------------------
190