#include static void setup_item (GtkSignalListItemFactory *self, GObject *object) { GtkListItem *list_item = GTK_LIST_ITEM (object); GtkWidget *child = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (child), 0); gtk_list_item_set_child (list_item, child); } static void bind_item (GtkSignalListItemFactory *self, GObject *object) { GtkListItem *list_item = GTK_LIST_ITEM (object); GObject *item = gtk_list_item_get_item (list_item); GtkWidget *child = gtk_list_item_get_child (list_item); gtk_label_set_label (GTK_LABEL (child), gtk_string_object_get_string (GTK_STRING_OBJECT (item))); } static char * reverse_word (const char *word) { GString *s = g_string_new (""); const char *p; gunichar c; gboolean capitalize; capitalize = g_unichar_isupper (g_utf8_get_char (word)); p = word + strlen (word); while ((p = g_utf8_find_prev_char (word, p)) != NULL) { c = g_utf8_get_char (p); if (s->len == 0 && capitalize) c = g_unichar_toupper (c); else c = g_unichar_tolower (c); g_string_append_unichar (s, c); } return g_string_free (s, FALSE); } static void bind_item_reverse (GtkSignalListItemFactory *self, GObject *object) { GtkListItem *list_item = GTK_LIST_ITEM (object); GObject *item = gtk_list_item_get_item (list_item); GtkWidget *child = gtk_list_item_get_child (list_item); char *word; word = reverse_word (gtk_string_object_get_string (GTK_STRING_OBJECT (item))); gtk_label_set_label (GTK_LABEL (child), word); g_free (word); } static void setup_header (GtkSignalListItemFactory *self, GObject *object) { GtkListHeader *header = GTK_LIST_HEADER (object); GtkWidget *child = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (child), 0); gtk_list_header_set_child (header, child); } static char * get_first (GObject *this) { const char *s = gtk_string_object_get_string (GTK_STRING_OBJECT (this)); char buffer[6] = { 0, }; g_unichar_to_utf8 (g_unichar_toupper (g_utf8_get_char (s)), buffer); return g_strdup (buffer); } static void bind_header (GtkSignalListItemFactory *self, GObject *object) { GtkListHeader *header = GTK_LIST_HEADER (object); GObject *item = gtk_list_header_get_item (header); GtkWidget *child = gtk_list_header_get_child (header); PangoAttrList *attrs; char *string; string = get_first (item); gtk_label_set_label (GTK_LABEL (child), string); attrs = pango_attr_list_new (); pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE)); pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD)); gtk_label_set_attributes (GTK_LABEL (child), attrs); pango_attr_list_unref (attrs); g_free (string); } static const char *strings[] = { "Alpha", "Andromeda", "Anaphylaxis", "Anaheim", "Beer", "Branch", "Botulism", "Banana", "Bee", "Crane", "Caldera", "Copper", "Crowd", "Dora", "Dolphin", "Dam", "Ding", NULL, }; gboolean done_reading = FALSE; static gboolean dump_sections (gpointer data) { GtkSectionModel *model = data; if (!done_reading) return G_SOURCE_CONTINUE; for (unsigned int i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i++) { unsigned int s, e; gtk_section_model_get_section (model, i, &s, &e); g_print ("(%u %u)\n", s, e - 1); i = e; } return G_SOURCE_REMOVE; } static void read_lines_cb (GObject *object, GAsyncResult *result, gpointer data) { GBufferedInputStream *stream = G_BUFFERED_INPUT_STREAM (object); GtkStringList *stringlist = data; GError *error = NULL; gsize size; GPtrArray *lines; gssize n_filled; const char *buffer, *newline; n_filled = g_buffered_input_stream_fill_finish (stream, result, &error); if (n_filled < 0) { g_print ("Could not read data: %s\n", error->message); g_clear_error (&error); g_object_unref (stringlist); return; } buffer = g_buffered_input_stream_peek_buffer (stream, &size); if (n_filled == 0) { if (size) gtk_string_list_take (stringlist, g_utf8_make_valid (buffer, size)); g_object_unref (stringlist); done_reading = TRUE; return; } lines = NULL; while ((newline = memchr (buffer, '\n', size))) { if (newline > buffer) { if (lines == NULL) lines = g_ptr_array_new_with_free_func (g_free); g_ptr_array_add (lines, g_utf8_make_valid (buffer, newline - buffer)); } if (g_input_stream_skip (G_INPUT_STREAM (stream), newline - buffer + 1, NULL, &error) < 0) { g_clear_error (&error); break; } buffer = g_buffered_input_stream_peek_buffer (stream, &size); } if (lines == NULL) { g_buffered_input_stream_set_buffer_size (stream, g_buffered_input_stream_get_buffer_size (stream) + 4096); } else { g_ptr_array_add (lines, NULL); gtk_string_list_splice (stringlist, g_list_model_get_n_items (G_LIST_MODEL (stringlist)), 0, (const char **) lines->pdata); g_ptr_array_free (lines, TRUE); } g_buffered_input_stream_fill_async (stream, -1, G_PRIORITY_HIGH_IDLE, NULL, read_lines_cb, data); } static void file_is_open_cb (GObject *file, GAsyncResult *result, gpointer data) { GError *error = NULL; GFileInputStream *file_stream; GBufferedInputStream *stream; file_stream = g_file_read_finish (G_FILE (file), result, &error); if (file_stream == NULL) { g_print ("Could not open file: %s\n", error->message); g_error_free (error); g_object_unref (data); return; } stream = G_BUFFERED_INPUT_STREAM (g_buffered_input_stream_new (G_INPUT_STREAM (file_stream))); g_buffered_input_stream_fill_async (stream, -1, G_PRIORITY_HIGH_IDLE, NULL, read_lines_cb, data); g_object_unref (stream); } static void load_file (GtkStringList *list, GFile *file) { gtk_string_list_splice (list, 0, g_list_model_get_n_items (G_LIST_MODEL (list)), NULL); g_file_read_async (file, G_PRIORITY_HIGH_IDLE, NULL, file_is_open_cb, g_object_ref (list)); } static void toggle_cb (GtkCheckButton *check, GtkWidget *list) { GtkListItemFactory *header_factory = NULL; if (gtk_check_button_get_active (check)) { header_factory = gtk_signal_list_item_factory_new (); g_signal_connect (header_factory, "setup", G_CALLBACK (setup_header), NULL); g_signal_connect (header_factory, "bind", G_CALLBACK (bind_header), NULL); } g_object_set (list, "header-factory", header_factory, NULL); g_clear_object (&header_factory); } static void value_changed_cb (GtkAdjustment *adj, gpointer data) { g_print ("horizontal adjustment changed to %f\n", gtk_adjustment_get_value (adj)); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *sw; GtkWidget *lv; GtkWidget *gv; GtkWidget *cv; GtkWidget *header; GtkWidget *toggle; GtkWidget *switcher; GtkWidget *stack; GtkListItemFactory *factory; GtkExpression *expression; GtkSortListModel *sortmodel; GtkSelectionModel *selection; GtkStringList *stringlist; GtkAdjustment *adj; GtkColumnViewColumn *column; stringlist = gtk_string_list_new (NULL); if (argc > 1) { GFile *file = g_file_new_for_commandline_arg (argv[1]); load_file (stringlist, file); g_object_unref (file); } else { for (int i = 0; strings[i]; i++) gtk_string_list_append (stringlist, strings[i]); done_reading = TRUE; } gtk_init (); window = gtk_window_new (); gtk_window_set_default_size (GTK_WINDOW (window), 800, 600); header = gtk_header_bar_new (); gtk_window_set_titlebar (GTK_WINDOW (window), header); toggle = gtk_check_button_new (); gtk_widget_set_valign (toggle, GTK_ALIGN_CENTER); gtk_header_bar_pack_start (GTK_HEADER_BAR (header), toggle); stack = gtk_stack_new (); gtk_window_set_child (GTK_WINDOW (window), stack); switcher = gtk_stack_switcher_new (); gtk_header_bar_set_title_widget (GTK_HEADER_BAR (header), switcher); gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack)); expression = gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, "string"); sortmodel = gtk_sort_list_model_new (G_LIST_MODEL (stringlist), GTK_SORTER (gtk_string_sorter_new (expression))); expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL, 0, NULL, (GCallback) get_first, NULL, NULL); gtk_sort_list_model_set_section_sorter (sortmodel, GTK_SORTER (gtk_string_sorter_new (expression))); selection = GTK_SELECTION_MODEL (gtk_no_selection_new (G_LIST_MODEL (sortmodel))); /* list */ sw = gtk_scrolled_window_new (); gtk_stack_add_titled (GTK_STACK (stack), sw, "list", "List"); factory = gtk_signal_list_item_factory_new (); g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL); g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL); lv = gtk_list_view_new (g_object_ref (selection), factory); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), lv); g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_cb), lv); /* grid */ sw = gtk_scrolled_window_new (); gtk_stack_add_titled (GTK_STACK (stack), sw, "grid", "Grid"); factory = gtk_signal_list_item_factory_new (); g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL); g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL); gv = gtk_grid_view_new (g_object_ref (selection), factory); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), gv); g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_cb), gv); gtk_grid_view_set_min_columns (GTK_GRID_VIEW (gv), 5); adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (sw)); g_signal_connect (adj, "value-changed", G_CALLBACK (value_changed_cb), NULL); /* columns */ sw = gtk_scrolled_window_new (); gtk_stack_add_titled (GTK_STACK (stack), sw, "columns", "Columns"); cv = gtk_column_view_new (g_object_ref (selection)); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), cv); factory = gtk_signal_list_item_factory_new (); g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL); g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL); column = gtk_column_view_column_new ("Word", factory); gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column); gtk_column_view_column_set_expand (column, TRUE); gtk_column_view_column_set_resizable (column, TRUE); g_object_unref (column); factory = gtk_signal_list_item_factory_new (); g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL); g_signal_connect (factory, "bind", G_CALLBACK (bind_item_reverse), NULL); column = gtk_column_view_column_new ("Reverse", factory); gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column); gtk_column_view_column_set_expand (column, TRUE); gtk_column_view_column_set_resizable (column, TRUE); g_object_unref (column); g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_cb), cv); gtk_window_present (GTK_WINDOW (window)); g_timeout_add (500, dump_sections, selection); while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0) g_main_context_iteration (NULL, FALSE); g_object_unref (selection); return 0; }