Updated Spanish translation
[anjuta-git-plugin.git] / plugins / document-manager / search-box.c
blob4d6fae385492f491329e6015bdc0a9fac481cc46
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) Johannes Schmid 2007 <jhs@gnome.org>
5 *
6 * anjuta is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
13 * anjuta is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with anjuta. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include <glib/gi18n.h>
26 #include "search-box.h"
28 #include <gtk/gtkbutton.h>
29 #include <gtk/gtkcheckbutton.h>
30 #include <gtk/gtkimage.h>
31 #include <gtk/gtkentry.h>
33 #include <libanjuta/anjuta-shell.h>
34 #include <libanjuta/anjuta-status.h>
35 #include <libanjuta/anjuta-debug.h>
37 #include <libanjuta/interfaces/ianjuta-editor.h>
38 #include <libanjuta/interfaces/ianjuta-editor-search.h>
39 #include <libanjuta/interfaces/ianjuta-editor-selection.h>
41 #define ANJUTA_STOCK_GOTO_LINE "anjuta-goto-line"
43 typedef struct _SearchBoxPrivate SearchBoxPrivate;
45 struct _SearchBoxPrivate
47 GtkWidget* search_entry;
48 GtkWidget* case_check;
49 GtkWidget* search_button;
50 GtkWidget* close_button;
52 GtkWidget* goto_entry;
53 GtkWidget* goto_button;
55 IAnjutaEditor* current_editor;
56 AnjutaStatus* status;
58 /* Incremental search */
59 IAnjutaIterable* last_start;
62 #ifdef GET_PRIVATE
63 # undef GET_PRIVATE
64 #endif
65 #define GET_PRIVATE(o) \
66 (G_TYPE_INSTANCE_GET_PRIVATE((o), SEARCH_TYPE_BOX, SearchBoxPrivate))
68 G_DEFINE_TYPE (SearchBox, search_box, GTK_TYPE_HBOX);
70 static void
71 on_search_box_hide (GtkWidget* button, SearchBox* search_box)
73 gtk_widget_hide (GTK_WIDGET (search_box));
76 static void
77 on_document_changed (AnjutaDocman* docman, IAnjutaDocument* doc,
78 SearchBox* search_box)
80 SearchBoxPrivate* private = GET_PRIVATE(search_box);
82 if (!doc || !IANJUTA_IS_EDITOR (doc))
84 gtk_widget_hide (GTK_WIDGET (search_box));
85 private->current_editor = NULL;
87 else
89 private->current_editor = IANJUTA_EDITOR (doc);
93 static void
94 on_goto_activated (GtkWidget* widget, SearchBox* search_box)
96 SearchBoxPrivate* private = GET_PRIVATE(search_box);
97 const gchar* str_line = gtk_entry_get_text (GTK_ENTRY (private->goto_entry));
99 gint line = atoi (str_line);
100 if (line > 0)
102 ianjuta_editor_goto_line (private->current_editor, line, NULL);
106 static void
107 search_box_set_entry_color (SearchBox* search_box, gboolean found)
109 SearchBoxPrivate* private = GET_PRIVATE(search_box);
110 if (!found)
112 GdkColor red;
113 GdkColor white;
115 /* FIXME: a11y and theme */
117 gdk_color_parse ("#FF6666", &red);
118 gdk_color_parse ("white", &white);
120 gtk_widget_modify_base (private->search_entry,
121 GTK_STATE_NORMAL,
122 &red);
123 gtk_widget_modify_text (private->search_entry,
124 GTK_STATE_NORMAL,
125 &white);
127 else
129 gtk_widget_modify_base (private->search_entry,
130 GTK_STATE_NORMAL,
131 NULL);
132 gtk_widget_modify_text (private->search_entry,
133 GTK_STATE_NORMAL,
134 NULL);
138 static gboolean
139 on_goto_key_pressed (GtkWidget* entry, GdkEventKey* event, SearchBox* search_box)
141 SearchBoxPrivate* private = GET_PRIVATE(search_box);
142 switch (event->keyval)
144 case GDK_0:
145 case GDK_1:
146 case GDK_2:
147 case GDK_3:
148 case GDK_4:
149 case GDK_5:
150 case GDK_6:
151 case GDK_7:
152 case GDK_8:
153 case GDK_9:
154 case GDK_KP_0:
155 case GDK_KP_1:
156 case GDK_KP_2:
157 case GDK_KP_3:
158 case GDK_KP_4:
159 case GDK_KP_5:
160 case GDK_KP_6:
161 case GDK_KP_7:
162 case GDK_KP_8:
163 case GDK_KP_9:
164 case GDK_Return:
165 case GDK_KP_Enter:
166 case GDK_BackSpace:
167 case GDK_Delete:
169 /* This is a number or enter which is ok */
170 break;
172 case GDK_Escape:
174 gtk_widget_hide (GTK_WIDGET (search_box));
175 search_box_set_entry_color (search_box, TRUE);
176 if (private->current_editor)
178 ianjuta_document_grab_focus (IANJUTA_DOCUMENT (private->current_editor),
179 NULL);
182 default:
184 /* Not a number */
185 gdk_beep ();
186 return TRUE;
189 return FALSE;
192 static gboolean
193 on_entry_key_pressed (GtkWidget* entry, GdkEventKey* event, SearchBox* search_box)
195 SearchBoxPrivate* private = GET_PRIVATE(search_box);
196 switch (event->keyval)
198 case GDK_Escape:
200 gtk_widget_hide (GTK_WIDGET (search_box));
201 search_box_set_entry_color (search_box, TRUE);
202 if (private->current_editor)
204 ianjuta_document_grab_focus (IANJUTA_DOCUMENT (private->current_editor),
205 NULL);
208 default:
210 /* Do nothing... */
213 return FALSE;
216 static gboolean
217 on_search_focus_out (GtkWidget* widget, GdkEvent* event, SearchBox* search_box)
219 SearchBoxPrivate* private = GET_PRIVATE(search_box);
220 if (private->last_start)
222 g_object_unref (private->last_start);
223 private->last_start = NULL;
225 anjuta_status_pop (private->status);
227 return FALSE;
230 static void
231 on_incremental_search (GtkWidget* widget, SearchBox* search_box)
233 IAnjutaEditorCell* search_start;
234 IAnjutaEditorCell* search_end;
235 IAnjutaEditorCell* result_start;
236 IAnjutaEditorCell* result_end;
237 IAnjutaEditorSelection* selection;
238 SearchBoxPrivate* private = GET_PRIVATE(search_box);
239 gboolean case_sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (private->case_check));
240 const gchar* search_text = gtk_entry_get_text (GTK_ENTRY (private->search_entry));
241 gboolean found = FALSE;
243 if (!private->current_editor || !search_text || !strlen (search_text))
244 return;
246 if (private->last_start)
248 search_start = IANJUTA_EDITOR_CELL (private->last_start);
250 else
252 search_start =
253 IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (private->current_editor,
254 NULL));
257 search_end = IANJUTA_EDITOR_CELL (ianjuta_editor_get_end_position (private->current_editor,
258 NULL));
260 if (ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (private->current_editor),
261 search_text, case_sensitive,
262 search_start, search_end,
263 &result_start,
264 &result_end, NULL))
266 found = TRUE;
267 anjuta_status_pop (ANJUTA_STATUS (private->status));
269 if (found)
271 selection = IANJUTA_EDITOR_SELECTION (private->current_editor);
272 ianjuta_editor_selection_set (selection,
273 IANJUTA_ITERABLE (result_start),
274 IANJUTA_ITERABLE (result_end), NULL);
275 g_object_unref (result_start);
276 g_object_unref (result_end);
278 private->last_start = IANJUTA_ITERABLE (search_start);
280 else
282 g_object_unref (search_start);
283 private->last_start = NULL;
286 search_box_set_entry_color (search_box, found);
287 g_object_unref (search_end);
290 void
291 on_search_activated (GtkWidget* widget, SearchBox* search_box)
293 IAnjutaEditorCell* search_start;
294 IAnjutaIterable* real_start;
295 IAnjutaEditorCell* search_end;
296 IAnjutaEditorCell* result_start;
297 IAnjutaEditorCell* result_end;
298 IAnjutaEditorSelection* selection;
299 SearchBoxPrivate* private = GET_PRIVATE(search_box);
300 gboolean case_sensitive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (private->case_check));
301 const gchar* search_text = gtk_entry_get_text (GTK_ENTRY (private->search_entry));
302 gboolean found = FALSE;
304 if (!private->current_editor || !search_text || !strlen (search_text))
305 return;
307 selection = IANJUTA_EDITOR_SELECTION (private->current_editor);
309 if (ianjuta_editor_selection_has_selection (selection, NULL))
311 search_start =
312 IANJUTA_EDITOR_CELL (ianjuta_editor_selection_get_start (selection, NULL));
314 else
316 search_start =
317 IANJUTA_EDITOR_CELL (ianjuta_editor_get_position (private->current_editor,
318 NULL));
320 real_start =
321 ianjuta_iterable_clone (IANJUTA_ITERABLE (search_start), NULL);
323 search_end = IANJUTA_EDITOR_CELL (ianjuta_editor_get_end_position (private->current_editor,
324 NULL));
326 /* If a search_result is already selected, move the search start
327 * forward by one
329 if (ianjuta_editor_selection_has_selection (selection,
330 NULL))
332 IAnjutaIterable* selection_start =
333 ianjuta_editor_selection_get_start (selection, NULL);
334 if (ianjuta_iterable_compare (IANJUTA_ITERABLE (search_start),
335 selection_start, NULL) == 0)
337 gchar* selected_text =
338 ianjuta_editor_selection_get (selection, NULL);
339 if (case_sensitive)
341 if (g_str_has_prefix (selected_text, search_text))
343 ianjuta_iterable_next (IANJUTA_ITERABLE (search_start), NULL);
346 else if (strlen (selected_text) >= strlen (search_text))
348 gchar* selected_up = g_utf8_casefold (selected_text, strlen (search_text));
349 gchar* search_text_up = g_utf8_casefold (search_text, strlen (search_text));
350 if (g_str_equal (selected_up, search_text_up))
352 ianjuta_iterable_next (IANJUTA_ITERABLE (search_start), NULL);
354 g_free (selected_up);
355 g_free (search_text_up);
357 g_free (selected_text);
361 if (ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (private->current_editor),
362 search_text, case_sensitive,
363 search_start, search_end,
364 &result_start,
365 &result_end, NULL))
367 found = TRUE;
368 anjuta_status_pop (ANJUTA_STATUS (private->status));
370 else
372 /* Try to continue on top */
373 ianjuta_iterable_first (IANJUTA_ITERABLE (search_start), NULL);
374 if (ianjuta_editor_search_forward (IANJUTA_EDITOR_SEARCH (private->current_editor),
375 search_text, case_sensitive,
376 search_start, search_end,
377 &result_start,
378 &result_end, NULL))
380 if (ianjuta_iterable_compare (IANJUTA_ITERABLE (result_start),
381 real_start, NULL) != 0)
383 found = TRUE;
384 anjuta_status_push (private->status,
385 _("Search for \"%s\" reached end and was continued on top."), search_text);
387 else if (ianjuta_editor_selection_has_selection (selection, NULL))
389 anjuta_status_pop (private->status);
390 anjuta_status_push (private->status,
391 _("Search for \"%s\" reached end and was continued on top but no new match was found."), search_text);
395 if (found)
397 ianjuta_editor_selection_set (selection,
398 IANJUTA_ITERABLE (result_start),
399 IANJUTA_ITERABLE (result_end), NULL);
400 g_object_unref (result_start);
401 g_object_unref (result_end);
403 search_box_set_entry_color (search_box, found);
404 g_object_unref (real_start);
405 g_object_unref (search_end);
407 // We end the incremental search here
409 if (private->last_start)
411 g_object_unref (private->last_start);
412 private->last_start = NULL;
414 else
416 g_object_unref (search_start);
420 static void
421 search_box_init (SearchBox *object)
423 SearchBoxPrivate* private = GET_PRIVATE(object);
425 /* Button images */
426 GtkWidget* close =
427 gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
428 GtkWidget* search =
429 gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_SMALL_TOOLBAR);
430 GtkWidget* goto_image =
431 gtk_image_new_from_stock (ANJUTA_STOCK_GOTO_LINE, GTK_ICON_SIZE_SMALL_TOOLBAR);
433 /* Searching */
434 private->search_entry = gtk_entry_new();
435 g_signal_connect (G_OBJECT (private->search_entry), "activate",
436 G_CALLBACK (on_search_activated),
437 object);
438 g_signal_connect (G_OBJECT (private->search_entry), "key-press-event",
439 G_CALLBACK (on_entry_key_pressed),
440 object);
441 g_signal_connect (G_OBJECT (private->search_entry), "changed",
442 G_CALLBACK (on_incremental_search),
443 object);
444 g_signal_connect (G_OBJECT (private->search_entry), "focus-out-event",
445 G_CALLBACK (on_search_focus_out),
446 object);
448 private->case_check = gtk_check_button_new_with_label (_("Case sensitive"));
450 private->search_button = gtk_button_new ();
451 gtk_button_set_image (GTK_BUTTON (private->search_button), search);
452 gtk_button_set_relief (GTK_BUTTON (private->search_button), GTK_RELIEF_NONE);
453 g_signal_connect (G_OBJECT (private->search_button), "clicked",
454 G_CALLBACK (on_search_activated),
455 object);
458 private->close_button = gtk_button_new();
459 gtk_button_set_image (GTK_BUTTON (private->close_button), close);
460 gtk_button_set_relief (GTK_BUTTON (private->close_button), GTK_RELIEF_NONE);
462 g_signal_connect (G_OBJECT (private->close_button), "clicked",
463 G_CALLBACK (on_search_box_hide), object);
465 /* Goto line */
466 private->goto_entry = gtk_entry_new ();
467 gtk_entry_set_width_chars (GTK_ENTRY (private->goto_entry), 5);
468 g_signal_connect (G_OBJECT (private->goto_entry), "activate",
469 G_CALLBACK (on_goto_activated),
470 object);
471 g_signal_connect (G_OBJECT (private->goto_entry), "key-press-event",
472 G_CALLBACK (on_goto_key_pressed),
473 object);
474 private->goto_button = gtk_button_new();
475 gtk_button_set_image (GTK_BUTTON (private->goto_button), goto_image);
476 gtk_button_set_relief (GTK_BUTTON (private->goto_button), GTK_RELIEF_NONE);
477 g_signal_connect (G_OBJECT (private->goto_button), "clicked",
478 G_CALLBACK (on_goto_activated),
479 object);
481 gtk_box_pack_start (GTK_BOX (object), private->goto_entry, FALSE, FALSE, 0);
482 gtk_box_pack_start (GTK_BOX (object), private->goto_button, FALSE, FALSE, 0);
484 gtk_box_pack_start (GTK_BOX (object), private->search_entry, TRUE, TRUE, 5);
485 gtk_box_pack_start (GTK_BOX (object), private->search_button, FALSE, FALSE, 0);
486 gtk_box_pack_start (GTK_BOX (object), private->case_check, FALSE, FALSE, 0);
487 gtk_box_pack_end (GTK_BOX (object), private->close_button, FALSE, FALSE, 0);
489 gtk_widget_show_all (GTK_WIDGET (object));
491 private->last_start = NULL;
494 static void
495 search_box_finalize (GObject *object)
498 G_OBJECT_CLASS (search_box_parent_class)->finalize (object);
501 static void
502 search_box_class_init (SearchBoxClass *klass)
504 GObjectClass* object_class = G_OBJECT_CLASS (klass);
506 g_type_class_add_private (klass, sizeof (SearchBoxPrivate));
508 object_class->finalize = search_box_finalize;
511 GtkWidget*
512 search_box_new (AnjutaDocman *docman)
514 GtkWidget* search_box;
515 SearchBoxPrivate* private;
517 search_box = GTK_WIDGET (g_object_new (SEARCH_TYPE_BOX, "homogeneous",
518 FALSE, NULL));
519 g_signal_connect (G_OBJECT (docman), "document-changed",
520 G_CALLBACK (on_document_changed), search_box);
522 private = GET_PRIVATE (search_box);
523 private->status = anjuta_shell_get_status (docman->shell, NULL);
525 return search_box;
528 void
529 search_box_fill_search_focus (SearchBox* search_box)
531 SearchBoxPrivate* private = GET_PRIVATE(search_box);
532 IAnjutaEditor* te = private->current_editor;
534 if (IANJUTA_IS_EDITOR (te))
536 gchar *buffer;
538 buffer = ianjuta_editor_selection_get (IANJUTA_EDITOR_SELECTION (te), NULL);
539 if (buffer != NULL)
541 g_strstrip (buffer);
542 if (*buffer != 0)
544 gtk_entry_set_text (GTK_ENTRY (private->search_entry), buffer);
545 gtk_editable_select_region (GTK_EDITABLE (private->search_entry), 0, -1);
547 g_free (buffer);
551 gtk_widget_grab_focus (private->search_entry);
554 void
555 search_box_grab_line_focus (SearchBox* search_box)
557 SearchBoxPrivate* private = GET_PRIVATE(search_box);
558 gtk_widget_grab_focus (private->goto_entry);