1 /*
2  * Copyright 2011 kubtek <kubtek@mail.com>
3  *
4  * This file is part of StarDict.
5  *
6  * StarDict is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * StarDict is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with StarDict.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23 
24 #include <cstring>
25 #include <string>
26 #include <glib/gi18n.h>
27 
28 #ifdef _WIN32
29 #  include <gdk/gdkwin32.h>
30 #endif
31 
32 #include "stardict.h"
33 #include "desktop.h"
34 #include "conf.h"
35 #include "lib/utils.h"
36 #include "iskeyspressed.h"
37 
38 #include "floatwin.h"
39 
FloatWin()40 FloatWin::FloatWin()
41 {
42 	hide_window_timer = 0;
43 	lookup_running_timer = 0;
44 	now_window_width = 0;
45 	now_window_height = 0;
46 	button_box_once_shown = false;
47 	ismoving = false;
48 	menu = NULL;
49 	content_state = ContentState_Empty;
50 	have_real_content = false;
51 	window_positioned = false;
52 	IgnoreScanModifierKey = false;
53 }
54 
Create()55 void FloatWin::Create()
56 {
57 	FloatWindow = gtk_window_new(GTK_WINDOW_POPUP);
58 	gtk_widget_set_events(FloatWindow, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK);
59 	g_signal_connect (G_OBJECT (FloatWindow), "button_press_event", G_CALLBACK (vButtonPressCallback), this);
60 	g_signal_connect (G_OBJECT (FloatWindow), "button_release_event", G_CALLBACK (vButtonReleaseCallback), this);
61 	g_signal_connect (G_OBJECT (FloatWindow), "motion_notify_event", G_CALLBACK (vMotionNotifyCallback), this);
62 	g_signal_connect (G_OBJECT (FloatWindow), "enter_notify_event", G_CALLBACK (vEnterNotifyCallback), this);
63 	g_signal_connect (G_OBJECT (FloatWindow), "leave_notify_event", G_CALLBACK (vLeaveNotifyCallback), this);
64 
65 	GtkWidget *frame = gtk_frame_new(NULL);
66 	gtk_frame_set_shadow_type(GTK_FRAME(frame),GTK_SHADOW_ETCHED_OUT);
67 	gtk_container_add(GTK_CONTAINER(FloatWindow),frame);
68 	GtkWidget *vbox;
69 #if GTK_MAJOR_VERSION >= 3
70 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
71 #else
72 	vbox = gtk_vbox_new(false,0);
73 #endif
74 	gtk_container_set_border_width (GTK_CONTAINER (vbox), FLOATWIN_BORDER_WIDTH);
75 	gtk_container_add(GTK_CONTAINER(frame),vbox);
76 
77 	create_button_hbox();
78 	gtk_box_pack_start(GTK_BOX(vbox),button_hbox,false,false,0);
79 	view.reset(new ArticleView(GTK_BOX(vbox), (BookNameStyle)conf->get_int_at("dictionary/bookname_style"), true));
80 
81 	gtk_widget_show_all(frame);
82 	gtk_widget_hide(button_hbox); //show all will show hbox's children,now hide hbox only.
83 
84 	set_bg();
85 	set_transparent(conf->get_int_at("floating_window/transparent"));
86 	restore_locked_position();
87 
88 	conf->notify_add("/apps/stardict/preferences/floating_window/lock",
89 			 sigc::mem_fun(this, &FloatWin::on_lock_changed));
90 	conf->notify_add("/apps/stardict/preferences/dictionary/scan_selection",
91 			 sigc::mem_fun(this, &FloatWin::on_dict_scan_select_changed));
92 	conf->notify_add("/apps/stardict/preferences/floating_window/lock_x",
93 			 sigc::mem_fun(this, &FloatWin::on_lock_x_changed));
94 	conf->notify_add("/apps/stardict/preferences/floating_window/lock_y",
95 			 sigc::mem_fun(this, &FloatWin::on_lock_y_changed));
96 	conf->notify_add("/apps/stardict/preferences/floating_window/transparent",
97 			 sigc::mem_fun(this, &FloatWin::on_transparent_changed));
98 
99 	conf->notify_add("/apps/stardict/preferences/floating_window/use_custom_bg",
100 			 sigc::mem_fun(this, &FloatWin::on_use_custom_bg_changed));
101 	gtk_widget_realize(FloatWindow);
102 }
103 
End()104 void FloatWin::End()
105 {
106 	if (conf->get_bool_at("floating_window/lock")) {
107 		gint x, y;
108 		gtk_window_get_position(GTK_WINDOW(FloatWindow), &x, &y);
109 		conf->set_int_at("floating_window/lock_x", x);
110 		conf->set_int_at("floating_window/lock_y", y);
111 	}
112 
113 	destroy_hide_window_timer();
114 	if (menu)
115 		gtk_widget_destroy(menu);
116 	menu = NULL;
117 	if (FloatWindow)
118 		gtk_widget_destroy(FloatWindow);
119 	FloatWindow = NULL;
120 	content_state = ContentState_Empty;
121 	have_real_content = false;
122 }
123 
StartLookup(const char * sWord,bool IgnoreScanModifierKey)124 void FloatWin::StartLookup(const char* sWord, bool IgnoreScanModifierKey)
125 {
126 	QueryingWord = sWord;
127 	content_state = ContentState_Waiting;
128 	/* Set the text telling that lookup is running, but do not show the window.
129 	The window will be shown when a response arrives or when LOOKUP_RUNNING_TIMEOUT
130 	elapses. */
131 	const std::string markup = get_looking_up_markup(QueryingWord.c_str());
132 	view->set_pango_text(markup.c_str());
133 	set_busy_cursor();
134 	have_real_content = false;
135 	window_positioned = false;
136 	this->IgnoreScanModifierKey = IgnoreScanModifierKey;
137 	start_lookup_running_timer();
138 }
139 
EndLookup(void)140 void FloatWin::EndLookup(void)
141 {
142 	content_state = have_real_content ? ContentState_Found : ContentState_NotFound;
143 	if(!have_real_content) {
144 		const std::string markup = get_not_found_markup(QueryingWord.c_str(), _("<Not Found!>"));
145 		view->set_pango_text(markup.c_str());
146 		if (conf->get_bool_at("floating_window/show_if_not_found")) {
147 			Popup(!window_positioned);
148 			window_positioned = true;
149 		}
150 	}
151 	set_normal_cursor();
152 	destroy_lookup_running_timer();
153 }
154 
AppendTextLocalDict(gchar *** Word,gchar **** WordData,const gchar * sOriginWord)155 void FloatWin::AppendTextLocalDict(gchar ***Word, gchar ****WordData, const gchar *sOriginWord)
156 {
157 	view->begin_update();
158 	if(!have_real_content)
159 		view->clear();
160 	const std::string mark = get_head_word_markup(sOriginWord);
161 	view->append_pango_text(mark.c_str());
162 	int j,k;
163 	for (size_t i=0; i<gpAppFrame->scan_dictmask.size(); i++) {
164 		if (Word[i]) {
165 			view->AppendNewline();
166 			view->SetDictIndex(gpAppFrame->scan_dictmask[i]);
167 			if (gpAppFrame->scan_dictmask[i].type == InstantDictType_LOCAL)
168 				view->AppendHeader(gpAppFrame->oLibs.dict_name(gpAppFrame->scan_dictmask[i].index).c_str());
169 			else if (gpAppFrame->scan_dictmask[i].type == InstantDictType_VIRTUAL)
170 				view->AppendHeader(gpAppFrame->oStarDictPlugins->VirtualDictPlugins.dict_name(gpAppFrame->scan_dictmask[i].index));
171 			else if (gpAppFrame->scan_dictmask[i].type == InstantDictType_NET)
172 				view->AppendHeader(gpAppFrame->oStarDictPlugins->NetDictPlugins.dict_name(gpAppFrame->scan_dictmask[i].index));
173 			j=0;
174 			do {
175 				if (j==0) {
176 					if (strcmp(Word[i][j], sOriginWord))
177 						view->AppendWord(Word[i][j]);
178 				} else {
179 					view->AppendNewline();
180 					view->AppendWord(Word[i][j]);
181 				}
182 				view->AppendData(WordData[i][j][0], Word[i][j],
183 						 sOriginWord);
184 				k=1;
185 				while (WordData[i][j][k]) {
186 					view->AppendNewline();
187 					view->AppendDataSeparate();
188 					view->AppendData(WordData[i][j][k],
189 							 Word[i][j], sOriginWord);
190 					k++;
191 				}
192 				j++;
193 			} while (Word[i][j]);
194 		}
195 	}
196 	view->end_update();
197 	have_real_content = true;
198 	destroy_lookup_running_timer();
199 
200 	gboolean pronounced = false;
201 	readwordtype = gpAppFrame->oReadWord.canRead(sOriginWord);
202 	if (readwordtype != READWORD_CANNOT) {
203 		if (PronounceWord == sOriginWord) {
204 			pronounced = true;
205 		} else {
206 			PronounceWord = sOriginWord;
207 		}
208 	} else {
209 		for (size_t i=0;i< gpAppFrame->scan_dictmask.size(); i++) {
210 			if (Word[i] && strcmp(Word[i][0], sOriginWord)) {
211 				readwordtype = gpAppFrame->oReadWord.canRead(Word[i][0]);
212 				if (readwordtype != READWORD_CANNOT) {
213 					if (PronounceWord == Word[i][0])
214 						pronounced = true;
215 					else
216 						PronounceWord = Word[i][0];
217 				}
218 				break;
219 			}
220 		}
221 	}
222 	gtk_widget_set_sensitive(PronounceWordButton, readwordtype != READWORD_CANNOT);
223 
224 	Popup(!window_positioned);
225 	window_positioned = true;
226 	if ((readwordtype != READWORD_CANNOT) && (!pronounced) && conf->get_bool_at("floating_window/pronounce_when_popup"))
227 		gpAppFrame->oReadWord.read(PronounceWord.c_str(), readwordtype);
228 }
229 
AppendTextNetDict(NetDictResponse * resp)230 void FloatWin::AppendTextNetDict(NetDictResponse *resp)
231 {
232 	if(!resp->data)
233 		return;
234 
235 	view->begin_update();
236 	if(!have_real_content)
237 		view->clear();
238 	if (!have_real_content) {
239 		std::string mark = get_head_word_markup(resp->word);
240 		view->append_pango_text(mark.c_str());
241 	}
242 	InstantDictIndex dict_index;
243 	dict_index.type = InstantDictType_UNKNOWN;
244 	view->SetDictIndex(dict_index);
245 	view->AppendNewline();
246 	view->AppendHeader(resp->bookname);
247 	view->AppendWord(resp->word);
248 	view->AppendData(resp->data, resp->word, resp->word);
249 	view->end_update();
250 	have_real_content = true;
251 	destroy_lookup_running_timer();
252 
253 	gboolean pronounced = false;
254 	readwordtype = gpAppFrame->oReadWord.canRead(resp->word);
255 	if (readwordtype != READWORD_CANNOT) {
256 		if (PronounceWord == resp->word)
257 			pronounced = true;
258 		else
259 			PronounceWord = resp->word;
260 	}
261 	gtk_widget_set_sensitive(PronounceWordButton, readwordtype != READWORD_CANNOT);
262 
263 	Popup(!window_positioned);
264 	window_positioned = true;
265 	if ((readwordtype != READWORD_CANNOT) && (!pronounced) && conf->get_bool_at("floating_window/pronounce_when_popup"))
266 		gpAppFrame->oReadWord.read(PronounceWord.c_str(), readwordtype);
267 }
268 
AppendTextStarDictNet(const struct STARDICT::LookupResponse::DictResponse * dict_response)269 void FloatWin::AppendTextStarDictNet(const struct STARDICT::LookupResponse::DictResponse *dict_response)
270 {
271 	if(dict_response->dict_result_list.empty())
272 		return;
273 
274 	view->begin_update();
275 	if(!have_real_content)
276 		view->clear();
277 	if (!have_real_content) {
278 		std::string mark = get_head_word_markup(dict_response->oword);
279 		view->append_pango_text(mark.c_str());
280 	}
281 	InstantDictIndex dict_index;
282 	dict_index.type = InstantDictType_UNKNOWN;
283 	view->SetDictIndex(dict_index);
284 	for (std::list<struct STARDICT::LookupResponse::DictResponse::DictResult *>::const_iterator i = dict_response->dict_result_list.begin(); i != dict_response->dict_result_list.end(); ++i) {
285 		view->AppendNewline();
286 		view->AppendHeader((*i)->bookname);
287 		for (std::list<struct STARDICT::LookupResponse::DictResponse::DictResult::WordResult *>::iterator j = (*i)->word_result_list.begin(); j != (*i)->word_result_list.end(); ++j) {
288 			if (j == (*i)->word_result_list.begin()) {
289 				if (strcmp((*j)->word, dict_response->oword)) {
290 					view->AppendWord((*j)->word);
291 				}
292 			} else {
293 				view->AppendNewline();
294 				view->AppendWord((*j)->word);
295 			}
296 			std::list<char *>::iterator k = (*j)->datalist.begin();
297 			view->AppendData(*k, (*j)->word, dict_response->oword);
298 			for (++k; k != (*j)->datalist.end(); ++k) {
299 				view->AppendNewline();
300 				view->AppendDataSeparate();
301 				view->AppendData(*k, (*j)->word, dict_response->oword);
302 			}
303 		}
304 	}
305 	view->end_update();
306 	have_real_content = true;
307 	destroy_lookup_running_timer();
308 
309 	gboolean pronounced = false;
310 	readwordtype = gpAppFrame->oReadWord.canRead(dict_response->oword);
311 	if (readwordtype != READWORD_CANNOT) {
312 		if (PronounceWord == dict_response->oword)
313 			pronounced = true;
314 		else
315 			PronounceWord = dict_response->oword;
316 	} else {
317 		for (std::list<struct STARDICT::LookupResponse::DictResponse::DictResult *>::const_iterator i = dict_response->dict_result_list.begin(); i != dict_response->dict_result_list.end(); ++i) {
318 			std::list<struct STARDICT::LookupResponse::DictResponse::DictResult::WordResult *>::iterator j = (*i)->word_result_list.begin();
319 			if (j != (*i)->word_result_list.end() && strcmp((*j)->word, dict_response->oword)) {
320 				readwordtype = gpAppFrame->oReadWord.canRead((*j)->word);
321 				if (readwordtype != READWORD_CANNOT) {
322 					if (PronounceWord == (*j)->word)
323 						pronounced = true;
324 					else
325 						PronounceWord = (*j)->word;
326 				}
327 				break;
328 			}
329 		}
330 	}
331 	gtk_widget_set_sensitive(PronounceWordButton, readwordtype != READWORD_CANNOT);
332 
333 	Popup(!window_positioned);
334 	window_positioned = true;
335 	if ((readwordtype != READWORD_CANNOT) && (!pronounced) && conf->get_bool_at("floating_window/pronounce_when_popup"))
336 		gpAppFrame->oReadWord.read(PronounceWord.c_str(), readwordtype);
337 }
338 
AppendTextFuzzy(gchar **** ppppWord,gchar ***** pppppWordData,const gchar ** ppOriginWord,gint count,const gchar * sOriginWord)339 void FloatWin::AppendTextFuzzy(gchar ****ppppWord, gchar *****pppppWordData, const gchar ** ppOriginWord, gint count, const gchar *sOriginWord)
340 {
341 	view->begin_update();
342 	if(!have_real_content)
343 		view->clear();
344 
345 	std::string mark;
346 	gchar *m_str;
347 	mark = _("Fuzzy query");
348 	mark += " ";
349 	mark += get_head_word_markup(sOriginWord);
350 	mark += " ";
351 	mark += _("has succeeded.\n");
352 	if (count ==1)
353 		mark+= _("Found 1 word:\n");
354 	else {
355 		m_str=g_strdup_printf(_("Found %d words:\n"),count);
356 		mark += m_str;
357 		g_free(m_str);
358 	}
359 
360 	int j;
361 	for (j=0; j<count-1; j++) {
362 		mark += "<span size=\"x-large\">";
363 		m_str = g_markup_escape_text(ppOriginWord[j], -1);
364 		mark += m_str;
365 		g_free(m_str);
366 		mark += "</span> ";
367 	}
368 	mark += "<span size=\"x-large\">";
369 	m_str = g_markup_escape_text(ppOriginWord[count-1],-1);
370 	mark += m_str;
371 	g_free(m_str);
372 	mark += "</span>";
373 	view->append_pango_text(mark.c_str());
374 
375 	int m,n;
376 	for (j=0; j<count; j++) {
377 		if (!ppppWord[j])
378 			continue;
379 		mark = "\n\n";
380 		mark += get_head_word_markup(ppOriginWord[j]);
381 		view->append_pango_text(mark.c_str());
382 		for (size_t i=0; i<gpAppFrame->scan_dictmask.size(); i++) {
383 			if (ppppWord[j][i]) {
384 				view->AppendNewline();
385 				view->SetDictIndex(gpAppFrame->scan_dictmask[i]);
386 				if (gpAppFrame->scan_dictmask[i].type == InstantDictType_LOCAL)
387 					view->AppendHeader(gpAppFrame->oLibs.dict_name(gpAppFrame->scan_dictmask[i].index).c_str());
388 				else if (gpAppFrame->scan_dictmask[i].type == InstantDictType_VIRTUAL)
389 					view->AppendHeader(gpAppFrame->oStarDictPlugins->VirtualDictPlugins.dict_name(gpAppFrame->scan_dictmask[i].index));
390 				else if (gpAppFrame->scan_dictmask[i].type == InstantDictType_NET)
391 					view->AppendHeader(gpAppFrame->oStarDictPlugins->NetDictPlugins.dict_name(gpAppFrame->scan_dictmask[i].index));
392 				m=0;
393 				do {
394 					if (m==0) {
395 						if (strcmp(ppppWord[j][i][m], ppOriginWord[j]))
396 							view->AppendWord(ppppWord[j][i][m]);
397 					} else {
398 						view->AppendNewline();
399 						view->AppendWord(ppppWord[j][i][m]);
400 					}
401 					view->AppendData(pppppWordData[j][i][m][0], ppppWord[j][i][m],
402 							 sOriginWord);
403 					n=1;
404 					while (pppppWordData[j][i][m][n]) {
405 						view->AppendNewline();
406 						view->AppendDataSeparate();
407 						view->AppendData(pppppWordData[j][i][m][n],
408 								 ppppWord[j][i][m], sOriginWord);
409 						n++;
410 					}
411 					m++;
412 				} while (ppppWord[j][i][m]);
413 			}
414 		}
415 	}
416 	view->end_update();
417 	have_real_content = true;
418 	destroy_lookup_running_timer();
419 
420 	readwordtype = gpAppFrame->oReadWord.canRead(sOriginWord);
421 	if (readwordtype != READWORD_CANNOT)
422 		PronounceWord = sOriginWord;
423 	gtk_widget_set_sensitive(PronounceWordButton, readwordtype != READWORD_CANNOT);
424 
425 
426 	Popup(!window_positioned);
427 	window_positioned = true;
428 	/*bool pronounce_when_popup=
429 		conf->get_bool_at("floating_window/pronounce_when_popup");
430 
431 	if (canRead && pronounce_when_popup)
432 		gpAppFrame->oReadWord.read(PronounceWord.c_str());*/
433 }
434 
ShowPangoTips(const char * sWord,const char * text)435 void FloatWin::ShowPangoTips(const char *sWord, const char *text)
436 {
437 	QueryingWord = sWord;
438 	view->set_pango_text(text);
439 	have_real_content = true;
440 	IgnoreScanModifierKey = false;
441 	destroy_lookup_running_timer();
442 
443 	//gboolean pronounced = false;
444 	readwordtype = gpAppFrame->oReadWord.canRead(sWord);
445 	if (readwordtype != READWORD_CANNOT) {
446 		if (PronounceWord == sWord) {
447 			//pronounced = true;
448 		} else {
449 			PronounceWord = sWord;
450 		}
451 	}
452 	gtk_widget_set_sensitive(PronounceWordButton, readwordtype != READWORD_CANNOT);
453 
454 	Popup(true);
455 	window_positioned = true;
456 }
457 
Show()458 void FloatWin::Show()
459 {
460 	gtk_widget_show(FloatWindow);
461 #ifdef _WIN32
462 	gtk_window_present(GTK_WINDOW(FloatWindow));
463 #endif
464 	start_hide_window_timer();
465 }
466 
Hide()467 void FloatWin::Hide()
468 {
469 	destroy_hide_window_timer();
470 	button_box_once_shown = false;
471 	window_positioned = false;
472 	PronounceWord.clear();
473 	gtk_widget_hide(FloatWindow);
474 }
475 
on_query_click(GtkWidget * widget,FloatWin * oFloatWin)476 void FloatWin::on_query_click(GtkWidget *widget, FloatWin *oFloatWin)
477 {
478 	play_sound_on_event("buttonactive");
479 
480 	if (!conf->get_bool_at("floating_window/lock"))
481 		oFloatWin->Hide();
482 	gpAppFrame->Query(oFloatWin->QueryingWord.c_str());
483 	gpAppFrame->oDockLet->maximize_from_tray();
484 }
485 
on_save_click(GtkWidget * widget,FloatWin * oFloatWin)486 void FloatWin::on_save_click(GtkWidget *widget, FloatWin *oFloatWin)
487 {
488 	const std::string& filePath = conf->get_string_at("dictionary/export_file");
489 #ifdef _WIN32
490 	FILE *fp = fopen(abs_path_to_data_dir(filePath).c_str(), "a+");
491 #else
492 	FILE *fp = fopen(filePath.c_str(), "a+");
493 #endif
494 	if(fp) {
495 		if (conf->get_bool_at("dictionary/only_export_word")) {
496 			fputs(oFloatWin->QueryingWord.c_str(),fp);
497 			fputs("\n",fp);
498 		} else {
499 			fputs(oFloatWin->view->get_text().c_str(),fp);
500 			fputs("\n\n",fp);
501 		}
502 		fclose(fp);
503 	}
504 	play_sound_on_event("buttonactive");
505 }
506 
on_play_click(GtkWidget * widget,FloatWin * oFloatWin)507 void FloatWin::on_play_click(GtkWidget *widget, FloatWin *oFloatWin)
508 {
509 	gpAppFrame->oReadWord.read(oFloatWin->PronounceWord.c_str(), oFloatWin->readwordtype);
510 }
511 
on_stop_click(GtkWidget * widget,FloatWin * oFloatWin)512 void FloatWin::on_stop_click(GtkWidget *widget, FloatWin *oFloatWin)
513 {
514 	play_sound_on_event("buttonactive");
515 	conf->set_bool_at("dictionary/scan_selection", false);
516 }
517 
518 #ifndef CONFIG_GPE
on_help_click(GtkWidget * widget,FloatWin * oFloatWin)519 void FloatWin::on_help_click(GtkWidget *widget, FloatWin *oFloatWin)
520 {
521 	play_sound_on_event("buttonactive");
522 
523 	if (!conf->get_bool_at("floating_window/lock"))
524 		oFloatWin->Hide();
525 	show_help("stardict-scan-selection");
526 }
527 
on_quit_click(GtkWidget * widget,FloatWin * oFloatWin)528 void FloatWin::on_quit_click(GtkWidget *widget, FloatWin *oFloatWin)
529 {
530 	play_sound_on_event("buttonactive");
531 	gpAppFrame->Quit();
532 }
533 #endif
vLockCallback(GtkWidget * widget,FloatWin * oFloatWin)534 void FloatWin::vLockCallback(GtkWidget *widget, FloatWin *oFloatWin)
535 {
536 	play_sound_on_event("buttonactive");
537 	conf->set_bool_at("floating_window/lock",
538 		!conf->get_bool_at("floating_window/lock"));
539 }
540 
vHideWindowTimeOutCallback(gpointer data)541 gint FloatWin::vHideWindowTimeOutCallback(gpointer data)
542 {
543 	FloatWin *oFloatWin = static_cast<FloatWin *>(data);
544 	bool lock=
545 		conf->get_bool_at("floating_window/lock");
546 	if(lock || oFloatWin->ismoving || !gtk_widget_get_visible(GTK_WIDGET(oFloatWin->FloatWindow)))
547 		return true;
548 
549 	bool only_scan_while_modifier_key=
550 		conf->get_bool_at("dictionary/only_scan_while_modifier_key");
551 	bool hide_floatwin_when_modifier_key_released=
552 		conf->get_bool_at("dictionary/hide_floatwin_when_modifier_key_released");
553 	if (only_scan_while_modifier_key
554 		&& hide_floatwin_when_modifier_key_released
555 		&& !oFloatWin->IgnoreScanModifierKey) {
556 		GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(oFloatWin->FloatWindow));
557 		GdkDisplay *display = gdk_screen_get_display(screen);
558 		gint iCurrentX,iCurrentY;
559 #if GTK_MAJOR_VERSION >= 3
560 		GdkDeviceManager *device_manager = gdk_display_get_device_manager (display);
561 		GdkDevice  *pointer = gdk_device_manager_get_client_pointer (device_manager);
562 		gdk_device_get_position(pointer, NULL, &iCurrentX, &iCurrentY);
563 #else
564 		gdk_display_get_pointer(display, NULL, &iCurrentX, &iCurrentY, NULL);
565 #endif
566 		if (iCurrentX == oFloatWin->popup_pointer_x && iCurrentY==oFloatWin->popup_pointer_y) {
567 			bool released = !gpAppFrame->unlock_keys->is_pressed();
568 
569 			if (released) {
570 				oFloatWin->Hide();
571 				gpAppFrame->oSelection.LastClipWord.clear(); //so press modifier key again will pop up the floatwin.
572 				return true;
573 			}
574 		}
575 	}
576 
577 	if (oFloatWin->get_distance_pointer_to_window() > DISAPPEAR_DISTANCE){
578 		oFloatWin->Hide();
579 	}
580 
581 	return true;
582 }
583 
vLookupRunningTimeOutCallback(gpointer data)584 gint FloatWin::vLookupRunningTimeOutCallback(gpointer data)
585 {
586 	FloatWin *oFloatWin = static_cast<FloatWin *>(data);
587 	if(!oFloatWin->have_real_content) {
588 		if (conf->get_bool_at("floating_window/show_if_not_found")) {
589 			oFloatWin->Popup(!oFloatWin->window_positioned);
590 			oFloatWin->window_positioned = true;
591 		}
592 	}
593 	oFloatWin->lookup_running_timer = 0;
594 	return FALSE;
595 }
596 
vEnterNotifyCallback(GtkWidget * widget,GdkEventCrossing * event,FloatWin * oFloatWin)597 gboolean FloatWin::vEnterNotifyCallback (GtkWidget *widget, GdkEventCrossing *event, FloatWin *oFloatWin)
598 {
599 #ifdef _WIN32
600 	if ((event->detail==GDK_NOTIFY_ANCESTOR) || (event->detail==GDK_NOTIFY_NONLINEAR) || (event->detail==GDK_NOTIFY_NONLINEAR_VIRTUAL)) {
601 #else
602 	if ((event->detail==GDK_NOTIFY_NONLINEAR) || (event->detail==GDK_NOTIFY_NONLINEAR_VIRTUAL)) {
603 #endif
604 		if (!gtk_widget_get_visible(GTK_WIDGET(oFloatWin->button_hbox))) {
605 			gtk_widget_show(oFloatWin->button_hbox);
606 
607 			if (!oFloatWin->button_box_once_shown) {
608 				oFloatWin->button_box_once_shown = true;
609 				oFloatWin->button_box_show_first_time();
610 			}
611 		}
612 	}
613 	return true;
614 }
615 
616 gboolean FloatWin::vLeaveNotifyCallback (GtkWidget *widget, GdkEventCrossing *event, FloatWin *oFloatWin)
617 {
618 #ifdef _WIN32
619 	if (((event->detail==GDK_NOTIFY_ANCESTOR) || (event->detail==GDK_NOTIFY_NONLINEAR) || (event->detail==GDK_NOTIFY_NONLINEAR_VIRTUAL))&&(!oFloatWin->ismoving)) {
620 #else
621 	if (((event->detail==GDK_NOTIFY_NONLINEAR) || (event->detail==GDK_NOTIFY_NONLINEAR_VIRTUAL))&&(!oFloatWin->ismoving)) {
622 #endif
623 		gtk_widget_hide(oFloatWin->button_hbox);
624 	}
625 	return true;
626 }
627 
628 gboolean FloatWin::vButtonPressCallback (GtkWidget * widget, GdkEventButton * event , FloatWin *oFloatWin)
629 {
630 	if (event->type == GDK_BUTTON_PRESS) {
631 		if (event->button == 1) {
632 			gtk_window_get_position(GTK_WINDOW(widget),&(oFloatWin->press_window_x),&(oFloatWin->press_window_y));
633 			oFloatWin->press_x_root = (gint)(event->x_root);
634 			oFloatWin->press_y_root = (gint)(event->y_root);
635 			oFloatWin->ismoving = true;
636 		}
637 		else if (event->button == 3) {
638 			oFloatWin->show_popup_menu(event);
639 		}
640 	} else if (event->type == GDK_2BUTTON_PRESS) {
641 		if (oFloatWin->content_state == ContentState_NotFound) {
642 			std::string QueryingWord(oFloatWin->QueryingWord);
643 			gpAppFrame->LookupWithFuzzyToFloatWin(QueryingWord.c_str());
644 		} else
645 			oFloatWin->Hide();
646 	}
647 
648 	return true;
649 }
650 
651 gboolean FloatWin::vButtonReleaseCallback (GtkWidget * widget, GdkEventButton * event , FloatWin *oFloatWin)
652 {
653 	if (event->button == 1) {
654 		oFloatWin->ismoving = false;
655 	}
656 
657 	return true;
658 }
659 
660 gboolean FloatWin::vMotionNotifyCallback (GtkWidget * widget, GdkEventMotion * event , FloatWin *oFloatWin)
661 {
662 	if (event->state & GDK_BUTTON1_MASK) {
663 		gint x,y;
664 		x = oFloatWin->press_window_x + (gint)(event->x_root) - oFloatWin->press_x_root;
665 		y = oFloatWin->press_window_y + (gint)(event->y_root) - oFloatWin->press_y_root;
666 		if (x<0)
667 			x = 0;
668 		if (y<0)
669 			y = 0;
670 		gtk_window_move(GTK_WINDOW(oFloatWin->FloatWindow), x, y);
671 	}
672 
673 	return true;
674 }
675 
676 void FloatWin::on_menu_copy_activate(GtkWidget * widget, FloatWin *oFloatWin)
677 {
678 	GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
679 	gtk_clipboard_set_text(clipboard, oFloatWin->view->get_text().c_str(), -1);
680 }
681 
682 void FloatWin::on_menu_save_activate(GtkWidget * widget, FloatWin *oFloatWin)
683 {
684 	oFloatWin->on_save_click(widget, oFloatWin);
685 }
686 
687 void FloatWin::on_menu_query_activate(GtkWidget * widget, FloatWin *oFloatWin)
688 {
689 	oFloatWin->on_query_click(widget, oFloatWin);
690 }
691 
692 void FloatWin::on_menu_play_activate(GtkWidget * widget, FloatWin *oFloatWin)
693 {
694 	oFloatWin->on_play_click(widget, oFloatWin);
695 }
696 
697 void FloatWin::on_menu_fuzzyquery_activate(GtkWidget * widget, FloatWin *oFloatWin)
698 {
699 	/* do not use oFloatWin->QueryingWord directly,
700 	LookupWithFuzzyToFloatWin overwrite oFloatWin->QueryingWord */
701 	const std::string tQueryingWord(oFloatWin->QueryingWord);
702 	gpAppFrame->LookupWithFuzzyToFloatWin(tQueryingWord.c_str());
703 }
704 
705 void FloatWin::on_lock_changed(const baseconfval* )
706 {
707 	gtk_image_set_from_stock(GTK_IMAGE(LockImage),
708 		get_lock_image_stock_id(), GTK_ICON_SIZE_MENU);
709 }
710 
711 void FloatWin::on_dict_scan_select_changed(const baseconfval* scanval)
712 {
713 	bool scan = static_cast<const confval<bool> *>(scanval)->val_;
714 
715 	gtk_widget_set_sensitive(StopButton, scan);
716 }
717 
718 void FloatWin::on_lock_x_changed(const baseconfval* lock_x_val)
719 {
720 	int lock_x=static_cast<const confval<int> *>(lock_x_val)->val_;
721 	if (conf->get_bool_at("floating_window/lock")) {
722 		gint old_x, old_y;
723 		gtk_window_get_position(GTK_WINDOW(FloatWindow), &old_x, &old_y);
724 		if (lock_x!=old_x) {
725 			gtk_window_move(GTK_WINDOW(FloatWindow), lock_x, old_y);
726 		}
727 	}
728 }
729 
730 void FloatWin::on_lock_y_changed(const baseconfval* lock_y_val)
731 {
732 	int lock_y=static_cast<const confval<int> *>(lock_y_val)->val_;
733 
734 	if (conf->get_bool_at("floating_window/lock")) {
735 		gint old_x,old_y;
736 		gtk_window_get_position(GTK_WINDOW(FloatWindow), &old_x, &old_y);
737 		if (lock_y!=old_y) {
738 			gtk_window_move(GTK_WINDOW(FloatWindow), old_x, lock_y);
739 		}
740 	}
741 }
742 
743 void FloatWin::on_transparent_changed(const baseconfval* val)
744 {
745 	int transparent = static_cast<const confval<int> *>(val)->val_;
746 	set_transparent(transparent);
747 }
748 
749 void FloatWin::on_use_custom_bg_changed(const baseconfval* )
750 {
751 	set_bg();
752 }
753 
754 gint FloatWin::get_vscrollbar_width(void)
755 {
756 	static gint vscrollbar_width = 0;
757 	if (!vscrollbar_width) {
758 		if (view->vscroll_bar()) {
759 			GtkRequisition vscrollbar_requisition;
760 #if GTK_MAJOR_VERSION >= 3
761 			gtk_widget_get_preferred_size(view->vscroll_bar(), NULL, &vscrollbar_requisition);
762 #else
763 			gtk_widget_size_request(view->vscroll_bar(), &vscrollbar_requisition);
764 #endif
765 			vscrollbar_width = vscrollbar_requisition.width;
766 			vscrollbar_width += view->scroll_space();
767 		}
768 	}
769 	return vscrollbar_width;
770 }
771 
772 gint FloatWin::get_window_border_width(void)
773 {
774 	return 2*(FLOATWIN_BORDER_WIDTH+2); // 2 is the frame 's width. Or get it by gtk function? i am lazy,hoho
775 }
776 
777 void FloatWin::float_window_size(gint& window_width, gint& window_height)
778 {
779 	GtkRequisition requisition;
780 #if GTK_MAJOR_VERSION >= 3
781 	gtk_widget_get_preferred_size(view->widget(), NULL, &requisition);
782 #else
783 	gtk_widget_size_request(view->widget(), &requisition);
784 #endif
785 	int max_window_width=conf->get_int_at("floating_window/max_window_width");
786 	if (requisition.width > max_window_width) {
787 		// it is not really max window width setting.
788 		gtk_widget_set_size_request(view->widget(), max_window_width, -1);
789 		gtk_label_set_line_wrap(GTK_LABEL(view->widget()), true);
790 #if GTK_MAJOR_VERSION >= 3
791 		gtk_widget_get_preferred_size(view->widget(), NULL, &requisition); //update requisition
792 #else
793 		gtk_widget_size_request(view->widget(), &requisition); //update requisition
794 #endif
795 	}
796 	window_width = get_window_border_width() + requisition.width;
797 	int max_window_height=
798 		conf->get_int_at("floating_window/max_window_height");
799 	if (requisition.height > max_window_height) {
800 		const gint vscrollbar_width = get_vscrollbar_width();
801 		view->set_size(requisition.width + vscrollbar_width, max_window_height);
802 		window_height = get_window_border_width() + max_window_height;
803 		window_width += vscrollbar_width;
804 	} else {
805 		view->set_size(requisition.width, requisition.height);
806 		window_height = get_window_border_width() + requisition.height;
807 	}
808 
809 	gboolean button_hbox_visible = gtk_widget_get_visible(GTK_WIDGET(button_hbox));
810 	if (button_hbox_visible) {
811 		GtkAllocation allocation;
812 		gtk_widget_get_allocation(button_hbox, &allocation);
813 		window_height += allocation.height;
814 		if (window_width < allocation.width + get_window_border_width())
815 			window_width = allocation.width + get_window_border_width();
816 	}
817 }
818 
819 void FloatWin::float_window_position(gboolean usePointerPosition,
820 	gint window_width, gint window_height, gint& x, gint& y)
821 {
822 	GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(FloatWindow));
823 	if (usePointerPosition) {
824 		GdkDisplay *display = gdk_screen_get_display(screen);
825 #if GTK_MAJOR_VERSION >= 3
826 		GdkDeviceManager *device_manager = gdk_display_get_device_manager (display);
827 		GdkDevice  *pointer = gdk_device_manager_get_client_pointer (device_manager);
828 		gdk_device_get_position (pointer, NULL, &x, &y);
829 #else
830 		gdk_display_get_pointer(display, NULL, &x, &y, NULL);
831 #endif
832 		x += FLOATWIN_OFFSET_X;
833 		y += FLOATWIN_OFFSET_Y;
834 	} else {
835 		gtk_window_get_position(GTK_WINDOW(FloatWindow),&x,&y);
836 	}
837 	gint screen_width = gdk_screen_get_width(screen);
838 	gint screen_height = gdk_screen_get_height(screen);
839 	if (x + window_width > screen_width)
840 		x = screen_width - window_width;
841 	if (y + window_height > screen_height)
842 		y = screen_height - window_height;
843 }
844 
845 void FloatWin::remember_pointer_position(void)
846 {
847 	/* TODO: the user of this object should tell why the floating windows has to be shown. */
848 	bool pressed = gpAppFrame->unlock_keys->is_pressed();
849 
850 	if (pressed) {
851 		GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(FloatWindow));
852 		GdkDisplay *display = gdk_screen_get_display(screen);
853 #if GTK_MAJOR_VERSION >= 3
854 		GdkDeviceManager *device_manager = gdk_display_get_device_manager (display);
855 		GdkDevice  *pointer = gdk_device_manager_get_client_pointer (device_manager);
856 		gdk_device_get_position(pointer, NULL, &popup_pointer_x, &popup_pointer_y);
857 #else
858 		gdk_display_get_pointer(display, NULL, &popup_pointer_x, &popup_pointer_y, NULL);
859 #endif
860 	} else {
861 		// popup by middle click on the notification area icon,
862 		// so never hiden the floating window even mouse didn't moved as in FloatWin::vHideWindowTimeOutCallback().
863 		popup_pointer_x = -1;
864 		popup_pointer_y = -1;
865 	}
866 }
867 
868 void FloatWin::Popup(gboolean updatePosition)
869 {
870 	ismoving = true;
871 	gint window_width, window_height;
872 	float_window_size(window_width, window_height);
873 
874 	if (conf->get_bool_at("floating_window/lock")) {
875 		gtk_window_resize(GTK_WINDOW(FloatWindow),window_width,window_height);
876 		// need to make window's resize relate to other corner?
877 	} else {
878 		gint iCurrentX,iCurrentY;
879 		gboolean newPosition = (!gtk_widget_get_visible(GTK_WIDGET(FloatWindow))) || updatePosition;
880 		float_window_position(newPosition, window_width, window_height, iCurrentX, iCurrentY);
881 		if (newPosition) {
882 			button_box_once_shown = false;
883 			remember_pointer_position();
884 		}
885 		// don't use gdk_window_resize,it make the window can't be smaller latter!
886 		// note: must do resize before move should be better,as the vHideWindowTimeOutCallback() may hide it.
887 		gtk_window_resize(GTK_WINDOW(FloatWindow),window_width,window_height);
888 		gtk_window_move(GTK_WINDOW(FloatWindow),iCurrentX,iCurrentY);
889 	}
890 	now_window_width = window_width;
891 	now_window_height = window_height;
892 
893 	Show();
894 	ismoving = false;
895 }
896 
897 
898 void FloatWin::create_button_hbox(void)
899 {
900 #if GTK_MAJOR_VERSION >= 3
901 	button_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
902 #else
903 	button_hbox = gtk_hbox_new(false,0);
904 #endif
905 
906 	GtkWidget *button;
907 	button= gtk_button_new();
908 	gtk_container_add(GTK_CONTAINER(button),gtk_image_new_from_stock(GTK_STOCK_FIND,GTK_ICON_SIZE_MENU));
909 	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
910 	g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_query_click), this);
911 	g_signal_connect(G_OBJECT(button),"enter_notify_event", G_CALLBACK(stardict_on_enter_notify), NULL);
912 	gtk_box_pack_start(GTK_BOX(button_hbox),button,false,false,0);
913 	gtk_widget_set_tooltip_text(button,_("Query in the main window"));
914 
915 	button= gtk_button_new();
916 	gtk_container_add(GTK_CONTAINER(button),gtk_image_new_from_stock(GTK_STOCK_SAVE,GTK_ICON_SIZE_MENU));
917 	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
918 	g_signal_connect(G_OBJECT(button),"clicked",
919 			 G_CALLBACK(on_save_click), this);
920 	g_signal_connect(G_OBJECT(button),"enter_notify_event", G_CALLBACK(stardict_on_enter_notify), NULL);
921 	gtk_box_pack_start(GTK_BOX(button_hbox),button,false,false,0);
922 	gtk_widget_set_tooltip_text(button,_("Save to file"));
923 
924 	PronounceWordButton= gtk_button_new();
925 	gtk_container_add(GTK_CONTAINER(PronounceWordButton),gtk_image_new_from_stock(GTK_STOCK_EXECUTE,GTK_ICON_SIZE_MENU));
926 	gtk_button_set_relief (GTK_BUTTON (PronounceWordButton), GTK_RELIEF_NONE);
927 	g_signal_connect(G_OBJECT(PronounceWordButton),"clicked", G_CALLBACK(on_play_click), this);
928 	g_signal_connect(G_OBJECT(PronounceWordButton),"enter_notify_event", G_CALLBACK(stardict_on_enter_notify), NULL);
929 	gtk_box_pack_start(GTK_BOX(button_hbox),PronounceWordButton,false,false,0);
930 	gtk_widget_set_tooltip_text(PronounceWordButton,_("Pronounce the word"));
931 
932 	gtk_widget_set_sensitive(PronounceWordButton, false);
933 
934 	StopButton= gtk_button_new();
935 	gtk_container_add(GTK_CONTAINER(StopButton),gtk_image_new_from_stock(GTK_STOCK_STOP,GTK_ICON_SIZE_MENU));
936 	gtk_button_set_relief (GTK_BUTTON (StopButton), GTK_RELIEF_NONE);
937 	g_signal_connect(G_OBJECT(StopButton),"clicked", G_CALLBACK(on_stop_click), this);
938 	g_signal_connect(G_OBJECT(StopButton),"enter_notify_event", G_CALLBACK(stardict_on_enter_notify), NULL);
939 	gtk_box_pack_start(GTK_BOX(button_hbox),StopButton,false,false,0);
940 	gtk_widget_set_tooltip_text(StopButton, _("Stop selection-scanning"));
941 
942 	gtk_widget_set_sensitive(gpAppFrame->oFloatWin.StopButton,
943 													 conf->get_bool_at("dictionary/scan_selection"));
944 
945 #ifndef CONFIG_GPE
946 	button= gtk_button_new();
947 	gtk_container_add(GTK_CONTAINER(button),gtk_image_new_from_stock(GTK_STOCK_HELP,GTK_ICON_SIZE_MENU));
948 	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
949 	g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_help_click), this);
950 	g_signal_connect(G_OBJECT(button),"enter_notify_event", G_CALLBACK(stardict_on_enter_notify), NULL);
951 	gtk_box_pack_start(GTK_BOX(button_hbox),button,false,false,0);
952 	gtk_widget_set_tooltip_text(button,_("Help"));
953 
954 	button= gtk_button_new();
955 	gtk_container_add(GTK_CONTAINER(button),gtk_image_new_from_stock(GTK_STOCK_QUIT,GTK_ICON_SIZE_MENU));
956 	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
957 	g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(on_quit_click), this);
958 	g_signal_connect(G_OBJECT(button),"enter_notify_event", G_CALLBACK(stardict_on_enter_notify), NULL);
959 	gtk_box_pack_start(GTK_BOX(button_hbox),button,false,false,0);
960 	gtk_widget_set_tooltip_text(button,_("Quit"));
961 #endif
962 
963 	button = gtk_button_new();
964 
965 	LockImage= gtk_image_new_from_stock(get_lock_image_stock_id(),GTK_ICON_SIZE_MENU);
966 	gtk_container_add(GTK_CONTAINER(button),LockImage);
967 	gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
968 	g_signal_connect(G_OBJECT(button),"clicked", G_CALLBACK(vLockCallback),this);
969 	g_signal_connect(G_OBJECT(button),"enter_notify_event", G_CALLBACK(stardict_on_enter_notify), NULL);
970 	gtk_box_pack_end(GTK_BOX(button_hbox),button,false,false,0);
971 	gtk_widget_set_tooltip_text(button,_("Lock floating window"));
972 }
973 
974 int FloatWin::get_distance_pointer_to_window(void)
975 {
976 	GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(FloatWindow));
977 	GdkDisplay *display = gdk_screen_get_display(screen);
978 	gint iCurrentX,iCurrentY;
979 #if GTK_MAJOR_VERSION >= 3
980 	GdkDeviceManager *device_manager = gdk_display_get_device_manager (display);
981 	GdkDevice  *pointer = gdk_device_manager_get_client_pointer (device_manager);
982 	gdk_device_get_position(pointer, NULL, &iCurrentX, &iCurrentY);
983 #else
984 	gdk_display_get_pointer(display, NULL, &iCurrentX, &iCurrentY, NULL);
985 #endif
986 	gint window_x,window_y,window_width,window_height;
987 	gtk_window_get_position(GTK_WINDOW(FloatWindow),&window_x,&window_y);
988 	//notice: gtk_window_get_size() is not really uptodate,don't use it! see "gtk reference".
989 	//gtk_window_get_size(GTK_WINDOW(FloatWindow),&window_width,&window_height);
990 	window_width = now_window_width;
991 	window_height = now_window_height;
992 
993 	int distance;
994 	if (iCurrentX < window_x) {
995 		distance = (iCurrentX-window_x)*(iCurrentX-window_x);
996 		if (iCurrentY < window_y)
997 			distance += (iCurrentY-window_y)*(iCurrentY-window_y);
998 		else if (iCurrentY > window_y+window_height)
999 			distance += (iCurrentY-window_y-window_height)*(iCurrentY-window_y-window_height);
1000 	} else if (iCurrentX > window_x+window_width) {
1001 		distance = (iCurrentX-window_x-window_width)*(iCurrentX-window_x-window_width);
1002 		if (iCurrentY < window_y)
1003 			distance += (iCurrentY-window_y)*(iCurrentY-window_y);
1004 		else if ( iCurrentY > window_y+window_height )
1005 			distance += (iCurrentY-window_y-window_height)*(iCurrentY-window_y-window_height);
1006 	} else {
1007 		if (iCurrentY < window_y)
1008 			distance = (window_y - iCurrentY)*(window_y - iCurrentY);
1009 		else if (iCurrentY > window_y+window_height)
1010 			distance = (iCurrentY-window_y-window_height)*(iCurrentY-window_y-window_height);
1011 		else
1012 			distance = 0; //in the floating window.
1013 	}
1014 	return distance;
1015 }
1016 
1017 void FloatWin::button_box_show_first_time(void)
1018 {
1019 	gint iCurrentX,iCurrentY;
1020 	gtk_window_get_position(GTK_WINDOW(FloatWindow),&iCurrentX,&iCurrentY);
1021 	GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(FloatWindow));
1022 	gint screen_width = gdk_screen_get_width(screen);
1023 	gint screen_height = gdk_screen_get_height(screen);
1024 	GtkRequisition requisition;
1025 #if GTK_MAJOR_VERSION >= 3
1026 	gtk_widget_get_preferred_size(button_hbox, NULL, &requisition);
1027 #else
1028 	gtk_widget_size_request(button_hbox,&requisition);
1029 #endif
1030 	now_window_height += requisition.height;
1031 	requisition.width += get_window_border_width();
1032 	if (requisition.width > now_window_width)
1033 		now_window_width = requisition.width;
1034 	bool changed=false;
1035 	if (iCurrentX < 0) {
1036 		iCurrentX = 0;
1037 		changed = true;
1038 	} else if (iCurrentX + now_window_width > screen_width) {
1039 		iCurrentX = screen_width - now_window_width;
1040 		changed = true;
1041 	}
1042 	if (iCurrentY < 0) {
1043 		iCurrentY = 0;
1044 		changed = true;
1045 	} else if (iCurrentY + now_window_height > screen_height) {
1046 		iCurrentY = screen_height - now_window_height;
1047 		changed = true;
1048 	}
1049 	if (changed) {
1050 		gtk_window_move(GTK_WINDOW(FloatWindow),iCurrentX,iCurrentY);
1051 	}
1052 }
1053 
1054 void FloatWin::show_popup_menu(GdkEventButton * event)
1055 {
1056 	if (menu)
1057 		gtk_widget_destroy(menu);
1058 	menu = gtk_menu_new();
1059 
1060 	GtkWidget *menuitem;
1061 	menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Copy"));
1062 	GtkWidget *image;
1063 	image = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
1064 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
1065 	gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
1066 	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(on_menu_copy_activate), this);
1067 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1068 	menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Save"));
1069 	image = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU);
1070 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
1071 	gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
1072 	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(on_menu_save_activate), this);
1073 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1074 	menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Query"));
1075 	image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
1076 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
1077 	gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
1078 	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(on_menu_query_activate), this);
1079 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1080 
1081 	if (gtk_widget_get_sensitive(GTK_WIDGET(PronounceWordButton))) {
1082 		menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Play"));
1083 		image = gtk_image_new_from_stock(GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU);
1084 		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
1085 		gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
1086 		g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(on_menu_play_activate), this);
1087 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1088 	}
1089 
1090 	menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Fuzzy query"));
1091 	image = gtk_image_new_from_stock(GTK_STOCK_FIND_AND_REPLACE, GTK_ICON_SIZE_MENU);
1092 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
1093 	gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
1094 	g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(on_menu_fuzzyquery_activate), this);
1095 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1096 
1097 	gtk_widget_show_all(menu);
1098 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
1099 	play_sound_on_event("menushow");
1100 }
1101 
1102 void FloatWin::set_transparent(int transparent)
1103 {
1104 	g_assert(0 <= transparent && transparent <= 100);
1105 	gtk_window_set_opacity(GTK_WINDOW(FloatWindow), (100-transparent)/100.0);
1106 }
1107 
1108 void FloatWin::set_bg(void)
1109 {
1110 #if GTK_MAJOR_VERSION >= 3
1111 	GdkRGBA color;
1112 	const GdkRGBA *pcolor = NULL;
1113 	if (conf->get_bool_at("floating_window/use_custom_bg")) {
1114 		color.red = conf->get_double_at("floating_window/bg_red");
1115 		color.green = conf->get_double_at("floating_window/bg_green");
1116 		color.blue = conf->get_double_at("floating_window/bg_blue");
1117 		color.alpha = 1;
1118 		pcolor = &color;
1119 	}
1120 	gtk_widget_override_background_color(FloatWindow, GTK_STATE_FLAG_NORMAL, pcolor);
1121 	view->modify_bg(GTK_STATE_FLAG_NORMAL, pcolor);
1122 #else
1123 	GdkColor color;
1124 	const GdkColor *pcolor = NULL;
1125 	if (conf->get_bool_at("floating_window/use_custom_bg")) {
1126 		color.red = conf->get_int_at("floating_window/bg_red");
1127 		color.green = conf->get_int_at("floating_window/bg_green");
1128 		color.blue = conf->get_int_at("floating_window/bg_blue");
1129 		pcolor = &color;
1130 	}
1131 	gtk_widget_modify_bg(FloatWindow, GTK_STATE_NORMAL, pcolor);
1132 	view->modify_bg(GTK_STATE_NORMAL, pcolor);
1133 #endif
1134 }
1135 
1136 void FloatWin::set_bookname_style(BookNameStyle style)
1137 {
1138 	if (view.get()) {
1139 		view->set_bookname_style(style);
1140 	}
1141 }
1142 
1143 const gchar* FloatWin::get_lock_image_stock_id(void)
1144 {
1145 	if (conf->get_bool_at("floating_window/lock"))
1146 		return GTK_STOCK_GOTO_LAST;
1147 	else
1148 		return GTK_STOCK_GO_FORWARD;
1149 }
1150 
1151 /* Restore locked position of the window. When may this be usefull?
1152 The floating window is hiden initially. */
1153 void FloatWin::restore_locked_position(void)
1154 {
1155 	GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(FloatWindow));
1156 	gint screen_width = gdk_screen_get_width(screen);
1157 	gint screen_height = gdk_screen_get_height(screen);
1158 	int max_window_width=
1159 		conf->get_int_at("floating_window/max_window_width");
1160 	int max_window_height=
1161 		conf->get_int_at("floating_window/max_window_height");
1162 	if (max_window_width < MIN_MAX_FLOATWIN_WIDTH ||
1163 			max_window_width > screen_width)
1164 		conf->set_int_at("floating_window/max_window_width",
1165 									DEFAULT_MAX_FLOATWIN_WIDTH);
1166 	if (max_window_height < MIN_MAX_FLOATWIN_HEIGHT ||
1167 			max_window_height > screen_height)
1168 		conf->set_int_at("floating_window/max_window_height", DEFAULT_MAX_FLOATWIN_HEIGHT);
1169 
1170 	max_window_width=
1171 		conf->get_int_at("floating_window/max_window_width");
1172 	max_window_height=
1173 		conf->get_int_at("floating_window/max_window_height");
1174 
1175 	int lock_x=
1176 		conf->get_int_at("floating_window/lock_x");
1177 	int lock_y=
1178 		conf->get_int_at("floating_window/lock_y");
1179 	if (lock_x<0)
1180 		lock_x=0;
1181 	else if (lock_x > (screen_width - max_window_width))
1182 		lock_x = screen_width - max_window_width;
1183 	if (lock_y<0)
1184 		lock_y=0;
1185 	else if (lock_y > (screen_height - max_window_height))
1186 		lock_y = screen_height - max_window_height;
1187 	gtk_window_move(GTK_WINDOW(FloatWindow),lock_x,lock_y);
1188 }
1189 
1190 std::string FloatWin::get_not_found_markup(const gchar* sWord, const gchar* sReason)
1191 {
1192 	glib::CharStr str(g_markup_printf_escaped(
1193 		"<b><big>%s</big></b>\n<span foreground=\"blue\">%s</span>", sWord, sReason));
1194 	return get_impl(str);
1195 }
1196 
1197 std::string FloatWin::get_looking_up_markup(const gchar* sWord)
1198 {
1199 	glib::CharStr str(g_markup_printf_escaped(
1200 		"<b><big>%s</big></b>\n<span foreground=\"blue\">%s</span>", sWord, _("Looking up...")));
1201 	return get_impl(str);
1202 }
1203 
1204 std::string FloatWin::get_head_word_markup(const gchar* sWord)
1205 {
1206 	std::string markup = "<b><span size=\"x-large\">";
1207 	glib::CharStr str(g_markup_escape_text(sWord, -1));
1208 	markup += get_impl(str);
1209 	markup += "</span></b>";
1210 	return markup;
1211 }
1212 
1213 void FloatWin::set_busy_cursor(void)
1214 {
1215 	gdk_window_set_cursor(gtk_widget_get_window(FloatWindow), get_impl(gpAppFrame->oAppSkin.watch_cursor));
1216 }
1217 
1218 void FloatWin::set_normal_cursor(void)
1219 {
1220 	gdk_window_set_cursor(gtk_widget_get_window(FloatWindow), get_impl(gpAppFrame->oAppSkin.normal_cursor));
1221 }
1222 
1223 void FloatWin::start_hide_window_timer(void)
1224 {
1225 	if (!hide_window_timer)
1226 		hide_window_timer = g_timeout_add(FLOAT_TIMEOUT, vHideWindowTimeOutCallback, this);
1227 }
1228 
1229 void FloatWin::destroy_hide_window_timer(void)
1230 {
1231 	if (hide_window_timer) {
1232 		g_source_remove(hide_window_timer);
1233 		hide_window_timer = 0;
1234 	}
1235 }
1236 
1237 void FloatWin::start_lookup_running_timer(void)
1238 {
1239 	if (!lookup_running_timer)
1240 		lookup_running_timer = g_timeout_add(LOOKUP_RUNNING_TIMEOUT, vLookupRunningTimeOutCallback, this);
1241 }
1242 
1243 void FloatWin::destroy_lookup_running_timer(void)
1244 {
1245 	if (lookup_running_timer) {
1246 		g_source_remove(lookup_running_timer);
1247 		lookup_running_timer = 0;
1248 	}
1249 }
1250