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