1 /*
2  *  Copyright 2005-2021 Fabrice Colin
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <iostream>
20 #include <glibmm/ustring.h>
21 #include <glibmm/convert.h>
22 #include <gtkmm/menu.h>
23 
24 #include "config.h"
25 #include "NLS.h"
26 #include "Languages.h"
27 #include "TimeConverter.h"
28 #include "PinotUtils.h"
29 #include "QueryDialog.h"
30 
31 using namespace std;
32 using namespace Glib;
33 using namespace Gtk;
34 
QueryDialog(GtkDialog * & pParent,RefPtr<Builder> & refBuilder)35 QueryDialog::QueryDialog(GtkDialog *&pParent, RefPtr<Builder>& refBuilder) :
36 	Dialog(pParent),
37 	queryCancelButton(NULL),
38 	queryOkButton(NULL),
39 	addFilterButton(NULL),
40 	nameEntry(NULL),
41 	filterCombobox(NULL),
42 	queryTextview(NULL),
43 	indexCheckbutton(NULL),
44 	labelNameCombobox(NULL),
45 	resultsCountSpinbutton(NULL),
46 	sortOrderCombobox(NULL),
47 	stemmingCombobox(NULL),
48 	indexNewCheckbutton(NULL),
49 	m_name(""),
50 	m_badName(true)
51 {
52 	refBuilder->get_widget("queryCancelButton", queryCancelButton);
53 	refBuilder->get_widget("queryOkButton", queryOkButton);
54 	refBuilder->get_widget("addFilterButton", addFilterButton);
55 	refBuilder->get_widget("queryNameEntry", nameEntry);
56 	refBuilder->get_widget("filterCombobox", filterCombobox);
57 	refBuilder->get_widget("queryTextview", queryTextview);
58 	refBuilder->get_widget("indexCheckbutton", indexCheckbutton);
59 	refBuilder->get_widget("labelNameCombobox", labelNameCombobox);
60 	refBuilder->get_widget("resultsCountSpinbutton", resultsCountSpinbutton);
61 	refBuilder->get_widget("sortOrderCombobox", sortOrderCombobox);
62 	refBuilder->get_widget("stemmingCombobox", stemmingCombobox);
63 	refBuilder->get_widget("indexNewCheckbutton", indexNewCheckbutton);
64 
65 	queryCancelButton->signal_clicked().connect(sigc::mem_fun(*this, &QueryDialog::on_queryCancelButton_clicked), false);
66 	queryOkButton->signal_clicked().connect(sigc::mem_fun(*this, &QueryDialog::on_queryOkButton_clicked), false);
67 	nameEntry->signal_changed().connect(sigc::mem_fun(*this, &QueryDialog::on_nameEntry_changed), false);
68 	addFilterButton->signal_clicked().connect(sigc::mem_fun(*this, &QueryDialog::on_addFilterButton_clicked), false);
69 	indexCheckbutton->signal_toggled().connect(sigc::mem_fun(*this, &QueryDialog::on_indexCheckbutton_toggled), false);
70 }
71 
~QueryDialog()72 QueryDialog::~QueryDialog()
73 {
74 }
75 
setQueryProperties(const QueryProperties & properties)76 void QueryDialog::setQueryProperties(const QueryProperties &properties)
77 {
78 	m_name = properties.getName();
79 	m_properties = properties;
80 #ifdef DEBUG
81     clog << "QueryDialog::setQueryProperties: editing " << m_properties.getName() << endl;
82 #endif
83 
84 	// Name
85 	if (m_name.empty() == true)
86 	{
87 		nameEntry->set_text("");
88 		queryOkButton->set_sensitive(false);
89 	}
90 	else
91 	{
92 		nameEntry->set_text(m_name);
93 	}
94 	// Query text
95 	RefPtr<TextBuffer> refBuffer = queryTextview->get_buffer();
96 	if (refBuffer)
97 	{
98 		refBuffer->set_text(m_properties.getFreeQuery());
99 	}
100 	// Maximum number of results
101 	resultsCountSpinbutton->set_value((double)m_properties.getMaximumResultsCount());
102 	// Index results
103 	QueryProperties::IndexWhat indexResults = m_properties.getIndexResults();
104 	if (indexResults == QueryProperties::NEW_RESULTS)
105 	{
106 		indexCheckbutton->set_active(true);
107 		indexNewCheckbutton->set_active(true);
108 	}
109 	else if (indexResults == QueryProperties::ALL_RESULTS)
110 	{
111 		indexCheckbutton->set_active(true);
112 		indexNewCheckbutton->set_active(false);
113 	}
114 	else
115 	{
116 		indexCheckbutton->set_active(false);
117 		indexNewCheckbutton->set_active(false);
118 		indexNewCheckbutton->set_sensitive(false);
119 	}
120 
121 	// Populate
122 	populate_comboboxes();
123 }
124 
is_separator(const RefPtr<TreeModel> & model,const TreeModel::iterator & iter)125 bool QueryDialog::is_separator(const RefPtr<TreeModel>& model, const TreeModel::iterator& iter)
126 {
127 	if (iter)
128 	{
129 		const TreeModel::Path queryPath = model->get_path(iter);
130 		string rowPath(from_utf8(queryPath.to_string()));
131 		unsigned int rowPos = 0;
132 
133 		// FIXME: this is extremely hacky !
134 		if ((sscanf(rowPath.c_str(), "%u", &rowPos) == 1) &&
135 			(rowPos == 12))
136 		{
137 			return true;
138 		}
139 	}
140 
141 	return false;
142 }
143 
populate_comboboxes()144 void QueryDialog::populate_comboboxes()
145 {
146 	unsigned int labelNum = 1;
147 
148 	// All supported filters
149 	filterCombobox->set_row_separator_func(sigc::mem_fun(*this, &QueryDialog::is_separator));
150 	filterCombobox->append(_("Host name"));
151 	filterCombobox->append(_("File name"));
152 	filterCombobox->append(_("File extension"));
153 	filterCombobox->append(_("Title"));
154 	filterCombobox->append(_("URL"));
155 	filterCombobox->append(_("Directory"));
156 	filterCombobox->append(_("In URL"));
157 	filterCombobox->append(_("Path"));
158 	filterCombobox->append(_("Language code"));
159 	filterCombobox->append(_("MIME type"));
160 	filterCombobox->append(_("MIME class"));
161 	filterCombobox->append(_("Label"));
162 	// And separate numerical ranges
163 	filterCombobox->append("===");
164 	filterCombobox->append(_("Date"));
165 	filterCombobox->append(_("Time"));
166 	filterCombobox->append(_("Size"));
167 	filterCombobox->set_active(0);
168 
169 	// Sort order
170 	sortOrderCombobox->append(_("By relevance"));
171 	sortOrderCombobox->append(_("By date"));
172 	sortOrderCombobox->append(_("By size"));
173 	if (m_properties.getSortOrder() == QueryProperties::DATE_DESC)
174 	{
175 		sortOrderCombobox->set_active(1);
176 	}
177 	else if (m_properties.getSortOrder() == QueryProperties::SIZE_DESC)
178 	{
179 		sortOrderCombobox->set_active(2);
180 	}
181 	else
182 	{
183 		sortOrderCombobox->set_active(0);
184 	}
185 
186 	// Stemming language
187 	stemmingCombobox->append(_("None"));
188 	stemmingCombobox->set_active(0);
189 	string language(m_properties.getStemmingLanguage());
190 	for (unsigned int languageNum = 1; languageNum < Languages::m_count; ++languageNum)
191 	{
192 		ustring languageName(Languages::getIntlName(languageNum));
193 
194 		stemmingCombobox->append(languageName);
195 		// Is this the language we are looking for ?
196 		if (language == languageName)
197 		{
198 			stemmingCombobox->set_active(languageNum);
199 		}
200 	}
201 
202 	// Labels
203 	labelNameCombobox->append(_("None"));
204 	labelNameCombobox->set_active(0);
205 	// Add all labels to the label combo and select the one defined for the query
206 	set<string> &labels = PinotSettings::getInstance().m_labels;
207 	for (set<string>::const_iterator labelIter = labels.begin(); labelIter != labels.end(); ++labelIter)
208 	{
209 		string labelName(*labelIter);
210 
211 		labelNameCombobox->append(to_utf8(labelName));
212 		if (labelName == m_properties.getLabelName())
213 		{
214 			labelNameCombobox->set_active(labelNum);
215 		}
216 
217 		++labelNum;
218 	}
219 }
220 
getQueryProperties(void) const221 const QueryProperties &QueryDialog::getQueryProperties(void) const
222 {
223 	return m_properties;
224 }
225 
badName(void) const226 bool QueryDialog::badName(void) const
227 {
228 	return m_badName;
229 }
230 
on_queryCancelButton_clicked()231 void QueryDialog::on_queryCancelButton_clicked()
232 {
233 #ifdef DEBUG
234 	clog << "QueryDialog::on_queryCancelButton_clicked: called" << endl;
235 #endif
236 	close();
237 }
238 
on_queryOkButton_clicked()239 void QueryDialog::on_queryOkButton_clicked()
240 {
241 	ustring newName(nameEntry->get_text());
242 #ifdef DEBUG
243 	clog << "QueryDialog::on_queryOkButton_clicked: called" << endl;
244 #endif
245 
246 	// Name
247 	m_properties.setName(newName);
248 	m_badName = false;
249 	// Did the name change ?
250 	if (m_name != newName)
251 	{
252 		const std::map<string, QueryProperties> &queries = PinotSettings::getInstance().getQueries();
253 
254 		// Is it already used ?
255 		std::map<string, QueryProperties>::const_iterator queryIter = queries.find(newName);
256 		if (queryIter != queries.end())
257 		{
258 			// Yes, it is
259 			m_badName = true;
260 #ifdef DEBUG
261 			clog << "QueryDialog::on_queryOkButton_clicked: name in use" << endl;
262 #endif
263 		}
264 	}
265 
266 	// Query text
267 	RefPtr<TextBuffer> refBuffer = queryTextview->get_buffer();
268 	if (refBuffer)
269 	{
270 		m_properties.setFreeQuery(refBuffer->get_text());
271 	}
272 	// Maximum number of results
273 	m_properties.setMaximumResultsCount((unsigned int)resultsCountSpinbutton->get_value());
274 	// Sort order
275 	if (sortOrderCombobox->get_active_row_number() == 1)
276 	{
277 		m_properties.setSortOrder(QueryProperties::DATE_DESC);
278 	}
279 	else if (sortOrderCombobox->get_active_row_number() == 2)
280 	{
281 		m_properties.setSortOrder(QueryProperties::SIZE_DESC);
282 	}
283 	else
284 	{
285 		m_properties.setSortOrder(QueryProperties::RELEVANCE);
286 	}
287 	// Stemming language
288 	string languageName(from_utf8(stemmingCombobox->get_active_text()));
289 	int chosenLanguagePos = stemmingCombobox->get_active_row_number();
290 	if (chosenLanguagePos == 0)
291 	{
292 		languageName.clear();
293 	}
294 	m_properties.setStemmingLanguage(languageName);
295 	// Index results
296 	bool indexResults = indexCheckbutton->get_active();
297 	bool indexNewResults = indexNewCheckbutton->get_active();
298 	if ((indexResults == true) &&
299 		(indexNewResults == true))
300 	{
301 		m_properties.setIndexResults(QueryProperties::NEW_RESULTS);
302 	}
303 	else if ((indexResults == true) &&
304 		(indexNewResults == false))
305 	{
306 		m_properties.setIndexResults(QueryProperties::ALL_RESULTS);
307 	}
308 	else
309 	{
310 		m_properties.setIndexResults(QueryProperties::NOTHING);
311 	}
312 
313 	// Index label
314 	m_properties.setLabelName("");
315 	int chosenLabel = labelNameCombobox->get_active_row_number();
316 	if (chosenLabel > 0)
317 	{
318 		m_properties.setLabelName(from_utf8(labelNameCombobox->get_active_text()));
319 	}
320 
321 	close();
322 }
323 
on_nameEntry_changed()324 void QueryDialog::on_nameEntry_changed()
325 {
326 	ustring name = nameEntry->get_text();
327 	if (name.empty() == false)
328 	{
329 		queryOkButton->set_sensitive(true);
330 	}
331 	else
332 	{
333 		queryOkButton->set_sensitive(false);
334 	}
335 }
336 
on_addFilterButton_clicked()337 void QueryDialog::on_addFilterButton_clicked()
338 {
339 	ustring filter;
340 	time_t timeT = time(NULL);
341 	struct tm *tm = localtime(&timeT);
342 
343 	// What's the corresponding filter ?
344 	int chosenFilter = filterCombobox->get_active_row_number();
345 	// FIXME: should the filters be localized ?
346 	switch (chosenFilter)
347 	{
348 		case 0:
349 			filter = "site:localhost";
350 			break;
351 		case 1:
352 			filter = "file:xxx.txt";
353 			break;
354 		case 2:
355 			filter = "ext:txt";
356 			break;
357 		case 3:
358 			filter = "title:pinot";
359 			break;
360 		case 4:
361 			filter = "url:file:///home/xxx/yyy.txt";
362 			break;
363 		case 5:
364 			filter = "dir:/home/xxx";
365 			break;
366 		case 6:
367 			filter = "inurl:file:///home/xxx/yyy.tar.gz";
368 			break;
369 		case 7:
370 			filter = "path:Documents";
371 			break;
372 		case 8:
373 			filter = "lang:en";
374 			break;
375 		case 9:
376 			filter = "type:text/plain";
377 			break;
378 		case 10:
379 			filter = "class:text";
380 			break;
381 		case 11:
382 			filter = "label:New";
383 			break;
384 		case 12:
385 			// Separator
386 			break;
387 		case 13:
388 			filter = TimeConverter::toYYYYMMDDString(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
389 			filter += "..20991231";
390 			break;
391 		case 14:
392 			filter = TimeConverter::toHHMMSSString(tm->tm_hour, tm->tm_min, tm->tm_sec);
393 			filter += "..235959";
394 			break;
395 		case 15:
396 			filter += "0..10240b";
397 			break;
398 		default:
399 			break;
400 	}
401 
402 	RefPtr<TextBuffer> refBuffer = queryTextview->get_buffer();
403 	if (refBuffer)
404 	{
405 		ustring queryText = refBuffer->get_text();
406 		queryText += " ";
407 		queryText += filter;
408 		refBuffer->set_text(queryText);
409 	}
410 }
411 
on_indexCheckbutton_toggled()412 void QueryDialog::on_indexCheckbutton_toggled()
413 {
414 	if (indexCheckbutton->get_active() == false)
415 	{
416 		indexNewCheckbutton->set_active(false);
417 		indexNewCheckbutton->set_sensitive(false);
418 	}
419 	else
420 	{
421 		indexNewCheckbutton->set_sensitive(true);
422 	}
423 }
424 
425