| #include <gtk/gtk.h> |
| |
| 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; |
| } |