exampleappwin.c 8.53 KB
Newer Older
1
2
#include <gtk/gtk.h>

Matthias Clasen's avatar
Matthias Clasen committed
3
4
5
#include "exampleapp.h"
#include "exampleappwin.h"

6
7
8
struct _ExampleAppWindow
{
  GtkApplicationWindow parent;
Matthias Clasen's avatar
Matthias Clasen committed
9
10
};

11
12
13
14
15
16
17
18
19
20
21
22
typedef struct _ExampleAppWindowPrivate ExampleAppWindowPrivate;

struct _ExampleAppWindowPrivate
{
  GSettings *settings;
  GtkWidget *stack;
  GtkWidget *search;
  GtkWidget *searchbar;
  GtkWidget *searchentry;
  GtkWidget *gears;
  GtkWidget *sidebar;
  GtkWidget *words;
Matthias Clasen's avatar
Matthias Clasen committed
23
24
25
26
27
};

G_DEFINE_TYPE_WITH_PRIVATE(ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW);

static void
28
search_text_changed (GtkEntry *entry)
Matthias Clasen's avatar
Matthias Clasen committed
29
{
30
  ExampleAppWindow *win;
31
32
33
34
35
36
37
38
39
40
41
42
  ExampleAppWindowPrivate *priv;
  const gchar *text;
  GtkWidget *tab;
  GtkWidget *view;
  GtkTextBuffer *buffer;
  GtkTextIter start, match_start, match_end;

  text = gtk_entry_get_text (entry);

  if (text[0] == '\0')
    return;

43
  win = EXAMPLE_APP_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (entry)));
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  priv = example_app_window_get_instance_private (win);

  tab = gtk_stack_get_visible_child (GTK_STACK (priv->stack));
  view = gtk_bin_get_child (GTK_BIN (tab));
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));

  /* Very simple-minded search implementation */
  gtk_text_buffer_get_start_iter (buffer, &start);
  if (gtk_text_iter_forward_search (&start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE,
                                    &match_start, &match_end, NULL))
    {
      gtk_text_buffer_select_range (buffer, &match_start, &match_end);
      gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view), &match_start,
                                    0.0, FALSE, 0.0, 0.0);
    }
Matthias Clasen's avatar
Matthias Clasen committed
59
60
61
62
63
64
}

static void
find_word (GtkButton        *button,
           ExampleAppWindow *win)
{
65
66
  ExampleAppWindowPrivate *priv;
  const gchar *word;
Matthias Clasen's avatar
Matthias Clasen committed
67

68
  priv = example_app_window_get_instance_private (win);
Matthias Clasen's avatar
Matthias Clasen committed
69

70
71
  word = gtk_button_get_label (button);
  gtk_entry_set_text (GTK_ENTRY (priv->searchentry), word);
Matthias Clasen's avatar
Matthias Clasen committed
72
73
74
75
76
}

static void
update_words (ExampleAppWindow *win)
{
77
78
79
80
81
82
83
84
85
86
87
88
  ExampleAppWindowPrivate *priv;
  GHashTable *strings;
  GHashTableIter iter;
  GtkWidget *tab, *view, *row;
  GtkTextBuffer *buffer;
  GtkTextIter start, end;
  GList *children, *l;
  gchar *word, *key;

  priv = example_app_window_get_instance_private (win);

  tab = gtk_stack_get_visible_child (GTK_STACK (priv->stack));
89
90
91
92

  if (tab == NULL)
    return;

93
94
95
96
97
98
99
100
101
102
103
104
  view = gtk_bin_get_child (GTK_BIN (tab));
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));

  strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);

  gtk_text_buffer_get_start_iter (buffer, &start);
  while (!gtk_text_iter_is_end (&start))
    {
      while (!gtk_text_iter_starts_word (&start))
        {
          if (!gtk_text_iter_forward_char (&start))
            goto done;
Matthias Clasen's avatar
Matthias Clasen committed
105
        }
106
107
108
109
110
111
112
113
      end = start;
      if (!gtk_text_iter_forward_word_end (&end))
        goto done;
      word = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
      g_hash_table_add (strings, g_utf8_strdown (word, -1));
      g_free (word);
      start = end;
    }
Matthias Clasen's avatar
Matthias Clasen committed
114
115

done:
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  children = gtk_container_get_children (GTK_CONTAINER (priv->words));
  for (l = children; l; l = l->next)
    gtk_container_remove (GTK_CONTAINER (priv->words), GTK_WIDGET (l->data));
  g_list_free (children);

  g_hash_table_iter_init (&iter, strings);
  while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL))
    {
      row = gtk_button_new_with_label (key);
      g_signal_connect (row, "clicked",
                        G_CALLBACK (find_word), win);
      gtk_widget_show (row);
      gtk_container_add (GTK_CONTAINER (priv->words), row);
    }

  g_hash_table_unref (strings);
Matthias Clasen's avatar
Matthias Clasen committed
132
133
134
}

static void
135
136
visible_child_changed (GObject    *stack,
                       GParamSpec *pspec)
Matthias Clasen's avatar
Matthias Clasen committed
137
{
138
  ExampleAppWindow *win;
139
  ExampleAppWindowPrivate *priv;
Matthias Clasen's avatar
Matthias Clasen committed
140

141
  if (gtk_widget_in_destruction (GTK_WIDGET (stack)))
142
143
    return;

144
145
  win = EXAMPLE_APP_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (stack)));

146
147
148
  priv = example_app_window_get_instance_private (win);
  gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (priv->searchbar), FALSE);
  update_words (win);
Matthias Clasen's avatar
Matthias Clasen committed
149
150
151
152
153
154
155
}

static void
words_changed (GObject          *sidebar,
               GParamSpec       *pspec,
               ExampleAppWindow *win)
{
156
  update_words (win);
Matthias Clasen's avatar
Matthias Clasen committed
157
158
159
160
161
}

static void
example_app_window_init (ExampleAppWindow *win)
{
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  ExampleAppWindowPrivate *priv;
  GtkBuilder *builder;
  GMenuModel *menu;
  GAction *action;

  priv = example_app_window_get_instance_private (win);
  gtk_widget_init_template (GTK_WIDGET (win));
  priv->settings = g_settings_new ("org.gtk.exampleapp");

  g_settings_bind (priv->settings, "transition",
                   priv->stack, "transition-type",
                   G_SETTINGS_BIND_DEFAULT);

  g_settings_bind (priv->settings, "show-words",
                   priv->sidebar, "reveal-child",
                   G_SETTINGS_BIND_DEFAULT);

  g_object_bind_property (priv->search, "active",
                          priv->searchbar, "search-mode-enabled",
                          G_BINDING_BIDIRECTIONAL);

183
184
  g_signal_connect (priv->sidebar, "notify::reveal-child",
                    G_CALLBACK (words_changed), win);
185
186
187
188
189
190
191
192
193

  builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/gears-menu.ui");
  menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu"));
  gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->gears), menu);
  g_object_unref (builder);

  action = g_settings_create_action (priv->settings, "show-words");
  g_action_map_add_action (G_ACTION_MAP (win), action);
  g_object_unref (action);
Matthias Clasen's avatar
Matthias Clasen committed
194
195
196
197
198
}

static void
example_app_window_dispose (GObject *object)
{
199
200
  ExampleAppWindow *win;
  ExampleAppWindowPrivate *priv;
Matthias Clasen's avatar
Matthias Clasen committed
201

202
203
  win = EXAMPLE_APP_WINDOW (object);
  priv = example_app_window_get_instance_private (win);
Matthias Clasen's avatar
Matthias Clasen committed
204

205
  g_clear_object (&priv->settings);
Matthias Clasen's avatar
Matthias Clasen committed
206

207
  G_OBJECT_CLASS (example_app_window_parent_class)->dispose (object);
Matthias Clasen's avatar
Matthias Clasen committed
208
209
210
211
212
}

static void
example_app_window_class_init (ExampleAppWindowClass *class)
{
213
214
215
216
  G_OBJECT_CLASS (class)->dispose = example_app_window_dispose;

  gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
                                               "/org/gtk/exampleapp/window.ui");
217

218
219
220
221
222
223
224
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppWindow, stack);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppWindow, search);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppWindow, searchbar);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppWindow, searchentry);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppWindow, gears);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppWindow, words);
  gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppWindow, sidebar);
225
226
227
228

  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed);
  gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), visible_child_changed);

Matthias Clasen's avatar
Matthias Clasen committed
229
230
231
232
233
}

ExampleAppWindow *
example_app_window_new (ExampleApp *app)
{
234
  return g_object_new (EXAMPLE_APP_WINDOW_TYPE, "application", app, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
235
236
237
238
239
240
}

void
example_app_window_open (ExampleAppWindow *win,
                         GFile            *file)
{
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  ExampleAppWindowPrivate *priv;
  gchar *basename;
  GtkWidget *scrolled, *view;
  gchar *contents;
  gsize length;
  GtkTextBuffer *buffer;
  GtkTextTag *tag;
  GtkTextIter start_iter, end_iter;

  priv = example_app_window_get_instance_private (win);
  basename = g_file_get_basename (file);

  scrolled = gtk_scrolled_window_new (NULL, NULL);
  gtk_widget_show (scrolled);
  gtk_widget_set_hexpand (scrolled, TRUE);
  gtk_widget_set_vexpand (scrolled, TRUE);
  view = gtk_text_view_new ();
  gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
  gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
  gtk_widget_show (view);
  gtk_container_add (GTK_CONTAINER (scrolled), view);
  gtk_stack_add_titled (GTK_STACK (priv->stack), scrolled, basename, basename);

  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));

  if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL))
    {
      gtk_text_buffer_set_text (buffer, contents, length);
      g_free (contents);
    }

  tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
  g_settings_bind (priv->settings, "font",
                   tag, "font",
                   G_SETTINGS_BIND_DEFAULT);

  gtk_text_buffer_get_start_iter (buffer, &start_iter);
  gtk_text_buffer_get_end_iter (buffer, &end_iter);
  gtk_text_buffer_apply_tag (buffer, tag, &start_iter, &end_iter);

  g_free (basename);

  gtk_widget_set_sensitive (priv->search, TRUE);

  update_words (win);
Matthias Clasen's avatar
Matthias Clasen committed
286
}