summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/browserbox.c85
-rw-r--r--src/browserbox.h13
-rw-r--r--src/documentbox.c63
-rw-r--r--src/documentbox.h22
-rw-r--r--src/inlinebox.c60
-rw-r--r--src/inlinebox.h12
-rw-r--r--src/main.c57
7 files changed, 261 insertions, 51 deletions
diff --git a/src/browserbox.c b/src/browserbox.c
index fe69cde..9179ad1 100644
--- a/src/browserbox.c
+++ b/src/browserbox.c
@@ -118,26 +118,46 @@ static void builder_state_class_init (BuilderStateClass *klass)
object_class->dispose = builder_state_dispose;
}
+void scroll_to (BuilderState *bs, GObject *target)
+{
+ GtkAllocation widget_alloc, *alloc;
+ if (GTK_IS_WIDGET(target)) {
+ gtk_widget_get_allocation(GTK_WIDGET(target), &widget_alloc);
+ alloc = &widget_alloc;
+ } else if (IS_IB_TEXT(target)) {
+ alloc = &IB_TEXT(target)->alloc;
+ } else {
+ puts("Shouldn't happen");
+ return;
+ }
+ GtkAdjustment *adj =
+ gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(bs->docbox));
+ gtk_adjustment_set_value(adj, alloc->y);
+ gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(bs->docbox),
+ adj);
+}
void scroll_to_identifier(BuilderState *bs, const char *identifier)
{
- GtkWidget *target = g_hash_table_lookup(bs->identifiers, identifier);
- if (target) {
- GtkAllocation widget_alloc, *alloc;
- if (GTK_IS_WIDGET(target)) {
- gtk_widget_get_allocation(target, &widget_alloc);
- alloc = &widget_alloc;
- } else if (IS_IB_TEXT(target)) {
- alloc = &IB_TEXT(target)->alloc;
- } else {
- puts("Shouldn't happen");
- return;
- }
- GtkAdjustment *adj =
- gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(bs->docbox));
- gtk_adjustment_set_value(adj, alloc->y);
- gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(bs->docbox),
- adj);
+ GObject *target = g_hash_table_lookup(bs->identifiers, identifier);
+ if (target != NULL) {
+ scroll_to(bs, target);
+ }
+}
+
+void browser_box_set_status(BrowserBox *bb, const gchar *status_str) {
+ gtk_statusbar_remove_all(GTK_STATUSBAR(bb->status_bar),
+ gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"));
+ gtk_statusbar_push(GTK_STATUSBAR(bb->status_bar),
+ gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"),
+ status_str);
+}
+
+void browser_box_display_search_status (BrowserBox *bb) {
+ gchar status[MAX_SEARCH_STRING_LEN + 33];
+ if (bb->search_state == SEARCH_FORWARD) {
+ sprintf(status, "Forward search: %s", bb->search_string);
+ browser_box_set_status(bb, status);
}
}
@@ -1212,11 +1232,7 @@ void hover_link_cb (void *ptr,
gchar *url,
BrowserBox *bb)
{
- gtk_statusbar_remove_all(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"));
- gtk_statusbar_push(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"),
- url);
+ browser_box_set_status(bb, url);
}
@@ -1238,11 +1254,7 @@ void document_loaded(SoupSession *session,
htmlParseChunk(bs->parser, "", 0, 1);
gtk_widget_grab_focus(GTK_WIDGET(bs->docbox));
printf("word cache: %u\n", g_hash_table_size(word_cache));
- gtk_statusbar_remove_all(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"));
- gtk_statusbar_push(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"),
- "Ready");
+ browser_box_set_status(bb, "Ready");
}
void got_chunk(SoupMessage *msg,
@@ -1251,11 +1263,7 @@ void got_chunk(SoupMessage *msg,
{
BrowserBox *bb = ptr;
BuilderState *bs = bb->builder_state;
- gtk_statusbar_remove_all(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"));
- gtk_statusbar_push(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"),
- "Loading");
+ browser_box_set_status(bb, "Loading");
if (bs->parser == NULL) {
/* todo: maybe move it into got_headers */
char *uri_str = soup_uri_to_string(bs->uri, FALSE);
@@ -1285,11 +1293,7 @@ void got_chunk(SoupMessage *msg,
void got_headers(SoupMessage *msg, gpointer ptr)
{
BrowserBox *bb = ptr;
- gtk_statusbar_remove_all(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"));
- gtk_statusbar_push(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"),
- "Got headers");
+ browser_box_set_status(bb, "Got headers");
/* todo: check content type, don't assume HTML */
if (bb->builder_state != NULL) {
if (bb->builder_state->docbox != NULL) {
@@ -1306,11 +1310,7 @@ void got_headers(SoupMessage *msg, gpointer ptr)
void document_request_sm (BrowserBox *bb, SoupMessage *sm)
{
- gtk_statusbar_remove_all(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"));
- gtk_statusbar_push(GTK_STATUSBAR(bb->status_bar),
- gtk_statusbar_get_context_id(GTK_STATUSBAR(bb->status_bar), "status"),
- "Requesting");
+ browser_box_set_status(bb, "Requesting");
if (bb->builder_state != NULL) {
bb->builder_state->active = FALSE;
}
@@ -1394,6 +1394,7 @@ browser_box_init (BrowserBox *bb)
bb->forms = NULL;
bb->history = NULL;
bb->history_position = NULL;
+ bb->search_string[0] = 0;
return;
}
diff --git a/src/browserbox.h b/src/browserbox.h
index 5016c67..9af6f83 100644
--- a/src/browserbox.h
+++ b/src/browserbox.h
@@ -89,6 +89,15 @@ struct _BuilderState
typedef struct _BrowserBox BrowserBox;
typedef struct _BrowserBoxClass BrowserBoxClass;
+typedef enum _BTSState BTSState;
+enum _BTSState {
+ SEARCH_INACTIVE,
+ SEARCH_FORWARD,
+ SEARCH_BACKWARD
+};
+
+#define MAX_SEARCH_STRING_LEN 512
+
struct _BrowserBox
{
BlockBox parent_instance;
@@ -100,6 +109,8 @@ struct _BrowserBox
GList *forms;
GList *history;
GList *history_position;
+ BTSState search_state;
+ gchar search_string[MAX_SEARCH_STRING_LEN + 1];
GtkStack *tabs;
/* GHashTable *word_cache; */
};
@@ -125,6 +136,8 @@ void document_request_sm (BrowserBox *bb, SoupMessage *sm);
void document_request (BrowserBox *bb, SoupURI *uri);
gboolean history_back (BrowserBox *bb);
gboolean history_forward (BrowserBox *bb);
+void browser_box_set_status(BrowserBox *bb, const gchar *status_str);
+void browser_box_display_search_status (BrowserBox *bb);
GHashTable *word_cache;
diff --git a/src/documentbox.c b/src/documentbox.c
index b506385..f8a8e2b 100644
--- a/src/documentbox.c
+++ b/src/documentbox.c
@@ -95,6 +95,12 @@ static void
document_box_init (DocumentBox *db)
{
db->links = NULL;
+ db->search.ib = NULL;
+ db->search.start = 0;
+ db->search.end = -1;
+ db->search.str = NULL;
+ db->search.forward = TRUE;
+ db->search.state = START;
}
@@ -487,3 +493,60 @@ DocumentBox *document_box_new ()
db->sel.selection_active = FALSE;
return db;
}
+
+static void
+document_box_search (GtkWidget *widget, TextSearchState *tss) {
+ /* todo: backwards search */
+ if (tss->state == FOUND) {
+ return;
+ }
+ if (tss->state == START && (tss->ib == NULL || GTK_WIDGET(tss->ib) == widget)) {
+ /* No previous position or found the widget */
+ tss->state = LOOKING;
+ if (tss->ib != NULL) {
+ tss->ib->match_start = 0;
+ tss->ib->match_end = 0;
+ gtk_widget_queue_draw(GTK_WIDGET(tss->ib));
+ }
+ }
+ if (tss->state == LOOKING &&
+ (tss->ib == NULL || GTK_WIDGET(tss->ib) != widget)) {
+ tss->start = 0;
+ tss->end = -1;
+ }
+ if (tss->state == LOOKING && IS_INLINE_BOX(widget)) {
+ InlineBox *ib = INLINE_BOX(widget);
+ gint pos = inline_box_search(ib, tss->start, tss->end, tss->str);
+ if (pos != -1) {
+ tss->state = FOUND;
+ tss->ib = ib;
+ tss->start = pos;
+ tss->end = pos + strlen(tss->str);
+ ib->match_start = tss->start;
+ ib->match_end = tss->end;
+ gtk_widget_queue_draw(widget);
+ }
+ } else if (tss->state != FOUND && GTK_IS_CONTAINER(widget)) {
+ gtk_container_foreach(GTK_CONTAINER(widget),
+ (GtkCallback)document_box_search, tss);
+ }
+}
+
+gboolean
+document_box_find (DocumentBox *db, const gchar *str)
+{
+ /* todo: backwards search */
+ db->search.str = str;
+ db->search.state = START;
+ db->search.end = -1;
+ document_box_search(GTK_WIDGET(db), &(db->search));
+ if (db->search.state == FOUND) {
+ gtk_widget_grab_focus(GTK_WIDGET(db->search.ib));
+ return TRUE;
+ } else {
+ db->search.ib = NULL;
+ db->search.start = 0;
+ db->search.end = -1;
+ return FALSE;
+ }
+}
diff --git a/src/documentbox.h b/src/documentbox.h
index 351471b..64a2320 100644
--- a/src/documentbox.h
+++ b/src/documentbox.h
@@ -46,14 +46,32 @@ struct _SelectionState
gboolean selecting;
};
+typedef enum _TSState TSState;
+enum _TSState {
+ START,
+ LOOKING,
+ FOUND
+};
+
+typedef struct _TextSearchState TextSearchState;
+struct _TextSearchState
+{
+ InlineBox *ib;
+ gint start;
+ gint end;
+ const gchar *str;
+ gboolean forward;
+ TSState state;
+};
+
struct _DocumentBox
{
GtkScrolledWindow parent_instance;
GtkEventBox *evbox;
GList *links;
SelectionState sel;
+ TextSearchState search;
GdkWindow *event_window;
- /* GList *forms; */
};
struct _DocumentBoxClass
@@ -63,7 +81,7 @@ struct _DocumentBoxClass
GType document_box_get_type(void) G_GNUC_CONST;
DocumentBox *document_box_new(void);
-
+gboolean document_box_find (DocumentBox *db, const gchar *str);
G_END_DECLS
diff --git a/src/inlinebox.c b/src/inlinebox.c
index 43b75fa..7eeaeae 100644
--- a/src/inlinebox.c
+++ b/src/inlinebox.c
@@ -165,6 +165,29 @@ inline_box_draw (GtkWidget *widget,
sel_width, ibt->alloc.height);
gtk_style_context_remove_class(styleCtx, "rubberband");
}
+ /* duplication here (todo) */
+ if (ib->match_start <= text_position + text_len &&
+ ib->match_end >= text_position) {
+ guint sel_start = ibt->alloc.x, sel_width = ibt->alloc.width;
+ gint x_pos;
+ if (ib->match_start > text_position) {
+ pango_layout_index_to_line_x(ibt->layout,
+ ib->match_start - text_position,
+ FALSE, NULL, &x_pos);
+ sel_start += x_pos / PANGO_SCALE;
+ sel_width -= x_pos / PANGO_SCALE;
+ }
+ if (ib->match_end < text_position + text_len) {
+ pango_layout_index_to_line_x(ibt->layout,
+ ib->match_end - text_position,
+ FALSE, NULL, &x_pos);
+ sel_width -= ibt->alloc.width - x_pos / PANGO_SCALE;
+ }
+ gtk_style_context_add_class(styleCtx, "rubberband");
+ gtk_render_background(styleCtx, cr, sel_start, ibt->alloc.y,
+ sel_width, ibt->alloc.height);
+ gtk_style_context_remove_class(styleCtx, "rubberband");
+ }
gtk_render_layout(styleCtx, cr, ibt->alloc.x, ibt->alloc.y, ibt->layout);
@@ -583,3 +606,40 @@ inline_box_forall (GtkContainer *container, gboolean include_internals,
child = next;
}
}
+
+gchar*
+inline_box_get_text (InlineBox *ib)
+{
+ GList *child;
+ gchar **words = calloc(g_list_length(ib->children) + 1, sizeof(gchar*));
+ guint n = 0;
+ for (child = ib->children; child; child = child->next) {
+ if (IS_IB_TEXT(child->data)) {
+ words[n] = (gchar*)pango_layout_get_text(IB_TEXT(child->data)->layout);
+ n++;
+ }
+ }
+ gchar *result = g_strjoinv(NULL, words);
+ free(words);
+ return result;
+}
+
+gint
+inline_box_search (InlineBox *ib,
+ guint start,
+ gint end,
+ const gchar *str)
+{
+ gchar *orig_text = inline_box_get_text(ib);
+ gchar *text = g_utf8_strdown(orig_text, -1);
+ g_free(orig_text);
+ if (end != -1) {
+ end -= start;
+ }
+ gchar *result = g_strstr_len(text + start, end, str);
+ g_free(text);
+ if (result != NULL) {
+ return result - text;
+ }
+ return -1;
+}
diff --git a/src/inlinebox.h b/src/inlinebox.h
index 03e5ec8..24c11d3 100644
--- a/src/inlinebox.h
+++ b/src/inlinebox.h
@@ -101,6 +101,8 @@ struct _InlineBox
GObject *focused_object;
guint selection_start;
guint selection_end;
+ guint match_start;
+ guint match_end;
gboolean wrap;
};
@@ -109,10 +111,12 @@ struct _InlineBoxClass
GtkContainerClass parent_class;
};
-GType inline_box_get_type(void) G_GNUC_CONST;
-InlineBox *inline_box_new(void);
-void inline_box_add_text(InlineBox *container, IBText *text);
-void inline_box_break(InlineBox *container);
+GType inline_box_get_type (void) G_GNUC_CONST;
+InlineBox *inline_box_new (void);
+void inline_box_add_text (InlineBox *container, IBText *text);
+void inline_box_break (InlineBox *container);
+gchar *inline_box_get_text (InlineBox *ib);
+gint inline_box_search (InlineBox *ib, guint start, gint end, const gchar *str);
G_END_DECLS
diff --git a/src/main.c b/src/main.c
index 65adadf..0603b03 100644
--- a/src/main.c
+++ b/src/main.c
@@ -41,19 +41,70 @@ key_press_event_cb (GtkWidget *widget, GdkEventKey *ev, GtkStack *tabs)
return TRUE;
} else if (ev->keyval == GDK_KEY_w) {
GtkWidget *current_tab = gtk_stack_get_visible_child(tabs);
- if (current_tab) {
+ if (current_tab != NULL) {
gtk_widget_destroy(current_tab);
+ return TRUE;
+ }
+ } else if (ev->keyval == GDK_KEY_s) {
+ GtkWidget *current_tab = gtk_stack_get_visible_child(tabs);
+ if (current_tab != NULL) {
+ BrowserBox *bb = BROWSER_BOX(current_tab);
+ if (bb->search_state == SEARCH_INACTIVE) {
+ bb->search_state = SEARCH_FORWARD;
+ browser_box_display_search_status(bb);
+ } else if (bb->search_state == SEARCH_FORWARD) {
+ TextSearchState *tss = &(DOCUMENT_BOX(bb->builder_state->docbox)->search);
+ if (tss->state == FOUND) {
+ tss->start++;
+ }
+ document_box_find(DOCUMENT_BOX(bb->builder_state->docbox), bb->search_string);
+ browser_box_display_search_status(bb);
+ }
+ return TRUE;
+ }
+ } else if (ev->keyval == GDK_KEY_g) {
+ GtkWidget *current_tab = gtk_stack_get_visible_child(tabs);
+ if (current_tab != NULL) {
+ BrowserBox *bb = BROWSER_BOX(current_tab);
+ if (bb->search_state != SEARCH_INACTIVE) {
+ bb->search_state = SEARCH_INACTIVE;
+ bb->search_string[0] = 0;
+ browser_box_set_status(bb, "Interrupted");
+ }
}
- return TRUE;
}
}
GtkWidget *current_tab = gtk_stack_get_visible_child(tabs);
if (current_tab != NULL) {
BrowserBox *bb = BROWSER_BOX(current_tab);
- if (ev->keyval == GDK_KEY_Back || ev->keyval == GDK_KEY_BackSpace) {
+ if (ev->keyval == GDK_KEY_Back ||
+ (bb->search_state == SEARCH_INACTIVE &&
+ ev->keyval == GDK_KEY_BackSpace)) {
return history_back(bb);
} else if (ev->keyval == GDK_KEY_Forward) {
return history_forward(bb);
+ } else if (bb->search_state != SEARCH_INACTIVE &&
+ bb->builder_state != NULL &&
+ bb->builder_state->docbox != NULL) {
+ size_t ss_len = strlen(bb->search_string);
+ DocumentBox *db = DOCUMENT_BOX(bb->builder_state->docbox);
+ if (ev->keyval == GDK_KEY_BackSpace && ss_len > 0) {
+ /* todo: this won't work well for unicode */
+ bb->search_string[ss_len - 1] = 0;
+ document_box_find(db, bb->search_string);
+ browser_box_display_search_status(bb);
+ return TRUE;
+ }
+ if (ss_len + 4 < MAX_SEARCH_STRING_LEN) {
+ gunichar c = gdk_keyval_to_unicode(ev->keyval);
+ gint c_len = g_unichar_to_utf8(c, bb->search_string + ss_len);
+ if (c_len > 0) {
+ bb->search_string[ss_len + c_len] = 0;
+ browser_box_display_search_status(bb);
+ document_box_find(db, bb->search_string);
+ return TRUE;
+ }
+ }
}
}
return FALSE;