12 #define SELECTION_TIMEOUT 300
13 #define POPUPWND_TIMEOUT 1000
14 #define DISTANCE_OFFSET 20
16 Popupwnd *_popupwnd = NULL;
18 G_DEFINE_TYPE(Popupwnd, popupwnd, GTK_TYPE_WINDOW);
20 static void popupwnd_set_property(GObject *object, guint param_id, const GValue *value, GParamSpec *pspec)
22 Popupwnd *pw = POPUPWND(object);
27 pw->mainwnd = g_value_get_pointer(value);
28 Mainwnd *mw = MAINWND(pw->mainwnd);
29 pw->prefs = mw->prefs;
30 pw->builder = mw->builder;
31 GtkWidget *vbox = GTK_WIDGET(gtk_builder_get_object(GTK_BUILDER(pw->builder), "vbox9"));
32 gtk_widget_reparent(vbox, GTK_WIDGET(pw));
33 GtkContainer *scroll = GTK_CONTAINER(gtk_builder_get_object(GTK_BUILDER(pw->builder), "popupwnd_scroll"));
34 pw->text = g_object_new(TYPE_TEXTVIEW, "prefs", pw->prefs, NULL);
35 gtk_container_add(scroll, GTK_WIDGET(pw->text));
38 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
43 static gboolean popuwnd_hide_timeout_cb(gpointer data)
45 Popupwnd *pw = POPUPWND(data);
46 GtkWidget *wt = GTK_WIDGET(pw);
48 if(popupwnd_locked(pw))
50 if(!gtk_widget_get_visible(wt))
55 if(gtk_widget_get_visible(pw->menu))
59 int x, y, w, h, cx, cy;
60 gdk_window_get_root_origin(wt->window, &x, &y);
61 w = wt->allocation.width;
62 h = wt->allocation.height;
66 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(pw));
67 GdkDisplay *display = gdk_screen_get_display(screen);
68 gdk_display_get_pointer(display, NULL, &x, &y, NULL);
70 if((abs(x - cx) < (w/2 + DISTANCE_OFFSET)) && (abs(y - cy) < (h/2 + DISTANCE_OFFSET)))
77 static void popupwnd_timer(Popupwnd *self)
79 static gint timeout_id = 0;
81 g_source_remove(timeout_id);
83 timeout_id = g_timeout_add(POPUPWND_TIMEOUT, popuwnd_hide_timeout_cb, self);
86 static void popupwnd_class_init(PopupwndClass *klass)
88 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
89 gobject_class->set_property = popupwnd_set_property;
90 g_object_class_install_property(gobject_class, 1, g_param_spec_pointer("mainwnd", _("Mainwnd"), _("Mainwnd"), G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
93 static void popupwnd_init(Popupwnd *self)
100 self->timeout_id = 0;
101 self->lookup_started = False;
102 self->lookup_suspended = True;
106 self->results = g_sequence_new(result_free);
109 gtk_window_set_position(GTK_WINDOW(self), GTK_WIN_POS_CENTER);
112 const gchar* popupwnd_selection_copy(Popupwnd *self, gboolean force_copy)
114 GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
115 gchar *str = gtk_clipboard_wait_for_text(clipboard);
116 static gchar *sel = NULL, *sel_prev = NULL;
117 gint maxchar = prefs_get_int(self->prefs, "selection.maxchar");
118 if(!str || (self->lookup_suspended && !force_copy))
126 if(strlen(str) > maxchar)
143 if(strncmp(str, sel, maxchar))
150 if((sel_prev) ? !strncmp(sel_prev, sel, maxchar) : FALSE)
158 static void popupwnd_dict_changed_cb(GtkWidget *w, gpointer data)
160 _popupwnd->binfo = (BOOK_INFO*)data;
161 popupwnd_search(_popupwnd, NULL);
164 static gboolean popupwnd_dict_menu_append(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
166 Popupwnd *pw = POPUPWND(data);
167 static GSList *group = NULL;
168 static GtkWidget *submenu = NULL;
173 gtk_widget_destroy(pw->menu);
175 pw->menu = gtk_menu_new();
176 item = gtk_radio_menu_item_new_with_label(group, _("Main window selection"));
177 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(popupwnd_dict_changed_cb), NULL);
178 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
179 gtk_menu_shell_append(GTK_MENU_SHELL(pw->menu), item);
180 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
184 if(gtk_tree_path_get_depth(path) == 1)
187 submenu = gtk_menu_new();
188 gtk_tree_model_get(model, iter, DICT_ALIAS, &name, -1);
189 gtk_menu_shell_append(GTK_MENU_SHELL(pw->menu), item = gtk_menu_item_new_with_label(name));
190 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
197 gtk_tree_model_get(model, iter, DICT_ALIAS, &name, DICT_BINFO, &binfo, -1);
198 item = gtk_radio_menu_item_new_with_label(group, binfo->title);
199 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
200 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
201 if(binfo == pw->binfo)
202 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
203 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(popupwnd_dict_changed_cb), binfo);
209 static void popupwnd_dict_menu_create(Popupwnd *self, gboolean newmenu)
211 if(newmenu || !self->menu)
213 popupwnd_dict_menu_append(NULL, NULL, NULL, self);
214 GtkTreeModel *store = GTK_TREE_MODEL(gtk_builder_get_object(GTK_BUILDER(self->builder), "dicts_store"));
215 gtk_tree_model_foreach(GTK_TREE_MODEL(store), popupwnd_dict_menu_append, self);
219 void popupwnd_dict_btn_clicked_cb(GtkWidget *widget, gpointer data)
221 Mainwnd *mw = MAINWND(data);
222 Popupwnd *pw = mw->popupwnd;
223 popupwnd_dict_menu_create(pw, False);
224 gtk_widget_show_all(pw->menu);
225 gtk_menu_popup(GTK_MENU(pw->menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
228 void popupwnd_render(gpointer data, gpointer user_data)
230 Popupwnd *pw = POPUPWND(user_data);
231 const gchar *word = builder_get_str(pw->builder, "popupwnd_label");
232 RESULT *res = (RESULT*)data;
233 textview_open(pw->text, res, FALSE, word);
236 static void popupwnd_calculate_coordinates(Popupwnd *self, gint *x, gint *y, gint *w, gint *h)
238 GdkModifierType mask;
239 gint pointer_x, pointer_y, root_x, root_y;
241 *w = prefs_get_int(self->prefs, "popupwnd.w");
242 *h = prefs_get_int(self->prefs, "popupwnd.h");
243 *w = (*w > 100) ? *w : 100;
244 *h = (*h > 100) ? *h : 100;
245 gboolean remember_pos = prefs_get_int(self->prefs, "popupwnd.remember_pos");
246 if(remember_pos && (self->x >= 0) && (self->y >= 0))
253 GdkWindow *root_win = gdk_x11_window_foreign_new_for_display(gdk_display_get_default(), GDK_ROOT_WINDOW());
254 root_x = gdk_window_get_width(root_win);
255 root_y = gdk_window_get_height(root_win);
256 gdk_window_get_pointer(root_win, &pointer_x, &pointer_y, &mask);
268 void popupwnd_search(Popupwnd *self, const gchar *str)
270 GtkLabel *label = GTK_LABEL(gtk_builder_get_object(GTK_BUILDER(self->builder), "popupwnd_label"));
271 popupwnd_dict_menu_create(self, False);
272 textview_clear_textbuf(GTK_TEXT_VIEW(self->text));
274 gtk_label_set_text(label, str);
277 str = gtk_label_get_text(label);
279 textview_insert_message(self->text, "<No text selected>", TRUE);
281 if(!gtk_widget_get_visible(GTK_WIDGET(self)))
284 popupwnd_calculate_coordinates(self, &x, &y, &w, &h);
285 gtk_window_move(GTK_WINDOW(self), x, y);
286 gtk_widget_set_size_request(GTK_WIDGET(self), w, h);
289 popupwnd_timer(self);
290 gtk_widget_show_all(GTK_WIDGET(self));
291 while(gtk_events_pending())
292 gtk_main_iteration();
297 gint search_method = prefs_get_int(self->prefs, "popupwnd.search_method");
298 gint maxhits = prefs_get_int(self->prefs, "popupwnd.maxhits");
300 ebook_search_book(str, search_method, self->results, maxhits, self->binfo);
302 ebook_search(str, search_method, self->results, maxhits, False);
304 if(g_sequence_get_length(self->results))
305 g_sequence_foreach(self->results, popupwnd_render, self);
307 textview_insert_message(self->text, "<No hits>", TRUE);
310 gboolean popupwnd_search_cb(gpointer data)
312 Popupwnd *self = POPUPWND(data);
313 const gchar *sel = popupwnd_selection_copy(self, False);
316 gint jap_only = prefs_get_int(self->prefs, "popupwnd.jap_only");
318 if(!validate_utf8(sel, True))
320 popupwnd_search(self, sel);
325 void popupwnd_lookup_resume(Popupwnd *self)
327 if(self->lookup_started)
329 if(self->timeout_id != 0)
330 g_source_remove(self->timeout_id);
331 self->timeout_id = g_timeout_add(SELECTION_TIMEOUT, popupwnd_search_cb, self);
333 self->lookup_suspended = FALSE;
336 void popupwnd_lookup_start(Popupwnd *self)
338 self->lookup_started = TRUE;
339 popupwnd_lookup_resume(self);
342 void popupwnd_lookup_suspend(Popupwnd *self)
344 if(self->timeout_id != 0)
345 g_source_remove(self->timeout_id);
346 self->timeout_id = 0;
347 self->lookup_suspended = TRUE;
348 gtk_widget_hide(GTK_WIDGET(self));
351 void popupwnd_lookup_stop(Popupwnd *self)
353 self->lookup_started = False;
354 popupwnd_lookup_suspend(self);
357 void popupwnd_find_btn_clicked_cb(GtkWidget *widget, gpointer data)
359 Mainwnd *mw = MAINWND(data);
360 const gchar *str = builder_get_str(mw->builder, "popupwnd_label");
361 gtk_window_deiconify(GTK_WINDOW(mw));
362 mainwnd_search(mw, str, -1);
365 void popupwnd_close_btn_clicked_cb(GtkWidget *widget, gpointer data)
367 Mainwnd *mw = MAINWND(data);
368 Popupwnd *self = mw->popupwnd;
369 gtk_widget_hide(GTK_WIDGET(self));
372 gint popupwnd_evtbox_motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
374 Mainwnd *mw = MAINWND(data);
375 Popupwnd *self = mw->popupwnd;
378 gint x = self->dx + event->x_root;
379 gint y = self->dy + event->y_root;
382 gtk_window_move(GTK_WINDOW(self), x, y);
388 gint popupwnd_evtbox_button_press_event_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
390 Mainwnd *mw = MAINWND(data);
391 Popupwnd *self = mw->popupwnd;
392 if(event->type==GDK_2BUTTON_PRESS)
395 popupwnd_lookup_stop(self);
403 gtk_window_get_position(GTK_WINDOW(self), &wx, &wy);
410 gint popupwnd_evtbox_button_release_event_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
412 Mainwnd *mw = MAINWND(data);
413 Popupwnd *self = mw->popupwnd;
418 void popupwnd_search_selection(gpointer data)
420 Mainwnd *mw = MAINWND(data);
421 const gchar *word = popupwnd_selection_copy(mw->popupwnd, True);
422 popupwnd_search(mw->popupwnd, word);
425 gboolean popupwnd_locked(Popupwnd *self)
427 if(!gtk_widget_get_visible(GTK_WIDGET(self)))
429 GtkToggleButton *btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(GTK_BUILDER(self->builder), "popupwnd_lock_btn"));
430 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn));