Compare commits
176 Commits
matthiasc/
...
wip/multis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1995384f2 | ||
|
|
9b3f42c695 | ||
|
|
2cee1686a0 | ||
|
|
3e95a7a09a | ||
|
|
e14c079b69 | ||
|
|
9645142e8c | ||
|
|
917b1e2d6e | ||
|
|
dee6f30fab | ||
|
|
152c2b9d22 | ||
|
|
4bfc653311 | ||
|
|
27f19ca2a7 | ||
|
|
54ded2a284 | ||
|
|
fdcc452fd6 | ||
|
|
376bf3789b | ||
|
|
0e59cc5e22 | ||
|
|
3f4b8d269e | ||
|
|
4471171a6a | ||
|
|
93e2e5e171 | ||
|
|
a55d300357 | ||
|
|
3c409a61fd | ||
|
|
59a6a3e056 | ||
|
|
e1ab8db88d | ||
|
|
08beaa6448 | ||
|
|
65ef4b6bc8 | ||
|
|
dea0397e63 | ||
|
|
c246671634 | ||
|
|
98c25a449d | ||
|
|
71fd74c88f | ||
|
|
8efe92ec69 | ||
|
|
2f10212e7d | ||
|
|
13eb49c525 | ||
|
|
531dc407e3 | ||
|
|
fddd7c9fad | ||
|
|
1cbfdddb76 | ||
|
|
24df6869a3 | ||
|
|
ffbc361c30 | ||
|
|
48f44e87ab | ||
|
|
6ed9df3058 | ||
|
|
47ee4bc223 | ||
|
|
02096df981 | ||
|
|
63afc41eb8 | ||
|
|
5822d13763 | ||
|
|
55018bc21a | ||
|
|
78e055c080 | ||
|
|
77b7c56de2 | ||
|
|
804e253f80 | ||
|
|
534fda92fa | ||
|
|
0c1336f959 | ||
|
|
093c37a046 | ||
|
|
567548c11d | ||
|
|
b4a6262b70 | ||
|
|
81264fa7ad | ||
|
|
002fafc160 | ||
|
|
4c7d053db9 | ||
|
|
2b3f716ec3 | ||
|
|
a11c5e7741 | ||
|
|
35c177bae9 | ||
|
|
c25dc91fab | ||
|
|
52194f94dd | ||
|
|
87ae1d94df | ||
|
|
1717d30416 | ||
|
|
0852f011de | ||
|
|
da5f3a2ca3 | ||
|
|
b6d965af21 | ||
|
|
ec2600c0f0 | ||
|
|
f7f3a9bd61 | ||
|
|
c2102e5a1c | ||
|
|
ab470f0c25 | ||
|
|
89d48be883 | ||
|
|
30ee9b0ab9 | ||
|
|
5954d8f52b | ||
|
|
dfbc73e41a | ||
|
|
38dac9e7e1 | ||
|
|
a7947a3e6e | ||
|
|
9f258e9544 | ||
|
|
272078be16 | ||
|
|
493cb48b5f | ||
|
|
3cdb350769 | ||
|
|
2d380ca4d3 | ||
|
|
ec50a66897 | ||
|
|
d2efd6bb99 | ||
|
|
6bf147c651 | ||
|
|
6daa313718 | ||
|
|
dcab637dc1 | ||
|
|
4404f91685 | ||
|
|
8f1c09b963 | ||
|
|
10296ccefe | ||
|
|
2d3bcf5b33 | ||
|
|
9c089ec7d1 | ||
|
|
227d6977dd | ||
|
|
ac55bfb999 | ||
|
|
be87b6b62d | ||
|
|
a28cec61b0 | ||
|
|
31c5a46537 | ||
|
|
95b68384be | ||
|
|
8d8152279e | ||
|
|
0e84a9d47f | ||
|
|
6fa7319498 | ||
|
|
7acfb4d918 | ||
|
|
90aaab5db9 | ||
|
|
b9d08d8b23 | ||
|
|
33ff020d13 | ||
|
|
30f61d38d3 | ||
|
|
4ec6d15013 | ||
|
|
fcb4c8d19b | ||
|
|
e99b195c18 | ||
|
|
978119f268 | ||
|
|
b3d16b28fd | ||
|
|
136457f60c | ||
|
|
29d10ac5ea | ||
|
|
b668ad09d9 | ||
|
|
f4d0564074 | ||
|
|
1b0d8b68d9 | ||
|
|
734456d6e3 | ||
|
|
d2f4de1107 | ||
|
|
e72c56d2e8 | ||
|
|
73712a9dca | ||
|
|
2dbc847bab | ||
|
|
08f6e3d4e1 | ||
|
|
be1ffea531 | ||
|
|
97fbc3b13d | ||
|
|
5351a95900 | ||
|
|
b5d937a497 | ||
|
|
7c1da37676 | ||
|
|
25e896ec40 | ||
|
|
21f240e99a | ||
|
|
7b6a2f7b4d | ||
|
|
5b12a0fe1b | ||
|
|
eaefce91b2 | ||
|
|
382f543539 | ||
|
|
3c5f040e06 | ||
|
|
d58ba49d28 | ||
|
|
9435b7135d | ||
|
|
e802551369 | ||
|
|
522c1f2883 | ||
|
|
11de7bf1a0 | ||
|
|
d01138c4d5 | ||
|
|
637ba7afd2 | ||
|
|
715e597107 | ||
|
|
9dc9092bac | ||
|
|
9ee0283ffa | ||
|
|
9672aa089a | ||
|
|
a6dcfe7b58 | ||
|
|
545398e819 | ||
|
|
ccb13a4c92 | ||
|
|
6e756d9ad0 | ||
|
|
34709bc815 | ||
|
|
054299d45b | ||
|
|
f4ad74de9d | ||
|
|
b20105b7bc | ||
|
|
6600776865 | ||
|
|
3f23255e0f | ||
|
|
615a0ccae1 | ||
|
|
6a098e451e | ||
|
|
a94d4648c8 | ||
|
|
c3d67d3271 | ||
|
|
b9b4e3ebe4 | ||
|
|
adab2ba0de | ||
|
|
e6b82215d2 | ||
|
|
02619e8a47 | ||
|
|
0956255b50 | ||
|
|
4567c425ae | ||
|
|
9371102bf9 | ||
|
|
dbaec8756d | ||
|
|
d3e75bcb3b | ||
|
|
614a748d65 | ||
|
|
1fd1f71abf | ||
|
|
8bf8bdb8ef | ||
|
|
94cb76788d | ||
|
|
f16a9a5f5c | ||
|
|
dccef45600 | ||
|
|
2baa8b750c | ||
|
|
1420deb167 | ||
|
|
e3e8d72e72 | ||
|
|
2c48c1c9d9 | ||
|
|
92a1683675 |
@@ -169,6 +169,7 @@ constraint_view_init (ConstraintView *self)
|
||||
GListModel *guides;
|
||||
GListModel *children;
|
||||
GListModel *constraints;
|
||||
GtkFilter *filter;
|
||||
|
||||
manager = gtk_constraint_layout_new ();
|
||||
gtk_widget_set_layout_manager (GTK_WIDGET (self), manager);
|
||||
@@ -176,8 +177,12 @@ constraint_view_init (ConstraintView *self)
|
||||
all_children = gtk_widget_observe_children (GTK_WIDGET (self));
|
||||
all_constraints = gtk_constraint_layout_observe_constraints (GTK_CONSTRAINT_LAYOUT (manager));
|
||||
guides = gtk_constraint_layout_observe_guides (GTK_CONSTRAINT_LAYOUT (manager));
|
||||
constraints = (GListModel *)gtk_filter_list_model_new (all_constraints, omit_internal, NULL, NULL);
|
||||
children = (GListModel *)gtk_filter_list_model_new (all_children, omit_internal, NULL, NULL);
|
||||
filter = gtk_custom_filter_new (omit_internal, NULL, NULL);
|
||||
constraints = (GListModel *)gtk_filter_list_model_new (all_constraints, filter);
|
||||
g_object_unref (filter);
|
||||
filter = gtk_custom_filter_new (omit_internal, NULL, NULL);
|
||||
children = (GListModel *)gtk_filter_list_model_new (all_children, filter);
|
||||
g_object_unref (filter);
|
||||
|
||||
list = g_list_store_new (G_TYPE_LIST_MODEL);
|
||||
g_list_store_append (list, children);
|
||||
|
||||
@@ -184,58 +184,6 @@ max_input (GtkSpinButton *spin_button,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
min_output (GtkSpinButton *spin_button)
|
||||
{
|
||||
GtkAdjustment *adjustment;
|
||||
double value;
|
||||
GtkWidget *box, *text;
|
||||
|
||||
adjustment = gtk_spin_button_get_adjustment (spin_button);
|
||||
value = gtk_adjustment_get_value (adjustment);
|
||||
|
||||
box = gtk_widget_get_first_child (GTK_WIDGET (spin_button));
|
||||
text = gtk_widget_get_first_child (box);
|
||||
|
||||
if (value == 0.0)
|
||||
{
|
||||
gtk_editable_set_text (GTK_EDITABLE (spin_button), "");
|
||||
gtk_text_set_placeholder_text (GTK_TEXT (text), "unset");
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_text_set_placeholder_text (GTK_TEXT (text), "");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
max_output (GtkSpinButton *spin_button)
|
||||
{
|
||||
GtkAdjustment *adjustment;
|
||||
double value;
|
||||
GtkWidget *box, *text;
|
||||
|
||||
adjustment = gtk_spin_button_get_adjustment (spin_button);
|
||||
value = gtk_adjustment_get_value (adjustment);
|
||||
|
||||
box = gtk_widget_get_first_child (GTK_WIDGET (spin_button));
|
||||
text = gtk_widget_get_first_child (box);
|
||||
|
||||
if (value == (double)G_MAXINT)
|
||||
{
|
||||
gtk_editable_set_text (GTK_EDITABLE (spin_button), "");
|
||||
gtk_text_set_placeholder_text (GTK_TEXT (text), "unset");
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_text_set_placeholder_text (GTK_TEXT (text), "");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
guide_editor_constructed (GObject *object)
|
||||
{
|
||||
@@ -244,16 +192,12 @@ guide_editor_constructed (GObject *object)
|
||||
guide_strength_combo (editor->strength);
|
||||
|
||||
g_signal_connect (editor->min_width, "input", G_CALLBACK (min_input), NULL);
|
||||
g_signal_connect (editor->min_width, "output", G_CALLBACK (min_output), NULL);
|
||||
|
||||
g_signal_connect (editor->min_height, "input", G_CALLBACK (min_input), NULL);
|
||||
g_signal_connect (editor->min_height, "output", G_CALLBACK (min_output), NULL);
|
||||
|
||||
g_signal_connect (editor->max_width, "input", G_CALLBACK (max_input), NULL);
|
||||
g_signal_connect (editor->max_width, "output", G_CALLBACK (max_output), NULL);
|
||||
|
||||
g_signal_connect (editor->max_height, "input", G_CALLBACK (max_input), NULL);
|
||||
g_signal_connect (editor->max_height, "output", G_CALLBACK (max_output), NULL);
|
||||
|
||||
if (editor->guide)
|
||||
{
|
||||
|
||||
247
demos/gtk-demo/award.c
Normal file
247
demos/gtk-demo/award.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "award.h"
|
||||
|
||||
struct _GtkAward
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
char *explanation;
|
||||
char *name;
|
||||
char *title;
|
||||
GDateTime *granted; /* or NULL if not granted */
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_EXPLANATION,
|
||||
PROP_NAME,
|
||||
PROP_TITLE,
|
||||
PROP_GRANTED,
|
||||
|
||||
N_PROPS,
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
G_DEFINE_TYPE (GtkAward, gtk_award, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_award_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkAward *self = GTK_AWARD (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_EXPLANATION:
|
||||
self->explanation = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
self->name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
self->title = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_award_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkAward *self = GTK_AWARD (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_EXPLANATION:
|
||||
g_value_set_string (value, self->explanation);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, self->name);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, self->title);
|
||||
break;
|
||||
|
||||
case PROP_GRANTED:
|
||||
g_value_set_boxed (value, self->granted);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_award_dispose (GObject *object)
|
||||
{
|
||||
GtkAward *self = GTK_AWARD (object);
|
||||
|
||||
g_clear_pointer (&self->name, g_free);
|
||||
g_clear_pointer (&self->title, g_free);
|
||||
g_clear_pointer (&self->granted, g_date_time_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_award_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_award_class_init (GtkAwardClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->set_property = gtk_award_set_property;
|
||||
gobject_class->get_property = gtk_award_get_property;
|
||||
gobject_class->dispose = gtk_award_dispose;
|
||||
|
||||
properties[PROP_EXPLANATION] =
|
||||
g_param_spec_string ("explanation",
|
||||
"Explanation",
|
||||
"How to get the title",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_NAME] =
|
||||
g_param_spec_string ("name",
|
||||
"Name",
|
||||
"internal name of the award",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
"Title",
|
||||
"user-visible title",
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_GRANTED] =
|
||||
g_param_spec_boxed ("granted",
|
||||
"Granted",
|
||||
"Timestamp the award was granted or NULL if not granted yet",
|
||||
G_TYPE_DATE_TIME,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_award_init (GtkAward *self)
|
||||
{
|
||||
}
|
||||
|
||||
GListModel *
|
||||
gtk_award_get_list (void)
|
||||
{
|
||||
static GListModel *list = NULL;
|
||||
|
||||
if (list == NULL)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (GTK_TYPE_AWARD);
|
||||
builder = gtk_builder_new_from_resource ("/awards.ui");
|
||||
list = G_LIST_MODEL (gtk_builder_get_object (builder, "list"));
|
||||
g_object_ref (list);
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
return g_object_ref (list);
|
||||
}
|
||||
|
||||
const char *
|
||||
gtk_award_get_name (GtkAward *award)
|
||||
{
|
||||
return award->name;
|
||||
}
|
||||
|
||||
const char *
|
||||
gtk_award_get_title (GtkAward *award)
|
||||
{
|
||||
return award->title;
|
||||
}
|
||||
|
||||
GDateTime *
|
||||
gtk_award_get_granted (GtkAward *award)
|
||||
{
|
||||
return award->granted;
|
||||
}
|
||||
|
||||
GtkAward *
|
||||
award_find (const char *name)
|
||||
{
|
||||
GListModel *list;
|
||||
GtkAward *self;
|
||||
guint i;
|
||||
|
||||
list = gtk_award_get_list ();
|
||||
g_object_unref (list);
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (list); i++)
|
||||
{
|
||||
self = g_list_model_get_item (list, i);
|
||||
g_object_unref (self);
|
||||
|
||||
if (g_ascii_strcasecmp (name, self->name) == 0)
|
||||
return self;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
award (const char *name)
|
||||
{
|
||||
GtkAward *self;
|
||||
GNotification *notification;
|
||||
|
||||
self = award_find (name);
|
||||
if (self == NULL)
|
||||
{
|
||||
g_warning ("Did not find award \"%s\"", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->granted)
|
||||
return;
|
||||
|
||||
self->granted = g_date_time_new_now_utc ();
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GRANTED]);
|
||||
|
||||
notification = g_notification_new ("You won an award!");
|
||||
g_notification_set_body (notification, self->title);
|
||||
g_application_send_notification (g_application_get_default (), NULL, notification);
|
||||
g_object_unref (notification);
|
||||
}
|
||||
|
||||
18
demos/gtk-demo/award.h
Normal file
18
demos/gtk-demo/award.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef __AWARD_H__
|
||||
#define __AWARD_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_AWARD (gtk_award_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkAward, gtk_award, GTK, AWARD, GObject)
|
||||
|
||||
GListModel * gtk_award_get_list (void);
|
||||
|
||||
const char * gtk_award_get_name (GtkAward *award);
|
||||
const char * gtk_award_get_title (GtkAward *award);
|
||||
GDateTime * gtk_award_get_granted (GtkAward *award);
|
||||
|
||||
void award (const char *name);
|
||||
|
||||
#endif /* __AWARD_H__ */
|
||||
11
demos/gtk-demo/awardlistitem.ui
Normal file
11
demos/gtk-demo/awardlistitem.ui
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface domain="gtk40">
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="GtkListItem" bind-property="position"></property>
|
||||
<property name="margin">6</property>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
89
demos/gtk-demo/awards.ui
Normal file
89
demos/gtk-demo/awards.ui
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GListStore" id="list">
|
||||
<property name="item-type">GtkAward</property>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">demo-inspector</property>
|
||||
<!-- Transformers -->
|
||||
<property name="title" translatable="yes">You got a high-rise double-pump carburetor.</property>
|
||||
<property name="explanation" translatable="yes">Launch the inspector</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">demo-start</property>
|
||||
<!-- The Matrix -->
|
||||
<property name="title" translatable="yes">After this, there is no turning back.</property>
|
||||
<property name="explanation" translatable="yes">Start gtk-demo</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">listbox-reshare</property>
|
||||
<!-- Mean Girls -->
|
||||
<property name="title" translatable="yes">Trying to make fetch happen</property>
|
||||
<property name="explanation" translatable="yes">Reshare a tweet</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">listbox-100th-row</property>
|
||||
<!-- Aladdin -->
|
||||
<property name="title" translatable="yes">The ever impressive, long contained, often imitated, but never duplicated Genie of the lamp.</property>
|
||||
<property name="explanation" translatable="yes">Select a 100th row in a list</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">password-best</property>
|
||||
<!-- Spaceballs -->
|
||||
<property name="title" translatable="yes">I've got the same combination on my luggage!</property>
|
||||
<property name="explanation" translatable="yes">Use "12345" as the password</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">password-correct</property>
|
||||
<!-- Stanley Parable -->
|
||||
<property name="title" translatable="yes">Night Shark 1-1-5</property>
|
||||
<property name="explanation" translatable="yes">Correctly enter a password</property>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">puzzle-give-up</property>
|
||||
<!-- Pretty Woman -->
|
||||
<property name="title" translatable="yes">Big Mistake. Big. Huge!</property>
|
||||
<property name="explanation" translatable="yes">Close the puzzle without finishing it</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">puzzle-solve</property>
|
||||
<!-- The Incredibles -->
|
||||
<property name="title" translatable="yes">That was totally wicked!</property>
|
||||
<property name="explanation" translatable="yes">Solve a puzzle</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">puzzle-solve-animated</property>
|
||||
<!-- The Phantom Menace -->
|
||||
<property name="title" translatable="yes">A surprise to be sure but a welcome one.</property>
|
||||
<property name="explanation" translatable="yes">Solve an animated puzzle</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAward">
|
||||
<property name="name">puzzle-solve-large</property>
|
||||
<!-- Portal -->
|
||||
<property name="title" translatable="yes">Science isn't about WHY. It's about WHY NOT?!</property>
|
||||
<property name="explanation" translatable="yes">Solve a puzzle with at least 20 pieces</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
49
demos/gtk-demo/awardview.c
Normal file
49
demos/gtk-demo/awardview.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/* Awards
|
||||
*
|
||||
* This demo demonstrates how to use lists to show the awards you have collected
|
||||
* while exploring this demo.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* Include the header for accessing the awards */
|
||||
#include "award.h"
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_awardview (GtkWidget *do_widget)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *sw, *listview;
|
||||
GListModel *list;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Awards");
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (window), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_builder_list_item_factory_new_from_resource (NULL, "/awardview/awardlistitem.ui"));
|
||||
list = gtk_award_get_list ();
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), list);
|
||||
g_object_unref (list);
|
||||
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (sw), listview);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/">
|
||||
<file>awards.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/ui">
|
||||
<file preprocess="xml-stripblanks">main.ui</file>
|
||||
<file preprocess="xml-stripblanks">main-listitem.ui</file>
|
||||
<file preprocess="xml-stripblanks">appmenu.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/application_demo">
|
||||
@@ -9,6 +13,9 @@
|
||||
<file>application.ui</file>
|
||||
<file>menus.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/awardview">
|
||||
<file>awardlistitem.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/builder">
|
||||
<file>demo.ui</file>
|
||||
</gresource>
|
||||
@@ -111,6 +118,19 @@
|
||||
<file>gnome-fs-directory.png</file>
|
||||
<file>gnome-fs-regular.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_filebrowser">
|
||||
<file>listview_filebrowser.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_minesweeper">
|
||||
<file>listview_minesweeper.ui</file>
|
||||
<file>listview_minesweeper_cell.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_settings">
|
||||
<file>listview_settings.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview_weather">
|
||||
<file compressed="true">listview_weather.txt</file>
|
||||
</gresource>
|
||||
<gresource prefix="/shortcuts">
|
||||
<file>shortcuts.ui</file>
|
||||
<file>shortcuts-builder.ui</file>
|
||||
@@ -147,6 +167,7 @@
|
||||
</gresource>
|
||||
<gresource prefix="/sources">
|
||||
<file>application_demo.c</file>
|
||||
<file>awardview.c</file>
|
||||
<file>assistant.c</file>
|
||||
<file>builder.c</file>
|
||||
<file>changedisplay.c</file>
|
||||
@@ -189,6 +210,12 @@
|
||||
<file>infobar.c</file>
|
||||
<file>links.c</file>
|
||||
<file>listbox.c</file>
|
||||
<file>listview_applauncher.c</file>
|
||||
<file>listview_clocks.c</file>
|
||||
<file>listview_filebrowser.c</file>
|
||||
<file>listview_minesweeper.c</file>
|
||||
<file>listview_settings.c</file>
|
||||
<file>listview_weather.c</file>
|
||||
<file>list_store.c</file>
|
||||
<file>markup.c</file>
|
||||
<file>menus.c</file>
|
||||
|
||||
@@ -265,6 +265,14 @@ fishbowl_changes_toggled_cb (GtkToggleButton *button,
|
||||
gtk_button_set_icon_name (GTK_BUTTON (button), "changes-allow");
|
||||
}
|
||||
|
||||
char *
|
||||
format_header_cb (GObject *object,
|
||||
guint count,
|
||||
double fps)
|
||||
{
|
||||
return g_strdup_printf ("%u Icons, %.2f fps", count, fps);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_fishbowl (GtkWidget *do_widget)
|
||||
{
|
||||
|
||||
@@ -28,22 +28,12 @@
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label">fps</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="bowl" bind-property="framerate-string"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Icons, </property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" bind-source="bowl" bind-property="count"/>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="format_header_cb">
|
||||
<lookup name="count">bowl</lookup>
|
||||
<lookup name="framerate">bowl</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
|
||||
@@ -13,17 +13,16 @@ in_files = sys.argv[2:]
|
||||
file_output = """
|
||||
typedef GtkWidget *(*GDoDemoFunc) (GtkWidget *do_widget);
|
||||
|
||||
typedef struct _Demo Demo;
|
||||
typedef struct _DemoData DemoData;
|
||||
|
||||
struct _Demo
|
||||
struct _DemoData
|
||||
{
|
||||
gchar *name;
|
||||
gchar *title;
|
||||
gchar *filename;
|
||||
char *name;
|
||||
char *title;
|
||||
char *filename;
|
||||
GDoDemoFunc func;
|
||||
Demo *children;
|
||||
DemoData *children;
|
||||
};
|
||||
|
||||
"""
|
||||
|
||||
# Demo = namedtuple('Demo', ['name', 'title', 'file', 'func'])
|
||||
@@ -67,7 +66,7 @@ for demo in demos:
|
||||
i = 0
|
||||
for parent in parents:
|
||||
id = parent_ids[i]
|
||||
file_output += "\nDemo child" + str(id) + "[] = {\n"
|
||||
file_output += "\nDemoData child" + str(id) + "[] = {\n"
|
||||
# iterate over all demos and check if the name starts with the given parent name
|
||||
for child in demos:
|
||||
if child[1].startswith(parent + "/"):
|
||||
@@ -82,7 +81,7 @@ for parent in parents:
|
||||
# Sort demos by title
|
||||
demos = sorted(demos, key=lambda x: x[1])
|
||||
|
||||
file_output += "\nDemo gtk_demos[] = {\n"
|
||||
file_output += "\nDemoData gtk_demos[] = {\n"
|
||||
for demo in demos:
|
||||
# Do not generate one of these for demos with a parent demo
|
||||
if "/" not in demo[1]:
|
||||
|
||||
@@ -53,7 +53,6 @@ enum {
|
||||
PROP_BENCHMARK,
|
||||
PROP_COUNT,
|
||||
PROP_FRAMERATE,
|
||||
PROP_FRAMERATE_STRING,
|
||||
PROP_UPDATE_DELAY,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
@@ -289,14 +288,6 @@ gtk_fishbowl_get_property (GObject *object,
|
||||
g_value_set_double (value, gtk_fishbowl_get_framerate (fishbowl));
|
||||
break;
|
||||
|
||||
case PROP_FRAMERATE_STRING:
|
||||
{
|
||||
char *s = g_strdup_printf ("%.2f", gtk_fishbowl_get_framerate (fishbowl));
|
||||
g_value_set_string (value, s);
|
||||
g_free (s);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_UPDATE_DELAY:
|
||||
g_value_set_int64 (value, gtk_fishbowl_get_update_delay (fishbowl));
|
||||
break;
|
||||
@@ -350,13 +341,6 @@ gtk_fishbowl_class_init (GtkFishbowlClass *klass)
|
||||
0,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
props[PROP_FRAMERATE_STRING] =
|
||||
g_param_spec_string ("framerate-string",
|
||||
"Framerate as string",
|
||||
"Framerate as string, with 2 decimals",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
props[PROP_UPDATE_DELAY] =
|
||||
g_param_spec_int64 ("update-delay",
|
||||
"Update delay",
|
||||
@@ -508,7 +492,6 @@ gtk_fishbowl_do_update (GtkFishbowl *fishbowl)
|
||||
priv->framerate = ((int)(priv->framerate * 100))/100.0;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_FRAMERATE]);
|
||||
g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_FRAMERATE_STRING]);
|
||||
|
||||
if (!priv->benchmark)
|
||||
return;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "award.h"
|
||||
|
||||
static GdkPixbuf *avatar_pixbuf_other;
|
||||
static GtkWidget *window = NULL;
|
||||
@@ -234,9 +235,9 @@ reshare_clicked (GtkMessageRow *row,
|
||||
{
|
||||
GtkMessageRowPrivate *priv = row->priv;
|
||||
|
||||
award ("listbox-reshare");
|
||||
priv->message->n_reshares++;
|
||||
gtk_message_row_update (row);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -255,11 +256,14 @@ gtk_message_row_state_flags_changed (GtkWidget *widget,
|
||||
{
|
||||
GtkMessageRowPrivate *priv = GTK_MESSAGE_ROW (widget)->priv;
|
||||
GtkStateFlags flags;
|
||||
gboolean visible;
|
||||
|
||||
flags = gtk_widget_get_state_flags (widget);
|
||||
|
||||
gtk_widget_set_visible (priv->extra_buttons_box,
|
||||
flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED));
|
||||
visible = flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED) ? TRUE : FALSE;
|
||||
gtk_widget_set_visible (priv->extra_buttons_box, visible);
|
||||
if (visible && gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (widget)) % 100 == 99)
|
||||
award ("listbox-100th-row");
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_message_row_parent_class)->state_flags_changed (widget, previous_state_flags);
|
||||
}
|
||||
|
||||
199
demos/gtk-demo/listview_applauncher.c
Normal file
199
demos/gtk-demo/listview_applauncher.c
Normal file
@@ -0,0 +1,199 @@
|
||||
/* Lists/Application launcher
|
||||
*
|
||||
* This demo uses the GtkCoverFlow widget as a fancy application launcher.
|
||||
*
|
||||
* It is also a very small introduction to listviews.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* This is the function that creates the #GListModel that we need.
|
||||
* GTK list widgets need a #GListModel to display, as it support change
|
||||
* notifications, but various older APIs do not support one.
|
||||
*/
|
||||
static GListModel *
|
||||
create_application_list (void)
|
||||
{
|
||||
GListStore *store;
|
||||
GList *apps, *l;
|
||||
|
||||
/* We use a #GListStore here, which is a simple array-like list implementation
|
||||
* for manual management.
|
||||
* List models need to know what type of data they provide, so we need to
|
||||
* provide the type here. As we want to do a list of applications, #GAppInfo
|
||||
* is the object we provide.
|
||||
*/
|
||||
store = g_list_store_new (G_TYPE_APP_INFO);
|
||||
|
||||
apps = g_app_info_get_all ();
|
||||
|
||||
for (l = apps; l; l = l->next)
|
||||
{
|
||||
g_list_store_append (store, l->data);
|
||||
}
|
||||
|
||||
g_list_free_full (apps, g_object_unref);
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
/* This is the function we use for setting up new listitems to display.
|
||||
* We add just a #GtkImage here to display the application's icon as this is just
|
||||
* a simple demo.
|
||||
*/
|
||||
static void
|
||||
setup_listitem_cb (GtkListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *image;
|
||||
|
||||
image = gtk_image_new ();
|
||||
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
|
||||
|
||||
gtk_list_item_set_child (list_item, image);
|
||||
}
|
||||
|
||||
/* Here we need to prepare the listitem for displaying its item. We get the
|
||||
* listitem already set up from the previous function, so we can reuse the
|
||||
* #GtkImage widget we set up above.
|
||||
* We get the item - which we know is a #GAppInfo because it comes out of
|
||||
* the model we set up above, grab its icon and display it.
|
||||
*/
|
||||
static void
|
||||
bind_listitem_cb (GtkListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *image;
|
||||
GAppInfo *app_info;
|
||||
|
||||
image = gtk_list_item_get_child (list_item);
|
||||
app_info = gtk_list_item_get_item (list_item);
|
||||
|
||||
gtk_image_set_from_gicon (GTK_IMAGE (image), g_app_info_get_icon (app_info));
|
||||
}
|
||||
|
||||
/* In more complex code, we would also need functions to unbind and teardown
|
||||
* the listitem, but this is simple code, so the default implemntations are
|
||||
* enough. If we had connected signals, this step would have been necessary.
|
||||
*
|
||||
* The #GtkSignalListItemFactory documentation contains more information about
|
||||
* this step.
|
||||
*/
|
||||
|
||||
/* This function is called whenever an item in the list is activated. This is
|
||||
* the simple way to allow reacting to the Enter key or double-clicking on a
|
||||
* listitem.
|
||||
* Of course, it is possible to use far more complex interactions by turning
|
||||
* off activation and adding buttons or other widgets in the setup function
|
||||
* above, but this is a simple demo, so we'll use the simple way.
|
||||
*/
|
||||
static void
|
||||
activate_cb (GtkCoverFlow *coverflow,
|
||||
guint position,
|
||||
gpointer unused)
|
||||
{
|
||||
GAppInfo *app_info;
|
||||
GdkAppLaunchContext *context;
|
||||
GError *error = NULL;
|
||||
|
||||
app_info = g_list_model_get_item (gtk_cover_flow_get_model (coverflow), position);
|
||||
|
||||
/* Prepare the context for launching the application and launch it. This
|
||||
* code is explained in detail in the documentation for #GdkAppLaunchContext
|
||||
* and #GAppInfo.
|
||||
*/
|
||||
context = gdk_display_get_app_launch_context (gtk_widget_get_display (GTK_WIDGET (coverflow)));
|
||||
if (!g_app_info_launch (app_info,
|
||||
NULL,
|
||||
G_APP_LAUNCH_CONTEXT (context),
|
||||
&error))
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
/* And because error handling is important, even a simple demo has it:
|
||||
* We display an error dialog that something went wrong.
|
||||
*/
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (coverflow))),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Could not launch %s", g_app_info_get_display_name (app_info));
|
||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
|
||||
g_clear_error (&error);
|
||||
gtk_widget_show (dialog);
|
||||
}
|
||||
|
||||
g_object_unref (context);
|
||||
g_object_unref (app_info);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_applauncher (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *coverflow, *sw;;
|
||||
GListModel *model;
|
||||
GtkListItemFactory *factory;
|
||||
|
||||
/* Create a window and set a few defaults */
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 640, 320);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Application Launcher");
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK(gtk_widget_destroyed), &window);
|
||||
|
||||
/* The #GtkListitemFactory is what is used to create #GtkListItems
|
||||
* to display the data from the model. So it is absolutely necessary
|
||||
* to create one.
|
||||
* We will use a #GtkSignalListitemFactory because it is the simplest
|
||||
* one to use. Different ones are available for different use cases.
|
||||
* The most powerful one being the #GtkBuilderListItemFactory which
|
||||
* will to everything without requiring any code and just using a
|
||||
* #GtkBuilder .ui file.
|
||||
*/
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_listitem_cb), NULL);
|
||||
|
||||
/* Create the list widget here: We use a coverflow widgets, because it's
|
||||
* the coolest one. We could just as well use other list widgets such
|
||||
* as a #GtkListView or a #GtkGridView and the code would look quite
|
||||
* the same.
|
||||
*/
|
||||
coverflow = gtk_cover_flow_new_with_factory (factory);
|
||||
/* We connect the activate signal here. It's the function you remember
|
||||
* from above for launching the selected application.
|
||||
*/
|
||||
g_signal_connect (coverflow, "activate", G_CALLBACK (activate_cb), NULL);
|
||||
|
||||
/* And of course we need to set the data model. Here we call the function
|
||||
* we wrote above that gives us the list of applications and then we set
|
||||
* it on the coverflow list widget.
|
||||
* The coverflow will now take items from the model and use the factory
|
||||
* to create as many listitems as it needs to show itself to the user.
|
||||
*/
|
||||
model = create_application_list ();
|
||||
gtk_cover_flow_set_model (GTK_COVER_FLOW (coverflow), model);
|
||||
g_object_unref (model);
|
||||
|
||||
/* List widgets should always be contained in a #GtkScrolledWindow,
|
||||
* because otherwise they might get too large or they might not
|
||||
* be scrollable.
|
||||
*/
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (window), sw);
|
||||
gtk_container_add (GTK_CONTAINER (sw), coverflow);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
488
demos/gtk-demo/listview_clocks.c
Normal file
488
demos/gtk-demo/listview_clocks.c
Normal file
@@ -0,0 +1,488 @@
|
||||
/* Lists/Clocks
|
||||
*
|
||||
* This demo displays the time in different timezones.
|
||||
*
|
||||
* The goal is to show how to set up expressions that track changes
|
||||
* in objects and make them update widgets.
|
||||
*
|
||||
* For that, we create a GtkClock object that updates its time every
|
||||
* second and then use various ways to display that time.
|
||||
*
|
||||
* Typically, this will be done using GtkBuilder .ui files with the
|
||||
* help of the <binding> tag, but this demo shows the code that runs
|
||||
* behind that.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_CLOCK (gtk_clock_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkClock, gtk_clock, GTK, CLOCK, GObject)
|
||||
|
||||
/* This is our object. It's just a timezone */
|
||||
typedef struct _GtkClock GtkClock;
|
||||
struct _GtkClock
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/* We allow this to be NULL for the local timezone */
|
||||
GTimeZone *timezone;
|
||||
/* Name of the location we're displaying time for */
|
||||
char *location;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_LOCATION,
|
||||
PROP_TIME,
|
||||
PROP_TIMEZONE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
/* This function returns the current time in the clock's timezone.
|
||||
* Note that this returns a new object every time, so we need to
|
||||
* remember to unref it after use. */
|
||||
static GDateTime *
|
||||
gtk_clock_get_time (GtkClock *clock)
|
||||
{
|
||||
if (clock->timezone)
|
||||
return g_date_time_new_now (clock->timezone);
|
||||
else
|
||||
return g_date_time_new_now_local ();
|
||||
}
|
||||
|
||||
/* Here, we implement the functionality required by the GdkPaintable interface.
|
||||
* This way we have a trivial way to display an analog clock.
|
||||
* It also allows demonstrating how to directly use objects in the listview
|
||||
* later by making this object do something interesting. */
|
||||
static void
|
||||
gtk_clock_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height)
|
||||
{
|
||||
GtkClock *self = GTK_CLOCK (paintable);
|
||||
GDateTime *time;
|
||||
GskRoundedRect outline;
|
||||
|
||||
#define BLACK ((GdkRGBA) { 0, 0, 0, 1 })
|
||||
|
||||
/* save/restore() is necessary so we can undo the transforms we start
|
||||
* out with. */
|
||||
gtk_snapshot_save (snapshot);
|
||||
|
||||
/* First, we move the (0, 0) point to the center of the area so
|
||||
* we can draw everything relative to it. */
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (width / 2, height / 2));
|
||||
/* Next we scale it, so that we can pretend that the clock is
|
||||
* 100px in size. That way, we don't need to do any complicated
|
||||
* math later.
|
||||
* We use MIN() here so that we use the smaller dimension for sizing.
|
||||
* That way we don't overdraw but keep the aspect ratio. */
|
||||
gtk_snapshot_scale (snapshot, MIN (width, height) / 100.0, MIN (width, height) / 100.0);
|
||||
/* Now we have a circle with diameter 100px (and radius 50px) that
|
||||
* has its (0, 0) point at the center.
|
||||
* Let's draw a simple clock into it. */
|
||||
|
||||
time = gtk_clock_get_time (self);
|
||||
|
||||
/* First, draw a circle. This is a neat little trick to draw a circle
|
||||
* without requiring Cairo. */
|
||||
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-50, -50, 100, 100), 50);
|
||||
gtk_snapshot_append_border (snapshot,
|
||||
&outline,
|
||||
(float[4]) { 4, 4, 4, 4 },
|
||||
(GdkRGBA [4]) { BLACK, BLACK, BLACK, BLACK });
|
||||
|
||||
/* Next, draw the hour hand.
|
||||
* We do this using tranforms again: Instead of computing where the angle points
|
||||
* to, we just rotate everything and then draw the hand as if if was :00.
|
||||
* We don't even need to care about am/pm here because rotations just work. */
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_rotate (snapshot, 30 * g_date_time_get_hour (time) + 0.5 * g_date_time_get_minute (time));
|
||||
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -23, 4, 25), 2);
|
||||
gtk_snapshot_push_rounded_clip (snapshot, &outline);
|
||||
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
/* And the same as above for the minute hand. Just make this one longer
|
||||
* so people can tell the hands apart. */
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_rotate (snapshot, 6 * g_date_time_get_minute (time));
|
||||
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -43, 4, 45), 2);
|
||||
gtk_snapshot_push_rounded_clip (snapshot, &outline);
|
||||
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
/* and finally, the second indicator. */
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_rotate (snapshot, 6 * g_date_time_get_second (time));
|
||||
gsk_rounded_rect_init_from_rect (&outline, &GRAPHENE_RECT_INIT(-2, -43, 4, 10), 2);
|
||||
gtk_snapshot_push_rounded_clip (snapshot, &outline);
|
||||
gtk_snapshot_append_color (snapshot, &BLACK, &outline.bounds);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
/* And finally, don't forget to restore the initial save() that we did for
|
||||
* the initial transformations. */
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
g_date_time_unref (time);
|
||||
}
|
||||
|
||||
/* Our desired size is 100px. That sounds okay for an analog clock */
|
||||
static int
|
||||
gtk_clock_get_intrinsic_width (GdkPaintable *paintable)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_clock_get_intrinsic_height (GdkPaintable *paintable)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
/* Initialize the paintable interface. This way we turn our clock objects
|
||||
* into objects that can be drawn.
|
||||
* There are more functions to this interface to define desired size,
|
||||
* but this is enough.
|
||||
*/
|
||||
static void
|
||||
gtk_clock_paintable_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->snapshot = gtk_clock_snapshot;
|
||||
iface->get_intrinsic_width = gtk_clock_get_intrinsic_width;
|
||||
iface->get_intrinsic_height = gtk_clock_get_intrinsic_height;
|
||||
}
|
||||
|
||||
/* Finally, we define the type. The important part is adding the paintable
|
||||
* interface, so GTK knows that this object can indeed be drawm.
|
||||
*/
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkClock, gtk_clock, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
gtk_clock_paintable_init))
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_clock_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkClock *self = GTK_CLOCK (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_LOCATION:
|
||||
g_value_set_string (value, self->location);
|
||||
break;
|
||||
|
||||
case PROP_TIME:
|
||||
g_value_take_boxed (value, gtk_clock_get_time (self));
|
||||
break;
|
||||
|
||||
case PROP_TIMEZONE:
|
||||
g_value_set_boxed (value, self->timezone);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkClock *self = GTK_CLOCK (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_LOCATION:
|
||||
self->location = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_TIMEZONE:
|
||||
self->timezone = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the list of all the ticking clocks */
|
||||
static GSList *ticking_clocks = NULL;
|
||||
/* This is the id of the timeout source that is updating all ticking clocks */
|
||||
static guint ticking_clock_id = 0;
|
||||
|
||||
/* Every second, this function is called to tell everybody that the
|
||||
* clocks are ticking.
|
||||
*/
|
||||
static gboolean
|
||||
gtk_clock_tick (gpointer unused)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = ticking_clocks; l; l = l->next)
|
||||
{
|
||||
GtkClock *clock = l->data;
|
||||
|
||||
/* We will now return a different value for the time porperty,
|
||||
* so notify about that.
|
||||
*/
|
||||
g_object_notify_by_pspec (G_OBJECT (clock), properties[PROP_TIME]);
|
||||
/* We will also draw the hands of the clock differently.
|
||||
* So notify about that, too.
|
||||
*/
|
||||
gdk_paintable_invalidate_contents (GDK_PAINTABLE (clock));
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_stop_ticking (GtkClock *self)
|
||||
{
|
||||
ticking_clocks = g_slist_remove (ticking_clocks, self);
|
||||
|
||||
/* If no clock is remaining, stop running the tick updates */
|
||||
if (ticking_clocks == NULL && ticking_clock_id != 0)
|
||||
g_clear_handle_id (&ticking_clock_id, g_source_remove);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_start_ticking (GtkClock *self)
|
||||
{
|
||||
/* if no clock is ticking yet, start */
|
||||
if (ticking_clock_id == 0)
|
||||
ticking_clock_id = g_timeout_add_seconds (1, gtk_clock_tick, NULL);
|
||||
|
||||
ticking_clocks = g_slist_prepend (ticking_clocks, self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_finalize (GObject *object)
|
||||
{
|
||||
GtkClock *self = GTK_CLOCK (object);
|
||||
|
||||
gtk_clock_stop_ticking (self);
|
||||
|
||||
g_free (self->location);
|
||||
g_clear_pointer (&self->timezone, g_time_zone_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_clock_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_class_init (GtkClockClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_clock_get_property;
|
||||
gobject_class->set_property = gtk_clock_set_property;
|
||||
gobject_class->finalize = gtk_clock_finalize;
|
||||
|
||||
properties[PROP_LOCATION] =
|
||||
g_param_spec_string ("location", NULL, NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
properties[PROP_TIME] =
|
||||
g_param_spec_boxed ("time", NULL, NULL, G_TYPE_DATE_TIME, G_PARAM_READABLE);
|
||||
properties[PROP_TIMEZONE] =
|
||||
g_param_spec_boxed ("timezone", NULL, NULL, G_TYPE_TIME_ZONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_clock_init (GtkClock *self)
|
||||
{
|
||||
gtk_clock_start_ticking (self);
|
||||
}
|
||||
|
||||
static GtkClock *
|
||||
gtk_clock_new (const char *location,
|
||||
GTimeZone *timezone)
|
||||
{
|
||||
GtkClock *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_CLOCK,
|
||||
"location", location,
|
||||
"timezone", timezone,
|
||||
NULL);
|
||||
|
||||
g_clear_pointer (&timezone, g_time_zone_unref);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_clocks_model (void)
|
||||
{
|
||||
GListStore *result;
|
||||
GtkClock *clock;
|
||||
|
||||
result = g_list_store_new (GTK_TYPE_CLOCK);
|
||||
|
||||
/* local time */
|
||||
clock = gtk_clock_new ("local", NULL);
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
/* UTC time */
|
||||
clock = gtk_clock_new ("UTC", g_time_zone_new_utc ());
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
/* A bunch of timezones with GTK hackers */
|
||||
clock = gtk_clock_new ("San Francisco", g_time_zone_new ("America/Los_Angeles"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("Boston", g_time_zone_new ("America/New_York"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("London", g_time_zone_new ("Europe/London"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("Berlin", g_time_zone_new ("Europe/Berlin"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("Moscow", g_time_zone_new ("Europe/Moscow"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("New Delhi", g_time_zone_new ("Asia/Kolkata"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
clock = gtk_clock_new ("Shanghai", g_time_zone_new ("Asia/Shanghai"));
|
||||
g_list_store_append (result, clock);
|
||||
g_object_unref (clock);
|
||||
|
||||
return G_LIST_MODEL (result);
|
||||
}
|
||||
|
||||
static char *
|
||||
convert_time_to_string (GObject *image,
|
||||
GDateTime *time,
|
||||
gpointer unused)
|
||||
{
|
||||
return g_date_time_format (time, "%x\n%X");
|
||||
}
|
||||
|
||||
/* And this function is the crux for this whole demo.
|
||||
* It shows how to use expressions to set up bindings.
|
||||
*/
|
||||
static void
|
||||
setup_listitem_cb (GtkListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *box, *picture, *location_label, *time_label;
|
||||
GtkExpression *clock_expression, *expression;
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_list_item_set_child (list_item, box);
|
||||
|
||||
/* First, we create an expression that gets us the clock from the listitem:
|
||||
* 1. Create an expression that gets the list item.
|
||||
* 2. Use that expression's "item" property to get the clock
|
||||
*/
|
||||
expression = gtk_constant_expression_new (GTK_TYPE_LIST_ITEM, list_item);
|
||||
clock_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, "item");
|
||||
|
||||
/* Bind the clock's location to a label.
|
||||
* This is easy: We just get the "location" property of the clock.
|
||||
*/
|
||||
expression = gtk_property_expression_new (GTK_TYPE_CLOCK,
|
||||
gtk_expression_ref (clock_expression),
|
||||
"location");
|
||||
/* Now create the label and bind the expression to it. */
|
||||
location_label = gtk_label_new (NULL);
|
||||
gtk_expression_bind (expression, location_label, location_label, "label");
|
||||
gtk_container_add (GTK_CONTAINER (box), location_label);
|
||||
|
||||
|
||||
/* Here we bind the item itself to a GdkPicture.
|
||||
* This is simply done by using the clock expression itself.
|
||||
*/
|
||||
expression = gtk_expression_ref (clock_expression);
|
||||
/* Now create the widget and bind the expression to it. */
|
||||
picture = gtk_picture_new ();
|
||||
gtk_expression_bind (expression, picture, picture, "paintable");
|
||||
gtk_container_add (GTK_CONTAINER (box), picture);
|
||||
|
||||
|
||||
/* And finally, everything comes together.
|
||||
* We create a label for displaying the time as text.
|
||||
* For that, we need to transform the "GDateTime" of the
|
||||
* time property into a string so that the label can display it.
|
||||
*/
|
||||
expression = gtk_property_expression_new (GTK_TYPE_CLOCK,
|
||||
gtk_expression_ref (clock_expression),
|
||||
"time");
|
||||
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
|
||||
NULL,
|
||||
1, (GtkExpression *[1]) { expression },
|
||||
G_CALLBACK (convert_time_to_string),
|
||||
NULL, NULL);
|
||||
/* Now create the label and bind the expression to it. */
|
||||
time_label = gtk_label_new (NULL);
|
||||
gtk_expression_bind (expression, time_label, time_label, "label");
|
||||
gtk_container_add (GTK_CONTAINER (box), time_label);
|
||||
|
||||
gtk_expression_unref (clock_expression);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_clocks (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *gridview, *sw;
|
||||
GtkListItemFactory *factory;
|
||||
GListModel *model;
|
||||
GtkNoSelection *selection;
|
||||
|
||||
/* This is the normal window setup code every demo does */
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
/* List widgets go into a scrolled window. Always. */
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (window), sw);
|
||||
|
||||
/* Create the factory that creates the listitems. Because we
|
||||
* used bindings above during setup, we only need to connect
|
||||
* to the setup signal.
|
||||
* The bindings take care of the bind step.
|
||||
*/
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
|
||||
|
||||
gridview = gtk_grid_view_new_with_factory (factory);
|
||||
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
|
||||
model = create_clocks_model ();
|
||||
selection = gtk_no_selection_new (model);
|
||||
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), G_LIST_MODEL (selection));
|
||||
gtk_container_add (GTK_CONTAINER (sw), gridview);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
255
demos/gtk-demo/listview_filebrowser.c
Normal file
255
demos/gtk-demo/listview_filebrowser.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/* Lists/File browser
|
||||
*
|
||||
* This demo shows off the different layouts that are quickly achievable
|
||||
* with GtkGridView by implementing a file browser with different views.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
/* Create a simple object that holds the data for the different views */
|
||||
typedef struct _FileBrowserView FileBrowserView;
|
||||
struct _FileBrowserView
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemFactory *factory;
|
||||
char *icon_name;
|
||||
GtkOrientation orientation;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_ICON_NAME,
|
||||
PROP_ORIENTATION,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
#define FILE_BROWSER_TYPE_VIEW (file_browser_view_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (FileBrowserView, file_browser_view, FILE_BROWSER, VIEW, GObject);
|
||||
|
||||
G_DEFINE_TYPE (FileBrowserView, file_browser_view, G_TYPE_OBJECT);
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
file_browser_view_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, self->factory);
|
||||
break;
|
||||
|
||||
case PROP_ICON_NAME:
|
||||
g_value_set_string (value, self->icon_name);
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
g_value_set_enum (value, self->orientation);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_set_object (&self->factory, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_ICON_NAME:
|
||||
g_free (self->icon_name);
|
||||
self->icon_name = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_ORIENTATION:
|
||||
self->orientation = g_value_get_enum (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_finalize (GObject *object)
|
||||
{
|
||||
FileBrowserView *self = FILE_BROWSER_VIEW (object);
|
||||
|
||||
g_object_unref (self->factory);
|
||||
g_free (self->icon_name);
|
||||
|
||||
G_OBJECT_CLASS (file_browser_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
file_browser_view_class_init (FileBrowserViewClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = file_browser_view_get_property;
|
||||
gobject_class->set_property = file_browser_view_set_property;
|
||||
gobject_class->finalize = file_browser_view_finalize;
|
||||
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
"factory",
|
||||
"factory to use in the main view",
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE);
|
||||
properties[PROP_ICON_NAME] =
|
||||
g_param_spec_string ("icon-name",
|
||||
"icon name",
|
||||
"icon to display for selecting this view",
|
||||
NULL,
|
||||
G_PARAM_READWRITE);
|
||||
properties[PROP_ORIENTATION] =
|
||||
g_param_spec_enum ("orientation",
|
||||
"orientation",
|
||||
"orientation of the view",
|
||||
GTK_TYPE_ORIENTATION,
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void file_browser_view_init (FileBrowserView *self)
|
||||
{
|
||||
}
|
||||
|
||||
char *
|
||||
filebrowser_get_display_name (GObject *object,
|
||||
GFileInfo *info)
|
||||
{
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
return g_strdup (g_file_info_get_attribute_string (info, "standard::display-name"));
|
||||
}
|
||||
|
||||
char *
|
||||
filebrowser_get_content_type (GObject *object,
|
||||
GFileInfo *info)
|
||||
{
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
return g_strdup (g_file_info_get_attribute_string (info, "standard::content-type"));
|
||||
}
|
||||
|
||||
char *
|
||||
filebrowser_get_size (GObject *object,
|
||||
GFileInfo *info)
|
||||
{
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
return g_format_size (g_file_info_get_attribute_uint64 (info, "standard::size"));
|
||||
}
|
||||
|
||||
GIcon *
|
||||
filebrowser_get_icon (GObject *object,
|
||||
GFileInfo *info)
|
||||
{
|
||||
GIcon *icon;
|
||||
|
||||
if (info)
|
||||
icon = G_ICON (g_file_info_get_attribute_object (info, "standard::icon"));
|
||||
else
|
||||
icon = NULL;
|
||||
|
||||
if (icon)
|
||||
g_object_ref (icon);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
void
|
||||
filebrowser_up_clicked_cb (GtkButton *button,
|
||||
GtkDirectoryList *list)
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
file = g_file_get_parent (gtk_directory_list_get_file (list));
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
gtk_directory_list_set_file (list, file);
|
||||
}
|
||||
|
||||
void
|
||||
filebrowser_view_activated_cb (GtkGridView *view,
|
||||
guint pos,
|
||||
GtkDirectoryList *list)
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
info = g_list_model_get_item (gtk_grid_view_get_model (view), pos);
|
||||
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
|
||||
gtk_directory_list_set_file (list, G_FILE (g_file_info_get_attribute_object (info, "standard::file")));
|
||||
|
||||
g_object_unref (info);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_listview_filebrowser (GtkWidget *do_widget)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *view;
|
||||
GtkBuilder *builder;
|
||||
GtkDirectoryList *dirlist;
|
||||
GFile *file;
|
||||
char *cwd;
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/listview_filebrowser/listview_filebrowser.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
/* Create the model and fill it with the contents of the current directory */
|
||||
cwd = g_get_current_dir ();
|
||||
file = g_file_new_for_path (cwd);
|
||||
g_free (cwd);
|
||||
dirlist = GTK_DIRECTORY_LIST (gtk_builder_get_object (builder, "dirlist"));
|
||||
gtk_directory_list_set_file (dirlist, file);
|
||||
g_object_unref (file);
|
||||
|
||||
/* grab focus in the view */
|
||||
view = GTK_WIDGET (gtk_builder_get_object (builder, "view"));
|
||||
gtk_widget_grab_focus (view);
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
240
demos/gtk-demo/listview_filebrowser.ui
Normal file
240
demos/gtk-demo/listview_filebrowser.ui
Normal file
@@ -0,0 +1,240 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GListStore" id="viewlist">
|
||||
<property name="item-type">FileBrowserView</property>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="filebrowser_get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_display_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="icon-name">view-list-symbolic</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="icon-name">view-grid-symbolic</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">large</property>
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="filebrowser_get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="wrap">1</property>
|
||||
<property name="wrap-mode">word-char</property>
|
||||
<property name="lines">2</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="width-chars">10</property>
|
||||
<property name="max-width-chars">30</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_display_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="orientation">vertical</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="FileBrowserView">
|
||||
<property name="icon-name">view-paged-symbolic</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-size">large</property>
|
||||
<binding name="gicon">
|
||||
<closure type="GIcon" function="filebrowser_get_icon">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_display_name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_size">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<closure type="gchararray" function="filebrowser_get_content_type">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</closure>
|
||||
</binding>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkDirectoryList" id="dirlist">
|
||||
<property name="attributes">standard::name,standard::display-name,standard::icon,standard::size,standard::content-type</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">File browser</property>
|
||||
<property name="default-width">600</property>
|
||||
<property name="default-height">400</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="">
|
||||
<property name="show-title-buttons">1</property>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">go-up</property>
|
||||
<signal name="clicked" handler="filebrowser_up_clicked_cb" object="dirlist" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkListView">
|
||||
<property name="valign">center</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
<style>
|
||||
<class name="linked"/>
|
||||
</style>
|
||||
<property name="model">
|
||||
<object class="GtkSingleSelection" id="selected-view">
|
||||
<property name="model">viewlist</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkImage">
|
||||
<binding name="icon-name">
|
||||
<lookup type="FileBrowserView" name="icon-name">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="can-focus">1</property>
|
||||
<child>
|
||||
<object class="GtkGridView" id="view">
|
||||
<property name="model">dirlist</property>
|
||||
<property name="max-columns">15</property>
|
||||
<binding name="factory">
|
||||
<lookup name="factory" type="FileBrowserView">
|
||||
<lookup name="selected-item">selected-view</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="orientation">
|
||||
<lookup name="orientation" type="FileBrowserView">
|
||||
<lookup name="selected-item">selected-view</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<signal name="activate" handler="filebrowser_view_activated_cb" object="dirlist" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
473
demos/gtk-demo/listview_minesweeper.c
Normal file
473
demos/gtk-demo/listview_minesweeper.c
Normal file
@@ -0,0 +1,473 @@
|
||||
/* Lists/Minesweeper
|
||||
*
|
||||
* This demo shows how to develop a user interface for small game using a
|
||||
* gridview.
|
||||
*
|
||||
* It demonstrates how to use the activate signal and single-press behavior
|
||||
* to implement rather different interaction behavior to a typical list.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/*** The cell object ***/
|
||||
|
||||
/* Create an object that holds the data for a cell in the game */
|
||||
typedef struct _SweeperCell SweeperCell;
|
||||
struct _SweeperCell
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
gboolean is_mine;
|
||||
gboolean is_visible;
|
||||
guint neighbor_mines;
|
||||
};
|
||||
|
||||
enum {
|
||||
CELL_PROP_0,
|
||||
CELL_PROP_LABEL,
|
||||
|
||||
N_CELL_PROPS
|
||||
};
|
||||
|
||||
#define SWEEPER_TYPE_CELL (sweeper_cell_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (SweeperCell, sweeper_cell, SWEEPER, CELL, GObject);
|
||||
|
||||
G_DEFINE_TYPE (SweeperCell, sweeper_cell, G_TYPE_OBJECT);
|
||||
static GParamSpec *cell_properties[N_CELL_PROPS] = { NULL, };
|
||||
|
||||
static const char *
|
||||
sweeper_cell_get_label (SweeperCell *self)
|
||||
{
|
||||
static const char *minecount_labels[10] = { "", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||||
|
||||
if (!self->is_visible)
|
||||
return "?";
|
||||
|
||||
if (self->is_mine)
|
||||
return "💣";
|
||||
|
||||
return minecount_labels[self->neighbor_mines];
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SweeperCell *self = SWEEPER_CELL (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case CELL_PROP_LABEL:
|
||||
g_value_set_string (value, sweeper_cell_get_label (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_class_init (SweeperCellClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = sweeper_cell_get_property;
|
||||
|
||||
cell_properties[CELL_PROP_LABEL] =
|
||||
g_param_spec_string ("label",
|
||||
"label",
|
||||
"label to display for this row",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_CELL_PROPS, cell_properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_init (SweeperCell *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_cell_reveal (SweeperCell *self)
|
||||
{
|
||||
if (self->is_visible)
|
||||
return;
|
||||
|
||||
self->is_visible = TRUE;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), cell_properties[CELL_PROP_LABEL]);
|
||||
}
|
||||
|
||||
static SweeperCell *
|
||||
sweeper_cell_new ()
|
||||
{
|
||||
return g_object_new (SWEEPER_TYPE_CELL, NULL);
|
||||
}
|
||||
|
||||
/*** The board object ***/
|
||||
|
||||
/* Create an object that holds the data for the game */
|
||||
typedef struct _SweeperGame SweeperGame;
|
||||
struct _SweeperGame
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GPtrArray *cells;
|
||||
guint width;
|
||||
guint height;
|
||||
gboolean playing;
|
||||
gboolean win;
|
||||
};
|
||||
|
||||
enum {
|
||||
GAME_PROP_0,
|
||||
GAME_PROP_HEIGHT,
|
||||
GAME_PROP_PLAYING,
|
||||
GAME_PROP_WIDTH,
|
||||
GAME_PROP_WIN,
|
||||
|
||||
N_GAME_PROPS
|
||||
};
|
||||
|
||||
#define SWEEPER_TYPE_GAME (sweeper_game_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (SweeperGame, sweeper_game, SWEEPER, GAME, GObject);
|
||||
|
||||
static GType
|
||||
sweeper_game_list_model_get_item_type (GListModel *model)
|
||||
{
|
||||
return SWEEPER_TYPE_GAME;
|
||||
}
|
||||
|
||||
static guint
|
||||
sweeper_game_list_model_get_n_items (GListModel *model)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (model);
|
||||
|
||||
return self->width * self->height;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
sweeper_game_list_model_get_item (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (model);
|
||||
|
||||
return g_object_ref (g_ptr_array_index (self->cells, position));
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = sweeper_game_list_model_get_item_type;
|
||||
iface->get_n_items = sweeper_game_list_model_get_n_items;
|
||||
iface->get_item = sweeper_game_list_model_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (SweeperGame, sweeper_game, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, sweeper_game_list_model_init))
|
||||
|
||||
static GParamSpec *game_properties[N_GAME_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
sweeper_game_dispose (GObject *object)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (object);
|
||||
|
||||
g_clear_pointer (&self->cells, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (sweeper_game_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SweeperGame *self = SWEEPER_GAME (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case GAME_PROP_HEIGHT:
|
||||
g_value_set_uint (value, self->height);
|
||||
break;
|
||||
|
||||
case GAME_PROP_PLAYING:
|
||||
g_value_set_boolean (value, self->playing);
|
||||
break;
|
||||
|
||||
case GAME_PROP_WIDTH:
|
||||
g_value_set_uint (value, self->width);
|
||||
break;
|
||||
|
||||
case GAME_PROP_WIN:
|
||||
g_value_set_boolean (value, self->win);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_class_init (SweeperGameClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = sweeper_game_dispose;
|
||||
gobject_class->get_property = sweeper_game_get_property;
|
||||
|
||||
game_properties[GAME_PROP_HEIGHT] =
|
||||
g_param_spec_uint ("height",
|
||||
"height",
|
||||
"height of the game grid",
|
||||
1, G_MAXUINT, 8,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_PLAYING] =
|
||||
g_param_spec_boolean ("playing",
|
||||
"playing",
|
||||
"if the game is still going on",
|
||||
FALSE,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_WIDTH] =
|
||||
g_param_spec_uint ("width",
|
||||
"width",
|
||||
"width of the game grid",
|
||||
1, G_MAXUINT, 8,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
game_properties[GAME_PROP_WIN] =
|
||||
g_param_spec_boolean ("win",
|
||||
"win",
|
||||
"if the game was won",
|
||||
FALSE,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_GAME_PROPS, game_properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_reset_board (SweeperGame *self,
|
||||
guint width,
|
||||
guint height)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_ptr_array_set_size (self->cells, 0);
|
||||
|
||||
for (i = 0; i < width * height; i++)
|
||||
{
|
||||
g_ptr_array_add (self->cells, sweeper_cell_new ());
|
||||
}
|
||||
|
||||
if (self->width != width)
|
||||
{
|
||||
self->width = width;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIDTH]);
|
||||
}
|
||||
if (self->height != height)
|
||||
{
|
||||
self->height = height;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_HEIGHT]);
|
||||
}
|
||||
if (!self->playing)
|
||||
{
|
||||
self->playing = TRUE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_PLAYING]);
|
||||
}
|
||||
if (self->win)
|
||||
{
|
||||
self->win = FALSE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIN]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_place_mines (SweeperGame *self,
|
||||
guint n_mines)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < n_mines; i++)
|
||||
{
|
||||
SweeperCell *cell;
|
||||
|
||||
do {
|
||||
cell = g_ptr_array_index (self->cells, g_random_int_range (0, self->cells->len));
|
||||
} while (cell->is_mine);
|
||||
|
||||
cell->is_mine = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static SweeperCell *
|
||||
get_cell (SweeperGame *self,
|
||||
guint x,
|
||||
guint y)
|
||||
{
|
||||
return g_ptr_array_index (self->cells, y * self->width + x);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_count_neighbor_mines (SweeperGame *self,
|
||||
guint width,
|
||||
guint height)
|
||||
{
|
||||
guint x, y, x2, y2;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
SweeperCell *cell = get_cell (self, x, y);
|
||||
|
||||
for (y2 = MAX (1, y) - 1; y2 < MIN (height, y + 2); y2++)
|
||||
{
|
||||
for (x2 = MAX (1, x) - 1; x2 < MIN (width, x + 2); x2++)
|
||||
{
|
||||
SweeperCell *other = get_cell (self, x2, y2);
|
||||
|
||||
if (other->is_mine)
|
||||
cell->neighbor_mines++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_new_game (SweeperGame *self,
|
||||
guint width,
|
||||
guint height,
|
||||
guint n_mines)
|
||||
{
|
||||
guint n_items_before;
|
||||
|
||||
g_return_if_fail (n_mines <= width * height);
|
||||
|
||||
n_items_before = self->width * self->height;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
sweeper_game_reset_board (self, width, height);
|
||||
sweeper_game_place_mines (self, n_mines);
|
||||
sweeper_game_count_neighbor_mines (self, width, height);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items_before, width * height);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_init (SweeperGame *self)
|
||||
{
|
||||
self->cells = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
sweeper_game_new_game (self, 8, 8, 10);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_end (SweeperGame *self,
|
||||
gboolean win)
|
||||
{
|
||||
if (self->playing)
|
||||
{
|
||||
self->playing = FALSE;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_PLAYING]);
|
||||
}
|
||||
if (self->win != win)
|
||||
{
|
||||
self->win = win;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), game_properties[GAME_PROP_WIN]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_check_finished (SweeperGame *self)
|
||||
{
|
||||
guint i;
|
||||
|
||||
if (!self->playing)
|
||||
return;
|
||||
|
||||
for (i = 0; i < self->cells->len; i++)
|
||||
{
|
||||
SweeperCell *cell = g_ptr_array_index (self->cells, i);
|
||||
|
||||
/* There's still a non-revealed cell that isn't a mine */
|
||||
if (!cell->is_visible && !cell->is_mine)
|
||||
return;
|
||||
}
|
||||
|
||||
sweeper_game_end (self, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
sweeper_game_reveal_cell (SweeperGame *self,
|
||||
guint position)
|
||||
{
|
||||
SweeperCell *cell;
|
||||
|
||||
if (!self->playing)
|
||||
return;
|
||||
|
||||
cell = g_ptr_array_index (self->cells, position);
|
||||
sweeper_cell_reveal (cell);
|
||||
|
||||
if (cell->is_mine)
|
||||
sweeper_game_end (self, FALSE);
|
||||
|
||||
sweeper_game_check_finished (self);
|
||||
}
|
||||
|
||||
void
|
||||
minesweeper_cell_clicked_cb (GtkGridView *gridview,
|
||||
guint pos,
|
||||
SweeperGame *game)
|
||||
{
|
||||
sweeper_game_reveal_cell (game, pos);
|
||||
}
|
||||
|
||||
void
|
||||
minesweeper_new_game_cb (GtkButton *button,
|
||||
SweeperGame *game)
|
||||
{
|
||||
sweeper_game_new_game (game, 8, 8, 10);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_minesweeper (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (SWEEPER_TYPE_GAME);
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/listview_minesweeper/listview_minesweeper.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
48
demos/gtk-demo/listview_minesweeper.ui
Normal file
48
demos/gtk-demo/listview_minesweeper.ui
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="SweeperGame" id="game">
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">Minesweeper</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="">
|
||||
<property name="show-title-buttons">1</property>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label">New Game</property>
|
||||
<signal name="clicked" handler="minesweeper_new_game_cb" object="game" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">trophy-gold</property>
|
||||
<binding name="visible">
|
||||
<lookup name="win">game</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGridView" id="view">
|
||||
<property name="model">
|
||||
<object class="GtkNoSelection">
|
||||
<property name="model">game</property>
|
||||
</object>
|
||||
</property>
|
||||
<binding name="max-columns">
|
||||
<lookup name="width">game</lookup>
|
||||
</binding>
|
||||
<binding name="min-columns">
|
||||
<lookup name="width">game</lookup>
|
||||
</binding>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="resource">/listview_minesweeper/listview_minesweeper_cell.ui</property>
|
||||
</object>
|
||||
</property>
|
||||
<signal name="activate" handler="minesweeper_cell_clicked_cb" object="game" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
16
demos/gtk-demo/listview_minesweeper_cell.ui
Normal file
16
demos/gtk-demo/listview_minesweeper_cell.ui
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<binding name="label">
|
||||
<lookup name="label" type="SweeperCell">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
256
demos/gtk-demo/listview_settings.c
Normal file
256
demos/gtk-demo/listview_settings.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/* Lists/Settings
|
||||
*
|
||||
* This demo shows a settings viewer for GSettings.
|
||||
*
|
||||
* It demonstrates how to implement support for trees with listview.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* Create an object that wraps GSettingsSchemaKey because that's a boxed type */
|
||||
typedef struct _SettingsKey SettingsKey;
|
||||
struct _SettingsKey
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GSettings *settings;
|
||||
GSettingsSchemaKey *key;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_NAME,
|
||||
PROP_SUMMARY,
|
||||
PROP_DESCRIPTION,
|
||||
PROP_VALUE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
#define SETTINGS_TYPE_KEY (settings_key_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (SettingsKey, settings_key, SETTINGS, KEY, GObject);
|
||||
|
||||
G_DEFINE_TYPE (SettingsKey, settings_key, G_TYPE_OBJECT);
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
settings_key_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SettingsKey *self = SETTINGS_KEY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_DESCRIPTION:
|
||||
g_value_set_string (value, g_settings_schema_key_get_description (self->key));
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, g_settings_schema_key_get_name (self->key));
|
||||
break;
|
||||
|
||||
case PROP_SUMMARY:
|
||||
g_value_set_string (value, g_settings_schema_key_get_summary (self->key));
|
||||
break;
|
||||
|
||||
case PROP_VALUE:
|
||||
{
|
||||
GVariant *variant = g_settings_get_value (self->settings, g_settings_schema_key_get_name (self->key));
|
||||
g_value_take_string (value, g_variant_print (variant, FALSE));
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
settings_key_class_init (SettingsKeyClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = settings_key_get_property;
|
||||
|
||||
properties[PROP_DESCRIPTION] =
|
||||
g_param_spec_string ("description", NULL, NULL, NULL, G_PARAM_READABLE);
|
||||
properties[PROP_NAME] =
|
||||
g_param_spec_string ("name", NULL, NULL, NULL, G_PARAM_READABLE);
|
||||
properties[PROP_SUMMARY] =
|
||||
g_param_spec_string ("summary", NULL, NULL, NULL, G_PARAM_READABLE);
|
||||
properties[PROP_VALUE] =
|
||||
g_param_spec_string ("value", NULL, NULL, NULL, G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
settings_key_init (SettingsKey *self)
|
||||
{
|
||||
}
|
||||
|
||||
static SettingsKey *
|
||||
settings_key_new (GSettings *settings,
|
||||
GSettingsSchemaKey *key)
|
||||
{
|
||||
SettingsKey *result = g_object_new (SETTINGS_TYPE_KEY, NULL);
|
||||
|
||||
result->settings = g_object_ref (settings);
|
||||
result->key = g_settings_schema_key_ref (key);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
strvcmp (gconstpointer p1,
|
||||
gconstpointer p2)
|
||||
{
|
||||
const char * const *s1 = p1;
|
||||
const char * const *s2 = p2;
|
||||
|
||||
return strcmp (*s1, *s2);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
transform_settings_to_keys (GBinding *binding,
|
||||
const GValue *from_value,
|
||||
GValue *to_value,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkTreeListRow *treelistrow;
|
||||
GSettings *settings;
|
||||
GSettingsSchema *schema;
|
||||
GListStore *store;
|
||||
char **keys;
|
||||
guint i;
|
||||
|
||||
treelistrow = g_value_get_object (from_value);
|
||||
if (treelistrow == NULL)
|
||||
return TRUE;
|
||||
settings = gtk_tree_list_row_get_item (treelistrow);
|
||||
g_object_get (settings, "settings-schema", &schema, NULL);
|
||||
|
||||
store = g_list_store_new (SETTINGS_TYPE_KEY);
|
||||
|
||||
keys = g_settings_schema_list_keys (schema);
|
||||
qsort (keys, g_strv_length (keys), sizeof (char *), strvcmp);
|
||||
|
||||
for (i = 0; keys[i] != NULL; i++)
|
||||
{
|
||||
GSettingsSchemaKey *almost_there = g_settings_schema_get_key (schema, keys[i]);
|
||||
SettingsKey *finally = settings_key_new (settings, almost_there);
|
||||
g_list_store_append (store, finally);
|
||||
g_object_unref (finally);
|
||||
g_settings_schema_key_unref (almost_there);
|
||||
}
|
||||
|
||||
g_strfreev (keys);
|
||||
g_settings_schema_unref (schema);
|
||||
g_object_unref (settings);
|
||||
|
||||
g_value_take_object (to_value, store);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_settings_model (gpointer item,
|
||||
gpointer unused)
|
||||
{
|
||||
GSettings *settings = item;
|
||||
char **schemas;
|
||||
GListStore *result;
|
||||
guint i;
|
||||
|
||||
if (settings == NULL)
|
||||
{
|
||||
g_settings_schema_source_list_schemas (g_settings_schema_source_get_default (),
|
||||
TRUE,
|
||||
&schemas,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
schemas = g_settings_list_children (settings);
|
||||
}
|
||||
|
||||
if (schemas == NULL || schemas[0] == NULL)
|
||||
{
|
||||
g_free (schemas);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qsort (schemas, g_strv_length (schemas), sizeof (char *), strvcmp);
|
||||
|
||||
result = g_list_store_new (G_TYPE_SETTINGS);
|
||||
for (i = 0; schemas[i] != NULL; i++)
|
||||
{
|
||||
GSettings *child;
|
||||
|
||||
if (settings == NULL)
|
||||
child = g_settings_new (schemas[i]);
|
||||
else
|
||||
child = g_settings_get_child (settings, schemas[i]);
|
||||
|
||||
g_list_store_append (result, child);
|
||||
g_object_unref (child);
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (result);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_settings (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *listview, *columnview;
|
||||
GListModel *model;
|
||||
GtkTreeListModel *treemodel;
|
||||
GtkSingleSelection *selection;
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (SETTINGS_TYPE_KEY);
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/listview_settings/listview_settings.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
listview = GTK_WIDGET (gtk_builder_get_object (builder, "listview"));
|
||||
columnview = GTK_WIDGET (gtk_builder_get_object (builder, "columnview"));
|
||||
model = create_settings_model (NULL, NULL);
|
||||
treemodel = gtk_tree_list_model_new (FALSE,
|
||||
model,
|
||||
TRUE,
|
||||
create_settings_model,
|
||||
NULL,
|
||||
NULL);
|
||||
selection = gtk_single_selection_new (G_LIST_MODEL (treemodel));
|
||||
g_object_bind_property_full (selection, "selected-item",
|
||||
columnview, "model",
|
||||
G_BINDING_SYNC_CREATE,
|
||||
transform_settings_to_keys,
|
||||
NULL,
|
||||
NULL, NULL);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (selection));
|
||||
g_object_unref (selection);
|
||||
g_object_unref (treemodel);
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
108
demos/gtk-demo/listview_settings.ui
Normal file
108
demos/gtk-demo/listview_settings.ui
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">Settings</property>
|
||||
<property name="default-width">600</property>
|
||||
<property name="default-height">400</property>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<child>
|
||||
<object class="GtkListView" id="listview">
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkTreeExpander" id="expander">
|
||||
<binding name="list-row">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</binding>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="schema" type="GSettings">
|
||||
<lookup name="item">expander</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<child>
|
||||
<object class="GtkColumnView" id="columnview">
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Name</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="name" type="SettingsKey">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Value</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="value" type="SettingsKey">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
316
demos/gtk-demo/listview_weather.c
Normal file
316
demos/gtk-demo/listview_weather.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/* Lists/Weather
|
||||
*
|
||||
* This demo shows a few of the rarer features of GtkListView and
|
||||
* how they can be used to display weather information.
|
||||
*
|
||||
* The hourly weather info uses a horizontal listview. This is easy
|
||||
* to achieve because GtkListView implements the GtkOrientable interface.
|
||||
*
|
||||
* To make the items in the list stand out more, the listview uses
|
||||
* separators.
|
||||
*
|
||||
* A GtkNoSelectionModel is used to make sure no item in the list can be
|
||||
* selected. All other interactions with the items is still possible.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_WEATHER_INFO (gtk_weather_info_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkWeatherInfo, gtk_weather_info, GTK, WEATHER_INFO, GObject)
|
||||
|
||||
typedef enum {
|
||||
GTK_WEATHER_CLEAR,
|
||||
GTK_WEATHER_FEW_CLOUDS,
|
||||
GTK_WEATHER_FOG,
|
||||
GTK_WEATHER_OVERCAST,
|
||||
GTK_WEATHER_SCATTERED_SHOWERS,
|
||||
GTK_WEATHER_SHOWERS,
|
||||
GTK_WEATHER_SNOW,
|
||||
GTK_WEATHER_STORM
|
||||
} GtkWeatherType;
|
||||
|
||||
struct _GtkWeatherInfo
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
gint64 timestamp;
|
||||
int temperature;
|
||||
GtkWeatherType weather_type;
|
||||
};
|
||||
|
||||
struct _GtkWeatherInfoClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static void
|
||||
gtk_weather_info_class_init (GtkWeatherInfoClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_weather_info_init (GtkWeatherInfo *self)
|
||||
{
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE (GtkWeatherInfo, gtk_weather_info, G_TYPE_OBJECT);
|
||||
|
||||
static GtkWeatherInfo *
|
||||
gtk_weather_info_new (GDateTime *timestamp,
|
||||
GtkWeatherInfo *copy_from)
|
||||
{
|
||||
GtkWeatherInfo *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_WEATHER_INFO, NULL);
|
||||
|
||||
result->timestamp = g_date_time_to_unix (timestamp);
|
||||
if (copy_from)
|
||||
{
|
||||
result->temperature = copy_from->temperature;
|
||||
result->weather_type = copy_from->weather_type;
|
||||
g_object_unref (copy_from);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GDateTime *
|
||||
parse_timestamp (const char *string,
|
||||
GTimeZone *timezone)
|
||||
{
|
||||
char *with_seconds;
|
||||
GDateTime *result;
|
||||
|
||||
with_seconds = g_strconcat (string, ":00", NULL);
|
||||
result = g_date_time_new_from_iso8601 (with_seconds, timezone);
|
||||
g_free (with_seconds);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GtkWeatherType
|
||||
parse_weather_type (const char *clouds,
|
||||
const char *precip,
|
||||
GtkWeatherType fallback)
|
||||
{
|
||||
if (strstr (precip, "SN"))
|
||||
return GTK_WEATHER_SNOW;
|
||||
|
||||
if (strstr (precip, "TS"))
|
||||
return GTK_WEATHER_STORM;
|
||||
|
||||
if (strstr (precip, "DZ"))
|
||||
return GTK_WEATHER_SCATTERED_SHOWERS;
|
||||
|
||||
if (strstr (precip, "SH") || strstr (precip, "RA"))
|
||||
return GTK_WEATHER_SHOWERS;
|
||||
|
||||
if (strstr (precip, "FG"))
|
||||
return GTK_WEATHER_FOG;
|
||||
|
||||
if (g_str_equal (clouds, "M") ||
|
||||
g_str_equal (clouds, ""))
|
||||
return fallback;
|
||||
|
||||
if (strstr (clouds, "OVC") ||
|
||||
strstr (clouds, "BKN"))
|
||||
return GTK_WEATHER_OVERCAST;
|
||||
|
||||
if (strstr (clouds, "BKN") ||
|
||||
strstr (clouds, "SCT"))
|
||||
return GTK_WEATHER_FEW_CLOUDS;
|
||||
|
||||
if (strstr (clouds, "VV"))
|
||||
return GTK_WEATHER_FOG;
|
||||
|
||||
return GTK_WEATHER_CLEAR;
|
||||
}
|
||||
|
||||
static double
|
||||
parse_temperature (const char *s,
|
||||
double fallback)
|
||||
{
|
||||
char *endptr;
|
||||
double d;
|
||||
|
||||
d = g_ascii_strtod (s, &endptr);
|
||||
if (*endptr != '\0')
|
||||
return fallback;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_weather_model (void)
|
||||
{
|
||||
GListStore *store;
|
||||
GTimeZone *utc;
|
||||
GDateTime *timestamp;
|
||||
GtkWeatherInfo *info;
|
||||
GBytes *data;
|
||||
char **lines;
|
||||
guint i;
|
||||
|
||||
store = g_list_store_new (GTK_TYPE_WEATHER_INFO);
|
||||
data = g_resources_lookup_data ("/listview_weather/listview_weather.txt", 0, NULL);
|
||||
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
|
||||
|
||||
utc = g_time_zone_new_utc ();
|
||||
timestamp = g_date_time_new (utc, 2011, 1, 1, 0, 0, 0);
|
||||
info = gtk_weather_info_new (timestamp, NULL);
|
||||
g_list_store_append (store, info);
|
||||
|
||||
for (i = 0; lines[i] != NULL && *lines[i]; i++)
|
||||
{
|
||||
char **fields;
|
||||
GDateTime *date;
|
||||
|
||||
fields = g_strsplit (lines[i], ",", 0);
|
||||
date = parse_timestamp (fields[0], utc);
|
||||
while (g_date_time_difference (date, timestamp) > 30 * G_TIME_SPAN_MINUTE)
|
||||
{
|
||||
GDateTime *new_timestamp = g_date_time_add_hours (timestamp, 1);
|
||||
g_date_time_unref (timestamp);
|
||||
timestamp = new_timestamp;
|
||||
info = gtk_weather_info_new (timestamp, info);
|
||||
g_list_store_append (store, info);
|
||||
}
|
||||
|
||||
info->temperature = parse_temperature (fields[1], info->temperature);
|
||||
info->weather_type = parse_weather_type (fields[2], fields[3], info->weather_type);
|
||||
g_date_time_unref (date);
|
||||
g_strfreev (fields);
|
||||
}
|
||||
|
||||
g_strfreev (lines);
|
||||
g_bytes_unref (data);
|
||||
g_time_zone_unref (utc);
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_widget (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *box, *child;
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_list_item_set_child (list_item, box);
|
||||
|
||||
child = gtk_label_new (NULL);
|
||||
gtk_label_set_width_chars (GTK_LABEL (child), 5);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
|
||||
child = gtk_image_new ();
|
||||
gtk_image_set_icon_size (GTK_IMAGE (child), GTK_ICON_SIZE_LARGE);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
|
||||
child = gtk_label_new (NULL);
|
||||
gtk_widget_set_vexpand (child, TRUE);
|
||||
gtk_widget_set_valign (child, GTK_ALIGN_END);
|
||||
gtk_label_set_width_chars (GTK_LABEL (child), 4);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_widget (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *box, *child;
|
||||
GtkWeatherInfo *info;
|
||||
GDateTime *timestamp;
|
||||
char *s;
|
||||
|
||||
box = gtk_list_item_get_child (list_item);
|
||||
info = gtk_list_item_get_item (list_item);
|
||||
|
||||
child = gtk_widget_get_first_child (box);
|
||||
timestamp = g_date_time_new_from_unix_utc (info->timestamp);
|
||||
s = g_date_time_format (timestamp, "%R");
|
||||
gtk_label_set_text (GTK_LABEL (child), s);
|
||||
g_free (s);
|
||||
g_date_time_unref (timestamp);
|
||||
|
||||
child = gtk_widget_get_next_sibling (child);
|
||||
switch (info->weather_type)
|
||||
{
|
||||
case GTK_WEATHER_CLEAR:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-clear-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_FEW_CLOUDS:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-few-clouds-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_FOG:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-fog-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_OVERCAST:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-overcast-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_SCATTERED_SHOWERS:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-scattered-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_SHOWERS:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-showers-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_SNOW:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-snow-symbolic");
|
||||
break;
|
||||
case GTK_WEATHER_STORM:
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (child), "weather-storm-symbolic");
|
||||
break;
|
||||
default:
|
||||
gtk_image_clear (GTK_IMAGE (child));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
child = gtk_widget_get_next_sibling (child);
|
||||
s = g_strdup_printf ("%d°", info->temperature);
|
||||
gtk_label_set_text (GTK_LABEL (child), s);
|
||||
g_free (s);
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
do_listview_weather (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *listview, *sw;;
|
||||
GListModel *model, *selection;
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Weather");
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK(gtk_widget_destroyed), &window);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (window), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_functions_list_item_factory_new (setup_widget,
|
||||
bind_widget,
|
||||
NULL, NULL));
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
|
||||
model = create_weather_model ();
|
||||
selection = G_LIST_MODEL (gtk_no_selection_new (model));
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (model);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (sw), listview);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
70116
demos/gtk-demo/listview_weather.txt
Normal file
70116
demos/gtk-demo/listview_weather.txt
Normal file
File diff suppressed because it is too large
Load Diff
22
demos/gtk-demo/main-listitem.ui
Normal file
22
demos/gtk-demo/main-listitem.ui
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkTreeExpander" id="expander">
|
||||
<binding name="list-row">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</binding>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<binding name="label">
|
||||
<lookup name="title" type="GtkDemo">
|
||||
<lookup name="item">expander</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "award.h"
|
||||
#include "demos.h"
|
||||
|
||||
static GtkWidget *info_view;
|
||||
@@ -15,19 +16,99 @@ static GtkWidget *source_view;
|
||||
|
||||
static gchar *current_file = NULL;
|
||||
|
||||
static GtkWidget *window;
|
||||
static GtkWidget *notebook;
|
||||
static GtkWidget *treeview;
|
||||
static GtkWidget *listview;
|
||||
static GtkSingleSelection *selection;
|
||||
static GtkWidget *headerbar;
|
||||
|
||||
enum {
|
||||
NAME_COLUMN,
|
||||
TITLE_COLUMN,
|
||||
FILENAME_COLUMN,
|
||||
FUNC_COLUMN,
|
||||
STYLE_COLUMN,
|
||||
NUM_COLUMNS
|
||||
typedef struct _GtkDemo GtkDemo;
|
||||
struct _GtkDemo
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
const char *name;
|
||||
const char *title;
|
||||
const char *filename;
|
||||
GDoDemoFunc func;
|
||||
GListModel *children_model;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FILENAME,
|
||||
PROP_NAME,
|
||||
PROP_TITLE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
# define GTK_TYPE_DEMO (gtk_demo_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkDemo, gtk_demo, GTK, DEMO, GObject);
|
||||
|
||||
G_DEFINE_TYPE (GtkDemo, gtk_demo, G_TYPE_OBJECT);
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_demo_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDemo *self = GTK_DEMO (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FILENAME:
|
||||
g_value_set_string (value, self->filename);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, self->name);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, self->title);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gtk_demo_class_init (GtkDemoClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_demo_get_property;
|
||||
|
||||
properties[PROP_FILENAME] =
|
||||
g_param_spec_string ("filename",
|
||||
"filename",
|
||||
"filename",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
properties[PROP_NAME] =
|
||||
g_param_spec_string ("name",
|
||||
"name",
|
||||
"name",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
properties[PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
"title",
|
||||
"title",
|
||||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void gtk_demo_init (GtkDemo *self)
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _CallbackData CallbackData;
|
||||
struct _CallbackData
|
||||
{
|
||||
@@ -35,6 +116,27 @@ struct _CallbackData
|
||||
GtkTreePath *path;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
gtk_demo_run (GtkDemo *self,
|
||||
GtkWidget *window)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
if (!self->func)
|
||||
return FALSE;
|
||||
|
||||
result = self->func (window);
|
||||
if (result == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (GTK_IS_WINDOW (result))
|
||||
{
|
||||
gtk_window_set_transient_for (GTK_WINDOW (result), GTK_WINDOW (window));
|
||||
gtk_window_set_modal (GTK_WINDOW (result), TRUE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
activate_about (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
@@ -111,69 +213,7 @@ activate_inspector (GSimpleAction *action,
|
||||
gpointer user_data)
|
||||
{
|
||||
gtk_window_set_interactive_debugging (TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
window_closed_cb (GtkWidget *window, gpointer data)
|
||||
{
|
||||
CallbackData *cbdata = data;
|
||||
GtkTreeIter iter;
|
||||
PangoStyle style;
|
||||
|
||||
gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (cbdata->model), &iter,
|
||||
STYLE_COLUMN, &style,
|
||||
-1);
|
||||
if (style == PANGO_STYLE_ITALIC)
|
||||
gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
|
||||
STYLE_COLUMN, PANGO_STYLE_NORMAL,
|
||||
-1);
|
||||
|
||||
gtk_tree_path_free (cbdata->path);
|
||||
g_free (cbdata);
|
||||
}
|
||||
|
||||
static void
|
||||
run_example_for_row (GtkWidget *window,
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter)
|
||||
{
|
||||
PangoStyle style;
|
||||
GDoDemoFunc func;
|
||||
GtkWidget *demo;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (model),
|
||||
iter,
|
||||
FUNC_COLUMN, &func,
|
||||
STYLE_COLUMN, &style,
|
||||
-1);
|
||||
|
||||
if (func)
|
||||
{
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model),
|
||||
iter,
|
||||
STYLE_COLUMN, (style == PANGO_STYLE_ITALIC ? PANGO_STYLE_NORMAL : PANGO_STYLE_ITALIC),
|
||||
-1);
|
||||
demo = (func) (window);
|
||||
|
||||
if (demo != NULL)
|
||||
{
|
||||
CallbackData *cbdata;
|
||||
|
||||
cbdata = g_new (CallbackData, 1);
|
||||
cbdata->model = model;
|
||||
cbdata->path = gtk_tree_model_get_path (model, iter);
|
||||
|
||||
if (GTK_IS_WINDOW (demo))
|
||||
{
|
||||
gtk_window_set_transient_for (GTK_WINDOW (demo), GTK_WINDOW (window));
|
||||
gtk_window_set_modal (GTK_WINDOW (demo), TRUE);
|
||||
}
|
||||
|
||||
g_signal_connect (demo, "destroy",
|
||||
G_CALLBACK (window_closed_cb), cbdata);
|
||||
}
|
||||
}
|
||||
award ("demo-inspector");
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -181,14 +221,10 @@ activate_run (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWidget *window = user_data;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeListRow *row = gtk_single_selection_get_selected_item (selection);
|
||||
GtkDemo *demo = gtk_tree_list_row_get_item (row);
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
|
||||
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
||||
run_example_for_row (window, model, &iter);
|
||||
gtk_demo_run (demo, window);
|
||||
}
|
||||
|
||||
/* Stupid syntax highlighting.
|
||||
@@ -893,81 +929,72 @@ load_file (const gchar *demoname,
|
||||
}
|
||||
|
||||
static void
|
||||
selection_cb (GtkTreeSelection *selection,
|
||||
GtkTreeModel *model)
|
||||
|
||||
selection_cb (GtkSingleSelection *selection,
|
||||
GParamSpec *pspec,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
char *name;
|
||||
char *filename;
|
||||
char *title;
|
||||
GtkTreeListRow *row = gtk_single_selection_get_selected_item (selection);
|
||||
GtkDemo *demo = gtk_tree_list_row_get_item (row);
|
||||
|
||||
if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
|
||||
return;
|
||||
if (demo->filename)
|
||||
load_file (demo->name, demo->filename);
|
||||
|
||||
gtk_tree_model_get (model, &iter,
|
||||
NAME_COLUMN, &name,
|
||||
TITLE_COLUMN, &title,
|
||||
FILENAME_COLUMN, &filename,
|
||||
-1);
|
||||
|
||||
if (filename)
|
||||
load_file (name, filename);
|
||||
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), title);
|
||||
|
||||
g_free (name);
|
||||
g_free (title);
|
||||
g_free (filename);
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), demo->title);
|
||||
}
|
||||
|
||||
static void
|
||||
populate_model (GtkTreeModel *model)
|
||||
static GListModel *
|
||||
create_demo_model (void)
|
||||
{
|
||||
Demo *d = gtk_demos;
|
||||
GListStore *store = g_list_store_new (GTK_TYPE_DEMO);
|
||||
DemoData *demo = gtk_demos;
|
||||
|
||||
/* this code only supports 1 level of children. If we
|
||||
* want more we probably have to use a recursing function.
|
||||
*/
|
||||
while (d->title)
|
||||
while (demo->title)
|
||||
{
|
||||
Demo *children = d->children;
|
||||
GtkTreeIter iter;
|
||||
GtkDemo *d = GTK_DEMO (g_object_new (GTK_TYPE_DEMO, NULL));
|
||||
DemoData *children = demo->children;
|
||||
|
||||
gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
|
||||
d->name = demo->name;
|
||||
d->title = demo->title;
|
||||
d->filename = demo->filename;
|
||||
d->func = demo->func;
|
||||
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model),
|
||||
&iter,
|
||||
NAME_COLUMN, d->name,
|
||||
TITLE_COLUMN, d->title,
|
||||
FILENAME_COLUMN, d->filename,
|
||||
FUNC_COLUMN, d->func,
|
||||
STYLE_COLUMN, PANGO_STYLE_NORMAL,
|
||||
-1);
|
||||
g_list_store_append (store, d);
|
||||
|
||||
d++;
|
||||
|
||||
if (!children)
|
||||
continue;
|
||||
|
||||
while (children->title)
|
||||
if (children)
|
||||
{
|
||||
GtkTreeIter child_iter;
|
||||
d->children_model = G_LIST_MODEL (g_list_store_new (GTK_TYPE_DEMO));
|
||||
|
||||
gtk_tree_store_append (GTK_TREE_STORE (model), &child_iter, &iter);
|
||||
while (children->title)
|
||||
{
|
||||
GtkDemo *child = GTK_DEMO (g_object_new (GTK_TYPE_DEMO, NULL));
|
||||
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model),
|
||||
&child_iter,
|
||||
NAME_COLUMN, children->name,
|
||||
TITLE_COLUMN, children->title,
|
||||
FILENAME_COLUMN, children->filename,
|
||||
FUNC_COLUMN, children->func,
|
||||
STYLE_COLUMN, PANGO_STYLE_NORMAL,
|
||||
-1);
|
||||
child->name = children->name;
|
||||
child->title = children->title;
|
||||
child->filename = children->filename;
|
||||
child->func = children->func;
|
||||
|
||||
children++;
|
||||
g_list_store_append (G_LIST_STORE (d->children_model), child);
|
||||
children++;
|
||||
}
|
||||
}
|
||||
|
||||
demo++;
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
get_child_model (gpointer item,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkDemo *demo = item;
|
||||
|
||||
if (demo->children_model)
|
||||
return g_object_ref (G_LIST_MODEL (demo->children_model));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -987,22 +1014,6 @@ startup (GApplication *app)
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
row_activated_cb (GtkWidget *tree_view,
|
||||
GtkTreePath *path,
|
||||
GtkTreeViewColumn *column)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkWidget *window;
|
||||
GtkTreeModel *model;
|
||||
|
||||
window = GTK_WIDGET (gtk_widget_get_root (tree_view));
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
|
||||
gtk_tree_model_get_iter (model, &iter, path);
|
||||
|
||||
run_example_for_row (window, model, &iter);
|
||||
}
|
||||
|
||||
static void
|
||||
start_cb (GtkMenuItem *item, GtkWidget *scrollbar)
|
||||
{
|
||||
@@ -1029,18 +1040,27 @@ scrollbar_popup (GtkWidget *scrollbar, GtkWidget *menu)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
main_activate_cb (GtkListView *listview,
|
||||
guint position,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkTreeListRow *row = g_list_model_get_item (gtk_list_view_get_model (listview), position);
|
||||
GtkDemo *demo = gtk_tree_list_row_get_item (row);
|
||||
|
||||
gtk_demo_run (demo, window);
|
||||
}
|
||||
|
||||
static void
|
||||
activate (GApplication *app)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
GtkWindow *window;
|
||||
GtkWidget *widget;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GtkWidget *sw;
|
||||
GtkWidget *scrollbar;
|
||||
GtkWidget *menu;
|
||||
GtkWidget *item;
|
||||
GListModel *listmodel;
|
||||
GtkTreeListModel *treemodel;
|
||||
|
||||
static GActionEntry win_entries[] = {
|
||||
{ "run", activate_run, NULL, NULL, NULL }
|
||||
@@ -1048,8 +1068,8 @@ activate (GApplication *app)
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/ui/main.ui");
|
||||
|
||||
window = (GtkWindow *)gtk_builder_get_object (builder, "window");
|
||||
gtk_application_add_window (GTK_APPLICATION (app), window);
|
||||
window = (GtkWidget *)gtk_builder_get_object (builder, "window");
|
||||
gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (window));
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (window),
|
||||
win_entries, G_N_ELEMENTS (win_entries),
|
||||
window);
|
||||
@@ -1059,8 +1079,7 @@ activate (GApplication *app)
|
||||
info_view = (GtkWidget *)gtk_builder_get_object (builder, "info-textview");
|
||||
source_view = (GtkWidget *)gtk_builder_get_object (builder, "source-textview");
|
||||
headerbar = (GtkWidget *)gtk_builder_get_object (builder, "headerbar");
|
||||
treeview = (GtkWidget *)gtk_builder_get_object (builder, "treeview");
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
|
||||
listview = (GtkWidget *)gtk_builder_get_object (builder, "listview");
|
||||
|
||||
sw = (GtkWidget *)gtk_builder_get_object (builder, "source-scrolledwindow");
|
||||
scrollbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (sw));
|
||||
@@ -1079,17 +1098,19 @@ activate (GApplication *app)
|
||||
|
||||
load_file (gtk_demos[0].name, gtk_demos[0].filename);
|
||||
|
||||
populate_model (model);
|
||||
listmodel = create_demo_model ();
|
||||
treemodel = gtk_tree_list_model_new (FALSE,
|
||||
G_LIST_MODEL (listmodel),
|
||||
FALSE,
|
||||
get_child_model,
|
||||
NULL,
|
||||
NULL);
|
||||
selection = gtk_single_selection_new (G_LIST_MODEL (treemodel));
|
||||
g_signal_connect (selection, "notify::selected-item", G_CALLBACK (selection_cb), NULL);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview),
|
||||
G_LIST_MODEL (selection));
|
||||
|
||||
g_signal_connect (treeview, "row-activated", G_CALLBACK (row_activated_cb), model);
|
||||
|
||||
widget = (GtkWidget *)gtk_builder_get_object (builder, "treeview-selection");
|
||||
g_signal_connect (widget, "changed", G_CALLBACK (selection_cb), model);
|
||||
|
||||
gtk_tree_model_get_iter_first (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)), &iter);
|
||||
gtk_tree_selection_select_iter (GTK_TREE_SELECTION (widget), &iter);
|
||||
|
||||
gtk_tree_view_collapse_all (GTK_TREE_VIEW (treeview));
|
||||
award ("demo-start");
|
||||
|
||||
gtk_widget_show (GTK_WIDGET (window));
|
||||
|
||||
@@ -1106,7 +1127,7 @@ auto_quit (gpointer data)
|
||||
static void
|
||||
list_demos (void)
|
||||
{
|
||||
Demo *d, *c;
|
||||
DemoData *d, *c;
|
||||
|
||||
d = gtk_demos;
|
||||
|
||||
@@ -1133,7 +1154,7 @@ command_line (GApplication *app,
|
||||
const gchar *name = NULL;
|
||||
gboolean autoquit = FALSE;
|
||||
gboolean list = FALSE;
|
||||
Demo *d, *c;
|
||||
DemoData *d, *c;
|
||||
GDoDemoFunc func = 0;
|
||||
GtkWidget *window, *demo;
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
<property name="default-width">800</property>
|
||||
<property name="default-height">600</property>
|
||||
<property name="title">GTK Demo</property>
|
||||
<signal name="destroy" handler="gtk_main_quit" swapped="no"/>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="headerbar">
|
||||
<property name="show-title-buttons">1</property>
|
||||
@@ -66,32 +65,15 @@
|
||||
<property name="can-focus">1</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="min-content-width">150</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkTreeView" id="treeview">
|
||||
<property name="can-focus">1</property>
|
||||
<property name="model">treestore</property>
|
||||
<property name="headers-visible">0</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection">
|
||||
<property name="mode">browse</property>
|
||||
<object class="GtkListView" id="listview">
|
||||
<signal name="activate" handler="main_activate_cb" swapped="no" />
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="resource">/ui/main-listitem.ui</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn">
|
||||
<child>
|
||||
<object class="GtkCellRendererText"/>
|
||||
<attributes>
|
||||
<attribute name="style">4</attribute>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText">
|
||||
<property name="text"> </property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
demos = files([
|
||||
'application_demo.c',
|
||||
'assistant.c',
|
||||
'awardview.c',
|
||||
'builder.c',
|
||||
'changedisplay.c',
|
||||
'clipboard.c',
|
||||
@@ -43,6 +44,12 @@ demos = files([
|
||||
'listbox.c',
|
||||
'flowbox.c',
|
||||
'list_store.c',
|
||||
'listview_applauncher.c',
|
||||
'listview_clocks.c',
|
||||
'listview_filebrowser.c',
|
||||
'listview_minesweeper.c',
|
||||
'listview_settings.c',
|
||||
'listview_weather.c',
|
||||
'markup.c',
|
||||
'menus.c',
|
||||
'modelbutton.c',
|
||||
@@ -86,6 +93,7 @@ demos = files([
|
||||
gtkdemo_deps = [ libgtk_dep, ]
|
||||
|
||||
extra_demo_sources = files(['main.c',
|
||||
'award.c',
|
||||
'gtkfishbowl.c',
|
||||
'fontplane.c',
|
||||
'gtkgears.c',
|
||||
@@ -96,7 +104,7 @@ extra_demo_sources = files(['main.c',
|
||||
if harfbuzz_dep.found() and pangoft_dep.found()
|
||||
demos += files('font_features.c')
|
||||
extra_demo_sources += files(['script-names.c', 'language-names.c'])
|
||||
gtkdemo_deps += [ harfbuzz_dep, ]
|
||||
gtkdemo_deps += [ harfbuzz_dep, epoxy_dep ]
|
||||
endif
|
||||
|
||||
if os_unix
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "award.h"
|
||||
|
||||
static GtkWidget *entry;
|
||||
static GtkWidget *entry2;
|
||||
static GtkWidget *button;
|
||||
@@ -25,6 +27,18 @@ update_button (GObject *object,
|
||||
|
||||
gtk_widget_set_sensitive (button,
|
||||
text[0] != '\0' && g_str_equal (text, text2));
|
||||
|
||||
if (g_str_equal (text, text2) &&
|
||||
g_ascii_strcasecmp (text, "12345") == 0)
|
||||
award ("password-best");
|
||||
}
|
||||
|
||||
static void
|
||||
button_pressed (GtkButton *button,
|
||||
GtkWidget *window)
|
||||
{
|
||||
award ("password-correct");
|
||||
gtk_widget_destroy (window);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
@@ -72,7 +86,7 @@ do_password_entry (GtkWidget *do_widget)
|
||||
|
||||
button = gtk_button_new_with_mnemonic ("_Done");
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
|
||||
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (button_pressed), window);
|
||||
gtk_widget_set_sensitive (button, FALSE);
|
||||
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button);
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "puzzlepiece.h"
|
||||
#include "paintable.h"
|
||||
|
||||
/* Give out awards */
|
||||
#include "award.h"
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
static GtkWidget *frame = NULL;
|
||||
static GtkWidget *choices = NULL;
|
||||
@@ -156,6 +159,14 @@ check_solved (GtkWidget *grid)
|
||||
picture = gtk_grid_get_child_at (GTK_GRID (grid), pos_x, pos_y);
|
||||
gtk_picture_set_paintable (GTK_PICTURE (picture), piece);
|
||||
|
||||
/* Hand out a bunch of awards
|
||||
*/
|
||||
award ("puzzle-solve");
|
||||
if ((gdk_paintable_get_flags (piece) & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
|
||||
award ("puzzle-solve-animated");
|
||||
if (height * width > 20)
|
||||
award ("puzzle-solve-large");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -401,6 +412,18 @@ add_choice (GtkWidget *choices,
|
||||
gtk_container_add (GTK_CONTAINER (choices), icon);
|
||||
}
|
||||
|
||||
static void
|
||||
widget_destroyed (GtkWidget *widget,
|
||||
GtkWidget **widget_pointer)
|
||||
{
|
||||
if (widget_pointer)
|
||||
*widget_pointer = NULL;
|
||||
|
||||
if (!solved)
|
||||
award ("puzzle-give-up");
|
||||
}
|
||||
|
||||
|
||||
GtkWidget *
|
||||
do_sliding_puzzle (GtkWidget *do_widget)
|
||||
{
|
||||
@@ -469,7 +492,7 @@ do_sliding_puzzle (GtkWidget *do_widget)
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
G_CALLBACK (widget_destroyed), &window);
|
||||
|
||||
frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, (float) gdk_paintable_get_intrinsic_aspect_ratio (puzzle), FALSE);
|
||||
gtk_container_add (GTK_CONTAINER (window), frame);
|
||||
|
||||
@@ -1663,6 +1663,7 @@ static void
|
||||
activate (GApplication *app)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
GtkBuilderScope *scope;
|
||||
GtkWindow *window;
|
||||
GtkWidget *widget;
|
||||
GtkWidget *widget2;
|
||||
@@ -1716,18 +1717,23 @@ activate (GApplication *app)
|
||||
g_object_unref (provider);
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
gtk_builder_add_callback_symbol (builder, "on_entry_icon_release", (GCallback)on_entry_icon_release);
|
||||
gtk_builder_add_callback_symbol (builder, "on_scale_button_value_changed", (GCallback)on_scale_button_value_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "on_scale_button_query_tooltip", (GCallback)on_scale_button_query_tooltip);
|
||||
gtk_builder_add_callback_symbol (builder, "on_record_button_toggled", (GCallback)on_record_button_toggled);
|
||||
gtk_builder_add_callback_symbol (builder, "on_page_combo_changed", (GCallback)on_page_combo_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "on_range_from_changed", (GCallback)on_range_from_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "on_range_to_changed", (GCallback)on_range_to_changed);
|
||||
gtk_builder_add_callback_symbol (builder, "tab_close_cb", (GCallback)tab_close_cb);
|
||||
gtk_builder_add_callback_symbol (builder, "increase_icon_size", (GCallback)increase_icon_size);
|
||||
gtk_builder_add_callback_symbol (builder, "decrease_icon_size", (GCallback)decrease_icon_size);
|
||||
gtk_builder_add_callback_symbol (builder, "reset_icon_size", (GCallback)reset_icon_size);
|
||||
gtk_builder_add_callback_symbol (builder, "osd_frame_pressed", (GCallback)osd_frame_pressed);
|
||||
scope = gtk_builder_cscope_new ();
|
||||
gtk_builder_cscope_add_callback_symbols (GTK_BUILDER_CSCOPE (scope),
|
||||
"on_entry_icon_release", (GCallback)on_entry_icon_release,
|
||||
"on_scale_button_value_changed", (GCallback)on_scale_button_value_changed,
|
||||
"on_scale_button_query_tooltip", (GCallback)on_scale_button_query_tooltip,
|
||||
"on_record_button_toggled", (GCallback)on_record_button_toggled,
|
||||
"on_page_combo_changed", (GCallback)on_page_combo_changed,
|
||||
"on_range_from_changed", (GCallback)on_range_from_changed,
|
||||
"on_range_to_changed", (GCallback)on_range_to_changed,
|
||||
"tab_close_cb", (GCallback)tab_close_cb,
|
||||
"increase_icon_size", (GCallback)increase_icon_size,
|
||||
"decrease_icon_size", (GCallback)decrease_icon_size,
|
||||
"reset_icon_size", (GCallback)reset_icon_size,
|
||||
"osd_frame_pressed", (GCallback)osd_frame_pressed,
|
||||
NULL);
|
||||
gtk_builder_set_scope (builder, scope);
|
||||
g_object_unref (scope);
|
||||
gtk_builder_add_from_resource (builder, "/org/gtk/WidgetFactory4/widget-factory.ui", NULL);
|
||||
|
||||
window = (GtkWindow *)gtk_builder_get_object (builder, "window");
|
||||
|
||||
@@ -44,15 +44,23 @@
|
||||
|
||||
<chapter id="Lists">
|
||||
<title>GListModel support</title>
|
||||
<xi:include href="xml/gtkfilter.xml" />
|
||||
<xi:include href="xml/gtkfilterlistmodel.xml" />
|
||||
<xi:include href="xml/gtkflattenlistmodel.xml" />
|
||||
<xi:include href="xml/gtkmaplistmodel.xml" />
|
||||
<xi:include href="xml/gtkslicelistmodel.xml" />
|
||||
<xi:include href="xml/gtksorter.xml" />
|
||||
<xi:include href="xml/gtkstringsorter.xml" />
|
||||
<xi:include href="xml/gtknumericsorter.xml" />
|
||||
<xi:include href="xml/gtkcustomsorter.xml" />
|
||||
<xi:include href="xml/gtkmultisorter.xml" />
|
||||
<xi:include href="xml/gtktreelistrowsorter.xml" />
|
||||
<xi:include href="xml/gtksortlistmodel.xml" />
|
||||
<xi:include href="xml/gtktreelistmodel.xml" />
|
||||
<xi:include href="xml/gtkselectionmodel.xml" />
|
||||
<xi:include href="xml/gtknoselection.xml" />
|
||||
<xi:include href="xml/gtksingleselection.xml" />
|
||||
<xi:include href="xml/gtkdirectorylist.xml" />
|
||||
</chapter>
|
||||
|
||||
<chapter id="Application">
|
||||
@@ -87,6 +95,10 @@
|
||||
<xi:include href="xml/gtkrevealer.xml" />
|
||||
<xi:include href="xml/gtklistbox.xml" />
|
||||
<xi:include href="xml/gtkflowbox.xml" />
|
||||
<xi:include href="xml/gtksignallistitemfactory.xml" />
|
||||
<xi:include href="xml/gtklistview.xml" />
|
||||
<xi:include href="xml/gtkgridview.xml" />
|
||||
<xi:include href="xml/gtktreeexpander.xml" />
|
||||
<xi:include href="xml/gtkstack.xml" />
|
||||
<xi:include href="xml/gtkstackswitcher.xml" />
|
||||
<xi:include href="xml/gtkstacksidebar.xml" />
|
||||
|
||||
@@ -493,6 +493,136 @@ gtk_single_selection_set_can_unselect
|
||||
gtk_single_selection_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistitem</FILE>
|
||||
<TITLE>GtkListItem</TITLE>
|
||||
GtkListItem
|
||||
gtk_list_item_get_item
|
||||
gtk_list_item_get_position
|
||||
gtk_list_item_get_child
|
||||
gtk_list_item_set_child
|
||||
gtk_list_item_get_selected
|
||||
gtk_list_item_get_selectable
|
||||
gtk_list_item_set_selectable
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_ITEM
|
||||
GTK_LIST_ITEM_CLASS
|
||||
GTK_LIST_ITEM_GET_CLASS
|
||||
GTK_IS_LIST_ITEM
|
||||
GTK_IS_LIST_ITEM_CLASS
|
||||
GTK_TYPE_LIST_ITEM
|
||||
<SUBSECTION Private>
|
||||
gtk_list_item_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtksignallistitemfactory</FILE>
|
||||
<TITLE>GtkSignalListItemFactory</TITLE>
|
||||
GtkSignalListItemFactory
|
||||
gtk_signal_list_item_factory_new
|
||||
<SUBSECTION Standard>
|
||||
GTK_SIGNAL_LIST_ITEM_FACTORY
|
||||
GTK_SIGNAL_LIST_ITEM_FACTORY_CLASS
|
||||
GTK_SIGNAL_LIST_ITEM_FACTORY_GET_CLASS
|
||||
GTK_IS_SIGNAL_LIST_ITEM_FACTORY
|
||||
GTK_IS_SIGNAL_LIST_ITEM_FACTORY_CLASS
|
||||
GTK_TYPE_SIGNAL_LIST_ITEM_FACTORY
|
||||
<SUBSECTION Private>
|
||||
gtk_signal_list_item_factory_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistview</FILE>
|
||||
<TITLE>GtkListView</TITLE>
|
||||
GtkListView
|
||||
gtk_list_view_new
|
||||
gtk_list_view_new_with_factory
|
||||
gtk_list_view_set_factory
|
||||
gtk_list_view_get_factory
|
||||
gtk_list_view_set_model
|
||||
gtk_list_view_get_model
|
||||
gtk_list_view_set_show_separators
|
||||
gtk_list_view_get_show_separators
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_VIEW
|
||||
GTK_LIST_VIEW_CLASS
|
||||
GTK_LIST_VIEW_GET_CLASS
|
||||
GTK_IS_LIST_VIEW
|
||||
GTK_IS_LIST_VIEW_CLASS
|
||||
GTK_TYPE_LIST_VIEW
|
||||
<SUBSECTION Private>
|
||||
gtk_list_view_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkcolumnview</FILE>
|
||||
<TITLE>GtkColumnView</TITLE>
|
||||
GtkColumnView
|
||||
gtk_column_view_new
|
||||
gtk_column_view_get_columns
|
||||
gtk_column_view_get_model
|
||||
gtk_column_view_set_model
|
||||
gtk_column_view_get_sorter
|
||||
gtk_column_view_get_show_separators
|
||||
gtk_column_view_set_show_separators
|
||||
gtk_column_view_sort_by_column
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW
|
||||
GTK_COLUMN_VIEW_CLASS
|
||||
GTK_COLUMN_VIEW_GET_CLASS
|
||||
GTK_IS_COLUMN_VIEW
|
||||
GTK_IS_COLUMN_VIEW_CLASS
|
||||
GTK_TYPE_COLUMN_VIEW
|
||||
<SUBSECTION Private>
|
||||
gtk_column_view_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkcolumnviewcolumn</FILE>
|
||||
<TITLE>GtkColumnViewColumn</TITLE>
|
||||
GtkColumnViewColumn
|
||||
gtk_column_view_column_new
|
||||
gtk_column_view_column_new_with_factory
|
||||
gtk_column_view_column_get_column_view
|
||||
gtk_column_view_column_set_factory
|
||||
gtk_column_view_column_get_factory
|
||||
gtk_column_view_column_set_title
|
||||
gtk_column_view_column_get_title
|
||||
gtk_column_view_column_set_sorter
|
||||
gtk_column_view_column_get_sorter
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW_COLUMN
|
||||
GTK_COLUMN_VIEW_COLUMN_CLASS
|
||||
GTK_COLUMN_VIEW_COLUMN_GET_CLASS
|
||||
GTK_IS_COLUMN_VIEW_COLUMN
|
||||
GTK_IS_COLUMN_VIEW_COLUMN_CLASS
|
||||
GTK_TYPE_COLUMN_VIEW_COLUMN
|
||||
<SUBSECTION Private>
|
||||
gtk_column_view_column_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkgridview</FILE>
|
||||
<TITLE>GtkGridView</TITLE>
|
||||
GtkGridView
|
||||
gtk_grid_view_new
|
||||
gtk_grid_view_set_model
|
||||
gtk_grid_view_get_model
|
||||
gtk_grid_view_set_max_columns
|
||||
gtk_grid_view_get_max_columns
|
||||
gtk_grid_view_set_min_columns
|
||||
gtk_grid_view_get_min_columns
|
||||
<SUBSECTION Standard>
|
||||
GTK_GRID_VIEW
|
||||
GTK_GRID_VIEW_CLASS
|
||||
GTK_GRID_VIEW_GET_CLASS
|
||||
GTK_IS_GRID_VIEW
|
||||
GTK_IS_GRID_VIEW_CLASS
|
||||
GTK_TYPE_GRID_VIEW
|
||||
<SUBSECTION Private>
|
||||
gtk_grid_view_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbuildable</FILE>
|
||||
GtkBuildable
|
||||
@@ -516,21 +646,41 @@ GTK_BUILDABLE_CLASS
|
||||
GTK_BUILDABLE_GET_IFACE
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbuilderscope</FILE>
|
||||
<TITLE>GtkBuilderScope</TITLE>
|
||||
gtk_builder_cscope_new
|
||||
gtk_builder_cscope_add_callback_symbol
|
||||
gtk_builder_cscope_add_callback_symbols
|
||||
gtk_builder_cscope_lookup_callback_symbol
|
||||
<SUBSECTION Standard>
|
||||
GTK_BUILDER_SCOPE
|
||||
GTK_IS_BUILDER_SCOPE
|
||||
GTK_TYPE_BUILDER_SCOPE
|
||||
GTK_BUILDER_SCOPE_INTERFACE
|
||||
GTK_IS_BUILDER_SCOPE_INTERFACE
|
||||
GTK_BUILDER_SCOPE_GET_INTERFACE
|
||||
GTK_BUILDER_CSCOPE
|
||||
GTK_IS_BUILDER_CSCOPE
|
||||
GTK_TYPE_BUILDER_CSCOPE
|
||||
GTK_BUILDER_CSCOPE_CLASS
|
||||
GTK_IS_BUILDER_CSCOPE_CLASS
|
||||
GTK_BUILDER_CSCOPE_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_builder_scope_get_type
|
||||
gtk_builder_cscope_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbuilder</FILE>
|
||||
<TITLE>GtkBuilder</TITLE>
|
||||
GtkBuilder
|
||||
GtkBuilderClosureFunc
|
||||
GtkBuilderError
|
||||
gtk_builder_new
|
||||
gtk_builder_new_from_file
|
||||
gtk_builder_new_from_resource
|
||||
gtk_builder_new_from_string
|
||||
gtk_builder_add_callback_symbol
|
||||
gtk_builder_add_callback_symbols
|
||||
gtk_builder_lookup_callback_symbol
|
||||
gtk_builder_create_closure
|
||||
gtk_builder_create_cclosure
|
||||
gtk_builder_add_from_file
|
||||
gtk_builder_add_from_resource
|
||||
gtk_builder_add_from_string
|
||||
@@ -541,10 +691,13 @@ gtk_builder_extend_with_template
|
||||
gtk_builder_get_object
|
||||
gtk_builder_get_objects
|
||||
gtk_builder_expose_object
|
||||
gtk_builder_set_current_object
|
||||
gtk_builder_get_current_object
|
||||
gtk_builder_set_scope
|
||||
gtk_builder_get_scope
|
||||
gtk_builder_set_translation_domain
|
||||
gtk_builder_get_translation_domain
|
||||
gtk_builder_get_type_from_name
|
||||
gtk_builder_set_closure_func
|
||||
gtk_builder_value_from_string
|
||||
gtk_builder_value_from_string_type
|
||||
GTK_BUILDER_WARN_INVALID_CHILD_TYPE
|
||||
@@ -560,7 +713,6 @@ GTK_BUILDER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_builder_get_type
|
||||
gtk_builder_error_quark
|
||||
GtkBuilderPrivate
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
@@ -1323,6 +1475,51 @@ GTK_TYPE_FILE_FILTER
|
||||
gtk_file_filter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkdirectorylist</FILE>
|
||||
<TITLE>GtkDirectoryList</TITLE>
|
||||
GtkDirectoryList
|
||||
gtk_directory_list_new
|
||||
gtk_directory_list_get_attributes
|
||||
gtk_directory_list_set_attributes
|
||||
gtk_directory_list_get_file
|
||||
gtk_directory_list_set_file
|
||||
gtk_directory_list_get_io_priority
|
||||
gtk_directory_list_set_io_priority
|
||||
gtk_directory_list_is_loading
|
||||
gtk_directory_list_get_error
|
||||
<SUBSECTION Standard>
|
||||
GTK_DIRECTORY_LIST
|
||||
GTK_IS_DIRECTORY_LIST
|
||||
GTK_TYPE_DIRECTORY_LIST
|
||||
GTK_DIRECTORY_LIST_CLASS
|
||||
GTK_IS_DIRECTORY_LIST_CLASS
|
||||
GTK_DIRECTORY_LIST_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_directory_list_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkfilter</FILE>
|
||||
<TITLE>GtkFilter</TITLE>
|
||||
GtkFilter
|
||||
gtk_filter_match
|
||||
gtk_filter_get_strictness
|
||||
<SUBSECTION>
|
||||
gtk_filter_changed
|
||||
<SUBSECTION>
|
||||
gtk_custom_filter_new
|
||||
<SUBSECTION Standard>
|
||||
GTK_FILTER
|
||||
GTK_IS_FILTER
|
||||
GTK_TYPE_FILTER
|
||||
GTK_FILTER_CLASS
|
||||
GTK_IS_FILTER_CLASS
|
||||
GTK_FILTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_filter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkfilterlistmodel</FILE>
|
||||
<TITLE>GtkFilterListModel</TITLE>
|
||||
@@ -1331,9 +1528,8 @@ gtk_filter_list_model_new
|
||||
gtk_filter_list_model_new_for_type
|
||||
gtk_filter_list_model_set_model
|
||||
gtk_filter_list_model_get_model
|
||||
gtk_filter_list_model_set_filter_func
|
||||
gtk_filter_list_model_has_filter
|
||||
gtk_filter_list_model_refilter
|
||||
gtk_filter_list_model_set_filter
|
||||
gtk_filter_list_model_get_filter
|
||||
<SUBSECTION Standard>
|
||||
GTK_FILTER_LIST_MODEL
|
||||
GTK_IS_FILTER_LIST_MODEL
|
||||
@@ -2663,17 +2859,121 @@ GTK_SLICE_LIST_MODEL_GET_CLASS
|
||||
gtk_slice_list_model_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtksorter</FILE>
|
||||
<TITLE>GtkSorter</TITLE>
|
||||
GtkSorter
|
||||
GtkSorterChange
|
||||
gtk_sorter_compare
|
||||
gtk_sorter_changed
|
||||
<SUBSECTION Standard>
|
||||
GTK_SORTER
|
||||
GTK_IS_SORTER
|
||||
GTK_TYPE_SORTER
|
||||
GTK_SORTER_CLASS
|
||||
GTK_IS_SORTER_CLASS
|
||||
GTK_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkstringsorter</FILE>
|
||||
<TITLE>GtkStringSorter</TITLE>
|
||||
GtkStringSorter
|
||||
gtk_string_sorter_new
|
||||
gtk_string_sorter_get_expression
|
||||
gtk_string_sorter_set_expression
|
||||
gtk_string_sorter_get_ignore_case
|
||||
gtk_string_sorter_set_ignore_case
|
||||
<SUBSECTION Standard>
|
||||
GTK_STRING_SORTER
|
||||
GTK_IS_STRING_SORTER
|
||||
GTK_TYPE_STRING_SORTER
|
||||
GTK_IS_STRING_SORTER_CLASS
|
||||
GTK_STRING_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_string_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtknumericsorter</FILE>
|
||||
<TITLE>GtkNumericSorter</TITLE>
|
||||
GtkNumericSorter
|
||||
gtk_numeric_sorter_new
|
||||
gtk_numeric_sorter_get_expression
|
||||
gtk_numeric_sorter_set_expression
|
||||
gtk_numeric_sorter_get_sort_increasing
|
||||
gtk_numeric_sorter_set_sort_increasing
|
||||
<SUBSECTION Standard>
|
||||
GTK_NUMERIC_SORTER
|
||||
GTK_IS_NUMERIC_SORTER
|
||||
GTK_TYPE_NUMERIC_SORTER
|
||||
GTK_IS_NUMERIC_SORTER_CLASS
|
||||
GTK_NUMERIC_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_numeric_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkcustomsorter</FILE>
|
||||
<TITLE>GtkCustomSorter</TITLE>
|
||||
GtkCustomSorter
|
||||
gtk_custom_sorter_new
|
||||
<SUBSECTION Standard>
|
||||
GTK_CUSTOM_SORTER
|
||||
GTK_IS_CUSTOM_SORTER
|
||||
GTK_TYPE_CUSTOM_SORTER
|
||||
GTK_IS_CUSTOM_SORTER_CLASS
|
||||
GTK_CUSTOM_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_custom_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkmultisorter</FILE>
|
||||
<TITLE>GtkMultiSorter</TITLE>
|
||||
GtkMultiSorter
|
||||
gtk_multi_sorter_new
|
||||
gtk_multi_sorter_append
|
||||
gtk_multi_sorter_remove
|
||||
<SUBSECTION Standard>
|
||||
GTK_MULTI_SORTER
|
||||
GTK_IS_MULTI_SORTER
|
||||
GTK_TYPE_MULTI_SORTER
|
||||
GTK_IS_MULTI_SORTER_CLASS
|
||||
GTK_MULTI_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_multi_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtktreelistrowsorter</FILE>
|
||||
<TITLE>GtkTreeListRowSorter</TITLE>
|
||||
GtkTreeListRowSorter
|
||||
gtk_tree_list_row_sorter_new
|
||||
gtk_tree_list_row_sorter_get_sorter
|
||||
gtk_tree_list_row_sorter_set_sorter
|
||||
<SUBSECTION Standard>
|
||||
GTK_TREE_LIST_ROW_SORTER
|
||||
GTK_IS_TREE_LIST_ROW_SORTER
|
||||
GTK_TYPE_TREE_LIST_ROW_SORTER
|
||||
GTK_IS_TREE_LIST_ROW_SORTER_CLASS
|
||||
GTK_TREE_LIST_ROW_SORTER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_tree_list_row_sorter_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtksortlistmodel</FILE>
|
||||
<TITLE>GtkSortListModel</TITLE>
|
||||
GtkSortListModel
|
||||
gtk_sort_list_model_new
|
||||
gtk_sort_list_model_new_for_type
|
||||
gtk_sort_list_model_set_sort_func
|
||||
gtk_sort_list_model_has_sort
|
||||
gtk_sort_list_model_set_sorter
|
||||
gtk_sort_list_model_get_sorter
|
||||
gtk_sort_list_model_set_model
|
||||
gtk_sort_list_model_get_model
|
||||
gtk_sort_list_model_resort
|
||||
<SUBSECTION Standard>
|
||||
GTK_SORT_LIST_MODEL
|
||||
GTK_IS_SORT_LIST_MODEL
|
||||
@@ -3472,6 +3772,26 @@ gtk_tree_list_model_get_type
|
||||
gtk_tree_list_row_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtktreeexpander</FILE>
|
||||
<TITLE>GtkTreeExpander</TITLE>
|
||||
gtk_tree_expander_new
|
||||
gtk_tree_expander_get_child
|
||||
gtk_tree_expander_set_child
|
||||
gtk_tree_expander_get_item
|
||||
gtk_tree_expander_get_list_row
|
||||
gtk_tree_expander_set_list_row
|
||||
<SUBSECTION Standard>
|
||||
GTK_TREE_EXPANDER
|
||||
GTK_IS_TREE_EXPANDER
|
||||
GTK_TYPE_TREE_EXPANDER
|
||||
GTK_TREE_EXPANDER_CLASS
|
||||
GTK_IS_TREE_EXPANDER_CLASS
|
||||
GTK_TREE_EXPANDER_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_tree_expander_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtktreemodel</FILE>
|
||||
<TITLE>GtkTreeModel</TITLE>
|
||||
@@ -4559,6 +4879,7 @@ gtk_widget_get_allocation
|
||||
gtk_widget_get_allocated_baseline
|
||||
gtk_widget_get_width
|
||||
gtk_widget_get_height
|
||||
gtk_widget_get_size
|
||||
gtk_widget_compute_bounds
|
||||
gtk_widget_compute_transform
|
||||
gtk_widget_compute_point
|
||||
@@ -4666,7 +4987,7 @@ gtk_widget_class_bind_template_child_internal_private
|
||||
gtk_widget_class_bind_template_child_full
|
||||
gtk_widget_class_bind_template_callback
|
||||
gtk_widget_class_bind_template_callback_full
|
||||
gtk_widget_class_set_closure_func
|
||||
gtk_widget_class_set_template_scope
|
||||
|
||||
<SUBSECTION>
|
||||
gtk_widget_observe_children
|
||||
|
||||
@@ -72,8 +72,10 @@ gtk_file_chooser_dialog_get_type
|
||||
gtk_file_chooser_get_type
|
||||
gtk_file_chooser_widget_get_type
|
||||
gtk_file_filter_get_type
|
||||
gtk_filter_list_model_get_type
|
||||
gtk_fixed_get_type
|
||||
gtk_fixed_layout_get_type
|
||||
gtk_flatten_list_model_get_type
|
||||
gtk_flow_box_get_type
|
||||
gtk_flow_box_child_get_type
|
||||
gtk_font_button_get_type
|
||||
@@ -111,6 +113,7 @@ gtk_list_store_get_type
|
||||
gtk_list_box_get_type
|
||||
gtk_list_box_row_get_type
|
||||
gtk_lock_button_get_type
|
||||
gtk_map_list_model_get_type
|
||||
gtk_media_controls_get_type
|
||||
gtk_media_file_get_type
|
||||
gtk_media_stream_get_type
|
||||
@@ -122,9 +125,12 @@ gtk_menu_shell_get_type
|
||||
gtk_menu_tool_button_get_type
|
||||
gtk_message_dialog_get_type
|
||||
gtk_mount_operation_get_type
|
||||
gtk_multi_sorter_get_type
|
||||
gtk_native_get_type
|
||||
gtk_no_selection_get_type
|
||||
gtk_notebook_get_type
|
||||
gtk_notebook_page_get_type
|
||||
gtk_numeric_sorter_get_type
|
||||
gtk_orientable_get_type
|
||||
gtk_overlay_get_type
|
||||
gtk_pad_controller_get_type
|
||||
@@ -171,7 +177,9 @@ gtk_shortcuts_group_get_type
|
||||
gtk_shortcuts_shortcut_get_type
|
||||
gtk_single_selection_get_type
|
||||
gtk_size_group_get_type
|
||||
gtk_slice_list_model_get_type
|
||||
gtk_snapshot_get_type
|
||||
gtk_sort_list_model_get_type
|
||||
gtk_spin_button_get_type
|
||||
gtk_spinner_get_type
|
||||
gtk_stack_get_type
|
||||
@@ -198,6 +206,8 @@ gtk_tool_button_get_type
|
||||
gtk_tool_item_get_type
|
||||
gtk_tree_drag_dest_get_type
|
||||
gtk_tree_drag_source_get_type
|
||||
gtk_tree_list_model_get_type
|
||||
gtk_tree_list_row_sorter_get_type
|
||||
gtk_tree_model_filter_get_type
|
||||
gtk_tree_model_get_type
|
||||
gtk_tree_model_sort_get_type
|
||||
|
||||
@@ -136,6 +136,16 @@ static double gdk_paintable_default_get_intrinsic_aspect_ratio (GdkPaintable *pa
|
||||
return (double) width / height;
|
||||
};
|
||||
|
||||
static void
|
||||
g_value_object_transform_value (const GValue *src_value,
|
||||
GValue *dest_value)
|
||||
{
|
||||
if (src_value->data[0].v_pointer && g_type_is_a (G_OBJECT_TYPE (src_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
|
||||
dest_value->data[0].v_pointer = g_object_ref (src_value->data[0].v_pointer);
|
||||
else
|
||||
dest_value->data[0].v_pointer = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_paintable_default_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
@@ -146,6 +156,9 @@ gdk_paintable_default_init (GdkPaintableInterface *iface)
|
||||
iface->get_intrinsic_height = gdk_paintable_default_get_intrinsic_height;
|
||||
iface->get_intrinsic_aspect_ratio = gdk_paintable_default_get_intrinsic_aspect_ratio;
|
||||
|
||||
g_value_register_transform_func (G_TYPE_OBJECT, GDK_TYPE_PAINTABLE, g_value_object_transform_value);
|
||||
g_value_register_transform_func (GDK_TYPE_PAINTABLE, G_TYPE_OBJECT, g_value_object_transform_value);
|
||||
|
||||
/**
|
||||
* GdkPaintable::invalidate-contents
|
||||
* @paintable: a #GdkPaintable
|
||||
|
||||
@@ -45,9 +45,6 @@ typedef struct _GdkWaylandSurfaceClass GdkWaylandSurfaceClass;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gdk_wayland_surface_get_type (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkSurface * gdk_wayland_surface_new_subsurface (GdkDisplay *display,
|
||||
const GdkRectangle *position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
struct wl_surface *gdk_wayland_surface_get_wl_surface (GdkSurface *surface);
|
||||
|
||||
|
||||
@@ -1730,7 +1730,9 @@ _gtk_tree_view_accessible_changed (GtkTreeView *treeview,
|
||||
GtkTreeViewAccessible *accessible;
|
||||
guint i;
|
||||
|
||||
accessible = GTK_TREE_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (treeview)));
|
||||
accessible = GTK_TREE_VIEW_ACCESSIBLE (_gtk_widget_peek_accessible (GTK_WIDGET (treeview)));
|
||||
if (accessible == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < gtk_tree_view_get_n_columns (treeview); i++)
|
||||
{
|
||||
|
||||
@@ -92,6 +92,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureSingle, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureSwipe, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGestureZoom, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGrid, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkGridView, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkHeaderBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIMContext, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkIMContextSimple, g_object_unref)
|
||||
@@ -104,6 +105,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkInfoBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLevelBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLinkButton, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListStore, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkListView, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLockButton, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkMenuBar, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkMenuButton, g_object_unref)
|
||||
|
||||
27
gtk/gtk.h
27
gtk/gtk.h
@@ -55,6 +55,8 @@
|
||||
#include <gtk/gtkbox.h>
|
||||
#include <gtk/gtkbuildable.h>
|
||||
#include <gtk/gtkbuilder.h>
|
||||
#include <gtk/gtkbuilderlistitemfactory.h>
|
||||
#include <gtk/gtkbuilderscope.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkcalendar.h>
|
||||
#include <gtk/gtkcellarea.h>
|
||||
@@ -81,19 +83,25 @@
|
||||
#include <gtk/gtkcolorchooserdialog.h>
|
||||
#include <gtk/gtkcolorchooserwidget.h>
|
||||
#include <gtk/gtkcolorutils.h>
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
#include <gtk/gtkcolumnviewcolumn.h>
|
||||
#include <gtk/gtkcombobox.h>
|
||||
#include <gtk/gtkcomboboxtext.h>
|
||||
#include <gtk/gtkconstraintlayout.h>
|
||||
#include <gtk/gtkconstraint.h>
|
||||
#include <gtk/gtkcontainer.h>
|
||||
#include <gtk/gtkcoverflow.h>
|
||||
#include <gtk/gtkcssprovider.h>
|
||||
#include <gtk/gtkcustomlayout.h>
|
||||
#include <gtk/gtkcustomsorter.h>
|
||||
#include <gtk/gtkdebug.h>
|
||||
#include <gtk/gtkdialog.h>
|
||||
#include <gtk/gtkdirectorylist.h>
|
||||
#include <gtk/gtkdnd.h>
|
||||
#include <gtk/gtkdragdest.h>
|
||||
#include <gtk/gtkdragsource.h>
|
||||
#include <gtk/gtkdrawingarea.h>
|
||||
#include <gtk/gtkdropdown.h>
|
||||
#include <gtk/gtkeditable.h>
|
||||
#include <gtk/gtkentry.h>
|
||||
#include <gtk/gtkentrybuffer.h>
|
||||
@@ -105,6 +113,7 @@
|
||||
#include <gtk/gtkeventcontrollermotion.h>
|
||||
#include <gtk/gtkeventcontrollerscroll.h>
|
||||
#include <gtk/gtkexpander.h>
|
||||
#include <gtk/gtkexpression.h>
|
||||
#include <gtk/gtkfixed.h>
|
||||
#include <gtk/gtkfixedlayout.h>
|
||||
#include <gtk/gtkfilechooser.h>
|
||||
@@ -113,7 +122,9 @@
|
||||
#include <gtk/gtkfilechoosernative.h>
|
||||
#include <gtk/gtkfilechooserwidget.h>
|
||||
#include <gtk/gtkfilefilter.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
#include <gtk/gtkfilterlistmodel.h>
|
||||
#include <gtk/gtkfilters.h>
|
||||
#include <gtk/gtkflattenlistmodel.h>
|
||||
#include <gtk/gtkflowbox.h>
|
||||
#include <gtk/gtkfontbutton.h>
|
||||
@@ -121,6 +132,7 @@
|
||||
#include <gtk/gtkfontchooserdialog.h>
|
||||
#include <gtk/gtkfontchooserwidget.h>
|
||||
#include <gtk/gtkframe.h>
|
||||
#include <gtk/gtkfunctionslistitemfactory.h>
|
||||
#include <gtk/gtkgesture.h>
|
||||
#include <gtk/gtkgestureclick.h>
|
||||
#include <gtk/gtkgesturedrag.h>
|
||||
@@ -134,6 +146,7 @@
|
||||
#include <gtk/gtkglarea.h>
|
||||
#include <gtk/gtkgrid.h>
|
||||
#include <gtk/gtkgridlayout.h>
|
||||
#include <gtk/gtkgridview.h>
|
||||
#include <gtk/gtkheaderbar.h>
|
||||
#include <gtk/gtkicontheme.h>
|
||||
#include <gtk/gtkiconview.h>
|
||||
@@ -146,9 +159,13 @@
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
#include <gtk/gtklayoutchild.h>
|
||||
#include <gtk/gtklevelbar.h>
|
||||
#include <gtk/gtklistbase.h>
|
||||
#include <gtk/gtklinkbutton.h>
|
||||
#include <gtk/gtklistbox.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
#include <gtk/gtklistitemfactory.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtklistview.h>
|
||||
#include <gtk/gtklockbutton.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <gtk/gtkmaplistmodel.h>
|
||||
@@ -163,10 +180,13 @@
|
||||
#include <gtk/gtkmenutoolbutton.h>
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtkmountoperation.h>
|
||||
#include <gtk/gtkmultiselection.h>
|
||||
#include <gtk/gtkmultisorter.h>
|
||||
#include <gtk/gtknative.h>
|
||||
#include <gtk/gtknativedialog.h>
|
||||
#include <gtk/gtknoselection.h>
|
||||
#include <gtk/gtknotebook.h>
|
||||
#include <gtk/gtknumericsorter.h>
|
||||
#include <gtk/gtkorientable.h>
|
||||
#include <gtk/gtkoverlay.h>
|
||||
#include <gtk/gtkpadcontroller.h>
|
||||
@@ -200,6 +220,7 @@
|
||||
#include <gtk/gtksearchentry.h>
|
||||
#include <gtk/gtkselection.h>
|
||||
#include <gtk/gtkselectionmodel.h>
|
||||
#include <gtk/gtkselectionmonitor.h>
|
||||
#include <gtk/gtkseparator.h>
|
||||
#include <gtk/gtkseparatormenuitem.h>
|
||||
#include <gtk/gtkseparatortoolitem.h>
|
||||
@@ -210,9 +231,11 @@
|
||||
#include <gtk/gtkshortcutsshortcut.h>
|
||||
#include <gtk/gtkshortcutswindow.h>
|
||||
#include <gtk/gtkshow.h>
|
||||
#include <gtk/gtksignallistitemfactory.h>
|
||||
#include <gtk/gtksingleselection.h>
|
||||
#include <gtk/gtkslicelistmodel.h>
|
||||
#include <gtk/gtksnapshot.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
#include <gtk/gtksortlistmodel.h>
|
||||
#include <gtk/gtkstacksidebar.h>
|
||||
#include <gtk/gtksizegroup.h>
|
||||
@@ -222,6 +245,8 @@
|
||||
#include <gtk/gtkstack.h>
|
||||
#include <gtk/gtkstackswitcher.h>
|
||||
#include <gtk/gtkstatusbar.h>
|
||||
#include <gtk/gtkstringfilter.h>
|
||||
#include <gtk/gtkstringsorter.h>
|
||||
#include <gtk/gtkstylecontext.h>
|
||||
#include <gtk/gtkstyleprovider.h>
|
||||
#include <gtk/gtkswitch.h>
|
||||
@@ -242,7 +267,9 @@
|
||||
#include <gtk/gtktooltip.h>
|
||||
#include <gtk/gtktestutils.h>
|
||||
#include <gtk/gtktreednd.h>
|
||||
#include <gtk/gtktreeexpander.h>
|
||||
#include <gtk/gtktreelistmodel.h>
|
||||
#include <gtk/gtktreelistrowsorter.h>
|
||||
#include <gtk/gtktreemodel.h>
|
||||
#include <gtk/gtktreemodelfilter.h>
|
||||
#include <gtk/gtktreemodelsort.h>
|
||||
|
||||
723
gtk/gtkbuilder.c
723
gtk/gtkbuilder.c
@@ -162,23 +162,6 @@
|
||||
* “last_modification_time” attribute is also allowed, but it does not
|
||||
* have a meaning to the builder.
|
||||
*
|
||||
* By default, GTK+ tries to find functions (like the handlers for
|
||||
* signals) by using g_module_symbol(), but this can be changed by
|
||||
* passing a custom #GtkBuilderClosureFunc to gtk_builder_set_closure_func().
|
||||
* Bindings in particular will want to make use of this functionality to
|
||||
* allow language-specific name mangling and namespacing.
|
||||
*
|
||||
* The default closure function uses symbols explicitly added to @builder
|
||||
* with prior calls to gtk_builder_add_callback_symbol(). In the case that
|
||||
* symbols are not explicitly added; it uses #GModule’s introspective
|
||||
* features (by opening the module %NULL) to look at the application’s symbol
|
||||
* table. From here it tries to match the signal function names given in the
|
||||
* interface description with symbols in the application.
|
||||
*
|
||||
* Note that unless gtk_builder_add_callback_symbol() is called for
|
||||
* all signal callbacks which are referenced by the loaded XML, this
|
||||
* functionality will require that #GModule be supported on the platform.
|
||||
*
|
||||
* If you rely on #GModule support to lookup callbacks in the symbol table,
|
||||
* the following details should be noted:
|
||||
*
|
||||
@@ -230,16 +213,18 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h> /* strlen */
|
||||
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbuilderprivate.h"
|
||||
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbuilderlistitemfactory.h"
|
||||
#include "gtkbuilderscopeprivate.h"
|
||||
#include "gtkdebug.h"
|
||||
#include "gtkexpression.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkicontheme.h"
|
||||
#include "gtktestutils.h"
|
||||
|
||||
static void gtk_builder_finalize (GObject *object);
|
||||
static void gtk_builder_set_property (GObject *object,
|
||||
@@ -253,6 +238,8 @@ static void gtk_builder_get_property (GObject *object,
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CURRENT_OBJECT,
|
||||
PROP_SCOPE,
|
||||
PROP_TRANSLATION_DOMAIN,
|
||||
LAST_PROP
|
||||
};
|
||||
@@ -273,27 +260,35 @@ typedef struct
|
||||
{
|
||||
gchar *domain;
|
||||
GHashTable *objects;
|
||||
GHashTable *callbacks;
|
||||
GSList *delayed_properties;
|
||||
GSList *signals;
|
||||
GSList *bindings;
|
||||
GModule *module;
|
||||
gchar *filename;
|
||||
gchar *resource_prefix;
|
||||
GType template_type;
|
||||
|
||||
GtkBuilderClosureFunc closure_func;
|
||||
gpointer closure_data;
|
||||
GDestroyNotify closure_destroy;
|
||||
GObject *current_object;
|
||||
GtkBuilderScope *scope;
|
||||
} GtkBuilderPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkBuilder, gtk_builder, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_builder_dispose (GObject *object)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object));
|
||||
|
||||
g_clear_object (&priv->current_object);
|
||||
g_clear_object (&priv->scope);
|
||||
|
||||
G_OBJECT_CLASS (gtk_builder_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_class_init (GtkBuilderClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_builder_dispose;
|
||||
gobject_class->finalize = gtk_builder_finalize;
|
||||
gobject_class->set_property = gtk_builder_set_property;
|
||||
gobject_class->get_property = gtk_builder_get_property;
|
||||
@@ -313,6 +308,30 @@ gtk_builder_class_init (GtkBuilderClass *klass)
|
||||
NULL,
|
||||
GTK_PARAM_READWRITE);
|
||||
|
||||
/**
|
||||
* GtkBuilder:current-object:
|
||||
*
|
||||
* The object the builder is evaluating for.
|
||||
*/
|
||||
builder_props[PROP_CURRENT_OBJECT] =
|
||||
g_param_spec_object ("current-object",
|
||||
P_("Current object"),
|
||||
P_("The object the builder is evaluating for"),
|
||||
G_TYPE_OBJECT,
|
||||
GTK_PARAM_READWRITE);
|
||||
|
||||
/**
|
||||
* GtkBuilder:scope:
|
||||
*
|
||||
* The scope the builder is operating in
|
||||
*/
|
||||
builder_props[PROP_SCOPE] =
|
||||
g_param_spec_object ("scope",
|
||||
P_("Scope"),
|
||||
P_("The scope the builder is operating in"),
|
||||
GTK_TYPE_BUILDER_SCOPE,
|
||||
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
||||
|
||||
g_object_class_install_properties (gobject_class, LAST_PROP, builder_props);
|
||||
}
|
||||
|
||||
@@ -336,18 +355,11 @@ gtk_builder_finalize (GObject *object)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object));
|
||||
|
||||
if (priv->closure_destroy)
|
||||
priv->closure_destroy (priv->closure_data);
|
||||
|
||||
g_clear_pointer (&priv->module, g_module_close);
|
||||
|
||||
g_free (priv->domain);
|
||||
g_free (priv->filename);
|
||||
g_free (priv->resource_prefix);
|
||||
|
||||
g_hash_table_destroy (priv->objects);
|
||||
if (priv->callbacks)
|
||||
g_hash_table_destroy (priv->callbacks);
|
||||
|
||||
g_slist_free_full (priv->signals, (GDestroyNotify)_free_signal_info);
|
||||
|
||||
@@ -364,9 +376,18 @@ gtk_builder_set_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CURRENT_OBJECT:
|
||||
gtk_builder_set_current_object (builder, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SCOPE:
|
||||
gtk_builder_set_scope (builder, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_TRANSLATION_DOMAIN:
|
||||
gtk_builder_set_translation_domain (builder, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -384,88 +405,24 @@ gtk_builder_get_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CURRENT_OBJECT:
|
||||
g_value_set_object (value, priv->current_object);
|
||||
break;
|
||||
|
||||
case PROP_SCOPE:
|
||||
g_value_set_object (value, priv->scope);
|
||||
break;
|
||||
|
||||
case PROP_TRANSLATION_DOMAIN:
|
||||
g_value_set_string (value, priv->domain);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Try to map a type name to a _get_type function
|
||||
* and call it, eg:
|
||||
*
|
||||
* GtkWindow -> gtk_window_get_type
|
||||
* GtkHBox -> gtk_hbox_get_type
|
||||
* GtkUIManager -> gtk_ui_manager_get_type
|
||||
* GWeatherLocation -> gweather_location_get_type
|
||||
*
|
||||
* Keep in sync with testsuite/gtk/typename.c !
|
||||
*/
|
||||
static gchar *
|
||||
type_name_mangle (const gchar *name)
|
||||
{
|
||||
GString *symbol_name = g_string_new ("");
|
||||
gint i;
|
||||
|
||||
for (i = 0; name[i] != '\0'; i++)
|
||||
{
|
||||
/* skip if uppercase, first or previous is uppercase */
|
||||
if ((name[i] == g_ascii_toupper (name[i]) &&
|
||||
i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
|
||||
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
|
||||
name[i-1] == g_ascii_toupper (name[i-1]) &&
|
||||
name[i-2] == g_ascii_toupper (name[i-2])))
|
||||
g_string_append_c (symbol_name, '_');
|
||||
g_string_append_c (symbol_name, g_ascii_tolower (name[i]));
|
||||
}
|
||||
g_string_append (symbol_name, "_get_type");
|
||||
|
||||
return g_string_free (symbol_name, FALSE);
|
||||
}
|
||||
|
||||
GModule *
|
||||
gtk_builder_get_module (GtkBuilder *builder)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
if (priv->module == NULL)
|
||||
{
|
||||
if (!g_module_supported ())
|
||||
return NULL;
|
||||
|
||||
priv->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
|
||||
}
|
||||
|
||||
return priv->module;
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_builder_resolve_type_lazily (GtkBuilder *builder,
|
||||
const gchar *name)
|
||||
{
|
||||
GModule *module;
|
||||
GTypeGetFunc func;
|
||||
gchar *symbol;
|
||||
GType gtype = G_TYPE_INVALID;
|
||||
|
||||
module = gtk_builder_get_module (builder);
|
||||
if (!module)
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
symbol = type_name_mangle (name);
|
||||
|
||||
if (g_module_symbol (module, symbol, (gpointer)&func))
|
||||
gtype = func ();
|
||||
|
||||
g_free (symbol);
|
||||
|
||||
return gtype;
|
||||
}
|
||||
|
||||
/*
|
||||
* GtkBuilder virtual methods
|
||||
*/
|
||||
@@ -565,7 +522,18 @@ gtk_builder_get_parameters (GtkBuilder *builder,
|
||||
const char *property_name = g_intern_string (prop->pspec->name);
|
||||
GValue property_value = G_VALUE_INIT;
|
||||
|
||||
if (G_IS_PARAM_SPEC_OBJECT (prop->pspec) &&
|
||||
if (prop->value)
|
||||
{
|
||||
g_value_init (&property_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
|
||||
|
||||
if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == GTK_TYPE_EXPRESSION)
|
||||
g_value_set_boxed (&property_value, prop->value);
|
||||
else
|
||||
{
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
else if (G_IS_PARAM_SPEC_OBJECT (prop->pspec) &&
|
||||
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PIXBUF) &&
|
||||
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_TEXTURE) &&
|
||||
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PAINTABLE) &&
|
||||
@@ -710,13 +678,43 @@ gtk_builder_take_bindings (GtkBuilder *builder,
|
||||
|
||||
for (l = bindings; l; l = l->next)
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
info->target = target;
|
||||
CommonInfo *common_info = l->data;
|
||||
|
||||
if (common_info->tag_type == TAG_BINDING)
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
info->target = target;
|
||||
}
|
||||
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
|
||||
{
|
||||
BindingExpressionInfo *info = l->data;
|
||||
info->target = target;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
priv->bindings = g_slist_concat (priv->bindings, bindings);
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_special_construct_parameters (GtkBuilder *builder,
|
||||
GType object_type,
|
||||
ObjectProperties *construct_parameters)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
if (g_type_is_a (object_type, GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
|
||||
{
|
||||
g_value_init (&value, GTK_TYPE_BUILDER_SCOPE);
|
||||
g_value_set_object (&value, priv->scope);
|
||||
object_properties_add (construct_parameters, "scope", &value);
|
||||
}
|
||||
}
|
||||
|
||||
GObject *
|
||||
_gtk_builder_construct (GtkBuilder *builder,
|
||||
ObjectInfo *info,
|
||||
@@ -814,6 +812,8 @@ _gtk_builder_construct (GtkBuilder *builder,
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure_special_construct_parameters (builder, info->type, construct_parameters);
|
||||
|
||||
obj = g_object_new_with_properties (info->type,
|
||||
construct_parameters->len,
|
||||
(const char **) construct_parameters->names->pdata,
|
||||
@@ -996,11 +996,13 @@ _gtk_builder_add_signals (GtkBuilder *builder,
|
||||
g_slist_copy (signals));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_apply_delayed_properties (GtkBuilder *builder)
|
||||
static gboolean
|
||||
gtk_builder_apply_delayed_properties (GtkBuilder *builder,
|
||||
GError **error)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GSList *l, *props;
|
||||
gboolean result = TRUE;
|
||||
|
||||
/* take the list over from the builder->priv.
|
||||
*
|
||||
@@ -1015,53 +1017,100 @@ gtk_builder_apply_delayed_properties (GtkBuilder *builder)
|
||||
DelayedProperty *property = l->data;
|
||||
GObject *object, *obj;
|
||||
|
||||
object = g_hash_table_lookup (priv->objects, property->object);
|
||||
g_assert (object != NULL);
|
||||
if (result)
|
||||
{
|
||||
object = g_hash_table_lookup (priv->objects, property->object);
|
||||
g_assert (object != NULL);
|
||||
|
||||
obj = _gtk_builder_lookup_object (builder, property->value, property->line, property->col);
|
||||
if (obj)
|
||||
g_object_set (object, property->pspec->name, obj, NULL);
|
||||
obj = gtk_builder_lookup_object (builder, property->value, property->line, property->col, error);
|
||||
if (obj)
|
||||
g_object_set (object, property->pspec->name, obj, NULL);
|
||||
else
|
||||
result = FALSE;
|
||||
}
|
||||
|
||||
g_free (property->value);
|
||||
g_free (property->object);
|
||||
g_slice_free (DelayedProperty, property);
|
||||
}
|
||||
g_slist_free (props);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void
|
||||
free_binding_info (gpointer data,
|
||||
gpointer user)
|
||||
{
|
||||
BindingInfo *info = data;
|
||||
|
||||
g_free (info->source);
|
||||
g_free (info->source_property);
|
||||
g_slice_free (BindingInfo, data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gtk_builder_create_bindings (GtkBuilder *builder)
|
||||
static inline gboolean
|
||||
gtk_builder_create_bindings (GtkBuilder *builder,
|
||||
GError **error)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GSList *l;
|
||||
gboolean result = TRUE;
|
||||
|
||||
for (l = priv->bindings; l; l = l->next)
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
GObject *source;
|
||||
CommonInfo *common_info = l->data;
|
||||
|
||||
source = _gtk_builder_lookup_object (builder, info->source, info->line, info->col);
|
||||
if (source)
|
||||
g_object_bind_property (source, info->source_property,
|
||||
info->target, info->target_pspec->name,
|
||||
info->flags);
|
||||
if (common_info->tag_type == TAG_BINDING)
|
||||
{
|
||||
BindingInfo *info = l->data;
|
||||
GObject *source;
|
||||
|
||||
free_binding_info (info, NULL);
|
||||
source = gtk_builder_lookup_object (builder, info->source, info->line, info->col, error);
|
||||
if (source)
|
||||
g_object_bind_property (source, info->source_property,
|
||||
info->target, info->target_pspec->name,
|
||||
info->flags);
|
||||
else
|
||||
error = NULL;
|
||||
|
||||
_free_binding_info (info, NULL);
|
||||
}
|
||||
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
|
||||
{
|
||||
BindingExpressionInfo *info = l->data;
|
||||
GtkExpression *expression;
|
||||
GObject *object;
|
||||
|
||||
if (info->object_name)
|
||||
{
|
||||
object = gtk_builder_lookup_object (builder, info->object_name, info->line, info->col, error);
|
||||
if (object == NULL)
|
||||
{
|
||||
error = NULL;
|
||||
result = FALSE;
|
||||
}
|
||||
}
|
||||
else if (priv->current_object)
|
||||
{
|
||||
object = priv->current_object;
|
||||
}
|
||||
else
|
||||
{
|
||||
object = info->target;
|
||||
}
|
||||
|
||||
if (object)
|
||||
{
|
||||
expression = expression_info_construct (builder, info->expr, error);
|
||||
if (expression == NULL)
|
||||
{
|
||||
g_prefix_error (error, "%s:%d:%d: ", priv->filename, info->line, info->col);
|
||||
error = NULL;
|
||||
result = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_expression_bind (expression, info->target, object, info->target_pspec->name);
|
||||
}
|
||||
}
|
||||
|
||||
free_binding_expression_info (info);
|
||||
}
|
||||
}
|
||||
|
||||
g_slist_free (priv->bindings);
|
||||
priv->bindings = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1222,7 +1271,7 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
|
||||
/**
|
||||
* gtk_builder_extend_with_template:
|
||||
* @builder: a #GtkBuilder
|
||||
* @widget: the widget that is being extended
|
||||
* @object: the object that is being extended
|
||||
* @template_type: the type that the template is for
|
||||
* @buffer: the string to parse
|
||||
* @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
|
||||
@@ -1238,7 +1287,7 @@ gtk_builder_add_objects_from_file (GtkBuilder *builder,
|
||||
*/
|
||||
gboolean
|
||||
gtk_builder_extend_with_template (GtkBuilder *builder,
|
||||
GtkWidget *widget,
|
||||
GObject *object,
|
||||
GType template_type,
|
||||
const gchar *buffer,
|
||||
gssize length,
|
||||
@@ -1248,9 +1297,9 @@ gtk_builder_extend_with_template (GtkBuilder *builder,
|
||||
GError *tmp_error;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), 0);
|
||||
g_return_val_if_fail (g_type_name (template_type) != NULL, 0);
|
||||
g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (widget), template_type), 0);
|
||||
g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (object), template_type), 0);
|
||||
g_return_val_if_fail (buffer && buffer[0], 0);
|
||||
|
||||
tmp_error = NULL;
|
||||
@@ -1261,7 +1310,7 @@ gtk_builder_extend_with_template (GtkBuilder *builder,
|
||||
priv->resource_prefix = NULL;
|
||||
priv->template_type = template_type;
|
||||
|
||||
gtk_builder_expose_object (builder, g_type_name (template_type), G_OBJECT (widget));
|
||||
gtk_builder_expose_object (builder, g_type_name (template_type), object);
|
||||
_gtk_builder_parser_parse_buffer (builder, "<input>",
|
||||
buffer, length,
|
||||
NULL,
|
||||
@@ -1660,30 +1709,106 @@ gtk_builder_expose_object (GtkBuilder *builder,
|
||||
}
|
||||
|
||||
/**
|
||||
* GtkBuilderClosureFunc:
|
||||
* @builder: a #GtkBuilder
|
||||
* @function_name: name of the function to create a closure for
|
||||
* @swapped: if the closure should swap user data and instance
|
||||
* @object: (nullable): object to use as user data for the closure
|
||||
* @user_data: user data passed when setting the function
|
||||
* @error: location for error when creating the closure fails
|
||||
* gtk_builder_get_current_object:
|
||||
* @self: a #GtkBuilder
|
||||
*
|
||||
* Prototype of function used to create closures by @builder. It is meant
|
||||
* for influencing how @function_name is resolved.
|
||||
* Gets the current object set via gtk_builder_set_current_object().
|
||||
*
|
||||
* This function is most useful for bindings and can be used with
|
||||
* gtk_builder_set_closure_func() or gtk_widget_class_set_closure_func()
|
||||
* to allow creating closures for functions defined in the binding's
|
||||
* language.
|
||||
* Returns: (nullable) (transfer none): the current object
|
||||
**/
|
||||
GObject *
|
||||
gtk_builder_get_current_object (GtkBuilder *self)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (self), NULL);
|
||||
|
||||
return priv->current_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_set_current_object:
|
||||
* @self: a #GtkBuilder
|
||||
* @current_object: (nullable) (transfer none): the new current object or
|
||||
* %NULL for none
|
||||
*
|
||||
* If the given @function_name does not match a function name or when the
|
||||
* arguments cannot be supported by the bindings, bindings should return
|
||||
* %NULL and set @error. Usually %GTK_BUILDER_ERROR_INVALID_FUNCTION will
|
||||
* be the right error code to use.
|
||||
* Sets the current object for the @self. The current object can be
|
||||
* tought of as the `this` object that the builder is working for and
|
||||
* will often be used as the default object when an object is optional.
|
||||
*
|
||||
* Returns: (nullable): a new #GClosure or %NULL when no closure could
|
||||
* be created and @error was set.
|
||||
*/
|
||||
* gtk_widget_init_template() for example will set the current object to
|
||||
* the widget the template is inited for.
|
||||
* For functions like gtk_builder_new_from_resource(), the current object
|
||||
* will be %NULL.
|
||||
**/
|
||||
void
|
||||
gtk_builder_set_current_object (GtkBuilder *self,
|
||||
GObject *current_object)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (self));
|
||||
g_return_if_fail (current_object || G_IS_OBJECT (current_object));
|
||||
|
||||
if (!g_set_object (&priv->current_object, current_object))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), builder_props[PROP_CURRENT_OBJECT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_get_scope:
|
||||
* @self: a #GtkBuilder
|
||||
*
|
||||
* Gets the scope in use that was set via gtk_builder_set_scope().
|
||||
*
|
||||
* See the #GtkBuilderScope documentation for details.
|
||||
*
|
||||
* Returns: (transfer none): the current scope
|
||||
**/
|
||||
GtkBuilderScope *
|
||||
gtk_builder_get_scope (GtkBuilder *self)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (self), NULL);
|
||||
|
||||
return priv->scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_set_current_object:
|
||||
* @self: a #GtkBuilder
|
||||
* @scope: (nullable) (transfer none): the scope to use or
|
||||
* %NULL for the default
|
||||
*
|
||||
* Sets the scope the builder should operate in.
|
||||
*
|
||||
* If @scope is %NULL a new #GtkBuilderCScope will be created.
|
||||
*
|
||||
* See the #GtkBuilderScope documentation for details.
|
||||
**/
|
||||
void
|
||||
gtk_builder_set_scope (GtkBuilder *self,
|
||||
GtkBuilderScope *scope)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (self));
|
||||
g_return_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope));
|
||||
|
||||
if (scope && priv->scope == scope)
|
||||
return;
|
||||
|
||||
g_clear_object (&priv->scope);
|
||||
|
||||
if (scope)
|
||||
priv->scope = g_object_ref (scope);
|
||||
else
|
||||
priv->scope = gtk_builder_cscope_new ();
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), builder_props[PROP_SCOPE]);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_builder_connect_signals (GtkBuilder *builder,
|
||||
@@ -1755,9 +1880,9 @@ gboolean
|
||||
_gtk_builder_finish (GtkBuilder *builder,
|
||||
GError **error)
|
||||
{
|
||||
gtk_builder_apply_delayed_properties (builder);
|
||||
gtk_builder_create_bindings (builder);
|
||||
return gtk_builder_connect_signals (builder, error);
|
||||
return gtk_builder_apply_delayed_properties (builder, error)
|
||||
&& gtk_builder_create_bindings (builder, error)
|
||||
&& gtk_builder_connect_signals (builder, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2460,21 +2585,15 @@ GType
|
||||
gtk_builder_get_type_from_name (GtkBuilder *builder,
|
||||
const gchar *type_name)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GType type;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
|
||||
g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
type = gtk_builder_scope_get_type_from_name (priv->scope, builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
type = gtk_builder_resolve_type_lazily (builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
gtk_test_register_all_types ();
|
||||
type = g_type_from_name (type_name);
|
||||
}
|
||||
}
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
if (G_TYPE_IS_CLASSED (type))
|
||||
g_type_class_unref (g_type_class_ref (type));
|
||||
@@ -2542,172 +2661,11 @@ _gtk_builder_get_template_type (GtkBuilder *builder)
|
||||
return priv->template_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_add_callback_symbol:
|
||||
* @builder: a #GtkBuilder
|
||||
* @callback_name: The name of the callback, as expected in the XML
|
||||
* @callback_symbol: (scope async): The callback pointer
|
||||
*
|
||||
* Adds the @callback_symbol to the scope of @builder under the given @callback_name.
|
||||
*
|
||||
* Using this function overrides the behavior of gtk_builder_create_closure()
|
||||
* for any callback symbols that are added. Using this method allows for better
|
||||
* encapsulation as it does not require that callback symbols be declared in
|
||||
* the global namespace.
|
||||
*/
|
||||
void
|
||||
gtk_builder_add_callback_symbol (GtkBuilder *builder,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (builder));
|
||||
g_return_if_fail (callback_name && callback_name[0]);
|
||||
g_return_if_fail (callback_symbol != NULL);
|
||||
|
||||
if (!priv->callbacks)
|
||||
priv->callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
|
||||
g_hash_table_insert (priv->callbacks, g_strdup (callback_name), callback_symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_add_callback_symbols:
|
||||
* @builder: a #GtkBuilder
|
||||
* @first_callback_name: The name of the callback, as expected in the XML
|
||||
* @first_callback_symbol: (scope async): The callback pointer
|
||||
* @...: A list of callback name and callback symbol pairs terminated with %NULL
|
||||
*
|
||||
* A convenience function to add many callbacks instead of calling
|
||||
* gtk_builder_add_callback_symbol() for each symbol.
|
||||
*/
|
||||
void
|
||||
gtk_builder_add_callback_symbols (GtkBuilder *builder,
|
||||
const gchar *first_callback_name,
|
||||
GCallback first_callback_symbol,
|
||||
...)
|
||||
{
|
||||
va_list var_args;
|
||||
const gchar *callback_name;
|
||||
GCallback callback_symbol;
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (builder));
|
||||
g_return_if_fail (first_callback_name && first_callback_name[0]);
|
||||
g_return_if_fail (first_callback_symbol != NULL);
|
||||
|
||||
callback_name = first_callback_name;
|
||||
callback_symbol = first_callback_symbol;
|
||||
|
||||
va_start (var_args, first_callback_symbol);
|
||||
|
||||
do {
|
||||
|
||||
gtk_builder_add_callback_symbol (builder, callback_name, callback_symbol);
|
||||
|
||||
callback_name = va_arg (var_args, const gchar*);
|
||||
|
||||
if (callback_name)
|
||||
callback_symbol = va_arg (var_args, GCallback);
|
||||
|
||||
} while (callback_name != NULL);
|
||||
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_lookup_callback_symbol: (skip)
|
||||
* @builder: a #GtkBuilder
|
||||
* @callback_name: The name of the callback
|
||||
*
|
||||
* Fetches a symbol previously added to @builder
|
||||
* with gtk_builder_add_callback_symbols()
|
||||
*
|
||||
* This function is intended for possible use in language bindings
|
||||
* or for any case that one might be customizing signal connections
|
||||
* using gtk_builder_set_closure_func().
|
||||
*
|
||||
* Returns: (nullable): The callback symbol in @builder for @callback_name, or %NULL
|
||||
*/
|
||||
GCallback
|
||||
gtk_builder_lookup_callback_symbol (GtkBuilder *builder,
|
||||
const gchar *callback_name)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
|
||||
g_return_val_if_fail (callback_name && callback_name[0], NULL);
|
||||
|
||||
if (!priv->callbacks)
|
||||
return NULL;
|
||||
|
||||
return g_hash_table_lookup (priv->callbacks, callback_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_set_closure_func: (skip)
|
||||
* @builder: a #GtkBuilder
|
||||
* @closure_func: (allow-none): function to call when creating
|
||||
* closures or %NULL to use the default
|
||||
* @user_data: (nullable): user data to pass to @closure_func
|
||||
* @user_destroy: destroy function for user data
|
||||
*
|
||||
* Sets the function to call for creating closures.
|
||||
* gtk_builder_create_closure() will use this function instead
|
||||
* of gtk_builder_create_cclosure().
|
||||
*
|
||||
* This is useful for bindings.
|
||||
**/
|
||||
void
|
||||
gtk_builder_set_closure_func (GtkBuilder *builder,
|
||||
GtkBuilderClosureFunc closure_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER (builder));
|
||||
|
||||
if (priv->closure_destroy)
|
||||
priv->closure_destroy (priv->closure_data);
|
||||
|
||||
priv->closure_func = closure_func;
|
||||
priv->closure_data = user_data;
|
||||
priv->closure_destroy = user_destroy;
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
gtk_builder_create_closure_for_funcptr (GtkBuilder *builder,
|
||||
GCallback callback,
|
||||
gboolean swapped,
|
||||
GObject *object)
|
||||
{
|
||||
GClosure *closure;
|
||||
|
||||
if (object)
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_object_swap (callback, object);
|
||||
else
|
||||
closure = g_cclosure_new_object (callback, object);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_swap (callback, NULL, NULL);
|
||||
else
|
||||
closure = g_cclosure_new (callback, NULL, NULL);
|
||||
}
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_create_closure:
|
||||
* @builder: a #GtkBuilder
|
||||
* @function_name: name of the function to look up
|
||||
* @swapped: %TRUE to create a swapped closure
|
||||
* @flags: closure creation flags
|
||||
* @object: (nullable): Object to create the closure with
|
||||
* @error: (allow-none): return location for an error, or %NULL
|
||||
*
|
||||
@@ -2723,11 +2681,11 @@ gtk_builder_create_closure_for_funcptr (GtkBuilder *builder,
|
||||
* Returns: (nullable): A new closure for invoking @function_name
|
||||
**/
|
||||
GClosure *
|
||||
gtk_builder_create_closure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
gtk_builder_create_closure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
|
||||
@@ -2736,66 +2694,7 @@ gtk_builder_create_closure (GtkBuilder *builder,
|
||||
g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
if (priv->closure_func)
|
||||
return priv->closure_func (builder, function_name, swapped, object, priv->closure_data, error);
|
||||
else
|
||||
return gtk_builder_create_cclosure (builder, function_name, swapped, object, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_create_cclosure: (skip)
|
||||
* @builder: a #GtkBuilder
|
||||
* @function_name: name of the function to look up
|
||||
* @swapped: %TRUE to create a swapped closure
|
||||
* @object: (nullable): Object to create the closure with
|
||||
* @error: (allow-none): return location for an error, or %NULL
|
||||
*
|
||||
* This is the default function used by gtk_builder_set_closure_func(). Some bindings
|
||||
* with C support may want to call this function as a fallback from their closure
|
||||
* function.
|
||||
*
|
||||
* This function has no purpose otherwise.
|
||||
*
|
||||
* This function will prefer callbacks added via gtk_builder_add_callback_symbol()
|
||||
* to looking up public symbols.
|
||||
*
|
||||
* Returns: (nullable): A new closure for invoking @function_name
|
||||
**/
|
||||
GClosure *
|
||||
gtk_builder_create_cclosure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
GModule *module = gtk_builder_get_module (builder);
|
||||
GCallback func;
|
||||
|
||||
func = gtk_builder_lookup_callback_symbol (builder, function_name);
|
||||
if (func)
|
||||
return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object);
|
||||
|
||||
if (module == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Could not look up function `%s`: GModule is not supported.",
|
||||
function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!g_module_symbol (module, function_name, (gpointer)&func))
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"No function named `%s`.",
|
||||
function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gtk_builder_create_closure_for_funcptr (builder, func, swapped, object);
|
||||
return gtk_builder_scope_create_closure (priv->scope, builder, function_name, flags, object, error);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2983,6 +2882,28 @@ _gtk_builder_check_parent (GtkBuilder *builder,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GObject *
|
||||
gtk_builder_lookup_object (GtkBuilder *builder,
|
||||
const gchar *name,
|
||||
gint line,
|
||||
gint col,
|
||||
GError **error)
|
||||
{
|
||||
GtkBuilderPrivate *priv = gtk_builder_get_instance_private (builder);
|
||||
GObject *obj;
|
||||
|
||||
obj = g_hash_table_lookup (priv->objects, name);
|
||||
if (obj == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_ID,
|
||||
"%s:%d:%d Object with ID %s not found",
|
||||
priv->filename, line, col, name);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* @builder: a #GtkBuilder
|
||||
* @name: object name to look up
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkapplication.h>
|
||||
#include <gtk/gtkbuilderscope.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@@ -98,11 +98,6 @@ GType gtk_builder_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBuilder* gtk_builder_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_set_closure_func (GtkBuilder *builder,
|
||||
GtkBuilderClosureFunc closure_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_builder_add_from_file (GtkBuilder *builder,
|
||||
const gchar *filename,
|
||||
@@ -142,11 +137,21 @@ void gtk_builder_expose_object (GtkBuilder *builder,
|
||||
const gchar *name,
|
||||
GObject *object);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GObject * gtk_builder_get_current_object (GtkBuilder *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_set_current_object (GtkBuilder *self,
|
||||
GObject *current_object);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_set_translation_domain (GtkBuilder *builder,
|
||||
const gchar *domain);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const gchar* gtk_builder_get_translation_domain (GtkBuilder *builder);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBuilderScope *gtk_builder_get_scope (GtkBuilder *builder);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_set_scope (GtkBuilder *builder,
|
||||
GtkBuilderScope *scope);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_builder_get_type_from_name (GtkBuilder *builder,
|
||||
const char *type_name);
|
||||
|
||||
@@ -171,27 +176,9 @@ GtkBuilder * gtk_builder_new_from_string (const gchar *string,
|
||||
gssize length);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_add_callback_symbol (GtkBuilder *builder,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_add_callback_symbols (GtkBuilder *builder,
|
||||
const gchar *first_callback_name,
|
||||
GCallback first_callback_symbol,
|
||||
...) G_GNUC_NULL_TERMINATED;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GCallback gtk_builder_lookup_callback_symbol (GtkBuilder *builder,
|
||||
const gchar *callback_name);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GClosure * gtk_builder_create_closure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GObject *object,
|
||||
GError **error);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GClosure * gtk_builder_create_cclosure (GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
gboolean swapped,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error);
|
||||
|
||||
@@ -210,7 +197,7 @@ GClosure * gtk_builder_create_cclosure (GtkBuilder *builder,
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_builder_extend_with_template (GtkBuilder *builder,
|
||||
GtkWidget *widget,
|
||||
GObject *object,
|
||||
GType template_type,
|
||||
const gchar *buffer,
|
||||
gssize length,
|
||||
|
||||
328
gtk/gtkbuilderlistitemfactory.c
Normal file
328
gtk/gtkbuilderlistitemfactory.c
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkbuilderlistitemfactory.h"
|
||||
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
struct _GtkBuilderListItemFactory
|
||||
{
|
||||
GtkListItemFactory parent_instance;
|
||||
|
||||
GtkBuilderScope *scope;
|
||||
GBytes *bytes;
|
||||
char *resource;
|
||||
};
|
||||
|
||||
struct _GtkBuilderListItemFactoryClass
|
||||
{
|
||||
GtkListItemFactoryClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_BYTES,
|
||||
PROP_RESOURCE,
|
||||
PROP_SCOPE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (factory);
|
||||
GtkBuilder *builder;
|
||||
GError *error = NULL;
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_builder_list_item_factory_parent_class)->setup (factory, widget, list_item);
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
|
||||
gtk_builder_set_current_object (builder, G_OBJECT (list_item));
|
||||
if (self->scope)
|
||||
gtk_builder_set_scope (builder, self->scope);
|
||||
|
||||
if (!gtk_builder_extend_with_template (builder, G_OBJECT (list_item), GTK_TYPE_LIST_ITEM,
|
||||
(const gchar *)g_bytes_get_data (self->bytes, NULL),
|
||||
g_bytes_get_size (self->bytes),
|
||||
&error))
|
||||
{
|
||||
g_critical ("Error building template for list item: %s", error->message);
|
||||
g_error_free (error);
|
||||
|
||||
/* This should never happen, if the template XML cannot be built
|
||||
* then it is a critical programming error.
|
||||
*/
|
||||
g_object_unref (builder);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_BYTES:
|
||||
g_value_set_boxed (value, self->bytes);
|
||||
break;
|
||||
|
||||
case PROP_RESOURCE:
|
||||
g_value_set_string (value, self->resource);
|
||||
break;
|
||||
|
||||
case PROP_SCOPE:
|
||||
g_value_set_object (value, self->scope);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_builder_list_item_factory_set_bytes (GtkBuilderListItemFactory *self,
|
||||
GBytes *bytes)
|
||||
{
|
||||
if (bytes == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (self->bytes)
|
||||
{
|
||||
g_critical ("Data for GtkBuilderListItemFactory has already been set.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->bytes = g_bytes_ref (bytes);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_BYTES:
|
||||
gtk_builder_list_item_factory_set_bytes (self, g_value_get_boxed (value));
|
||||
break;
|
||||
|
||||
case PROP_RESOURCE:
|
||||
{
|
||||
GError *error = NULL;
|
||||
GBytes *bytes;
|
||||
const char *resource;
|
||||
|
||||
resource = g_value_get_string (value);
|
||||
if (resource == NULL)
|
||||
break;
|
||||
|
||||
bytes = g_resources_lookup_data (resource, 0, &error);
|
||||
if (bytes)
|
||||
{
|
||||
if (gtk_builder_list_item_factory_set_bytes (self, bytes))
|
||||
self->resource = g_strdup (resource);
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_critical ("Unable to load resource for list item template: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_SCOPE:
|
||||
self->scope = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_finalize (GObject *object)
|
||||
{
|
||||
GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
|
||||
|
||||
g_clear_object (&self->scope);
|
||||
g_bytes_unref (self->bytes);
|
||||
g_free (self->resource);
|
||||
|
||||
G_OBJECT_CLASS (gtk_builder_list_item_factory_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_class_init (GtkBuilderListItemFactoryClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gtk_builder_list_item_factory_finalize;
|
||||
gobject_class->get_property = gtk_builder_list_item_factory_get_property;
|
||||
gobject_class->set_property = gtk_builder_list_item_factory_set_property;
|
||||
|
||||
factory_class->setup = gtk_builder_list_item_factory_setup;
|
||||
|
||||
/**
|
||||
* GtkBuilderListItemFactory:bytes:
|
||||
*
|
||||
* bytes containing the UI definition
|
||||
*/
|
||||
properties[PROP_BYTES] =
|
||||
g_param_spec_boxed ("bytes",
|
||||
P_("Bytes"),
|
||||
P_("bytes containing the UI definition"),
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkBuilderListItemFactory:resource:
|
||||
*
|
||||
* resource containing the UI definition
|
||||
*/
|
||||
properties[PROP_RESOURCE] =
|
||||
g_param_spec_string ("resource",
|
||||
P_("Resource"),
|
||||
P_("resource containing the UI definition"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkBuilderListItemFactory:scope:
|
||||
*
|
||||
* scope to use when instantiating listitems
|
||||
*/
|
||||
properties[PROP_SCOPE] =
|
||||
g_param_spec_object ("scope",
|
||||
P_("Scope"),
|
||||
P_("scope to use when instantiating listitems"),
|
||||
GTK_TYPE_BUILDER_SCOPE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_list_item_factory_init (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_builder_list_item_factory_new_from_bytes (GtkBuilderScope *scope,
|
||||
GBytes *bytes)
|
||||
{
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
|
||||
"bytes", bytes,
|
||||
"scope", scope,
|
||||
NULL);
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_builder_list_item_factory_new_from_resource (GtkBuilderScope *scope,
|
||||
const char *resource_path)
|
||||
{
|
||||
g_return_val_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope), NULL);
|
||||
g_return_val_if_fail (resource_path != NULL, NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
|
||||
"resource", resource_path,
|
||||
"scope", scope,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_get_bytes:
|
||||
* @self: a #GtkBuilderListItemFactory
|
||||
*
|
||||
* Gets the data used as the #GtkBuilder UI template for constructing
|
||||
* listitems.
|
||||
*
|
||||
* Returns: (transfer none): The GtkBuilder data
|
||||
*
|
||||
**/
|
||||
GBytes *
|
||||
gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
|
||||
|
||||
return self->bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_get_resource:
|
||||
* @self: a #GtkBuilderListItemFactory
|
||||
*
|
||||
* If the data references a resource, gets the path of that resource.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The path to the resource or %NULL
|
||||
* if none
|
||||
**/
|
||||
const char *
|
||||
gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
|
||||
|
||||
return self->resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_list_item_factory_get_scope:
|
||||
* @self: a #GtkBuilderListItemFactory
|
||||
*
|
||||
* Gets the scope used when constructing listitems.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The scope used when constructing listitems
|
||||
**/
|
||||
GtkBuilderScope *
|
||||
gtk_builder_list_item_factory_get_scope (GtkBuilderListItemFactory *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
|
||||
|
||||
return self->scope;
|
||||
}
|
||||
|
||||
60
gtk/gtkbuilderlistitemfactory.h
Normal file
60
gtk/gtkbuilderlistitemfactory.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_BUILDER_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_BUILDER_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistitemfactory.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_BUILDER_LIST_ITEM_FACTORY (gtk_builder_list_item_factory_get_type ())
|
||||
#define GTK_BUILDER_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactory))
|
||||
#define GTK_BUILDER_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactoryClass))
|
||||
#define GTK_IS_BUILDER_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_BUILDER_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY))
|
||||
#define GTK_BUILDER_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_BUILDER_LIST_ITEM_FACTORY, GtkBuilderListItemFactoryClass))
|
||||
|
||||
typedef struct _GtkBuilderListItemFactory GtkBuilderListItemFactory;
|
||||
typedef struct _GtkBuilderListItemFactoryClass GtkBuilderListItemFactoryClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_builder_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_builder_list_item_factory_new_from_bytes (GtkBuilderScope *scope,
|
||||
GBytes *bytes);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_builder_list_item_factory_new_from_resource (GtkBuilderScope *scope,
|
||||
const char *resource_path);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GBytes * gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBuilderScope * gtk_builder_list_item_factory_get_scope (GtkBuilderListItemFactory *self) G_GNUC_PURE;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BUILDER_LIST_ITEM_FACTORY_H__ */
|
||||
@@ -18,17 +18,17 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <gmodule.h>
|
||||
#include "gtkbuilderprivate.h"
|
||||
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbuilderscopeprivate.h"
|
||||
#include "gtkdebug.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkversion.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include "gtkbuilderprivate.h"
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkdebug.h"
|
||||
#include "gtkversion.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkintl.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
@@ -485,21 +485,6 @@ builder_construct (ParserData *data,
|
||||
return object;
|
||||
}
|
||||
|
||||
static GType
|
||||
_get_type_by_symbol (const gchar *symbol)
|
||||
{
|
||||
static GModule *module = NULL;
|
||||
GTypeGetFunc func;
|
||||
|
||||
if (!module)
|
||||
module = g_module_open (NULL, 0);
|
||||
|
||||
if (!g_module_symbol (module, symbol, (gpointer)&func))
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
return func ();
|
||||
}
|
||||
|
||||
static void
|
||||
parse_requires (ParserData *data,
|
||||
const gchar *element_name,
|
||||
@@ -612,7 +597,7 @@ parse_object (GtkBuildableParseContext *context,
|
||||
/* Call the GType function, and return the GType, it's guaranteed afterwards
|
||||
* that g_type_from_name on the name will return our GType
|
||||
*/
|
||||
object_type = _get_type_by_symbol (type_func);
|
||||
object_type = gtk_builder_scope_get_type_from_function (gtk_builder_get_scope (data->builder), data->builder, type_func);
|
||||
if (object_type == G_TYPE_INVALID)
|
||||
{
|
||||
g_set_error (error,
|
||||
@@ -928,7 +913,8 @@ parse_property (ParserData *data,
|
||||
{
|
||||
BindingInfo *binfo;
|
||||
|
||||
binfo = g_slice_new (BindingInfo);
|
||||
binfo = g_slice_new0 (BindingInfo);
|
||||
binfo->tag_type = TAG_BINDING;
|
||||
binfo->target = NULL;
|
||||
binfo->target_pspec = pspec;
|
||||
binfo->source = g_strdup (bind_source);
|
||||
@@ -947,7 +933,7 @@ parse_property (ParserData *data,
|
||||
return;
|
||||
}
|
||||
|
||||
info = g_slice_new (PropertyInfo);
|
||||
info = g_slice_new0 (PropertyInfo);
|
||||
info->tag_type = TAG_PROPERTY;
|
||||
info->pspec = pspec;
|
||||
info->text = g_string_new ("");
|
||||
@@ -960,14 +946,471 @@ parse_property (ParserData *data,
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_binding (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
BindingExpressionInfo *info;
|
||||
const char *name = NULL;
|
||||
const char *object_name = NULL;
|
||||
ObjectInfo *object_info;
|
||||
GParamSpec *pspec = NULL;
|
||||
|
||||
object_info = state_peek_info (data, ObjectInfo);
|
||||
if (!object_info ||
|
||||
!(object_info->tag_type == TAG_OBJECT ||
|
||||
object_info->tag_type == TAG_TEMPLATE))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING, "name", &name,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
pspec = g_object_class_find_property (object_info->oclass, name);
|
||||
|
||||
if (!pspec)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"Invalid property: %s.%s",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"%s.%s is a construct-only property",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
else if (!(pspec->flags & G_PARAM_WRITABLE))
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_PROPERTY,
|
||||
"%s.%s is a non-writable property",
|
||||
g_type_name (object_info->type), name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
info = g_slice_new0 (BindingExpressionInfo);
|
||||
info->tag_type = TAG_BINDING_EXPRESSION;
|
||||
info->target = NULL;
|
||||
info->target_pspec = pspec;
|
||||
info->object_name = g_strdup (object_name);
|
||||
gtk_buildable_parse_context_get_position (&data->ctx, &info->line, &info->col);
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
free_property_info (PropertyInfo *info)
|
||||
{
|
||||
if (info->value)
|
||||
{
|
||||
if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == GTK_TYPE_EXPRESSION)
|
||||
gtk_expression_unref (info->value);
|
||||
else
|
||||
g_assert_not_reached();
|
||||
}
|
||||
g_string_free (info->text, TRUE);
|
||||
g_free (info->context);
|
||||
g_slice_free (PropertyInfo, info);
|
||||
}
|
||||
|
||||
static void
|
||||
free_expression_info (ExpressionInfo *info)
|
||||
{
|
||||
switch (info->expression_type)
|
||||
{
|
||||
case EXPRESSION_EXPRESSION:
|
||||
gtk_expression_unref (info->expression);
|
||||
break;
|
||||
|
||||
case EXPRESSION_CONSTANT:
|
||||
g_string_free (info->constant.text, TRUE);
|
||||
break;
|
||||
|
||||
case EXPRESSION_CLOSURE:
|
||||
g_free (info->closure.function_name);
|
||||
g_free (info->closure.object_name);
|
||||
g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
|
||||
break;
|
||||
|
||||
case EXPRESSION_PROPERTY:
|
||||
g_clear_pointer (&info->property.expression, free_expression_info);
|
||||
g_free (info->property.property_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
g_slice_free (ExpressionInfo, info);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_expression_parent (ParserData *data)
|
||||
{
|
||||
CommonInfo *common_info = state_peek_info (data, CommonInfo);
|
||||
|
||||
if (common_info == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (common_info->tag_type == TAG_PROPERTY)
|
||||
{
|
||||
PropertyInfo *prop_info = (PropertyInfo *) common_info;
|
||||
|
||||
return G_PARAM_SPEC_VALUE_TYPE (prop_info->pspec) == GTK_TYPE_EXPRESSION;
|
||||
}
|
||||
else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
|
||||
{
|
||||
BindingExpressionInfo *expr_info = (BindingExpressionInfo *) common_info;
|
||||
|
||||
return expr_info->expr == NULL;
|
||||
}
|
||||
else if (common_info->tag_type == TAG_EXPRESSION)
|
||||
{
|
||||
ExpressionInfo *expr_info = (ExpressionInfo *) common_info;
|
||||
|
||||
switch (expr_info->expression_type)
|
||||
{
|
||||
case EXPRESSION_CLOSURE:
|
||||
return TRUE;
|
||||
case EXPRESSION_CONSTANT:
|
||||
return FALSE;
|
||||
case EXPRESSION_PROPERTY:
|
||||
return expr_info->property.expression == NULL;
|
||||
case EXPRESSION_EXPRESSION:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_constant_expression (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
ExpressionInfo *info;
|
||||
const char *type_name = NULL;
|
||||
GType type;
|
||||
|
||||
if (!check_expression_parent (data))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type", &type_name,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type_name == NULL)
|
||||
type = G_TYPE_INVALID;
|
||||
else
|
||||
{
|
||||
type = gtk_builder_get_type_from_name (data->builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_VALUE,
|
||||
"Invalid type '%s'", type_name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info = g_slice_new0 (ExpressionInfo);
|
||||
info->tag_type = TAG_EXPRESSION;
|
||||
info->expression_type = EXPRESSION_CONSTANT;
|
||||
info->constant.type = type;
|
||||
info->constant.text = g_string_new (NULL);
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_closure_expression (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
ExpressionInfo *info;
|
||||
const char *type_name;
|
||||
const char *function_name;
|
||||
const char *object_name = NULL;
|
||||
gboolean swapped = -1;
|
||||
GType type;
|
||||
|
||||
if (!check_expression_parent (data))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING, "type", &type_name,
|
||||
G_MARKUP_COLLECT_STRING, "function", &function_name,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
|
||||
G_MARKUP_COLLECT_TRISTATE|G_MARKUP_COLLECT_OPTIONAL, "swapped", &swapped,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
type = gtk_builder_get_type_from_name (data->builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_VALUE,
|
||||
"Invalid type '%s'", type_name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Swapped defaults to FALSE except when object is set */
|
||||
if (swapped == -1)
|
||||
{
|
||||
if (object_name)
|
||||
swapped = TRUE;
|
||||
else
|
||||
swapped = FALSE;
|
||||
}
|
||||
|
||||
info = g_slice_new0 (ExpressionInfo);
|
||||
info->tag_type = TAG_EXPRESSION;
|
||||
info->expression_type = EXPRESSION_CLOSURE;
|
||||
info->closure.type = type;
|
||||
info->closure.swapped = swapped;
|
||||
info->closure.function_name = g_strdup (function_name);
|
||||
info->closure.object_name = g_strdup (object_name);
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_lookup_expression (ParserData *data,
|
||||
const gchar *element_name,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
GError **error)
|
||||
{
|
||||
ExpressionInfo *info;
|
||||
const char *property_name;
|
||||
const char *type_name = NULL;
|
||||
GType type;
|
||||
|
||||
if (!check_expression_parent (data))
|
||||
{
|
||||
error_invalid_tag (data, element_name, NULL, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_markup_collect_attributes (element_name, names, values, error,
|
||||
G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type", &type_name,
|
||||
G_MARKUP_COLLECT_STRING, "name", &property_name,
|
||||
G_MARKUP_COLLECT_INVALID))
|
||||
{
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type_name == NULL)
|
||||
{
|
||||
type = G_TYPE_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = gtk_builder_get_type_from_name (data->builder, type_name);
|
||||
if (type == G_TYPE_INVALID)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_VALUE,
|
||||
"Invalid type '%s'", type_name);
|
||||
_gtk_builder_prefix_error (data->builder, &data->ctx, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info = g_slice_new0 (ExpressionInfo);
|
||||
info->tag_type = TAG_EXPRESSION;
|
||||
info->expression_type = EXPRESSION_PROPERTY;
|
||||
info->property.this_type = type;
|
||||
info->property.property_name = g_strdup (property_name);
|
||||
|
||||
state_push (data, info);
|
||||
}
|
||||
|
||||
GtkExpression *
|
||||
expression_info_construct (GtkBuilder *builder,
|
||||
ExpressionInfo *info,
|
||||
GError **error)
|
||||
{
|
||||
switch (info->expression_type)
|
||||
{
|
||||
case EXPRESSION_EXPRESSION:
|
||||
break;
|
||||
|
||||
case EXPRESSION_CONSTANT:
|
||||
{
|
||||
GtkExpression *expr;
|
||||
|
||||
if (info->constant.type == G_TYPE_INVALID)
|
||||
{
|
||||
GObject *o = gtk_builder_lookup_object (builder, info->constant.text->str, 0, 0, error);
|
||||
if (o == NULL)
|
||||
return NULL;
|
||||
|
||||
expr = gtk_object_expression_new (o);
|
||||
}
|
||||
else
|
||||
{
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
if (!gtk_builder_value_from_string_type (builder,
|
||||
info->constant.type,
|
||||
info->constant.text->str,
|
||||
&value,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
if (G_VALUE_HOLDS_OBJECT (&value))
|
||||
expr = gtk_object_expression_new (g_value_get_object (&value));
|
||||
else
|
||||
expr = gtk_constant_expression_new_for_value (&value);
|
||||
|
||||
g_value_unset (&value);
|
||||
}
|
||||
|
||||
g_string_free (info->constant.text, TRUE);
|
||||
info->expression_type = EXPRESSION_EXPRESSION;
|
||||
info->expression = expr;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXPRESSION_CLOSURE:
|
||||
{
|
||||
GObject *object;
|
||||
GClosure *closure;
|
||||
guint i, n_params;
|
||||
GtkExpression **params;
|
||||
GtkExpression *expression;
|
||||
GSList *l;
|
||||
|
||||
if (info->closure.object_name)
|
||||
{
|
||||
object = gtk_builder_lookup_object (builder, info->closure.object_name, 0, 0, error);
|
||||
if (object == NULL)
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
object = NULL;
|
||||
}
|
||||
|
||||
closure = gtk_builder_create_closure (builder,
|
||||
info->closure.function_name,
|
||||
info->closure.swapped,
|
||||
object,
|
||||
error);
|
||||
if (closure == NULL)
|
||||
return NULL;
|
||||
n_params = g_slist_length (info->closure.params);
|
||||
params = g_newa (GtkExpression *, n_params);
|
||||
i = n_params;
|
||||
for (l = info->closure.params; l; l = l->next)
|
||||
{
|
||||
params[--i] = expression_info_construct (builder, l->data, error);
|
||||
if (params[i] == NULL)
|
||||
return NULL;
|
||||
}
|
||||
expression = gtk_closure_expression_new (info->closure.type, closure, n_params, params);
|
||||
g_free (info->closure.function_name);
|
||||
g_free (info->closure.object_name);
|
||||
g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
|
||||
info->expression_type = EXPRESSION_EXPRESSION;
|
||||
info->expression = expression;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXPRESSION_PROPERTY:
|
||||
{
|
||||
GtkExpression *expression;
|
||||
GType type;
|
||||
|
||||
if (info->property.expression)
|
||||
{
|
||||
expression = expression_info_construct (builder, info->property.expression, error);
|
||||
if (expression == NULL)
|
||||
return NULL;
|
||||
free_expression_info (info->property.expression);
|
||||
}
|
||||
else
|
||||
expression = NULL;
|
||||
|
||||
if (info->property.this_type != G_TYPE_INVALID)
|
||||
type = info->property.this_type;
|
||||
else if (expression != NULL)
|
||||
type = gtk_expression_get_value_type (expression);
|
||||
else
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
|
||||
"%s:%d:%d Lookups require a type attribute if they don't have an expression.",
|
||||
"???", 0, 0);
|
||||
return NULL;
|
||||
}
|
||||
expression = gtk_property_expression_new (type,
|
||||
expression,
|
||||
info->property.property_name);
|
||||
g_free (info->property.property_name);
|
||||
info->expression_type = EXPRESSION_EXPRESSION;
|
||||
info->expression = expression;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
return gtk_expression_ref (info->expression);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_signal (ParserData *data,
|
||||
const gchar *element_name,
|
||||
@@ -1052,6 +1495,24 @@ _free_signal_info (SignalInfo *info,
|
||||
g_slice_free (SignalInfo, info);
|
||||
}
|
||||
|
||||
void
|
||||
_free_binding_info (BindingInfo *info,
|
||||
gpointer user)
|
||||
{
|
||||
g_free (info->source);
|
||||
g_free (info->source_property);
|
||||
g_slice_free (BindingInfo, info);
|
||||
}
|
||||
|
||||
void
|
||||
free_binding_expression_info (BindingExpressionInfo *info)
|
||||
{
|
||||
if (info->expr)
|
||||
free_expression_info (info->expr);
|
||||
g_free (info->object_name);
|
||||
g_slice_free (BindingExpressionInfo, info);
|
||||
}
|
||||
|
||||
static void
|
||||
free_requires_info (RequiresInfo *info,
|
||||
gpointer user_data)
|
||||
@@ -1292,6 +1753,8 @@ start_element (GtkBuildableParseContext *context,
|
||||
}
|
||||
else if (strcmp (element_name, "property") == 0)
|
||||
parse_property (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "binding") == 0)
|
||||
parse_binding (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "child") == 0)
|
||||
parse_child (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "signal") == 0)
|
||||
@@ -1302,6 +1765,12 @@ start_element (GtkBuildableParseContext *context,
|
||||
parse_requires (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "interface") == 0)
|
||||
parse_interface (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "constant") == 0)
|
||||
parse_constant_expression (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "closure") == 0)
|
||||
parse_closure_expression (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "lookup") == 0)
|
||||
parse_lookup_expression (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "menu") == 0)
|
||||
_gtk_builder_menu_start (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "placeholder") == 0)
|
||||
@@ -1377,6 +1846,30 @@ end_element (GtkBuildableParseContext *context,
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
else if (strcmp (element_name, "binding") == 0)
|
||||
{
|
||||
BindingExpressionInfo *binfo = state_pop_info (data, BindingExpressionInfo);
|
||||
CommonInfo *info = state_peek_info (data, CommonInfo);
|
||||
|
||||
g_assert (info != NULL);
|
||||
|
||||
if (binfo->expr == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_TAG,
|
||||
"Binding tag requires an expression");
|
||||
free_binding_expression_info (binfo);
|
||||
}
|
||||
else if (info->tag_type == TAG_OBJECT ||
|
||||
info->tag_type == TAG_TEMPLATE)
|
||||
{
|
||||
ObjectInfo *object_info = (ObjectInfo*)info;
|
||||
object_info->bindings = g_slist_prepend (object_info->bindings, binfo);
|
||||
}
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
else if (strcmp (element_name, "object") == 0 ||
|
||||
strcmp (element_name, "template") == 0)
|
||||
{
|
||||
@@ -1437,6 +1930,49 @@ end_element (GtkBuildableParseContext *context,
|
||||
signal_info->object_name = g_strdup (object_info->id);
|
||||
object_info->signals = g_slist_prepend (object_info->signals, signal_info);
|
||||
}
|
||||
else if (strcmp (element_name, "constant") == 0 ||
|
||||
strcmp (element_name, "closure") == 0 ||
|
||||
strcmp (element_name, "lookup") == 0)
|
||||
{
|
||||
ExpressionInfo *expression_info = state_pop_info (data, ExpressionInfo);
|
||||
CommonInfo *parent_info = state_peek_info (data, CommonInfo);
|
||||
|
||||
if (parent_info->tag_type == TAG_BINDING_EXPRESSION)
|
||||
{
|
||||
BindingExpressionInfo *expr_info = (BindingExpressionInfo *) parent_info;
|
||||
|
||||
expr_info->expr = expression_info;
|
||||
}
|
||||
else if (parent_info->tag_type == TAG_PROPERTY)
|
||||
{
|
||||
PropertyInfo *prop_info = (PropertyInfo *) parent_info;
|
||||
|
||||
prop_info->value = expression_info_construct (data->builder, expression_info, error);
|
||||
}
|
||||
else if (parent_info->tag_type == TAG_EXPRESSION)
|
||||
{
|
||||
ExpressionInfo *expr_info = (ExpressionInfo *) parent_info;
|
||||
|
||||
switch (expr_info->expression_type)
|
||||
{
|
||||
case EXPRESSION_CLOSURE:
|
||||
expr_info->closure.params = g_slist_prepend (expr_info->closure.params, expression_info);
|
||||
break;
|
||||
case EXPRESSION_PROPERTY:
|
||||
expr_info->property.expression = expression_info;
|
||||
break;
|
||||
case EXPRESSION_EXPRESSION:
|
||||
case EXPRESSION_CONSTANT:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
else if (strcmp (element_name, "requires") == 0)
|
||||
{
|
||||
RequiresInfo *req_info = state_pop_info (data, RequiresInfo);
|
||||
@@ -1517,6 +2053,33 @@ text (GtkBuildableParseContext *context,
|
||||
|
||||
g_string_append_len (prop_info->text, text, text_len);
|
||||
}
|
||||
else if (strcmp (gtk_buildable_parse_context_get_element (context), "constant") == 0)
|
||||
{
|
||||
ExpressionInfo *expr_info = (ExpressionInfo *) info;
|
||||
|
||||
g_string_append_len (expr_info->constant.text, text, text_len);
|
||||
}
|
||||
else if (strcmp (gtk_buildable_parse_context_get_element (context), "lookup") == 0)
|
||||
{
|
||||
ExpressionInfo *expr_info = (ExpressionInfo *) info;
|
||||
|
||||
while (g_ascii_isspace (*text) && text_len > 0)
|
||||
{
|
||||
text++;
|
||||
text_len--;
|
||||
}
|
||||
while (text_len > 0 && g_ascii_isspace (text[text_len - 1]))
|
||||
text_len--;
|
||||
if (expr_info->property.expression == NULL && text_len > 0)
|
||||
{
|
||||
ExpressionInfo *constant = g_slice_new0 (ExpressionInfo);
|
||||
constant->tag_type = TAG_EXPRESSION;
|
||||
constant->expression_type = EXPRESSION_CONSTANT;
|
||||
constant->constant.type = G_TYPE_INVALID;
|
||||
constant->constant.text = g_string_new_len (text, text_len);
|
||||
expr_info->property.expression = constant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1531,6 +2094,12 @@ free_info (CommonInfo *info)
|
||||
case TAG_CHILD:
|
||||
free_child_info ((ChildInfo *)info);
|
||||
break;
|
||||
case TAG_BINDING:
|
||||
_free_binding_info ((BindingInfo *)info, NULL);
|
||||
break;
|
||||
case TAG_BINDING_EXPRESSION:
|
||||
free_binding_expression_info ((BindingExpressionInfo *) info);
|
||||
break;
|
||||
case TAG_PROPERTY:
|
||||
free_property_info ((PropertyInfo *)info);
|
||||
break;
|
||||
@@ -1540,6 +2109,9 @@ free_info (CommonInfo *info)
|
||||
case TAG_REQUIRES:
|
||||
free_requires_info ((RequiresInfo *)info, NULL);
|
||||
break;
|
||||
case TAG_EXPRESSION:
|
||||
free_expression_info ((ExpressionInfo *)info);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
@@ -1594,10 +2166,10 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
|
||||
if (!gtk_buildable_parse_context_parse (&data.ctx, buffer, length, error))
|
||||
goto out;
|
||||
|
||||
if (!_gtk_builder_finish (builder, error))
|
||||
if (_gtk_builder_lookup_failed (builder, error))
|
||||
goto out;
|
||||
|
||||
if (_gtk_builder_lookup_failed (builder, error))
|
||||
if (!_gtk_builder_finish (builder, error))
|
||||
goto out;
|
||||
|
||||
/* Custom parser_finished */
|
||||
|
||||
@@ -21,16 +21,19 @@
|
||||
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkexpression.h"
|
||||
|
||||
enum {
|
||||
TAG_PROPERTY,
|
||||
TAG_MENU,
|
||||
TAG_BINDING,
|
||||
TAG_BINDING_EXPRESSION,
|
||||
TAG_REQUIRES,
|
||||
TAG_OBJECT,
|
||||
TAG_CHILD,
|
||||
TAG_SIGNAL,
|
||||
TAG_INTERFACE,
|
||||
TAG_TEMPLATE,
|
||||
TAG_EXPRESSION,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -64,6 +67,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
guint tag_type;
|
||||
GParamSpec *pspec;
|
||||
gpointer value;
|
||||
GString *text;
|
||||
gboolean translatable:1;
|
||||
gboolean bound:1;
|
||||
@@ -72,6 +76,36 @@ typedef struct {
|
||||
gint col;
|
||||
} PropertyInfo;
|
||||
|
||||
typedef struct _ExpressionInfo ExpressionInfo;
|
||||
struct _ExpressionInfo {
|
||||
guint tag_type;
|
||||
enum {
|
||||
EXPRESSION_EXPRESSION,
|
||||
EXPRESSION_CONSTANT,
|
||||
EXPRESSION_CLOSURE,
|
||||
EXPRESSION_PROPERTY
|
||||
} expression_type;
|
||||
union {
|
||||
GtkExpression *expression;
|
||||
struct {
|
||||
GType type;
|
||||
GString *text;
|
||||
} constant;
|
||||
struct {
|
||||
GType type;
|
||||
char *function_name;
|
||||
char *object_name;
|
||||
gboolean swapped;
|
||||
GSList *params;
|
||||
} closure;
|
||||
struct {
|
||||
GType this_type;
|
||||
char *property_name;
|
||||
ExpressionInfo *expression;
|
||||
} property;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
guint tag_type;
|
||||
gchar *object_name;
|
||||
@@ -84,6 +118,7 @@ typedef struct {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint tag_type;
|
||||
GObject *target;
|
||||
GParamSpec *target_pspec;
|
||||
gchar *source;
|
||||
@@ -93,6 +128,17 @@ typedef struct
|
||||
gint col;
|
||||
} BindingInfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint tag_type;
|
||||
GObject *target;
|
||||
GParamSpec *target_pspec;
|
||||
char *object_name;
|
||||
ExpressionInfo *expr;
|
||||
gint line;
|
||||
gint col;
|
||||
} BindingExpressionInfo;
|
||||
|
||||
typedef struct {
|
||||
guint tag_type;
|
||||
gchar *library;
|
||||
@@ -179,6 +225,12 @@ gboolean _gtk_builder_finish (GtkBuilder *builder,
|
||||
GError **error);
|
||||
void _free_signal_info (SignalInfo *info,
|
||||
gpointer user_data);
|
||||
void _free_binding_info (BindingInfo *info,
|
||||
gpointer user_data);
|
||||
void free_binding_expression_info (BindingExpressionInfo *info);
|
||||
GtkExpression * expression_info_construct (GtkBuilder *builder,
|
||||
ExpressionInfo *info,
|
||||
GError **error);
|
||||
|
||||
/* Internal API which might be made public at some point */
|
||||
gboolean _gtk_builder_boolean_from_string (const gchar *string,
|
||||
@@ -222,6 +274,11 @@ gboolean _gtk_builder_check_parent (GtkBuilder *builder,
|
||||
GtkBuildableParseContext *context,
|
||||
const gchar *parent_name,
|
||||
GError **error);
|
||||
GObject *gtk_builder_lookup_object (GtkBuilder *builder,
|
||||
const gchar *name,
|
||||
gint line,
|
||||
gint col,
|
||||
GError **error);
|
||||
GObject *_gtk_builder_lookup_object (GtkBuilder *builder,
|
||||
const gchar *name,
|
||||
gint line,
|
||||
|
||||
511
gtk/gtkbuilderscope.c
Normal file
511
gtk/gtkbuilderscope.c
Normal file
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkbuilderscopeprivate.h"
|
||||
|
||||
#include "gtkbuilder.h"
|
||||
#include "gtktestutils.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkbuilderscope
|
||||
* @Title: GtkBuilderScope
|
||||
* @Short_description: Bindings for GtkBuilder
|
||||
* @See_also: #GtkBuilder, #GClosure
|
||||
*
|
||||
* #GtkBuilderScope is an interface to provide support to #GtkBuilder, primarily
|
||||
* for looking up programming-language-specific values for strings that are
|
||||
* given in a #GtkBuilder UI file.
|
||||
*
|
||||
* The primary intended audience is bindings that want to provide deeper integration
|
||||
* of #GtkBuilder into the language.
|
||||
*
|
||||
* A #GtkBuilderScope instance may be used with multiple #GtkBuilder objects, even
|
||||
* at once.
|
||||
*
|
||||
* By default, GTK will use its own implementation of #GtkBuilderScope for the C
|
||||
* language which can be created via gtk_builder_cscope_new().
|
||||
*
|
||||
* #GtkBuilderCScope instances use symbols explicitly added to @builder
|
||||
* with prior calls to gtk_builder_scope_add_callback_symbol(). If developers want
|
||||
* to do that, they are encouraged to create their own scopes for that purpose.
|
||||
*
|
||||
* In the case that symbols are not explicitly added; GTK will uses #GModule’s
|
||||
* introspective features (by opening the module %NULL) to look at the application’s
|
||||
* symbol table. From here it tries to match the signal function names given in the
|
||||
* interface description with symbols in the application.
|
||||
*
|
||||
* Note that unless gtk_builder_scope_add_callback_symbol() is called for
|
||||
* all signal callbacks which are referenced by the loaded XML, this
|
||||
* functionality will require that #GModule be supported on the platform.
|
||||
*/
|
||||
|
||||
G_DEFINE_INTERFACE (GtkBuilderScope, gtk_builder_scope, G_TYPE_OBJECT)
|
||||
|
||||
static GType
|
||||
gtk_builder_scope_default_get_type_from_name (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name)
|
||||
{
|
||||
GType type;
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
if (type != G_TYPE_INVALID)
|
||||
return type;
|
||||
|
||||
gtk_test_register_all_types ();
|
||||
return g_type_from_name (type_name);
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_builder_scope_default_get_type_from_function (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name)
|
||||
{
|
||||
return G_TYPE_INVALID;
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
gtk_builder_scope_default_create_closure (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Creating closures is not supported by %s",
|
||||
G_OBJECT_TYPE_NAME (self));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_scope_default_init (GtkBuilderScopeInterface *iface)
|
||||
{
|
||||
iface->get_type_from_name = gtk_builder_scope_default_get_type_from_name;
|
||||
iface->get_type_from_function = gtk_builder_scope_default_get_type_from_function;
|
||||
iface->create_closure = gtk_builder_scope_default_create_closure;
|
||||
}
|
||||
|
||||
GType
|
||||
gtk_builder_scope_get_type_from_name (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_SCOPE (self), G_TYPE_INVALID);
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
|
||||
g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID);
|
||||
|
||||
return GTK_BUILDER_SCOPE_GET_IFACE (self)->get_type_from_name (self, builder, type_name);
|
||||
}
|
||||
|
||||
GType
|
||||
gtk_builder_scope_get_type_from_function (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_SCOPE (self), G_TYPE_INVALID);
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID);
|
||||
g_return_val_if_fail (function_name != NULL, G_TYPE_INVALID);
|
||||
|
||||
return GTK_BUILDER_SCOPE_GET_IFACE (self)->get_type_from_function (self, builder, function_name);
|
||||
}
|
||||
|
||||
GClosure *
|
||||
gtk_builder_scope_create_closure (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_SCOPE (self), NULL);
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
|
||||
g_return_val_if_fail (function_name != NULL, NULL);
|
||||
g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
return GTK_BUILDER_SCOPE_GET_IFACE (self)->create_closure (self, builder, function_name, flags, object, error);
|
||||
}
|
||||
|
||||
/*** GTK_BUILDER_CSCOPE ***/
|
||||
|
||||
typedef struct _GtkBuilderCScopePrivate GtkBuilderCScopePrivate;
|
||||
|
||||
struct _GtkBuilderCScopePrivate
|
||||
{
|
||||
GModule *module;
|
||||
GHashTable *callbacks;
|
||||
};
|
||||
|
||||
static void gtk_builder_cscope_scope_init (GtkBuilderScopeInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkBuilderCScope, gtk_builder_cscope, G_TYPE_OBJECT,
|
||||
G_ADD_PRIVATE(GtkBuilderCScope)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDER_SCOPE,
|
||||
gtk_builder_cscope_scope_init))
|
||||
|
||||
static GModule *
|
||||
gtk_builder_cscope_get_module (GtkBuilderCScope *self)
|
||||
{
|
||||
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
|
||||
|
||||
if (priv->module == NULL)
|
||||
{
|
||||
if (!g_module_supported ())
|
||||
return NULL;
|
||||
|
||||
priv->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
|
||||
}
|
||||
|
||||
return priv->module;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to map a type name to a _get_type function
|
||||
* and call it, eg:
|
||||
*
|
||||
* GtkWindow -> gtk_window_get_type
|
||||
* GtkHBox -> gtk_hbox_get_type
|
||||
* GtkUIManager -> gtk_ui_manager_get_type
|
||||
* GWeatherLocation -> gweather_location_get_type
|
||||
*
|
||||
* Keep in sync with testsuite/gtk/typename.c !
|
||||
*/
|
||||
static gchar *
|
||||
type_name_mangle (const gchar *name)
|
||||
{
|
||||
GString *symbol_name = g_string_new ("");
|
||||
gint i;
|
||||
|
||||
for (i = 0; name[i] != '\0'; i++)
|
||||
{
|
||||
/* skip if uppercase, first or previous is uppercase */
|
||||
if ((name[i] == g_ascii_toupper (name[i]) &&
|
||||
i > 0 && name[i-1] != g_ascii_toupper (name[i-1])) ||
|
||||
(i > 2 && name[i] == g_ascii_toupper (name[i]) &&
|
||||
name[i-1] == g_ascii_toupper (name[i-1]) &&
|
||||
name[i-2] == g_ascii_toupper (name[i-2])))
|
||||
g_string_append_c (symbol_name, '_');
|
||||
g_string_append_c (symbol_name, g_ascii_tolower (name[i]));
|
||||
}
|
||||
g_string_append (symbol_name, "_get_type");
|
||||
|
||||
return g_string_free (symbol_name, FALSE);
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_builder_cscope_resolve_type_lazily (GtkBuilderCScope *self,
|
||||
const gchar *name)
|
||||
{
|
||||
GModule *module;
|
||||
GType (*func) (void);
|
||||
gchar *symbol;
|
||||
GType gtype = G_TYPE_INVALID;
|
||||
|
||||
module = gtk_builder_cscope_get_module (self);
|
||||
if (!module)
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
symbol = type_name_mangle (name);
|
||||
|
||||
if (g_module_symbol (module, symbol, (gpointer)&func))
|
||||
gtype = func ();
|
||||
|
||||
g_free (symbol);
|
||||
|
||||
return gtype;
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_builder_cscope_get_type_from_name (GtkBuilderScope *scope,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name)
|
||||
{
|
||||
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (scope);
|
||||
GType type;
|
||||
|
||||
type = g_type_from_name (type_name);
|
||||
if (type != G_TYPE_INVALID)
|
||||
return type;
|
||||
|
||||
type = gtk_builder_cscope_resolve_type_lazily (self, type_name);
|
||||
if (type != G_TYPE_INVALID)
|
||||
return type;
|
||||
|
||||
gtk_test_register_all_types ();
|
||||
type = g_type_from_name (type_name);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static GCallback
|
||||
gtk_builder_cscope_get_callback (GtkBuilderCScope *self,
|
||||
const char *function_name,
|
||||
GError **error)
|
||||
{
|
||||
GModule *module;
|
||||
GCallback func;
|
||||
|
||||
func = gtk_builder_cscope_lookup_callback_symbol (self, function_name);
|
||||
if (func)
|
||||
return func;
|
||||
|
||||
module = gtk_builder_cscope_get_module (self);
|
||||
if (module == NULL)
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"Could not look up function `%s`: GModule is not supported.",
|
||||
function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!g_module_symbol (module, function_name, (gpointer)&func))
|
||||
{
|
||||
g_set_error (error,
|
||||
GTK_BUILDER_ERROR,
|
||||
GTK_BUILDER_ERROR_INVALID_FUNCTION,
|
||||
"No function named `%s`.",
|
||||
function_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_builder_cscope_get_type_from_function (GtkBuilderScope *scope,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name)
|
||||
{
|
||||
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (scope);
|
||||
GType (* type_func) (void);
|
||||
|
||||
type_func = (GType (*) (void)) gtk_builder_cscope_get_callback (self, function_name, NULL);
|
||||
if (!type_func)
|
||||
return G_TYPE_INVALID;
|
||||
|
||||
return type_func();
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
gtk_builder_cscope_create_closure_for_funcptr (GtkBuilderCScope *self,
|
||||
GtkBuilder *builder,
|
||||
GCallback callback,
|
||||
gboolean swapped,
|
||||
GObject *object)
|
||||
{
|
||||
GClosure *closure;
|
||||
|
||||
if (object == NULL)
|
||||
object = gtk_builder_get_current_object (builder);
|
||||
|
||||
if (object)
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_object_swap (callback, object);
|
||||
else
|
||||
closure = g_cclosure_new_object (callback, object);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (swapped)
|
||||
closure = g_cclosure_new_swap (callback, NULL, NULL);
|
||||
else
|
||||
closure = g_cclosure_new (callback, NULL, NULL);
|
||||
}
|
||||
|
||||
return closure;
|
||||
}
|
||||
|
||||
static GClosure *
|
||||
gtk_builder_cscope_create_closure (GtkBuilderScope *scope,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error)
|
||||
{
|
||||
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (scope);
|
||||
GCallback func;
|
||||
gboolean swapped;
|
||||
|
||||
swapped = flags & GTK_BUILDER_CLOSURE_SWAPPED;
|
||||
|
||||
func = gtk_builder_cscope_get_callback (self, function_name, error);
|
||||
if (!func)
|
||||
return NULL;
|
||||
|
||||
return gtk_builder_cscope_create_closure_for_funcptr (self, builder, func, swapped, object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_cscope_scope_init (GtkBuilderScopeInterface *iface)
|
||||
{
|
||||
iface->get_type_from_name = gtk_builder_cscope_get_type_from_name;
|
||||
iface->get_type_from_function = gtk_builder_cscope_get_type_from_function;
|
||||
iface->create_closure = gtk_builder_cscope_create_closure;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_cscope_finalize (GObject *object)
|
||||
{
|
||||
GtkBuilderCScope *self = GTK_BUILDER_CSCOPE (object);
|
||||
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->callbacks, g_hash_table_destroy);
|
||||
g_clear_pointer (&priv->module, g_module_close);
|
||||
|
||||
G_OBJECT_CLASS (gtk_builder_cscope_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_cscope_class_init (GtkBuilderCScopeClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_builder_cscope_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_builder_cscope_init (GtkBuilderCScope *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_cscope_new:
|
||||
*
|
||||
* Creates a new #GtkbuilderCScope object to use with future #GtkBuilder
|
||||
* instances.
|
||||
*
|
||||
* Calling this function is only necessary if you want to add custom
|
||||
* callbacks via gtk_builder_cscope_add_callback_symbol().
|
||||
*
|
||||
* Returns: a new #GtkBuilderCScope
|
||||
**/
|
||||
GtkBuilderScope *
|
||||
gtk_builder_cscope_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_BUILDER_CSCOPE, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_cscope_add_callback_symbol:
|
||||
* @self: a #GtkBuilderCScope
|
||||
* @callback_name: The name of the callback, as expected in the XML
|
||||
* @callback_symbol: (scope async): The callback pointer
|
||||
*
|
||||
* Adds the @callback_symbol to the scope of @builder under the given @callback_name.
|
||||
*
|
||||
* Using this function overrides the behavior of gtk_builder_create_closure()
|
||||
* for any callback symbols that are added. Using this method allows for better
|
||||
* encapsulation as it does not require that callback symbols be declared in
|
||||
* the global namespace.
|
||||
*/
|
||||
void
|
||||
gtk_builder_cscope_add_callback_symbol (GtkBuilderCScope *self,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol)
|
||||
{
|
||||
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER_CSCOPE (self));
|
||||
g_return_if_fail (callback_name && callback_name[0]);
|
||||
g_return_if_fail (callback_symbol != NULL);
|
||||
|
||||
if (!priv->callbacks)
|
||||
priv->callbacks = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
|
||||
g_hash_table_insert (priv->callbacks, g_strdup (callback_name), callback_symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_cscope_add_callback_symbols: (skip)
|
||||
* @self: a #GtkBuilderCScope
|
||||
* @first_callback_name: The name of the callback, as expected in the XML
|
||||
* @first_callback_symbol: (scope async): The callback pointer
|
||||
* @...: A list of callback name and callback symbol pairs terminated with %NULL
|
||||
*
|
||||
* A convenience function to add many callbacks instead of calling
|
||||
* gtk_builder_add_callback_symbol() for each symbol.
|
||||
*/
|
||||
void
|
||||
gtk_builder_cscope_add_callback_symbols (GtkBuilderCScope *self,
|
||||
const gchar *first_callback_name,
|
||||
GCallback first_callback_symbol,
|
||||
...)
|
||||
{
|
||||
va_list var_args;
|
||||
const gchar *callback_name;
|
||||
GCallback callback_symbol;
|
||||
|
||||
g_return_if_fail (GTK_IS_BUILDER_CSCOPE (self));
|
||||
g_return_if_fail (first_callback_name && first_callback_name[0]);
|
||||
g_return_if_fail (first_callback_symbol != NULL);
|
||||
|
||||
callback_name = first_callback_name;
|
||||
callback_symbol = first_callback_symbol;
|
||||
|
||||
va_start (var_args, first_callback_symbol);
|
||||
|
||||
do {
|
||||
|
||||
gtk_builder_cscope_add_callback_symbol (self, callback_name, callback_symbol);
|
||||
|
||||
callback_name = va_arg (var_args, const gchar*);
|
||||
|
||||
if (callback_name)
|
||||
callback_symbol = va_arg (var_args, GCallback);
|
||||
|
||||
} while (callback_name != NULL);
|
||||
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_lookup_callback_symbol: (skip)
|
||||
* @self: a #GtkBuilderCScope
|
||||
* @callback_name: The name of the callback
|
||||
*
|
||||
* Fetches a symbol previously added to @self
|
||||
* with gtk_builder_add_callback_symbols().
|
||||
*
|
||||
* Returns: (nullable): The callback symbol in @builder for @callback_name, or %NULL
|
||||
*/
|
||||
GCallback
|
||||
gtk_builder_cscope_lookup_callback_symbol (GtkBuilderCScope *self,
|
||||
const gchar *callback_name)
|
||||
{
|
||||
GtkBuilderCScopePrivate *priv = gtk_builder_cscope_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER_CSCOPE (self), NULL);
|
||||
g_return_val_if_fail (callback_name && callback_name[0], NULL);
|
||||
|
||||
if (priv->callbacks == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_hash_table_lookup (priv->callbacks, callback_name);
|
||||
}
|
||||
|
||||
102
gtk/gtkbuilderscope.h
Normal file
102
gtk/gtkbuilderscope.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_BUILDER_SCOPE_H__
|
||||
#define __GTK_BUILDER_SCOPE_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_BUILDER_SCOPE (gtk_builder_scope_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_INTERFACE (GtkBuilderScope, gtk_builder_scope, GTK, BUILDER_SCOPE, GObject)
|
||||
|
||||
/**
|
||||
* GtkBuilderClosureFlags:
|
||||
* @GTK_BUILDER_CLOSURE_SWAPPED: The closure should be created swapped. See
|
||||
* g_cclosure_new_swapped() for details.
|
||||
*
|
||||
* The list of flags that can be passed to gtk_builder_scope_create_closure().
|
||||
* New values may be added in the future for new features, so external
|
||||
* implementations of GtkBuilderScopeInterface should test the flags for unknown
|
||||
* values and raise a %@GTK_BUILDER_ERROR_INVALID_ATTRIBUTE error when they
|
||||
* encounter one.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_BUILDER_CLOSURE_SWAPPED = (1 << 0)
|
||||
} GtkBuilderClosureFlags;
|
||||
|
||||
struct _GtkBuilderScopeInterface
|
||||
{
|
||||
/*< private >*/
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/*< public >*/
|
||||
GType (* get_type_from_name) (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name);
|
||||
GType (* get_type_from_function) (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name);
|
||||
|
||||
GClosure * (* create_closure) (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct _GtkBuilderCScopeClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
#define GTK_TYPE_BUILDER_CSCOPE (gtk_builder_cscope_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_DERIVABLE_TYPE (GtkBuilderCScope, gtk_builder_cscope, GTK, BUILDER_CSCOPE, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBuilderScope * gtk_builder_cscope_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_cscope_add_callback_symbol (GtkBuilderCScope *self,
|
||||
const gchar *callback_name,
|
||||
GCallback callback_symbol);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_builder_cscope_add_callback_symbols (GtkBuilderCScope *self,
|
||||
const gchar *first_callback_name,
|
||||
GCallback first_callback_symbol,
|
||||
...) G_GNUC_NULL_TERMINATED;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GCallback gtk_builder_cscope_lookup_callback_symbol(GtkBuilderCScope *self,
|
||||
const gchar *callback_name);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BUILDER_SCOPE_H__ */
|
||||
43
gtk/gtkbuilderscopeprivate.h
Normal file
43
gtk/gtkbuilderscopeprivate.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_BUILDER_SCOPE_PRIVATE_H__
|
||||
#define __GTK_BUILDER_SCOPE_PRIVATE_H__
|
||||
|
||||
#include <gtk/gtkbuilderscope.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GType gtk_builder_scope_get_type_from_name (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *type_name);
|
||||
GType gtk_builder_scope_get_type_from_function(GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name);
|
||||
GClosure * gtk_builder_scope_create_closure (GtkBuilderScope *self,
|
||||
GtkBuilder *builder,
|
||||
const char *function_name,
|
||||
GtkBuilderClosureFlags flags,
|
||||
GObject *object,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BUILDER_SCOPE_PRIVATE_H__ */
|
||||
148
gtk/gtkcolumnlistitemfactory.c
Normal file
148
gtk/gtkcolumnlistitemfactory.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnlistitemfactoryprivate.h"
|
||||
|
||||
#include "gtkboxlayout.h"
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewlayoutprivate.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
struct _GtkColumnListItemFactory
|
||||
{
|
||||
GtkListItemFactory parent_instance;
|
||||
|
||||
GtkColumnView *view; /* no reference, the view references us */
|
||||
};
|
||||
|
||||
struct _GtkColumnListItemFactoryClass
|
||||
{
|
||||
GtkListItemFactoryClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnListItemFactory, gtk_column_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_setup (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkColumnListItemFactory *self = GTK_COLUMN_LIST_ITEM_FACTORY (factory);
|
||||
GListModel *columns;
|
||||
guint i;
|
||||
|
||||
/* FIXME: evil */
|
||||
gtk_widget_set_layout_manager (GTK_WIDGET (widget),
|
||||
gtk_column_view_layout_new (self->view));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->setup (factory, widget, list_item);
|
||||
|
||||
columns = gtk_column_view_get_columns (self->view);
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (columns); i++)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (columns, i);
|
||||
|
||||
gtk_column_list_item_factory_add_column (self,
|
||||
list_item->owner,
|
||||
column,
|
||||
FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_teardown (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->teardown (factory, widget, list_item);
|
||||
|
||||
while ((child = gtk_widget_get_first_child (GTK_WIDGET (widget))))
|
||||
{
|
||||
gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (widget), child);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_update (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_column_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
|
||||
|
||||
for (child = gtk_widget_get_first_child (GTK_WIDGET (widget));
|
||||
child;
|
||||
child = gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (child), position, item, selected);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_class_init (GtkColumnListItemFactoryClass *klass)
|
||||
{
|
||||
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
|
||||
|
||||
factory_class->setup = gtk_column_list_item_factory_setup;
|
||||
factory_class->teardown = gtk_column_list_item_factory_teardown;
|
||||
factory_class->update = gtk_column_list_item_factory_update;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_list_item_factory_init (GtkColumnListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkColumnListItemFactory *
|
||||
gtk_column_list_item_factory_new (GtkColumnView *view)
|
||||
{
|
||||
GtkColumnListItemFactory *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, NULL);
|
||||
|
||||
result->view = view;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory,
|
||||
GtkListItemWidget *list_item,
|
||||
GtkColumnViewColumn *column,
|
||||
gboolean check_bind)
|
||||
{
|
||||
GtkWidget *cell;
|
||||
|
||||
cell = gtk_column_view_cell_new (column);
|
||||
gtk_list_item_widget_add_child (GTK_LIST_ITEM_WIDGET (list_item), GTK_WIDGET (cell));
|
||||
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (cell),
|
||||
gtk_list_item_widget_get_position (list_item),
|
||||
gtk_list_item_widget_get_item (list_item),
|
||||
gtk_list_item_widget_get_selected (list_item));
|
||||
}
|
||||
62
gtk/gtkcolumnlistitemfactoryprivate.h
Normal file
62
gtk/gtkcolumnlistitemfactoryprivate.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_COLUMN_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#include <gtk/gtklistitemwidgetprivate.h>
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_LIST_ITEM_FACTORY (gtk_column_list_item_factory_get_type ())
|
||||
#define GTK_COLUMN_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactory))
|
||||
#define GTK_COLUMN_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactoryClass))
|
||||
#define GTK_IS_COLUMN_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_COLUMN_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY))
|
||||
#define GTK_COLUMN_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_LIST_ITEM_FACTORY, GtkColumnListItemFactoryClass))
|
||||
|
||||
/**
|
||||
* GtkColumnListItemFactory:
|
||||
*
|
||||
* The object for the #GtkColumnListItemFactory.
|
||||
**/
|
||||
typedef struct _GtkColumnListItemFactory GtkColumnListItemFactory;
|
||||
typedef struct _GtkColumnListItemFactoryClass GtkColumnListItemFactoryClass;
|
||||
|
||||
|
||||
GType gtk_column_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkColumnListItemFactory *
|
||||
gtk_column_list_item_factory_new (GtkColumnView *view);
|
||||
|
||||
void gtk_column_list_item_factory_add_column (GtkColumnListItemFactory *factory,
|
||||
GtkListItemWidget *list_item,
|
||||
GtkColumnViewColumn *column,
|
||||
gboolean check_bind);
|
||||
void gtk_column_list_item_factory_remove_column
|
||||
(GtkColumnListItemFactory *factory,
|
||||
GtkListItemWidget *list_item,
|
||||
guint col_pos,
|
||||
GtkColumnViewColumn *column);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_LIST_ITEM_FACTORY_H__ */
|
||||
781
gtk/gtkcolumnview.c
Normal file
781
gtk/gtkcolumnview.c
Normal file
@@ -0,0 +1,781 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewprivate.h"
|
||||
|
||||
#include "gtkboxlayout.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkcolumnlistitemfactoryprivate.h"
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewlayoutprivate.h"
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistview.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkscrollable.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcolumnview
|
||||
* @title: GtkColumnView
|
||||
* @short_description: A widget for displaying lists in multiple columns
|
||||
* @see_also: #GtkColumnViewColumn, #GtkTreeView
|
||||
*
|
||||
* GtkColumnView is a widget to present a view into a large dynamic list of items
|
||||
* using multiple columns.
|
||||
*/
|
||||
|
||||
struct _GtkColumnView
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GListStore *columns;
|
||||
|
||||
GtkWidget *header;
|
||||
|
||||
GtkListView *listview;
|
||||
GtkColumnListItemFactory *factory;
|
||||
|
||||
GtkSorter *sorter;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_COLUMNS,
|
||||
PROP_HADJUSTMENT,
|
||||
PROP_HSCROLL_POLICY,
|
||||
PROP_MODEL,
|
||||
PROP_SHOW_SEPARATORS,
|
||||
PROP_VADJUSTMENT,
|
||||
PROP_VSCROLL_POLICY,
|
||||
PROP_SORT_MODEL,
|
||||
PROP_SORTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTIVATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static GtkBuildableIface *parent_buildable_iface;
|
||||
|
||||
static void
|
||||
gtk_column_view_buildable_add_child (GtkBuildable *buildable,
|
||||
GtkBuilder *builder,
|
||||
GObject *child,
|
||||
const gchar *type)
|
||||
{
|
||||
if (GTK_IS_COLUMN_VIEW_COLUMN (child))
|
||||
{
|
||||
if (type != NULL)
|
||||
{
|
||||
GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_column_view_append_column (GTK_COLUMN_VIEW (buildable),
|
||||
GTK_COLUMN_VIEW_COLUMN (child));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent_buildable_iface->add_child (buildable, builder, child, type);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_buildable_interface_init (GtkBuildableIface *iface)
|
||||
{
|
||||
parent_buildable_iface = g_type_interface_peek_parent (iface);
|
||||
|
||||
iface->add_child = gtk_column_view_buildable_add_child;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkColumnView, gtk_column_view, GTK_TYPE_WIDGET,
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gtk_column_view_buildable_interface_init)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
gtk_column_view_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
gtk_column_view_measure_across (self, minimum, natural);
|
||||
}
|
||||
else
|
||||
{
|
||||
int header_min, header_nat, list_min, list_nat;
|
||||
|
||||
gtk_widget_measure (GTK_WIDGET (self->listview),
|
||||
orientation, for_size,
|
||||
&header_min, &header_nat,
|
||||
NULL, NULL);
|
||||
gtk_widget_measure (GTK_WIDGET (self->listview),
|
||||
orientation, for_size,
|
||||
&list_min, &list_nat,
|
||||
NULL, NULL);
|
||||
*minimum = header_min + list_min;
|
||||
*natural = header_nat + list_nat;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_column_view_allocate_columns (GtkColumnView *self,
|
||||
int width)
|
||||
{
|
||||
GtkScrollablePolicy scroll_policy;
|
||||
int col_min, col_nat, widget_min, widget_nat, extra, col_size, x;
|
||||
guint i;
|
||||
|
||||
gtk_column_view_measure_across (self, &col_min, &col_nat);
|
||||
gtk_widget_measure (GTK_WIDGET (self),
|
||||
GTK_ORIENTATION_HORIZONTAL, -1,
|
||||
&widget_min, &widget_nat,
|
||||
NULL, NULL);
|
||||
|
||||
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
{
|
||||
extra = widget_min - col_min;
|
||||
col_size = col_min;
|
||||
}
|
||||
else
|
||||
{
|
||||
extra = widget_nat - col_nat;
|
||||
col_size = col_nat;
|
||||
}
|
||||
width -= extra;
|
||||
width = MAX (width, col_size);
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
gtk_column_view_column_measure (column, &col_min, &col_nat);
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
col_size = col_min;
|
||||
else
|
||||
col_size = col_nat;
|
||||
|
||||
gtk_column_view_column_allocate (column, x, col_size);
|
||||
x += col_size;
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
return width + extra;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
|
||||
int full_width, header_height, min, nat;
|
||||
|
||||
full_width = gtk_column_view_allocate_columns (self, width);
|
||||
|
||||
gtk_widget_measure (self->header, GTK_ORIENTATION_VERTICAL, full_width, &min, &nat, NULL, NULL);
|
||||
if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)) == GTK_SCROLL_MINIMUM)
|
||||
header_height = min;
|
||||
else
|
||||
header_height = nat;
|
||||
gtk_widget_allocate (self->header, full_width, header_height, -1, NULL);
|
||||
|
||||
gtk_widget_allocate (GTK_WIDGET (self->listview),
|
||||
full_width, height - header_height, -1,
|
||||
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (0, header_height)));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_activate_cb (GtkListView *listview,
|
||||
guint pos,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
g_signal_emit (self, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
while (g_list_model_get_n_items (G_LIST_MODEL (self->columns)) > 0)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), 0);
|
||||
gtk_column_view_remove_column (self, column);
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
g_clear_pointer (&self->header, gtk_widget_unparent);
|
||||
|
||||
g_clear_pointer ((GtkWidget **) &self->listview, gtk_widget_unparent);
|
||||
g_clear_object (&self->factory);
|
||||
|
||||
g_clear_object (&self->sorter);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_finalize (GObject *object)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
g_object_unref (self->columns);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_COLUMNS:
|
||||
g_value_set_object (value, self->columns);
|
||||
break;
|
||||
|
||||
case PROP_HADJUSTMENT:
|
||||
g_value_set_object (value, gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)));
|
||||
break;
|
||||
|
||||
case PROP_HSCROLL_POLICY:
|
||||
g_value_set_enum (value, gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview)));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, gtk_list_view_get_model (self->listview));
|
||||
break;
|
||||
|
||||
case PROP_SHOW_SEPARATORS:
|
||||
g_value_set_boolean (value, gtk_list_view_get_show_separators (self->listview));
|
||||
break;
|
||||
|
||||
case PROP_VADJUSTMENT:
|
||||
g_value_set_object (value, gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->listview)));
|
||||
break;
|
||||
|
||||
case PROP_VSCROLL_POLICY:
|
||||
g_value_set_enum (value, gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)));
|
||||
break;
|
||||
|
||||
case PROP_SORTER:
|
||||
g_value_set_object (value, self->sorter);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_HADJUSTMENT:
|
||||
if (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
|
||||
{
|
||||
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HADJUSTMENT]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_HSCROLL_POLICY:
|
||||
if (gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview)) != g_value_get_enum (value))
|
||||
{
|
||||
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (self->listview), g_value_get_enum (value));
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HSCROLL_POLICY]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_column_view_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SHOW_SEPARATORS:
|
||||
gtk_column_view_set_show_separators (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_VADJUSTMENT:
|
||||
if (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
|
||||
{
|
||||
gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VADJUSTMENT]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_VSCROLL_POLICY:
|
||||
if (gtk_scrollable_get_vscroll_policy (GTK_SCROLLABLE (self->listview)) != g_value_get_enum (value))
|
||||
{
|
||||
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (self->listview), g_value_get_enum (value));
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VSCROLL_POLICY]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_class_init (GtkColumnViewClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
gpointer iface;
|
||||
|
||||
widget_class->measure = gtk_column_view_measure;
|
||||
widget_class->size_allocate = gtk_column_view_allocate;
|
||||
|
||||
gobject_class->dispose = gtk_column_view_dispose;
|
||||
gobject_class->finalize = gtk_column_view_finalize;
|
||||
gobject_class->get_property = gtk_column_view_get_property;
|
||||
gobject_class->set_property = gtk_column_view_set_property;
|
||||
|
||||
/* GtkScrollable implementation */
|
||||
iface = g_type_default_interface_peek (GTK_TYPE_SCROLLABLE);
|
||||
properties[PROP_HADJUSTMENT] =
|
||||
g_param_spec_override ("hadjustment",
|
||||
g_object_interface_find_property (iface, "hadjustment"));
|
||||
properties[PROP_HSCROLL_POLICY] =
|
||||
g_param_spec_override ("hscroll-policy",
|
||||
g_object_interface_find_property (iface, "hscroll-policy"));
|
||||
properties[PROP_VADJUSTMENT] =
|
||||
g_param_spec_override ("vadjustment",
|
||||
g_object_interface_find_property (iface, "vadjustment"));
|
||||
properties[PROP_VSCROLL_POLICY] =
|
||||
g_param_spec_override ("vscroll-policy",
|
||||
g_object_interface_find_property (iface, "vscroll-policy"));
|
||||
|
||||
/**
|
||||
* GtkColumnView:columns:
|
||||
*
|
||||
* The list of columns
|
||||
*/
|
||||
properties[PROP_COLUMNS] =
|
||||
g_param_spec_object ("columns",
|
||||
P_("Columns"),
|
||||
P_("List of columns"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnView:model:
|
||||
*
|
||||
* Model for the items displayed
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Model for the items displayed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnView:show-separators:
|
||||
*
|
||||
* Show separators between rows
|
||||
*/
|
||||
properties[PROP_SHOW_SEPARATORS] =
|
||||
g_param_spec_boolean ("show-separators",
|
||||
P_("Show separators"),
|
||||
P_("Show separators between rows"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
properties[PROP_SORT_MODEL] =
|
||||
g_param_spec_object ("sort-model",
|
||||
P_("Sort Model"),
|
||||
P_("Sort Model for the items displayed"),
|
||||
GTK_TYPE_SORT_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_SORTER] =
|
||||
g_param_spec_object ("sorter",
|
||||
P_("Sorter"),
|
||||
P_("Sorter"),
|
||||
GTK_TYPE_SORTER,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkColumnView::activate:
|
||||
* @self: The #GtkColumnView
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* The ::activate signal is emitted when a row has been activated by the user,
|
||||
* usually via activating the GtkListBase|list.activate-item action.
|
||||
*
|
||||
* This allows for a convenient way to handle activation in a columnview.
|
||||
* See gtk_list_item_set_activatable() for details on how to use this signal.
|
||||
*/
|
||||
signals[ACTIVATE] =
|
||||
g_signal_new (I_("activate"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_UINT);
|
||||
g_signal_set_va_marshaller (signals[ACTIVATE],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__UINTv);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("treeview"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_init (GtkColumnView *self)
|
||||
{
|
||||
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
|
||||
|
||||
self->header = gtk_list_item_widget_new (NULL, "header");
|
||||
gtk_widget_set_can_focus (self->header, FALSE);
|
||||
gtk_widget_set_layout_manager (self->header, gtk_column_view_layout_new (self));
|
||||
gtk_widget_set_parent (self->header, GTK_WIDGET (self));
|
||||
|
||||
self->sorter = gtk_column_view_sorter_new ();
|
||||
self->factory = gtk_column_list_item_factory_new (self);
|
||||
self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory (
|
||||
GTK_LIST_ITEM_FACTORY (g_object_ref (self->factory))));
|
||||
gtk_widget_set_hexpand (GTK_WIDGET (self->listview), TRUE);
|
||||
gtk_widget_set_vexpand (GTK_WIDGET (self->listview), TRUE);
|
||||
g_signal_connect (self->listview, "activate", G_CALLBACK (gtk_column_view_activate_cb), self);
|
||||
gtk_widget_set_parent (GTK_WIDGET (self->listview), GTK_WIDGET (self));
|
||||
|
||||
gtk_css_node_add_class (gtk_widget_get_css_node (GTK_WIDGET (self)),
|
||||
g_quark_from_static_string (I_("view")));
|
||||
|
||||
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_new:
|
||||
*
|
||||
* Creates a new empty #GtkColumnView.
|
||||
*
|
||||
* You most likely want to call gtk_column_view_set_factory() to
|
||||
* set up a way to map its items to widgets and gtk_column_view_set_model()
|
||||
* to set a model to provide items next.
|
||||
*
|
||||
* Returns: a new #GtkColumnView
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_column_view_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COLUMN_VIEW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_model:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Gets the model that's currently used to read the items displayed.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_column_view_get_model (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_view_get_model (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_model:
|
||||
* @self: a #GtkColumnView
|
||||
* @model: (allow-none) (transfer none): the model to use or %NULL for none
|
||||
*
|
||||
* Sets the #GListModel to use.
|
||||
*
|
||||
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
|
||||
* Otherwise, @self creates a #GtkSingleSelection for the selection.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_set_model (GtkColumnView *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (gtk_list_view_get_model (self->listview) == model)
|
||||
return;
|
||||
|
||||
gtk_list_view_set_model (self->listview, model);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_columns:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Gets the list of columns in this column view. This list is constant over
|
||||
* the lifetime of @self and can be used to monitor changes to the columns
|
||||
* of @self by connecting to the GListModel:items-changed signal.
|
||||
*
|
||||
* Returns: (transfer none): The list managing the columns
|
||||
**/
|
||||
GListModel *
|
||||
gtk_column_view_get_columns (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return G_LIST_MODEL (self->columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_show_separators:
|
||||
* @self: a #GtkColumnView
|
||||
* @show_separators: %TRUE to show separators
|
||||
*
|
||||
* Sets whether the list should show separators
|
||||
* between rows.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_set_show_separators (GtkColumnView *self,
|
||||
gboolean show_separators)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
|
||||
if (gtk_list_view_get_show_separators (self->listview) == show_separators)
|
||||
return;
|
||||
|
||||
gtk_list_view_set_show_separators (self->listview, show_separators);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SEPARATORS]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_show_separators:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns whether the list box should show separators
|
||||
* between rows.
|
||||
*
|
||||
* Returns: %TRUE if the list box shows separators
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_get_show_separators (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_view_get_show_separators (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_append_column:
|
||||
* @self: a #GtkColumnView
|
||||
* @column: a #GtkColumnViewColumn that hasn't been added to a
|
||||
* #GtkColumnView yet
|
||||
*
|
||||
* Appends the @column to the end of the columns in @self.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_append_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column));
|
||||
g_return_if_fail (gtk_column_view_column_get_column_view (column) == NULL);
|
||||
|
||||
gtk_column_view_column_set_column_view (column, self);
|
||||
g_list_store_append (self->columns, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_remove_column:
|
||||
* @self: a #GtkColumnView
|
||||
* @column: a #GtkColumnViewColumn that's part of @self
|
||||
*
|
||||
* Removes the @column from the list of columns of @self.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_remove_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column));
|
||||
g_return_if_fail (gtk_column_view_column_get_column_view (column) == self);
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *item = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
g_object_unref (item);
|
||||
if (item == column)
|
||||
break;
|
||||
}
|
||||
|
||||
gtk_column_view_sorter_remove_column (GTK_COLUMN_VIEW_SORTER (self->sorter), column);
|
||||
gtk_column_view_column_set_column_view (column, NULL);
|
||||
g_list_store_remove (self->columns, i);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_measure_across (GtkColumnView *self,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
guint i;
|
||||
int min, nat;
|
||||
|
||||
min = 0;
|
||||
nat = 0;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
int col_min, col_nat;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
gtk_column_view_column_measure (column, &col_min, &col_nat);
|
||||
min += col_min;
|
||||
nat += col_nat;
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
*minimum = min;
|
||||
*natural = nat;
|
||||
}
|
||||
|
||||
GtkListItemWidget *
|
||||
gtk_column_view_get_header_widget (GtkColumnView *self)
|
||||
{
|
||||
return GTK_LIST_ITEM_WIDGET (self->header);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_sorter:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns the sorter associated with the column view.
|
||||
*
|
||||
* To enable customizable sorting by clicking on column
|
||||
* headers, this sorter needs to be set on the sort
|
||||
* model(s) underneath the model that is displayed
|
||||
* by the view.
|
||||
*
|
||||
* See gtk_column_view_get_sorter() for setting up
|
||||
* per-column sorting.
|
||||
*
|
||||
* Returns: (transfer none): the #GtkSorter of @self
|
||||
*/
|
||||
GtkSorter *
|
||||
gtk_column_view_get_sorter (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return self->sorter;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_active_sorter_changed (GtkColumnView *self)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
gtk_column_view_column_active_sorter_changed (column);
|
||||
g_object_unref (column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_sort_by_column:
|
||||
* @self: a #GtkColumnView
|
||||
* @column: (allow-none): the #GtkColumnViewColumn to sort by, or %NULL
|
||||
* @direction: the direction to sort in
|
||||
*
|
||||
* Sets the sorting of the view.
|
||||
*
|
||||
* This function should be used to set up the initial sorting. At runtime,
|
||||
* users can change the sorting of a column view by clicking on the list headers.
|
||||
*
|
||||
* This call only has an effect if gtk_column_view_set_sort_model() has
|
||||
* been called to inform the view about the underlying sort model, and
|
||||
* if gtk_column_view_set_sorter() has been called on @column to associate
|
||||
* a sorter with the column.
|
||||
*
|
||||
* If @column is %NULL, the view will be unsorted.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_sort_by_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column,
|
||||
GtkSortType direction)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (column == NULL || GTK_IS_COLUMN_VIEW_COLUMN (column));
|
||||
g_return_if_fail (column == NULL || gtk_column_view_column_get_column_view (column) == self);
|
||||
|
||||
if (column == NULL)
|
||||
gtk_column_view_sorter_reset (GTK_COLUMN_VIEW_SORTER (self->sorter));
|
||||
else
|
||||
gtk_column_view_sorter_set_column (GTK_COLUMN_VIEW_SORTER (self->sorter),
|
||||
column,
|
||||
direction == GTK_SORT_DESCENDING);
|
||||
}
|
||||
87
gtk/gtkcolumnview.h
Normal file
87
gtk/gtkcolumnview.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_H__
|
||||
#define __GTK_COLUMN_VIEW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
#include <gtk/gtksortlistmodel.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW (gtk_column_view_get_type ())
|
||||
#define GTK_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnView))
|
||||
#define GTK_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
|
||||
#define GTK_IS_COLUMN_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW))
|
||||
#define GTK_IS_COLUMN_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW))
|
||||
#define GTK_COLUMN_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW, GtkColumnViewClass))
|
||||
|
||||
/**
|
||||
* GtkColumnView:
|
||||
*
|
||||
* GtkColumnView is the simple list implementation for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkColumnView GtkColumnView;
|
||||
typedef struct _GtkColumnViewClass GtkColumnViewClass;
|
||||
/* forward declaration */
|
||||
typedef struct _GtkColumnViewColumn GtkColumnViewColumn;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_column_view_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_column_view_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_column_view_get_columns (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_append_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_remove_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_column_view_get_model (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_model (GtkColumnView *self,
|
||||
GListModel *model);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_get_show_separators (GtkColumnView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_show_separators (GtkColumnView *self,
|
||||
gboolean show_separators);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSorter * gtk_column_view_get_sorter (GtkColumnView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_sort_by_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column,
|
||||
GtkSortType direction);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_H__ */
|
||||
190
gtk/gtkcolumnviewcell.c
Normal file
190
gtk/gtkcolumnviewcell.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewcellprivate.h"
|
||||
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
struct _GtkColumnViewCell
|
||||
{
|
||||
GtkListItemWidget parent_instance;
|
||||
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
/* This list isn't sorted - next/prev refer to list elements, not rows in the list */
|
||||
GtkColumnViewCell *next_cell;
|
||||
GtkColumnViewCell *prev_cell;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewCellClass
|
||||
{
|
||||
GtkListItemWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewCell, gtk_column_view_cell, GTK_TYPE_LIST_ITEM_WIDGET)
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_allocate (child, width, height, baseline, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_root (GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_column_view_cell_parent_class)->root (widget);
|
||||
|
||||
self->next_cell = gtk_column_view_column_get_first_cell (self->column);
|
||||
if (self->next_cell)
|
||||
self->next_cell->prev_cell = self;
|
||||
|
||||
gtk_column_view_column_add_cell (self->column, self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_unroot (GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
|
||||
|
||||
gtk_column_view_column_remove_cell (self->column, self);
|
||||
|
||||
if (self->prev_cell)
|
||||
self->prev_cell->next_cell = self->next_cell;
|
||||
if (self->next_cell)
|
||||
self->next_cell->prev_cell = self->prev_cell;
|
||||
|
||||
self->prev_cell = NULL;
|
||||
self->next_cell = NULL;
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_column_view_cell_parent_class)->unroot (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (object);
|
||||
|
||||
g_clear_object (&self->column);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_cell_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_class_init (GtkColumnViewCellClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
widget_class->root = gtk_column_view_cell_root;
|
||||
widget_class->unroot = gtk_column_view_cell_unroot;
|
||||
widget_class->measure = gtk_column_view_cell_measure;
|
||||
widget_class->size_allocate = gtk_column_view_cell_size_allocate;
|
||||
|
||||
gobject_class->dispose = gtk_column_view_cell_dispose;
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("cell"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_resize_func (GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewCell *self = GTK_COLUMN_VIEW_CELL (widget);
|
||||
|
||||
if (self->column)
|
||||
gtk_column_view_column_queue_resize (self->column);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_cell_init (GtkColumnViewCell *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
|
||||
gtk_widget_set_can_focus (widget, FALSE);
|
||||
/* FIXME: Figure out if settting the manager class to INVALID should work */
|
||||
gtk_widget_set_layout_manager (widget, NULL);
|
||||
widget->priv->resize_func = gtk_column_view_cell_resize_func;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_column_view_cell_new (GtkColumnViewColumn *column)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
|
||||
cell = g_object_new (GTK_TYPE_COLUMN_VIEW_CELL,
|
||||
"factory", gtk_column_view_column_get_factory (column),
|
||||
NULL);
|
||||
|
||||
cell->column = g_object_ref (column);
|
||||
|
||||
return GTK_WIDGET (cell);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_cell_remove (GtkColumnViewCell *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
|
||||
gtk_list_item_widget_remove_child (GTK_LIST_ITEM_WIDGET (gtk_widget_get_parent (widget)), widget);
|
||||
}
|
||||
|
||||
GtkColumnViewCell *
|
||||
gtk_column_view_cell_get_next (GtkColumnViewCell *self)
|
||||
{
|
||||
return self->next_cell;
|
||||
}
|
||||
|
||||
GtkColumnViewCell *
|
||||
gtk_column_view_cell_get_prev (GtkColumnViewCell *self)
|
||||
{
|
||||
return self->prev_cell;
|
||||
}
|
||||
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_cell_get_column (GtkColumnViewCell *self)
|
||||
{
|
||||
return self->column;
|
||||
}
|
||||
49
gtk/gtkcolumnviewcellprivate.h
Normal file
49
gtk/gtkcolumnviewcellprivate.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_CELL_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_CELL_PRIVATE_H__
|
||||
|
||||
#include "gtkcolumnviewcolumn.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_CELL (gtk_column_view_cell_get_type ())
|
||||
#define GTK_COLUMN_VIEW_CELL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCell))
|
||||
#define GTK_COLUMN_VIEW_CELL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCellClass))
|
||||
#define GTK_IS_COLUMN_VIEW_CELL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_CELL))
|
||||
#define GTK_IS_COLUMN_VIEW_CELL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_CELL))
|
||||
#define GTK_COLUMN_VIEW_CELL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_CELL, GtkColumnViewCellClass))
|
||||
|
||||
typedef struct _GtkColumnViewCell GtkColumnViewCell;
|
||||
typedef struct _GtkColumnViewCellClass GtkColumnViewCellClass;
|
||||
|
||||
GType gtk_column_view_cell_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * gtk_column_view_cell_new (GtkColumnViewColumn *column);
|
||||
|
||||
void gtk_column_view_cell_remove (GtkColumnViewCell *self);
|
||||
|
||||
GtkColumnViewCell * gtk_column_view_cell_get_next (GtkColumnViewCell *self);
|
||||
GtkColumnViewCell * gtk_column_view_cell_get_prev (GtkColumnViewCell *self);
|
||||
GtkColumnViewColumn * gtk_column_view_cell_get_column (GtkColumnViewCell *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_CELL_PRIVATE_H__ */
|
||||
629
gtk/gtkcolumnviewcolumn.c
Normal file
629
gtk/gtkcolumnviewcolumn.c
Normal file
@@ -0,0 +1,629 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
|
||||
#include "gtkcolumnviewprivate.h"
|
||||
#include "gtkcolumnviewtitleprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtksizegroup.h"
|
||||
#include "gtkstylecontext.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtksorter.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcolumnviewcolumn
|
||||
* @title: GtkColumnViewColumn
|
||||
* @short_description: The column added to GtkColumnView
|
||||
* @see_also: #GtkColumnView
|
||||
*
|
||||
* GtkColumnViewColumn represents the columns being added to #GtkColumnView.
|
||||
*/
|
||||
|
||||
struct _GtkColumnViewColumn
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemFactory *factory;
|
||||
char *title;
|
||||
GtkSorter *sorter;
|
||||
|
||||
/* data for the view */
|
||||
GtkColumnView *view;
|
||||
GtkWidget *header;
|
||||
|
||||
int minimum_size_request;
|
||||
int natural_size_request;
|
||||
int allocation_offset;
|
||||
int allocation_size;
|
||||
|
||||
/* This list isn't sorted - this is just caching for performance */
|
||||
GtkColumnViewCell *first_cell; /* no reference, just caching */
|
||||
};
|
||||
|
||||
struct _GtkColumnViewColumnClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_COLUMN_VIEW,
|
||||
PROP_FACTORY,
|
||||
PROP_TITLE,
|
||||
PROP_SORTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewColumn, gtk_column_view_column, G_TYPE_OBJECT)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_column_view_column_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
g_assert (self->view == NULL); /* would hold a ref otherwise */
|
||||
g_assert (self->first_cell == NULL); /* no view = no children */
|
||||
|
||||
g_clear_object (&self->factory);
|
||||
g_clear_object (&self->sorter);
|
||||
g_clear_pointer (&self->title, g_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_COLUMN_VIEW:
|
||||
g_value_set_object (value, self->view);
|
||||
break;
|
||||
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, self->factory);
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
g_value_set_string (value, self->title);
|
||||
break;
|
||||
|
||||
case PROP_SORTER:
|
||||
g_value_set_object (value, self->sorter);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_column_view_column_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_TITLE:
|
||||
gtk_column_view_column_set_title (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_SORTER:
|
||||
gtk_column_view_column_set_sorter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_column_view_column_dispose;
|
||||
gobject_class->get_property = gtk_column_view_column_get_property;
|
||||
gobject_class->set_property = gtk_column_view_column_set_property;
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:column-view:
|
||||
*
|
||||
* #GtkColumnView this column is a part of
|
||||
*/
|
||||
properties[PROP_COLUMN_VIEW] =
|
||||
g_param_spec_object ("column-view",
|
||||
P_("Column view"),
|
||||
P_("Column view this column is a part of"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:factory:
|
||||
*
|
||||
* Factory for populating list items
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
P_("Factory"),
|
||||
P_("Factory for populating list items"),
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:title:
|
||||
*
|
||||
* Title displayed in the header
|
||||
*/
|
||||
properties[PROP_TITLE] =
|
||||
g_param_spec_string ("title",
|
||||
P_("Title"),
|
||||
P_("Title displayed in the header"),
|
||||
NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
properties[PROP_SORTER] =
|
||||
g_param_spec_object ("sorter",
|
||||
P_("Sorter"),
|
||||
P_("Sorter for sorting list items"),
|
||||
GTK_TYPE_SORTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_init (GtkColumnViewColumn *self)
|
||||
{
|
||||
self->minimum_size_request = -1;
|
||||
self->natural_size_request = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_new:
|
||||
* @title: (nullable): Title to use for this column
|
||||
*
|
||||
* Creates a new #GtkColumnViewColumn.
|
||||
*
|
||||
* You most likely want to call gtk_column_add_column() next.
|
||||
*
|
||||
* Returns: a new #GtkColumnViewColumn
|
||||
**/
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_column_new (const char *title)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
|
||||
"title", title,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_new_with_factory:
|
||||
* @title: (nullable): Title to use for this column
|
||||
* @factory: (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new #GtkColumnViewColumn that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* You most likely want to call gtk_column_add_column() next.
|
||||
*
|
||||
* The function takes ownership of the
|
||||
* argument, so you can write code like
|
||||
* ```
|
||||
* column = gtk_column_view_column_new_with_factory (_("Name"),
|
||||
* gtk_builder_list_item_factory_new_from_resource ("/name.ui"));
|
||||
* ```
|
||||
*
|
||||
* Returns: a new #GtkColumnViewColumn using the given @factory
|
||||
**/
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_column_new_with_factory (const char *title,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkColumnViewColumn *result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN,
|
||||
"factory", factory,
|
||||
"title", title,
|
||||
NULL);
|
||||
|
||||
g_object_unref (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
GtkColumnViewCell *
|
||||
gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self)
|
||||
{
|
||||
return self->first_cell;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_add_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell)
|
||||
{
|
||||
self->first_cell = cell;
|
||||
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell)
|
||||
{
|
||||
if (cell == self->first_cell)
|
||||
self->first_cell = gtk_column_view_cell_get_next (cell);
|
||||
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (cell));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_queue_resize (GtkColumnViewColumn *self)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
|
||||
if (self->minimum_size_request < 0)
|
||||
return;
|
||||
|
||||
self->minimum_size_request = -1;
|
||||
self->natural_size_request = -1;
|
||||
|
||||
if (self->header)
|
||||
gtk_widget_queue_resize (self->header);
|
||||
|
||||
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
|
||||
{
|
||||
gtk_widget_queue_resize (GTK_WIDGET (cell));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_measure (GtkColumnViewColumn *self,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
if (self->minimum_size_request < 0)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
int min, nat, cell_min, cell_nat;
|
||||
|
||||
if (self->header)
|
||||
{
|
||||
gtk_widget_measure (self->header, GTK_ORIENTATION_HORIZONTAL, -1, &min, &nat, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
min = 0;
|
||||
nat = 0;
|
||||
}
|
||||
|
||||
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
|
||||
{
|
||||
gtk_widget_measure (GTK_WIDGET (cell),
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
-1,
|
||||
&cell_min, &cell_nat,
|
||||
NULL, NULL);
|
||||
|
||||
min = MAX (min, cell_min);
|
||||
nat = MAX (nat, cell_nat);
|
||||
}
|
||||
|
||||
self->minimum_size_request = min;
|
||||
self->natural_size_request = nat;
|
||||
}
|
||||
|
||||
*minimum = self->minimum_size_request;
|
||||
*natural = self->natural_size_request;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_allocate (GtkColumnViewColumn *self,
|
||||
int offset,
|
||||
int size)
|
||||
{
|
||||
self->allocation_offset = offset;
|
||||
self->allocation_size = size;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
if (offset)
|
||||
*offset = self->allocation_offset;
|
||||
if (size)
|
||||
*size = self->allocation_size;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_create_cells (GtkColumnViewColumn *self)
|
||||
{
|
||||
GtkWidget *row;
|
||||
|
||||
if (self->first_cell)
|
||||
return;
|
||||
|
||||
for (row = gtk_widget_get_first_child (GTK_WIDGET (self->view));
|
||||
row != NULL;
|
||||
row = gtk_widget_get_next_sibling (row))
|
||||
{
|
||||
GtkListItemWidget *list_item;
|
||||
GtkWidget *cell;
|
||||
|
||||
if (!gtk_widget_get_root (row))
|
||||
continue;
|
||||
|
||||
list_item = GTK_LIST_ITEM_WIDGET (row);
|
||||
cell = gtk_column_view_cell_new (self);
|
||||
gtk_list_item_widget_add_child (list_item, cell);
|
||||
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (cell),
|
||||
gtk_list_item_widget_get_position (list_item),
|
||||
gtk_list_item_widget_get_item (list_item),
|
||||
gtk_list_item_widget_get_selected (list_item));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_remove_cells (GtkColumnViewColumn *self)
|
||||
{
|
||||
while (self->first_cell)
|
||||
gtk_column_view_cell_remove (self->first_cell);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_create_header (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->header != NULL)
|
||||
return;
|
||||
|
||||
self->header = gtk_column_view_title_new (self);
|
||||
gtk_list_item_widget_add_child (gtk_column_view_get_header_widget (self->view),
|
||||
self->header);
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_remove_header (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->header == NULL)
|
||||
return;
|
||||
|
||||
gtk_list_item_widget_remove_child (gtk_column_view_get_header_widget (self->view),
|
||||
self->header);
|
||||
self->header = NULL;
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_column_ensure_cells (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->view && gtk_widget_get_root (GTK_WIDGET (self->view)))
|
||||
gtk_column_view_column_create_cells (self);
|
||||
else
|
||||
gtk_column_view_column_remove_cells (self);
|
||||
|
||||
if (self->view)
|
||||
gtk_column_view_column_create_header (self);
|
||||
else
|
||||
gtk_column_view_column_remove_header (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_column_view:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Gets the column view that's currently displaying this column.
|
||||
*
|
||||
* If @self has not been added to a column view yet, %NULL is returned.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The column view displaying @self.
|
||||
**/
|
||||
GtkColumnView *
|
||||
gtk_column_view_column_get_column_view (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->view;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_set_column_view (GtkColumnViewColumn *self,
|
||||
GtkColumnView *view)
|
||||
{
|
||||
if (self->view == view)
|
||||
return;
|
||||
|
||||
gtk_column_view_column_remove_cells (self);
|
||||
gtk_column_view_column_remove_header (self);
|
||||
|
||||
self->view = view;
|
||||
|
||||
gtk_column_view_column_ensure_cells (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLUMN_VIEW]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_factory:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Gets the factory that's currently used to populate list items for
|
||||
* this column.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_column_view_column_get_factory (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_factory:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
|
||||
*
|
||||
* Sets the #GtkListItemFactory to use for populating list items for this
|
||||
* column.
|
||||
**/
|
||||
void
|
||||
gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
if (!g_set_object (&self->factory, factory))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_title:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @title: (nullable): Title to use for this column
|
||||
*
|
||||
* Sets the title of this column. The title is displayed in the header of a
|
||||
* #GtkColumnView for this column and is therefor user-facing text that should
|
||||
* be translated.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_title (GtkColumnViewColumn *self,
|
||||
const char *title)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
|
||||
if (g_strcmp0 (self->title, title) == 0)
|
||||
return;
|
||||
|
||||
g_free (self->title);
|
||||
self->title = g_strdup (title);
|
||||
|
||||
if (self->header)
|
||||
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_title:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Returns the title set with gtk_column_view_column_set_title().
|
||||
*
|
||||
* Returns: (nullable) The column's title
|
||||
*/
|
||||
const char *
|
||||
gtk_column_view_column_get_title (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), FALSE);
|
||||
|
||||
return self->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_sorter:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @sorter: (nullable): the #GtkSorter to associate with @column
|
||||
*
|
||||
* Associates a sorter with the column.
|
||||
*
|
||||
* This sorter can be made active by clicking on the column
|
||||
* header, or by calling gtk_column_view_sort_by_column().
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_sorter (GtkColumnViewColumn *self,
|
||||
GtkSorter *sorter)
|
||||
{
|
||||
GtkColumnViewSorter *view_sorter;
|
||||
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
|
||||
|
||||
if (!g_set_object (&self->sorter, sorter))
|
||||
return;
|
||||
|
||||
if (self->header)
|
||||
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
|
||||
|
||||
view_sorter = GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (self->view));
|
||||
|
||||
if (gtk_column_view_sorter_remove_column (view_sorter, self))
|
||||
gtk_column_view_active_sorter_changed (self->view);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_sorter:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Returns the sorter that is associated with the column.
|
||||
*
|
||||
* Returns: (transfer none): the #GtkSorter of @self
|
||||
*/
|
||||
GtkSorter *
|
||||
gtk_column_view_column_get_sorter (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->sorter;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_active_sorter_changed (GtkColumnViewColumn *self)
|
||||
{
|
||||
if (self->header)
|
||||
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
|
||||
}
|
||||
77
gtk/gtkcolumnviewcolumn.h
Normal file
77
gtk/gtkcolumnviewcolumn.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_COLUMN_H__
|
||||
#define __GTK_COLUMN_VIEW_COLUMN_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_COLUMN (gtk_column_view_column_get_type ())
|
||||
#define GTK_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumn))
|
||||
#define GTK_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
|
||||
#define GTK_IS_COLUMN_VIEW_COLUMN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_COLUMN))
|
||||
#define GTK_IS_COLUMN_VIEW_COLUMN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_COLUMN))
|
||||
#define GTK_COLUMN_VIEW_COLUMN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_COLUMN, GtkColumnViewColumnClass))
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:
|
||||
*
|
||||
* GtkColumnViewColumns are added to #GtkColumnViews.
|
||||
*/
|
||||
typedef struct _GtkColumnViewColumnClass GtkColumnViewColumnClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_column_view_column_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnViewColumn * gtk_column_view_column_new (const char *title);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnViewColumn * gtk_column_view_column_new_with_factory (const char *title,
|
||||
GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkColumnView * gtk_column_view_column_get_column_view (GtkColumnViewColumn *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_factory (GtkColumnViewColumn *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_column_view_column_get_factory (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_title (GtkColumnViewColumn *self,
|
||||
const char *title);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_column_view_column_get_title (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_sorter (GtkColumnViewColumn *self,
|
||||
GtkSorter *sorter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSorter * gtk_column_view_column_get_sorter (GtkColumnViewColumn *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */
|
||||
49
gtk/gtkcolumnviewcolumnprivate.h
Normal file
49
gtk/gtkcolumnviewcolumnprivate.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__
|
||||
|
||||
#include "gtk/gtkcolumnviewcolumn.h"
|
||||
|
||||
#include "gtk/gtkcolumnviewcellprivate.h"
|
||||
|
||||
|
||||
void gtk_column_view_column_set_column_view (GtkColumnViewColumn *self,
|
||||
GtkColumnView *view);
|
||||
|
||||
void gtk_column_view_column_add_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell);
|
||||
void gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell);
|
||||
GtkColumnViewCell * gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self);
|
||||
|
||||
void gtk_column_view_column_queue_resize (GtkColumnViewColumn *self);
|
||||
void gtk_column_view_column_measure (GtkColumnViewColumn *self,
|
||||
int *minimum,
|
||||
int *natural);
|
||||
void gtk_column_view_column_allocate (GtkColumnViewColumn *self,
|
||||
int offset,
|
||||
int size);
|
||||
void gtk_column_view_column_get_allocation (GtkColumnViewColumn *self,
|
||||
int *offset,
|
||||
int *size);
|
||||
void gtk_column_view_column_active_sorter_changed (GtkColumnViewColumn *self);
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */
|
||||
154
gtk/gtkcolumnviewlayout.c
Normal file
154
gtk/gtkcolumnviewlayout.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewlayoutprivate.h"
|
||||
|
||||
#include "gtkcolumnviewcellprivate.h"
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewprivate.h"
|
||||
#include "gtkcolumnviewtitleprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
struct _GtkColumnViewLayout
|
||||
{
|
||||
GtkLayoutManager parent_instance;
|
||||
|
||||
GtkColumnView *view; /* no reference */
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK_TYPE_LAYOUT_MANAGER)
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_measure_along (GtkColumnViewLayout *self,
|
||||
GtkWidget *widget,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkOrientation orientation = GTK_ORIENTATION_VERTICAL;
|
||||
GtkWidget *child;
|
||||
|
||||
for (child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
int child_min = 0;
|
||||
int child_nat = 0;
|
||||
int child_min_baseline = -1;
|
||||
int child_nat_baseline = -1;
|
||||
|
||||
gtk_widget_measure (child, orientation, for_size,
|
||||
&child_min, &child_nat,
|
||||
&child_min_baseline, &child_nat_baseline);
|
||||
|
||||
*minimum = MAX (*minimum, child_min);
|
||||
*natural = MAX (*natural, child_nat);
|
||||
|
||||
if (child_min_baseline > -1)
|
||||
*minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
|
||||
if (child_nat_baseline > -1)
|
||||
*natural_baseline = MAX (*natural_baseline, child_nat_baseline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_measure (GtkLayoutManager *layout,
|
||||
GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkColumnViewLayout *self = GTK_COLUMN_VIEW_LAYOUT (layout);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
gtk_column_view_measure_across (GTK_COLUMN_VIEW (self->view),
|
||||
minimum,
|
||||
natural);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_column_view_layout_measure_along (self,
|
||||
widget,
|
||||
for_size,
|
||||
minimum,
|
||||
natural,
|
||||
minimum_baseline,
|
||||
natural_baseline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
|
||||
GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
for (child = _gtk_widget_get_first_child (widget);
|
||||
child != NULL;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
int col_x, col_width;
|
||||
|
||||
if (GTK_IS_COLUMN_VIEW_CELL (child))
|
||||
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
|
||||
else
|
||||
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
|
||||
|
||||
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
|
||||
gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_class_init (GtkColumnViewLayoutClass *klass)
|
||||
{
|
||||
GtkLayoutManagerClass *layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
|
||||
|
||||
layout_manager_class->measure = gtk_column_view_layout_measure;
|
||||
layout_manager_class->allocate = gtk_column_view_layout_allocate;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_layout_init (GtkColumnViewLayout *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkLayoutManager *
|
||||
gtk_column_view_layout_new (GtkColumnView *view)
|
||||
{
|
||||
GtkColumnViewLayout *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_COLUMN_VIEW_LAYOUT, NULL);
|
||||
|
||||
result->view = view;
|
||||
|
||||
return GTK_LAYOUT_MANAGER (result);
|
||||
}
|
||||
35
gtk/gtkcolumnviewlayoutprivate.h
Normal file
35
gtk/gtkcolumnviewlayoutprivate.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__
|
||||
|
||||
#include <gtk/gtkcolumnview.h>
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_LAYOUT (gtk_column_view_layout_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkColumnViewLayout, gtk_column_view_layout, GTK, COLUMN_VIEW_LAYOUT, GtkLayoutManager)
|
||||
|
||||
GtkLayoutManager * gtk_column_view_layout_new (GtkColumnView *view);
|
||||
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_LAYOUT_PRIVATE_H__ */
|
||||
34
gtk/gtkcolumnviewprivate.h
Normal file
34
gtk/gtkcolumnviewprivate.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_PRIVATE_H__
|
||||
|
||||
#include "gtk/gtkcolumnview.h"
|
||||
|
||||
#include "gtk/gtklistitemwidgetprivate.h"
|
||||
|
||||
GtkListItemWidget * gtk_column_view_get_header_widget (GtkColumnView *self);
|
||||
|
||||
void gtk_column_view_measure_across (GtkColumnView *self,
|
||||
int *minimum,
|
||||
int *natural);
|
||||
void gtk_column_view_active_sorter_changed (GtkColumnView *self);
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_PRIVATE_H__ */
|
||||
256
gtk/gtkcolumnviewsorter.c
Normal file
256
gtk/gtkcolumnviewsorter.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright © 2019 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
GtkSorter *sorter;
|
||||
gboolean inverted;
|
||||
gulong changed_id;
|
||||
} Sorter;
|
||||
|
||||
static void
|
||||
free_sorter (gpointer data)
|
||||
{
|
||||
Sorter *s = data;
|
||||
|
||||
g_signal_handler_disconnect (s->sorter, s->changed_id);
|
||||
g_object_unref (s->sorter);
|
||||
g_object_unref (s->column);
|
||||
g_free (s);
|
||||
}
|
||||
|
||||
struct _GtkColumnViewSorter
|
||||
{
|
||||
GtkSorter parent_instance;
|
||||
|
||||
GList *sorters;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewSorter, gtk_column_view_sorter, GTK_TYPE_SORTER)
|
||||
|
||||
static int
|
||||
gtk_column_view_sorter_compare (GtkSorter *sorter,
|
||||
gpointer item1,
|
||||
gpointer item2)
|
||||
{
|
||||
GtkColumnViewSorter *self = GTK_COLUMN_VIEW_SORTER (sorter);
|
||||
int result = 0;
|
||||
GList *l;
|
||||
|
||||
for (l = self->sorters; l; l = l->next)
|
||||
{
|
||||
Sorter *s = l->data;
|
||||
GtkSorter *ss = gtk_column_view_column_get_sorter (s->column);
|
||||
|
||||
result = gtk_sorter_compare (ss, item1, item2);
|
||||
if (s->inverted)
|
||||
result = - result;
|
||||
|
||||
if (result != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void changed_cb (GtkSorter *sorter, int change, gpointer data);
|
||||
|
||||
static void
|
||||
gtk_column_view_sorter_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewSorter *self = GTK_COLUMN_VIEW_SORTER (object);
|
||||
|
||||
g_list_free_full (self->sorters, free_sorter);
|
||||
self->sorters = NULL;
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_sorter_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_sorter_class_init (GtkColumnViewSorterClass *class)
|
||||
{
|
||||
GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
sorter_class->compare = gtk_column_view_sorter_compare;
|
||||
|
||||
object_class->dispose = gtk_column_view_sorter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_sorter_init (GtkColumnViewSorter *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkSorter *
|
||||
gtk_column_view_sorter_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COLUMN_VIEW_SORTER, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
changed_cb (GtkSorter *sorter, int change, gpointer data)
|
||||
{
|
||||
gtk_sorter_changed (GTK_SORTER (data), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = self->sorters; l; l = l->next)
|
||||
{
|
||||
Sorter *s = l->data;
|
||||
if (s->column == column)
|
||||
{
|
||||
self->sorters = g_list_remove_link (self->sorters, l);
|
||||
free_sorter (s);
|
||||
g_list_free (l);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_column_view_sorter_add_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
GtkSorter *sorter;
|
||||
Sorter *s;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
|
||||
|
||||
sorter = gtk_column_view_column_get_sorter (column);
|
||||
if (sorter == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (self->sorters != NULL)
|
||||
{
|
||||
s = self->sorters->data;
|
||||
if (s->column == column)
|
||||
{
|
||||
s->inverted = !s->inverted;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
remove_column (self, column);
|
||||
|
||||
s = g_new (Sorter, 1);
|
||||
s->column = g_object_ref (column);
|
||||
s->sorter = g_object_ref (sorter);
|
||||
s->changed_id = g_signal_connect (sorter, "changed", G_CALLBACK (changed_cb), self);
|
||||
s->inverted = FALSE;
|
||||
|
||||
self->sorters = g_list_prepend (self->sorters, s);
|
||||
|
||||
out:
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_column_view_sorter_remove_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
|
||||
|
||||
if (remove_column (self, column))
|
||||
{
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_column_view_sorter_set_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column,
|
||||
gboolean inverted)
|
||||
{
|
||||
GtkSorter *sorter;
|
||||
Sorter *s;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), FALSE);
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column), FALSE);
|
||||
|
||||
sorter = gtk_column_view_column_get_sorter (column);
|
||||
if (sorter == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_list_free_full (self->sorters, free_sorter);
|
||||
|
||||
s = g_new (Sorter, 1);
|
||||
s->column = g_object_ref (column);
|
||||
s->sorter = g_object_ref (sorter);
|
||||
s->changed_id = g_signal_connect (sorter, "changed", G_CALLBACK (changed_cb), self);
|
||||
s->inverted = inverted;
|
||||
|
||||
self->sorters = g_list_prepend (self->sorters, s);
|
||||
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_sorter_reset (GtkColumnViewSorter *self)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self));
|
||||
|
||||
if (self->sorters == NULL)
|
||||
return;
|
||||
|
||||
g_list_free_full (self->sorters, free_sorter);
|
||||
gtk_sorter_changed (GTK_SORTER (self), GTK_SORTER_CHANGE_DIFFERENT);
|
||||
}
|
||||
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_sorter_get_active (GtkColumnViewSorter *self,
|
||||
gboolean *inverted)
|
||||
{
|
||||
Sorter *s;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_SORTER (self), NULL);
|
||||
|
||||
if (self->sorters == NULL)
|
||||
return NULL;
|
||||
|
||||
s = self->sorters->data;
|
||||
|
||||
*inverted = s->inverted;
|
||||
|
||||
return s->column;
|
||||
}
|
||||
58
gtk/gtkcolumnviewsorterprivate.h
Normal file
58
gtk/gtkcolumnviewsorterprivate.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright © 2019 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_SORTER_H__
|
||||
#define __GTK_COLUMN_VIEW_SORTER_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
#include <gtk/gtkcolumnviewcolumn.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_SORTER (gtk_column_view_sorter_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkColumnViewSorter, gtk_column_view_sorter, GTK, COLUMN_VIEW_SORTER, GtkSorter)
|
||||
|
||||
GtkSorter * gtk_column_view_sorter_new (void);
|
||||
|
||||
gboolean gtk_column_view_sorter_add_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column);
|
||||
gboolean gtk_column_view_sorter_remove_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column);
|
||||
|
||||
void gtk_column_view_sorter_reset (GtkColumnViewSorter *self);
|
||||
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_sorter_get_active (GtkColumnViewSorter *self,
|
||||
gboolean *inverted);
|
||||
|
||||
gboolean gtk_column_view_sorter_set_column (GtkColumnViewSorter *self,
|
||||
GtkColumnViewColumn *column,
|
||||
gboolean inverted);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SORTER_H__ */
|
||||
|
||||
209
gtk/gtkcolumnviewtitle.c
Normal file
209
gtk/gtkcolumnviewtitle.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcolumnviewtitleprivate.h"
|
||||
|
||||
#include "gtkcolumnviewprivate.h"
|
||||
#include "gtkcolumnviewcolumnprivate.h"
|
||||
#include "gtkcolumnviewsorterprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklabel.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkbox.h"
|
||||
#include "gtkimage.h"
|
||||
#include "gtkgestureclick.h"
|
||||
|
||||
struct _GtkColumnViewTitle
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
GtkWidget *box;
|
||||
GtkWidget *title;
|
||||
GtkWidget *sort;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewTitleClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkColumnViewTitle, gtk_column_view_title, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
gtk_column_view_title_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_allocate (child, width, height, baseline, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_dispose (GObject *object)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (object);
|
||||
|
||||
g_clear_pointer (&self->box, gtk_widget_unparent);
|
||||
|
||||
g_clear_object (&self->column);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_title_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_class_init (GtkColumnViewTitleClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
widget_class->measure = gtk_column_view_title_measure;
|
||||
widget_class->size_allocate = gtk_column_view_title_size_allocate;
|
||||
|
||||
gobject_class->dispose = gtk_column_view_title_dispose;
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("button"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_resize_func (GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
|
||||
if (self->column)
|
||||
gtk_column_view_column_queue_resize (self->column);
|
||||
}
|
||||
|
||||
static void
|
||||
click_pressed_cb (GtkGestureClick *gesture,
|
||||
guint n_press,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
GtkSorter *sorter;
|
||||
GtkColumnView *view;
|
||||
GtkColumnViewSorter *view_sorter;
|
||||
|
||||
sorter = gtk_column_view_column_get_sorter (self->column);
|
||||
if (sorter == NULL)
|
||||
return;
|
||||
|
||||
view = gtk_column_view_column_get_column_view (self->column);
|
||||
view_sorter = GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (view));
|
||||
gtk_column_view_sorter_add_column (view_sorter, self->column);
|
||||
gtk_column_view_active_sorter_changed (view);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_init (GtkColumnViewTitle *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
GtkGesture *gesture;
|
||||
|
||||
widget->priv->resize_func = gtk_column_view_title_resize_func;
|
||||
|
||||
self->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_widget_set_parent (self->box, widget);
|
||||
|
||||
self->title = gtk_label_new (NULL);
|
||||
gtk_container_add (GTK_CONTAINER (self->box), self->title);
|
||||
|
||||
self->sort = gtk_image_new ();
|
||||
gtk_container_add (GTK_CONTAINER (self->box), self->sort);
|
||||
|
||||
gesture = gtk_gesture_click_new ();
|
||||
g_signal_connect (gesture, "pressed", G_CALLBACK (click_pressed_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_column_view_title_new (GtkColumnViewColumn *column)
|
||||
{
|
||||
GtkColumnViewTitle *title;
|
||||
|
||||
title = g_object_new (GTK_TYPE_COLUMN_VIEW_TITLE, NULL);
|
||||
|
||||
title->column = g_object_ref (column);
|
||||
gtk_column_view_title_update (title);
|
||||
|
||||
return GTK_WIDGET (title);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_title_update (GtkColumnViewTitle *self)
|
||||
{
|
||||
GtkSorter *sorter;
|
||||
GtkColumnView *view;
|
||||
GtkColumnViewSorter *view_sorter;
|
||||
gboolean inverted;
|
||||
GtkColumnViewColumn *active;
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (self->title), gtk_column_view_column_get_title (self->column));
|
||||
|
||||
sorter = gtk_column_view_column_get_sorter (self->column);
|
||||
|
||||
if (sorter)
|
||||
{
|
||||
view = gtk_column_view_column_get_column_view (self->column);
|
||||
view_sorter = GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (view));
|
||||
active = gtk_column_view_sorter_get_active (view_sorter, &inverted);
|
||||
|
||||
gtk_widget_show (self->sort);
|
||||
if (self->column == active)
|
||||
{
|
||||
if (inverted)
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (self->sort), "pan-down-symbolic");
|
||||
else
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (self->sort), "pan-up-symbolic");
|
||||
}
|
||||
else
|
||||
gtk_image_clear (GTK_IMAGE (self->sort));
|
||||
}
|
||||
else
|
||||
gtk_widget_hide (self->sort);
|
||||
}
|
||||
|
||||
GtkColumnViewColumn *
|
||||
gtk_column_view_title_get_column (GtkColumnViewTitle *self)
|
||||
{
|
||||
return self->column;
|
||||
}
|
||||
47
gtk/gtkcolumnviewtitleprivate.h
Normal file
47
gtk/gtkcolumnviewtitleprivate.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__
|
||||
#define __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__
|
||||
|
||||
#include "gtkcolumnviewcolumn.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COLUMN_VIEW_TITLE (gtk_column_view_title_get_type ())
|
||||
#define GTK_COLUMN_VIEW_TITLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitle))
|
||||
#define GTK_COLUMN_VIEW_TITLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitleClass))
|
||||
#define GTK_IS_COLUMN_VIEW_TITLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLUMN_VIEW_TITLE))
|
||||
#define GTK_IS_COLUMN_VIEW_TITLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COLUMN_VIEW_TITLE))
|
||||
#define GTK_COLUMN_VIEW_TITLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COLUMN_VIEW_TITLE, GtkColumnViewTitleClass))
|
||||
|
||||
typedef struct _GtkColumnViewTitle GtkColumnViewTitle;
|
||||
typedef struct _GtkColumnViewTitleClass GtkColumnViewTitleClass;
|
||||
|
||||
GType gtk_column_view_title_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * gtk_column_view_title_new (GtkColumnViewColumn *column);
|
||||
|
||||
void gtk_column_view_title_update (GtkColumnViewTitle *self);
|
||||
|
||||
GtkColumnViewColumn * gtk_column_view_title_get_column (GtkColumnViewTitle *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_TITLE_PRIVATE_H__ */
|
||||
704
gtk/gtkcoverflow.c
Normal file
704
gtk/gtkcoverflow.c
Normal file
@@ -0,0 +1,704 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcoverflow.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkorientable.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/* Extra items to display at most above + below the central item */
|
||||
#define GTK_COVER_FLOW_DISPLAY_ITEMS 5
|
||||
|
||||
/* Number of extra space we leave around the covers */
|
||||
#define GTK_COVER_FLOW_SCALE_ALONG 3
|
||||
#define GTK_COVER_FLOW_SCALE_ACROSS 2
|
||||
|
||||
/**
|
||||
* SECTION:gtkcoverflow
|
||||
* @title: GtkCoverFlow
|
||||
* @short_description: A coverflow list widget
|
||||
* @see_also: #GListModel
|
||||
*
|
||||
* GtkCoverFlow is a widget to present a coverflow list
|
||||
*/
|
||||
|
||||
struct _GtkCoverFlow
|
||||
{
|
||||
GtkListBase parent_instance;
|
||||
|
||||
int size_across;
|
||||
int size_along;
|
||||
};
|
||||
|
||||
struct _GtkCoverFlowClass
|
||||
{
|
||||
GtkListBaseClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_MODEL,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTIVATE,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkCoverFlow, gtk_cover_flow, GTK_TYPE_LIST_BASE)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_allocation_along (GtkListBase *base,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (offset)
|
||||
*offset = pos * self->size_along;
|
||||
if (size)
|
||||
*size = self->size_along;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_allocation_across (GtkListBase *base,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (offset)
|
||||
*offset = pos * self->size_across;
|
||||
if (size)
|
||||
*size = self->size_across;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_cover_flow_move_focus_along (GtkListBase *base,
|
||||
guint pos,
|
||||
int steps)
|
||||
{
|
||||
if (steps < 0)
|
||||
return pos - MIN (pos, -steps);
|
||||
else
|
||||
{
|
||||
pos += MIN (gtk_list_base_get_n_items (base) - pos - 1, steps);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_cover_flow_move_focus_across (GtkListBase *base,
|
||||
guint pos,
|
||||
int steps)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_cover_flow_get_position_from_allocation (GtkListBase *base,
|
||||
int across,
|
||||
int along,
|
||||
guint *pos,
|
||||
cairo_rectangle_int_t *area)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (base);
|
||||
|
||||
if (across >= self->size_across ||
|
||||
along >= self->size_along * gtk_list_base_get_n_items (base))
|
||||
return FALSE;
|
||||
|
||||
*pos = along / self->size_along;
|
||||
|
||||
if (area)
|
||||
{
|
||||
area->x = 0;
|
||||
area->width = self->size_across;
|
||||
area->y = *pos * self->size_along;
|
||||
area->height = self->size_along;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_children (GtkCoverFlow *self,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkListItemManagerItem *item;
|
||||
int min, nat, child_min, child_nat;
|
||||
|
||||
min = 0;
|
||||
nat = 0;
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
/* ignore unavailable items */
|
||||
if (item->widget == NULL)
|
||||
continue;
|
||||
|
||||
gtk_widget_measure (item->widget,
|
||||
orientation, for_size,
|
||||
&child_min, &child_nat, NULL, NULL);
|
||||
min = MAX (min, child_min);
|
||||
nat = MAX (nat, child_nat);
|
||||
}
|
||||
|
||||
*minimum = min;
|
||||
*natural = nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_across (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (for_size > 0)
|
||||
for_size /= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
|
||||
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
|
||||
|
||||
*minimum *= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
*natural *= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure_along (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (for_size > 0)
|
||||
for_size /= GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
|
||||
gtk_cover_flow_measure_children (self, orientation, for_size, minimum, natural);
|
||||
|
||||
*minimum *= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
*natural *= GTK_COVER_FLOW_SCALE_ALONG;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
|
||||
if (orientation == gtk_list_base_get_orientation (GTK_LIST_BASE (self)))
|
||||
gtk_cover_flow_measure_along (widget, orientation, for_size, minimum, natural);
|
||||
else
|
||||
gtk_cover_flow_measure_across (widget, orientation, for_size, minimum, natural);
|
||||
}
|
||||
|
||||
static GskTransform *
|
||||
transform_translate_oriented (GskTransform *transform,
|
||||
GtkOrientation orientation,
|
||||
GtkTextDirection dir,
|
||||
float across,
|
||||
float along)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (across, along));
|
||||
else if (dir == GTK_TEXT_DIR_LTR)
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (along, across));
|
||||
else
|
||||
return gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-along, across));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_size_allocate_child (GtkWidget *child,
|
||||
GtkOrientation orientation,
|
||||
GtkTextDirection dir,
|
||||
GskTransform *transform,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
{
|
||||
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-width / 2, -height / 2));
|
||||
gtk_widget_allocate (child, width, height, -1, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-height / 2, -width / 2));
|
||||
gtk_widget_allocate (child, height, width, -1, transform);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
GtkOrientation orientation, opposite_orientation;
|
||||
GtkTextDirection dir;
|
||||
GtkListItemManagerItem *item;
|
||||
GtkListItemManager *manager;
|
||||
guint i, pos;
|
||||
int x, y, along, across;
|
||||
|
||||
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
|
||||
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
|
||||
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
|
||||
|
||||
/* step 0: exit early if list is empty */
|
||||
if (gtk_list_item_manager_get_root (manager) == NULL)
|
||||
return;
|
||||
|
||||
/* step 1: determine size of children */
|
||||
along = orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
|
||||
across = opposite_orientation == GTK_ORIENTATION_HORIZONTAL ? width : height;
|
||||
self->size_along = along / GTK_COVER_FLOW_SCALE_ALONG;
|
||||
self->size_across = across / GTK_COVER_FLOW_SCALE_ACROSS;
|
||||
|
||||
/* step 2: update the adjustments */
|
||||
gtk_list_base_update_adjustments (GTK_LIST_BASE (self),
|
||||
self->size_across,
|
||||
self->size_along * gtk_list_base_get_n_items (GTK_LIST_BASE (self)),
|
||||
self->size_across,
|
||||
self->size_along,
|
||||
&x, &y);
|
||||
pos = gtk_list_base_get_anchor (GTK_LIST_BASE (self));
|
||||
|
||||
/* step 4: actually allocate the widgets */
|
||||
dir = _gtk_widget_get_direction (widget);
|
||||
i = 0;
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (manager);
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
if (item->widget)
|
||||
{
|
||||
/* start at the center */
|
||||
GskTransform *transform = transform_translate_oriented (NULL, orientation, dir, across / 2, along / 2);
|
||||
|
||||
if (i == pos)
|
||||
{
|
||||
/* nothing to do, we're already centered */
|
||||
}
|
||||
else if (MAX (pos, i) - MIN (pos, i) < GTK_COVER_FLOW_DISPLAY_ITEMS) /* ABS() doesn't work with guint */
|
||||
{
|
||||
int diff = i - pos;
|
||||
transform = gsk_transform_perspective (transform, MAX (width, height) * 2);
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
diff * self->size_along / 4);
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
(diff < 0 ? -1 : 1) * self->size_along / 2);
|
||||
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||||
transform = gsk_transform_rotate_3d (transform,
|
||||
diff > 0 ? 60 : -60,
|
||||
graphene_vec3_x_axis ());
|
||||
else
|
||||
transform = gsk_transform_rotate_3d (transform,
|
||||
diff < 0 ? 60 : -60,
|
||||
graphene_vec3_y_axis ());
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
0,
|
||||
(diff < 0 ? 1 : -1) * self->size_along / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform = transform_translate_oriented (transform,
|
||||
orientation, dir,
|
||||
- 2 * self->size_across,
|
||||
- 2 * self->size_along);
|
||||
}
|
||||
gtk_cover_flow_size_allocate_child (item->widget,
|
||||
orientation, dir,
|
||||
transform,
|
||||
self->size_across,
|
||||
self->size_along);
|
||||
}
|
||||
|
||||
i += item->n_items;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListBase *base = GTK_LIST_BASE (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, gtk_list_item_manager_get_factory (gtk_list_base_get_manager (base)));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, gtk_list_base_get_model (base));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkWidget *child;
|
||||
GtkListItemManagerItem *item;
|
||||
|
||||
item = gtk_list_item_manager_get_nth (gtk_list_base_get_manager (GTK_LIST_BASE (widget)),
|
||||
gtk_list_base_get_anchor (GTK_LIST_BASE (widget)),
|
||||
NULL);
|
||||
if (item == NULL || item->widget == NULL)
|
||||
{
|
||||
GTK_WIDGET_CLASS (gtk_cover_flow_parent_class)->snapshot (widget, snapshot);
|
||||
return;
|
||||
}
|
||||
|
||||
for (child = _gtk_widget_get_first_child (widget);
|
||||
child != item->widget;
|
||||
child = _gtk_widget_get_next_sibling (child))
|
||||
gtk_widget_snapshot_child (widget, child, snapshot);
|
||||
|
||||
for (child = _gtk_widget_get_last_child (widget);
|
||||
child != item->widget;
|
||||
child = _gtk_widget_get_prev_sibling (child))
|
||||
gtk_widget_snapshot_child (widget, child, snapshot);
|
||||
|
||||
gtk_widget_snapshot_child (widget, item->widget, snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_cover_flow_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_cover_flow_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_activate_item (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkCoverFlow *self = GTK_COVER_FLOW (widget);
|
||||
guint pos;
|
||||
|
||||
if (!g_variant_check_format_string (parameter, "u", FALSE))
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "u", &pos);
|
||||
if (pos >= gtk_list_base_get_n_items (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
g_signal_emit (widget, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_class_init (GtkCoverFlowClass *klass)
|
||||
{
|
||||
GtkListBaseClass *list_base_class = GTK_LIST_BASE_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
list_base_class->list_item_name = "cover";
|
||||
list_base_class->list_item_size = sizeof (GtkListItemManagerItem);
|
||||
list_base_class->list_item_augment_size = sizeof (GtkListItemManagerItemAugment);
|
||||
list_base_class->list_item_augment_func = gtk_list_item_manager_augment_node;
|
||||
list_base_class->get_allocation_along = gtk_cover_flow_get_allocation_along;
|
||||
list_base_class->get_allocation_across = gtk_cover_flow_get_allocation_across;
|
||||
list_base_class->get_position_from_allocation = gtk_cover_flow_get_position_from_allocation;
|
||||
list_base_class->move_focus_along = gtk_cover_flow_move_focus_along;
|
||||
list_base_class->move_focus_across = gtk_cover_flow_move_focus_across;
|
||||
|
||||
widget_class->measure = gtk_cover_flow_measure;
|
||||
widget_class->size_allocate = gtk_cover_flow_size_allocate;
|
||||
widget_class->snapshot = gtk_cover_flow_snapshot;
|
||||
|
||||
gobject_class->get_property = gtk_cover_flow_get_property;
|
||||
gobject_class->set_property = gtk_cover_flow_set_property;
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:factory:
|
||||
*
|
||||
* Factory for populating list items
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
P_("Factory"),
|
||||
P_("Factory for populating list items"),
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:model:
|
||||
*
|
||||
* Model for the items displayed
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Model for the items displayed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow::activate:
|
||||
* @self: The #GtkCoverFlow
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* The ::activate signal is emitted when an item has been activated by the user,
|
||||
* usually via activating the GtkCoverFlow|list.activate-item action.
|
||||
*
|
||||
* This allows for a convenient way to handle activation in a listview.
|
||||
* See gtk_list_item_set_activatable() for details on how to use this signal.
|
||||
*/
|
||||
signals[ACTIVATE] =
|
||||
g_signal_new (I_("activate"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__UINT,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_UINT);
|
||||
g_signal_set_va_marshaller (signals[ACTIVATE],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__UINTv);
|
||||
|
||||
/**
|
||||
* GtkCoverFlow|list.activate-item:
|
||||
* @position: position of item to activate
|
||||
*
|
||||
* Activates the item given in @position by emitting the GtkCoverFlow::activate
|
||||
* signal.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"list.activate-item",
|
||||
"u",
|
||||
gtk_cover_flow_activate_item);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("coverflow"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cover_flow_init (GtkCoverFlow *self)
|
||||
{
|
||||
gtk_list_base_set_anchor_max_widgets (GTK_LIST_BASE (self),
|
||||
0,
|
||||
GTK_COVER_FLOW_DISPLAY_ITEMS);
|
||||
|
||||
/* FIXME: this should overwrite the property default, but gobject properties... */
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_new:
|
||||
*
|
||||
* Creates a new empty #GtkCoverFlow.
|
||||
*
|
||||
* You most likely want to call gtk_cover_flow_set_factory() to
|
||||
* set up a way to map its items to widgets and gtk_cover_flow_set_model()
|
||||
* to set a model to provide items next.
|
||||
*
|
||||
* Returns: a new #GtkCoverFlow
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_cover_flow_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_COVER_FLOW, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_new_with_factory:
|
||||
* @factory: (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new #GtkCoverFlow that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* You most likely want to call gtk_cover_flow_set_model() to set
|
||||
* a model next.
|
||||
*
|
||||
* The function takes ownership of the
|
||||
* argument, so you can write code like
|
||||
* ```
|
||||
* cover_flow = gtk_cover_flow_new_with_factory (
|
||||
* gtk_builder_list_item_factory_newfrom_resource ("/resource.ui"));
|
||||
* ```
|
||||
*
|
||||
* Returns: a new #GtkCoverFlow using the given @factory
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_cover_flow_new_with_factory (GtkListItemFactory *factory)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_COVER_FLOW,
|
||||
"factory", factory,
|
||||
NULL);
|
||||
|
||||
g_object_unref (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_get_model:
|
||||
* @self: a #GtkCoverFlow
|
||||
*
|
||||
* Gets the model that's currently used to read the items displayed.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_cover_flow_get_model (GtkCoverFlow *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
|
||||
|
||||
return gtk_list_base_get_model (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_set_model:
|
||||
* @self: a #GtkCoverFlow
|
||||
* @model: (allow-none) (transfer none): the model to use or %NULL for none
|
||||
*
|
||||
* Sets the #GListModel to use.
|
||||
*
|
||||
* If the @model is a #GtkSelectionModel, it is used for managing the selection.
|
||||
* Otherwise, @self creates a #GtkSingleSelection for the selection.
|
||||
**/
|
||||
void
|
||||
gtk_cover_flow_set_model (GtkCoverFlow *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COVER_FLOW (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (!gtk_list_base_set_model (GTK_LIST_BASE (self), model))
|
||||
return;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_get_factory:
|
||||
* @self: a #GtkCoverFlow
|
||||
*
|
||||
* Gets the factory that's currently used to populate list items.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_cover_flow_get_factory (GtkCoverFlow *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COVER_FLOW (self), NULL);
|
||||
|
||||
return gtk_list_item_manager_get_factory (gtk_list_base_get_manager (GTK_LIST_BASE (self)));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_cover_flow_set_factory:
|
||||
* @self: a #GtkCoverFlow
|
||||
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
|
||||
*
|
||||
* Sets the #GtkListItemFactory to use for populating list items.
|
||||
**/
|
||||
void
|
||||
gtk_cover_flow_set_factory (GtkCoverFlow *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkListItemManager *manager;
|
||||
|
||||
g_return_if_fail (GTK_IS_COVER_FLOW (self));
|
||||
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
manager = gtk_list_base_get_manager (GTK_LIST_BASE (self));
|
||||
|
||||
if (factory == gtk_list_item_manager_get_factory (manager))
|
||||
return;
|
||||
|
||||
gtk_list_item_manager_set_factory (manager, factory);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
68
gtk/gtkcoverflow.h
Normal file
68
gtk/gtkcoverflow.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_COVER_FLOW_H__
|
||||
#define __GTK_COVER_FLOW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_COVER_FLOW (gtk_cover_flow_get_type ())
|
||||
#define GTK_COVER_FLOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COVER_FLOW, GtkCoverFlow))
|
||||
#define GTK_COVER_FLOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_COVER_FLOW, GtkCoverFlowClass))
|
||||
#define GTK_IS_COVER_FLOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COVER_FLOW))
|
||||
#define GTK_IS_COVER_FLOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_COVER_FLOW))
|
||||
#define GTK_COVER_FLOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_COVER_FLOW, GtkCoverFlowClass))
|
||||
|
||||
/**
|
||||
* GtkCoverFlow:
|
||||
*
|
||||
* GtkCoverFlow is the simple list implementation for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkCoverFlow GtkCoverFlow;
|
||||
typedef struct _GtkCoverFlowClass GtkCoverFlowClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_cover_flow_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_cover_flow_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_cover_flow_new_with_factory (GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_cover_flow_get_model (GtkCoverFlow *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_cover_flow_set_model (GtkCoverFlow *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_cover_flow_set_factory (GtkCoverFlow *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory *
|
||||
gtk_cover_flow_get_factory (GtkCoverFlow *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COVER_FLOW_H__ */
|
||||
108
gtk/gtkcustomsorter.c
Normal file
108
gtk/gtkcustomsorter.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright © 2019 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcustomsorter.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcustomsorter
|
||||
* @Title: GtkCustomSorter
|
||||
* @Short_description: Sorting with a callback
|
||||
*
|
||||
* GtkCustomSorter is a #GtkSorter implementation that sorts
|
||||
* via a traditional #GCompareDataFunc callback.
|
||||
*/
|
||||
struct _GtkCustomSorter
|
||||
{
|
||||
GtkSorter parent_instance;
|
||||
|
||||
GCompareDataFunc sort_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK_TYPE_SORTER)
|
||||
|
||||
static int
|
||||
gtk_custom_sorter_compare (GtkSorter *sorter,
|
||||
gpointer item1,
|
||||
gpointer item2)
|
||||
{
|
||||
GtkCustomSorter *self = GTK_CUSTOM_SORTER (sorter);
|
||||
|
||||
return self->sort_func (item1, item2, self->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_sorter_dispose (GObject *object)
|
||||
{
|
||||
GtkCustomSorter *self = GTK_CUSTOM_SORTER (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
G_OBJECT_CLASS (gtk_custom_sorter_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_sorter_class_init (GtkCustomSorterClass *class)
|
||||
{
|
||||
GtkSorterClass *sorter_class = GTK_SORTER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
sorter_class->compare = gtk_custom_sorter_compare;
|
||||
|
||||
object_class->dispose = gtk_custom_sorter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_sorter_init (GtkCustomSorter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_sorter_new:
|
||||
* @sort_func: the #GCompareDataFunc to use for sorting
|
||||
* @user_data: (nullable): user data to pass to @sort_func
|
||||
* @user_destroy: (nullable): destroy notify for @user_data
|
||||
*
|
||||
* Creates a new #GtkSorter that works by calling
|
||||
* @sort_func to compare items.
|
||||
*
|
||||
* Returns: a new #GTkSorter
|
||||
*/
|
||||
GtkSorter *
|
||||
gtk_custom_sorter_new (GCompareDataFunc sort_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkCustomSorter *sorter;
|
||||
|
||||
sorter = g_object_new (GTK_TYPE_CUSTOM_SORTER, NULL);
|
||||
|
||||
sorter->sort_func = sort_func;
|
||||
sorter->user_data = user_data;
|
||||
sorter->user_destroy = user_destroy;
|
||||
|
||||
return GTK_SORTER (sorter);
|
||||
}
|
||||
43
gtk/gtkcustomsorter.h
Normal file
43
gtk/gtkcustomsorter.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright © 2019 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_CUSTOM_SORTER_H__
|
||||
#define __GTK_CUSTOM_SORTER_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkexpression.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CUSTOM_SORTER (gtk_custom_sorter_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCustomSorter, gtk_custom_sorter, GTK, CUSTOM_SORTER, GtkSorter)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSorter * gtk_custom_sorter_new (GCompareDataFunc sort_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CUSTOM_SORTER_H__ */
|
||||
681
gtk/gtkdirectorylist.c
Normal file
681
gtk/gtkdirectorylist.c
Normal file
@@ -0,0 +1,681 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkdirectorylist.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkdirectorylist
|
||||
* @title: GtkDirectoryList
|
||||
* @short_description: A list model for directory listings
|
||||
* @see_also: #GListModel, g_file_enumerate_children()
|
||||
*
|
||||
* #GtkDirectoryList is a list model that wraps g_file_enumerate_children_async().
|
||||
* It presents a #GListModel and fills it asynchronously with the #GFileInfos
|
||||
* returned from that function.
|
||||
*
|
||||
* Enumeration will start automatically when a the GtkDirectoryList:file property
|
||||
* is set.
|
||||
*
|
||||
* While the #GtkDirectoryList is being filled, the GtkDirectoryList:loading
|
||||
* property will be set to %TRUE. You can listen to that property if you want
|
||||
* to show information like a #GtkSpinner or a "Loading..." text.
|
||||
* If loading fails at any point, the GtkDirectoryList:error property will be set
|
||||
* to give more indication about the failure.
|
||||
*
|
||||
* The #GFileInfos returned from a #GtkDirectoryList have the "standard::file"
|
||||
* attribute set to the #GFile they refer to. This way you can get at the file
|
||||
* that is referred to in the same way you would via g_file_enumerator_get_child().
|
||||
* This means you do not need access to the #GtkDirectoryList but can access
|
||||
* the #GFile directly from the #GFileInfo when operating with a #GtkListView or
|
||||
* similar.
|
||||
*/
|
||||
|
||||
/* random number that everyone else seems to use, too */
|
||||
#define FILES_PER_QUERY 100
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ATTRIBUTES,
|
||||
PROP_ERROR,
|
||||
PROP_FILE,
|
||||
PROP_IO_PRIORITY,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_LOADING,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
struct _GtkDirectoryList
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
char *attributes;
|
||||
int io_priority;
|
||||
GFile *file;
|
||||
|
||||
GCancellable *cancellable;
|
||||
GError *error; /* Error while loading */
|
||||
GSequence *items; /* Use GPtrArray or GListStore here? */
|
||||
};
|
||||
|
||||
struct _GtkDirectoryListClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static GType
|
||||
gtk_directory_list_get_item_type (GListModel *list)
|
||||
{
|
||||
return G_TYPE_FILE_INFO;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_directory_list_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (list);
|
||||
|
||||
return g_sequence_get_length (self->items);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_directory_list_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (list);
|
||||
GSequenceIter *iter;
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (self->items, position);
|
||||
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
return NULL;
|
||||
else
|
||||
return g_object_ref (g_sequence_get (iter));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_directory_list_get_item_type;
|
||||
iface->get_n_items = gtk_directory_list_get_n_items;
|
||||
iface->get_item = gtk_directory_list_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkDirectoryList, gtk_directory_list, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_directory_list_model_init))
|
||||
|
||||
static void
|
||||
gtk_directory_list_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ATTRIBUTES:
|
||||
gtk_directory_list_set_attributes (self, g_value_get_string (value));
|
||||
break;
|
||||
|
||||
case PROP_FILE:
|
||||
gtk_directory_list_set_file (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_IO_PRIORITY:
|
||||
gtk_directory_list_set_io_priority (self, g_value_get_int (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ATTRIBUTES:
|
||||
g_value_set_string (value, self->attributes);
|
||||
break;
|
||||
|
||||
case PROP_ERROR:
|
||||
g_value_set_boxed (value, self->error);
|
||||
break;
|
||||
|
||||
case PROP_FILE:
|
||||
g_value_set_object (value, self->file);
|
||||
break;
|
||||
|
||||
case PROP_IO_PRIORITY:
|
||||
g_value_set_int (value, self->io_priority);
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
g_value_set_gtype (value, G_TYPE_FILE_INFO);
|
||||
break;
|
||||
|
||||
case PROP_LOADING:
|
||||
g_value_set_boolean (value, gtk_directory_list_is_loading (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_directory_list_stop_loading (GtkDirectoryList *self)
|
||||
{
|
||||
if (self->cancellable == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_dispose (GObject *object)
|
||||
{
|
||||
GtkDirectoryList *self = GTK_DIRECTORY_LIST (object);
|
||||
|
||||
gtk_directory_list_stop_loading (self);
|
||||
|
||||
g_clear_object (&self->file);
|
||||
g_clear_pointer (&self->attributes, g_free);
|
||||
|
||||
g_clear_error (&self->error);
|
||||
g_clear_pointer (&self->items, g_sequence_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_directory_list_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_class_init (GtkDirectoryListClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
gobject_class->set_property = gtk_directory_list_set_property;
|
||||
gobject_class->get_property = gtk_directory_list_get_property;
|
||||
gobject_class->dispose = gtk_directory_list_dispose;
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:attributes:
|
||||
*
|
||||
* The attributes to query
|
||||
*/
|
||||
properties[PROP_ATTRIBUTES] =
|
||||
g_param_spec_string ("attributes",
|
||||
P_("attributes"),
|
||||
P_("Attributes to query"),
|
||||
NULL,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:error:
|
||||
*
|
||||
* Error encountered while loading files
|
||||
*/
|
||||
properties[PROP_ERROR] =
|
||||
g_param_spec_boxed ("error",
|
||||
P_("error"),
|
||||
P_("Error encountered while loading files"),
|
||||
G_TYPE_ERROR,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:file:
|
||||
*
|
||||
* File to query
|
||||
*/
|
||||
properties[PROP_FILE] =
|
||||
g_param_spec_object ("file",
|
||||
P_("File"),
|
||||
P_("The file to query"),
|
||||
G_TYPE_FILE,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:io-priority:
|
||||
*
|
||||
* Priority used when loading
|
||||
*/
|
||||
properties[PROP_IO_PRIORITY] =
|
||||
g_param_spec_int ("io-priority",
|
||||
P_("IO priority"),
|
||||
P_("Priority used when loading"),
|
||||
-G_MAXINT, G_MAXINT, G_PRIORITY_DEFAULT,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:item-type:
|
||||
*
|
||||
* The #GType for elements of this object
|
||||
*/
|
||||
properties[PROP_ITEM_TYPE] =
|
||||
g_param_spec_gtype ("item-type",
|
||||
P_("Item type"),
|
||||
P_("The type of elements of this object"),
|
||||
G_TYPE_FILE_INFO,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkDirectoryList:loading:
|
||||
*
|
||||
* %TRUE if files are being loaded
|
||||
*/
|
||||
properties[PROP_LOADING] =
|
||||
g_param_spec_boolean ("loading",
|
||||
P_("loading"),
|
||||
P_("TRUE if files are being loaded"),
|
||||
FALSE,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_init (GtkDirectoryList *self)
|
||||
{
|
||||
self->items = g_sequence_new (g_object_unref);
|
||||
self->io_priority = G_PRIORITY_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_new:
|
||||
* @file: (allow-none): The file to query
|
||||
* @attributes: (allow-none): The attributes to query with
|
||||
*
|
||||
* Creates a new #GtkDirectoryList querying the given @file with the given
|
||||
* @attributes.
|
||||
*
|
||||
* Returns: a new #GtkDirectoryList
|
||||
**/
|
||||
GtkDirectoryList *
|
||||
gtk_directory_list_new (const char *attributes,
|
||||
GFile *file)
|
||||
|
||||
{
|
||||
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_DIRECTORY_LIST,
|
||||
"attributes", attributes,
|
||||
"file", file,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_clear_items (GtkDirectoryList *self)
|
||||
{
|
||||
guint n_items;
|
||||
|
||||
n_items = g_sequence_get_length (self->items);
|
||||
if (n_items > 0)
|
||||
{
|
||||
g_sequence_remove_range (g_sequence_get_begin_iter (self->items),
|
||||
g_sequence_get_end_iter (self->items));
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, 0);
|
||||
}
|
||||
|
||||
if (self->error)
|
||||
{
|
||||
g_clear_error (&self->error);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_enumerator_closed_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_file_enumerator_close_finish (G_FILE_ENUMERATOR (source), res, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_got_files_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkDirectoryList *self = user_data; /* invalid if cancelled */
|
||||
GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source);
|
||||
GError *error = NULL;
|
||||
GList *l, *files;
|
||||
guint n;
|
||||
|
||||
files = g_file_enumerator_next_files_finish (enumerator, res, &error);
|
||||
|
||||
if (files == NULL)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
{
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_file_enumerator_close_async (enumerator,
|
||||
self->io_priority,
|
||||
NULL,
|
||||
gtk_directory_list_enumerator_closed_cb,
|
||||
NULL);
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
g_clear_object (&self->cancellable);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||
|
||||
if (error)
|
||||
{
|
||||
self->error = error;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
|
||||
}
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
return;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
for (l = files; l; l = l->next)
|
||||
{
|
||||
GFileInfo *info;
|
||||
GFile *file;
|
||||
|
||||
info = l->data;
|
||||
file = g_file_enumerator_get_child (enumerator, info);
|
||||
g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
|
||||
g_object_unref (file);
|
||||
g_sequence_append (self->items, info);
|
||||
n++;
|
||||
}
|
||||
g_list_free (files);
|
||||
|
||||
g_file_enumerator_next_files_async (enumerator,
|
||||
g_file_is_native (self->file) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
|
||||
self->io_priority,
|
||||
self->cancellable,
|
||||
gtk_directory_list_got_files_cb,
|
||||
self);
|
||||
|
||||
if (n > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), g_sequence_get_length (self->items) - n, 0, n);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_got_enumerator_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkDirectoryList *self = user_data; /* invalid if cancelled */
|
||||
GFile *file = G_FILE (source);
|
||||
GFileEnumerator *enumerator;
|
||||
GError *error = NULL;
|
||||
|
||||
enumerator = g_file_enumerate_children_finish (file, res, &error);
|
||||
if (enumerator == NULL)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
{
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
self->error = error;
|
||||
g_clear_object (&self->cancellable);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ERROR]);
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
return;
|
||||
}
|
||||
|
||||
g_file_enumerator_next_files_async (enumerator,
|
||||
g_file_is_native (file) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
|
||||
self->io_priority,
|
||||
self->cancellable,
|
||||
gtk_directory_list_got_files_cb,
|
||||
self);
|
||||
g_object_unref (enumerator);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_directory_list_start_loading (GtkDirectoryList *self)
|
||||
{
|
||||
gboolean was_loading;
|
||||
|
||||
was_loading = gtk_directory_list_stop_loading (self);
|
||||
gtk_directory_list_clear_items (self);
|
||||
|
||||
if (self->file == NULL)
|
||||
{
|
||||
if (was_loading)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||
return;
|
||||
}
|
||||
|
||||
self->cancellable = g_cancellable_new ();
|
||||
g_file_enumerate_children_async (self->file,
|
||||
self->attributes,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
self->io_priority,
|
||||
self->cancellable,
|
||||
gtk_directory_list_got_enumerator_cb,
|
||||
self);
|
||||
|
||||
if (!was_loading)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_set_file:
|
||||
* @self: a #GtkDirectoryList
|
||||
* @file: (allow-none): the #GFile to be enumerated
|
||||
*
|
||||
* Sets the @file to be enumerated and starts the enumeration.
|
||||
*
|
||||
* If @file is %NULL, the result will be an empty list.
|
||||
*/
|
||||
void
|
||||
gtk_directory_list_set_file (GtkDirectoryList *self,
|
||||
GFile *file)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
|
||||
g_return_if_fail (file == NULL || G_IS_FILE (file));
|
||||
|
||||
if (self->file == file ||
|
||||
(self->file && file && g_file_equal (self->file, file)))
|
||||
return;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
g_set_object (&self->file, file);
|
||||
|
||||
gtk_directory_list_start_loading (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILE]);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_get_file:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Gets the file whose children are currently enumerated.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The file whose children are enumerated
|
||||
**/
|
||||
GFile *
|
||||
gtk_directory_list_get_file (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), NULL);
|
||||
|
||||
return self->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_set_attributes:
|
||||
* @self: a #GtkDirectoryList
|
||||
* @attributes: (allow-none): the attributes to enumerate
|
||||
*
|
||||
* Sets the @attributes to be enumerated and starts the enumeration.
|
||||
*
|
||||
* If @attributes is %NULL, no attributes will be queried, but a list
|
||||
* of #GFileInfos will still be created.
|
||||
*/
|
||||
void
|
||||
gtk_directory_list_set_attributes (GtkDirectoryList *self,
|
||||
const char *attributes)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
|
||||
|
||||
if (self->attributes == attributes)
|
||||
return;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
g_free (self->attributes);
|
||||
self->attributes = g_strdup (attributes);
|
||||
|
||||
gtk_directory_list_start_loading (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ATTRIBUTES]);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_get_attributes:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Gets the attributes queried on the children.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The queried attributes
|
||||
*/
|
||||
const char *
|
||||
gtk_directory_list_get_attributes (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), NULL);
|
||||
|
||||
return self->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_set_io_priority:
|
||||
* @self: a #GtkDirectoryList
|
||||
* @io_priority: IO priority to use
|
||||
*
|
||||
* Sets the IO priority to use while loading directories.
|
||||
*
|
||||
* Setting the priority while @self is loading will reprioritize the
|
||||
* ongoing load as soon as possible.
|
||||
*
|
||||
* The default IO priority is %G_PRIORITY_DEFAULT, which is higher than
|
||||
* the GTK redraw priority. If you are loading a lot of directories in
|
||||
* parrallel, lowering it to something like %G_PRIORITY_DEFAULT_IDLE
|
||||
* may increase responsiveness.
|
||||
*/
|
||||
void
|
||||
gtk_directory_list_set_io_priority (GtkDirectoryList *self,
|
||||
int io_priority)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DIRECTORY_LIST (self));
|
||||
|
||||
if (self->io_priority == io_priority)
|
||||
return;
|
||||
|
||||
self->io_priority = io_priority;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_IO_PRIORITY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_get_io_priority:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Gets the IO priority set via gtk_directory_list_set_io_priority().
|
||||
*
|
||||
* Returns: The IO priority.
|
||||
*/
|
||||
int
|
||||
gtk_directory_list_get_io_priority (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), G_PRIORITY_DEFAULT);
|
||||
|
||||
return self->io_priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_is_loading:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Returns %TRUE if the children enumeration is currently in
|
||||
* progress.
|
||||
* Files will be added to @self from time to time while loading is
|
||||
* going on. The order in which are added is undefined and may change
|
||||
* inbetween runs.
|
||||
*
|
||||
* Returns: %TRUE if @self is loading
|
||||
*/
|
||||
gboolean
|
||||
gtk_directory_list_is_loading (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), FALSE);
|
||||
|
||||
return self->cancellable != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_directory_list_get_error:
|
||||
* @self: a #GtkDirectoryList
|
||||
*
|
||||
* Gets the loading error, if any.
|
||||
*
|
||||
* If an error occurs during the loading process, the loading process
|
||||
* will finish and this property allows querying the error that happened.
|
||||
* This error will persist until a file is loaded again.
|
||||
*
|
||||
* An error being set does not mean that no files were loaded, and all
|
||||
* successfully queried files will remain in the list.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The loading error or %NULL if
|
||||
* loading finished successfully.
|
||||
*/
|
||||
const GError *
|
||||
gtk_directory_list_get_error (GtkDirectoryList *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DIRECTORY_LIST (self), FALSE);
|
||||
|
||||
return self->error;
|
||||
}
|
||||
|
||||
67
gtk/gtkdirectorylist.h
Normal file
67
gtk/gtkdirectorylist.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_DIRECTORY_LIST_H__
|
||||
#define __GTK_DIRECTORY_LIST_H__
|
||||
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gio/gio.h>
|
||||
/* for GDK_AVAILABLE_IN_ALL */
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_DIRECTORY_LIST (gtk_directory_list_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkDirectoryList, gtk_directory_list, GTK, DIRECTORY_LIST, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkDirectoryList * gtk_directory_list_new (const char *attributes,
|
||||
GFile *file);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_directory_list_set_file (GtkDirectoryList *self,
|
||||
GFile *file);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GFile * gtk_directory_list_get_file (GtkDirectoryList *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_directory_list_set_attributes (GtkDirectoryList *self,
|
||||
const char *attributes);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_directory_list_get_attributes (GtkDirectoryList *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_directory_list_set_io_priority (GtkDirectoryList *self,
|
||||
int io_priority);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gtk_directory_list_get_io_priority (GtkDirectoryList *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_directory_list_is_loading (GtkDirectoryList *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GError * gtk_directory_list_get_error (GtkDirectoryList *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_DIRECTORY_LIST_H__ */
|
||||
469
gtk/gtkdropdown.c
Normal file
469
gtk/gtkdropdown.c
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright © 2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkdropdown.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistview.h"
|
||||
#include "gtklistitemfactory.h"
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
#include "gtkpopover.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtksingleselection.h"
|
||||
#include "gtknoselection.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
#include "gtkstylecontext.h"
|
||||
#include "gtkiconprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtknative.h"
|
||||
#include "gtktogglebutton.h"
|
||||
|
||||
|
||||
struct _GtkDropDown
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GtkListItemFactory *factory;
|
||||
GListModel *model;
|
||||
GListModel *selection;
|
||||
GListModel *popup_selection;
|
||||
|
||||
GtkWidget *popup;
|
||||
GtkWidget *button;
|
||||
|
||||
GtkWidget *popup_list;
|
||||
GtkWidget *button_item;
|
||||
|
||||
guint selected;
|
||||
};
|
||||
|
||||
struct _GtkDropDownClass
|
||||
{
|
||||
GtkDropDownClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_MODEL,
|
||||
PROP_SELECTED,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkDropDown, gtk_drop_down, GTK_TYPE_WIDGET)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
button_toggled (GtkWidget *widget,
|
||||
gpointer data)
|
||||
{
|
||||
GtkDropDown *self = data;
|
||||
|
||||
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
|
||||
gtk_popover_popup (GTK_POPOVER (self->popup));
|
||||
else
|
||||
gtk_popover_popdown (GTK_POPOVER (self->popup));
|
||||
}
|
||||
|
||||
static void
|
||||
row_activated (GtkListView *listview,
|
||||
guint position,
|
||||
gpointer data)
|
||||
{
|
||||
GtkDropDown *self = data;
|
||||
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), FALSE);
|
||||
gtk_popover_popdown (GTK_POPOVER (self->popup));
|
||||
|
||||
gtk_drop_down_set_selected (self, gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (self->popup_selection)));
|
||||
}
|
||||
|
||||
static void
|
||||
selection_changed (GtkSelectionModel *selection,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gpointer data)
|
||||
{
|
||||
GtkDropDown *self = data;
|
||||
guint selected;
|
||||
gpointer item;
|
||||
|
||||
selected = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (self->selection));
|
||||
item = gtk_single_selection_get_selected_item (GTK_SINGLE_SELECTION (self->selection));
|
||||
gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (self->button_item), selected, item, FALSE);
|
||||
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (self->popup_selection), selected);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drop_down_dispose (GObject *object)
|
||||
{
|
||||
GtkDropDown *self = GTK_DROP_DOWN (object);
|
||||
|
||||
g_clear_pointer (&self->popup, gtk_widget_unparent);
|
||||
g_clear_pointer (&self->button, gtk_widget_unparent);
|
||||
|
||||
g_clear_object (&self->model);
|
||||
if (self->selection)
|
||||
g_signal_handlers_disconnect_by_func (self->selection, selection_changed, self);
|
||||
g_clear_object (&self->selection);
|
||||
g_clear_object (&self->popup_selection);
|
||||
|
||||
G_OBJECT_CLASS (gtk_drop_down_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drop_down_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDropDown *self = GTK_DROP_DOWN (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, self->factory);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
g_value_set_uint (value, gtk_drop_down_get_selected (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drop_down_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkDropDown *self = GTK_DROP_DOWN (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_drop_down_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_drop_down_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
gtk_drop_down_set_selected (self, g_value_get_uint (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drop_down_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkDropDown *self = GTK_DROP_DOWN (widget);
|
||||
|
||||
gtk_widget_measure (self->button,
|
||||
orientation,
|
||||
size,
|
||||
minimum, natural,
|
||||
minimum_baseline, natural_baseline);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drop_down_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkDropDown *self = GTK_DROP_DOWN (widget);
|
||||
|
||||
gtk_widget_size_allocate (self->button, &(GtkAllocation) { 0, 0, width, height }, baseline);
|
||||
|
||||
gtk_native_check_resize (GTK_NATIVE (self->popup));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_drop_down_class_init (GtkDropDownClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_drop_down_dispose;
|
||||
gobject_class->get_property = gtk_drop_down_get_property;
|
||||
gobject_class->set_property = gtk_drop_down_set_property;
|
||||
|
||||
widget_class->measure = gtk_drop_down_measure;
|
||||
widget_class->size_allocate = gtk_drop_down_size_allocate;
|
||||
|
||||
/**
|
||||
* GtkDropDown:factory:
|
||||
*
|
||||
* Factory for populating list items
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
P_("Factory"),
|
||||
P_("Factory for populating list items"),
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkDropDown:model:
|
||||
*
|
||||
* Model for the items displayed
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("Model for the items displayed"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_SELECTED] =
|
||||
g_param_spec_uint ("selected",
|
||||
P_("Selected"),
|
||||
P_("Position of the selected item"),
|
||||
0, G_MAXUINT, GTK_INVALID_LIST_POSITION,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkdropdown.ui");
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkDropDown, button);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkDropDown, popup);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkDropDown, button_item);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkDropDown, popup_list);
|
||||
gtk_widget_class_bind_template_callback (widget_class, row_activated);
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, button_toggled);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("combobox"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_drop_down_init (GtkDropDown *self)
|
||||
{
|
||||
g_type_ensure (GTK_TYPE_ICON);
|
||||
g_type_ensure (GTK_TYPE_LIST_ITEM_WIDGET);
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
|
||||
self->selected = GTK_INVALID_LIST_POSITION;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_drop_down_new:
|
||||
*
|
||||
* Creates a new empty #GtkDropDown.
|
||||
*
|
||||
* You most likely want to call gtk_drop_down_set_factory() to
|
||||
* set up a way to map its items to widgets and gtk_drop_down_set_model()
|
||||
* to set a model to provide items next.
|
||||
*
|
||||
* Returns: a new #GtkDropDown
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_drop_down_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_DROP_DOWN, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_drop_down_new_with_factory:
|
||||
* @factory: (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new #GtkDropDown that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* You most likely want to call gtk_drop_down_set_model() to set
|
||||
* a model next.
|
||||
*
|
||||
* Returns: a new #GtkDropDown using the given @factory
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_drop_down_new_with_factory (GtkListItemFactory *factory)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_DROP_DOWN,
|
||||
"factory", factory,
|
||||
NULL);
|
||||
|
||||
g_object_unref (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_drop_down_get_model:
|
||||
* @self: a #GtkDropDown
|
||||
*
|
||||
* Gets the model that's currently used to read the items displayed.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
**/
|
||||
GListModel *
|
||||
gtk_drop_down_get_model (GtkDropDown *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DROP_DOWN (self), NULL);
|
||||
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_drop_down_set_model:
|
||||
* @self: a #GtkDropDown
|
||||
* @model: (allow-none) (transfer none): the model to use or %NULL for none
|
||||
*
|
||||
* Sets the #GListModel to use.
|
||||
*/
|
||||
void
|
||||
gtk_drop_down_set_model (GtkDropDown *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DROP_DOWN (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (!g_set_object (&self->model, model))
|
||||
return;
|
||||
|
||||
if (model == NULL)
|
||||
{
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (self->popup_list), NULL);
|
||||
|
||||
if (self->selection)
|
||||
g_signal_handlers_disconnect_by_func (self->selection, selection_changed, self);
|
||||
|
||||
g_clear_object (&self->selection);
|
||||
g_clear_object (&self->popup_selection);
|
||||
}
|
||||
else
|
||||
{
|
||||
GListModel *selection;
|
||||
|
||||
selection = G_LIST_MODEL (gtk_single_selection_new (model));
|
||||
g_set_object (&self->popup_selection, selection);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (self->popup_list), selection);
|
||||
g_object_unref (selection);
|
||||
|
||||
if (GTK_IS_SINGLE_SELECTION (model))
|
||||
selection = g_object_ref (model);
|
||||
else
|
||||
selection = G_LIST_MODEL (gtk_single_selection_new (model));
|
||||
g_set_object (&self->selection, selection);
|
||||
g_object_unref (selection);
|
||||
|
||||
g_signal_connect (self->selection, "selection-changed", G_CALLBACK (selection_changed), self);
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (self->selection), self->selected);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_drop_down_get_factory:
|
||||
* @self: a #GtkDropDown
|
||||
*
|
||||
* Gets the factory that's currently used to populate list items.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
**/
|
||||
GtkListItemFactory *
|
||||
gtk_drop_down_get_factory (GtkDropDown *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DROP_DOWN (self), NULL);
|
||||
|
||||
return self->factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_drop_down_set_factory:
|
||||
* @self: a #GtkDropDown
|
||||
* @factory: (allow-none) (transfer none): the factory to use or %NULL for none
|
||||
*
|
||||
* Sets the #GtkListItemFactory to use for populating list items.
|
||||
**/
|
||||
void
|
||||
gtk_drop_down_set_factory (GtkDropDown *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DROP_DOWN (self));
|
||||
g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
if (!g_set_object (&self->factory, factory))
|
||||
return;
|
||||
|
||||
gtk_list_view_set_factory (GTK_LIST_VIEW (self->popup_list), factory);
|
||||
gtk_list_item_widget_set_factory (GTK_LIST_ITEM_WIDGET (self->button_item), factory);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_drop_down_set_selected (GtkDropDown *self,
|
||||
guint position)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_DROP_DOWN (self));
|
||||
|
||||
if (self->selection == NULL)
|
||||
return;
|
||||
|
||||
if (gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (self->selection)) == position)
|
||||
return;
|
||||
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (self->selection), position);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_drop_down_get_selected (GtkDropDown *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_DROP_DOWN (self), GTK_INVALID_LIST_POSITION);
|
||||
|
||||
if (self->selection == NULL)
|
||||
return GTK_INVALID_LIST_POSITION;
|
||||
|
||||
return gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (self->selection));
|
||||
}
|
||||
57
gtk/gtkdropdown.h
Normal file
57
gtk/gtkdropdown.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright © 2018 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen
|
||||
*/
|
||||
|
||||
#ifndef __GTK_DROP_DOWN_H__
|
||||
#define __GTK_DROP_DOWN_H__
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_DROP_DOWN (gtk_drop_down_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkDropDown, gtk_drop_down, GTK, DROP_DOWN, GtkWidget)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_drop_down_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_drop_down_new_with_factory (GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_drop_down_get_model (GtkDropDown *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_drop_down_set_model (GtkDropDown *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_drop_down_set_factory (GtkDropDown *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory *
|
||||
gtk_drop_down_get_factory (GtkDropDown *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_drop_down_set_selected (GtkDropDown *self,
|
||||
guint position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_drop_down_get_selected (GtkDropDown *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_DROP_DOWN_H__ */
|
||||
1400
gtk/gtkexpression.c
Normal file
1400
gtk/gtkexpression.c
Normal file
File diff suppressed because it is too large
Load Diff
102
gtk/gtkexpression.h
Normal file
102
gtk/gtkexpression.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_EXPRESSION_H__
|
||||
#define __GTK_EXPRESSION_H__
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkExpression GtkExpression;
|
||||
typedef struct _GtkExpressionWatch GtkExpressionWatch;
|
||||
|
||||
typedef void (* GtkExpressionNotify) (gpointer user_data);
|
||||
|
||||
#define GTK_IS_EXPRESSION(expr) ((expr) != NULL)
|
||||
|
||||
#define GTK_TYPE_EXPRESSION (gtk_expression_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_expression_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_expression_ref (GtkExpression *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_expression_unref (GtkExpression *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_expression_get_value_type (GtkExpression *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_expression_is_static (GtkExpression *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_expression_evaluate (GtkExpression *self,
|
||||
gpointer this_,
|
||||
GValue *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpressionWatch * gtk_expression_watch (GtkExpression *self,
|
||||
gpointer this_,
|
||||
GtkExpressionNotify notify,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpressionWatch * gtk_expression_bind (GtkExpression *self,
|
||||
gpointer object,
|
||||
gpointer this_,
|
||||
const char * property);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpressionWatch * gtk_expression_watch_ref (GtkExpressionWatch *watch);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_expression_watch_unref (GtkExpressionWatch *watch);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_expression_watch_evaluate (GtkExpressionWatch *watch,
|
||||
GValue *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_expression_watch_unwatch (GtkExpressionWatch *watch);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_property_expression_new (GType this_type,
|
||||
GtkExpression *expression,
|
||||
const char *property_name);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_constant_expression_new (GType value_type,
|
||||
...);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_constant_expression_new_for_value (const GValue *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_object_expression_new (GObject *object);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_closure_expression_new (GType value_type,
|
||||
GClosure *closure,
|
||||
guint n_params,
|
||||
GtkExpression **params);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkExpression * gtk_cclosure_expression_new (GType value_type,
|
||||
GClosureMarshal marshal,
|
||||
guint n_params,
|
||||
GtkExpression **params,
|
||||
GCallback callback_func,
|
||||
gpointer user_data,
|
||||
GClosureNotify user_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_EXPRESSION_H__ */
|
||||
181
gtk/gtkfilter.c
Normal file
181
gtk/gtkfilter.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkfilter.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkfilter
|
||||
* @Title: GtkFilter
|
||||
* @Short_description: Filtering items in GTK
|
||||
* @See_also: #GtkFilerListModel
|
||||
*
|
||||
* #GtkFilter is the way to describe filters to be used in #GtkFilterListModel.
|
||||
*
|
||||
* The model will use a filter to determine if it should filter items or not
|
||||
* by calling gtk_filter_match() for each item and only keeping the ones
|
||||
* visible that the function returns %TRUE for.
|
||||
*
|
||||
* Filters may change what items they match through their lifetime. In that
|
||||
* case, they can call gtk_filter_changed() which will emit the #GtkFilter:changed
|
||||
* signal to notify that previous filter results are no longer valid and that
|
||||
* items should be checked via gtk_filter_match() again.
|
||||
*
|
||||
* GTK provides various premade filter implementations for common filtering
|
||||
* operations. These filters often include properties that can be linked to
|
||||
* various widgets to easily allow searches.
|
||||
*
|
||||
* However, in particular for large lists or complex search methods, it is
|
||||
* also possible to subclass #GtkFilter and provide one's own filter.
|
||||
*/
|
||||
|
||||
enum {
|
||||
CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkFilter, gtk_filter, G_TYPE_OBJECT)
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gboolean
|
||||
gtk_filter_default_match (GtkFilter *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_critical ("Filter of type '%s' does not implement GtkFilter::match", G_OBJECT_TYPE_NAME (self));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GtkFilterMatch
|
||||
gtk_filter_default_get_strictness (GtkFilter *self)
|
||||
{
|
||||
return GTK_FILTER_MATCH_SOME;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_class_init (GtkFilterClass *class)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||
|
||||
class->match = gtk_filter_default_match;
|
||||
class->get_strictness = gtk_filter_default_get_strictness;
|
||||
|
||||
/**
|
||||
* GtkFilter:changed:
|
||||
* @self: The #GtkFilter
|
||||
* @change: how the filter changed
|
||||
*
|
||||
* This signal is emitted whenever the filter changed. Users of the filter
|
||||
* should then check items again via gtk_filter_match().
|
||||
*
|
||||
* #GtkFilterListModel handles this signal automatically.
|
||||
*
|
||||
* Depending on the @change parameter, not all items need to be changed, but
|
||||
* only some. Refer to the #GtkFilterChange documentation for details.
|
||||
*/
|
||||
signals[CHANGED] =
|
||||
g_signal_new (I_("changed"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__ENUM,
|
||||
G_TYPE_NONE, 1,
|
||||
GTK_TYPE_FILTER_CHANGE);
|
||||
g_signal_set_va_marshaller (signals[CHANGED],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
g_cclosure_marshal_VOID__ENUMv);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_init (GtkFilter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_match:
|
||||
* @self: a #GtkFilter
|
||||
* @item: (type GObject) (transfer none): The item to check
|
||||
*
|
||||
* Checks if the given @item is matched by the filter or not.
|
||||
*
|
||||
* Returns: %TRUE if the filter matches the item and a filter model should
|
||||
* keep it, %FALSE if not.
|
||||
*/
|
||||
gboolean
|
||||
gtk_filter_match (GtkFilter *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER (self), FALSE);
|
||||
g_return_val_if_fail (item != NULL, FALSE);
|
||||
|
||||
return GTK_FILTER_GET_CLASS (self)->match (self, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_get_strictness:
|
||||
* @self: a #GtkFilter
|
||||
*
|
||||
* Gets the known strictness of @filters. If the strictness is not known,
|
||||
* %GTK_FILTER_MATCH_SOME is returned.
|
||||
*
|
||||
* This value may change after emission of the GtkFilter:changed signal.
|
||||
*
|
||||
* This function is meant purely for optimization purposes, filters can
|
||||
* choose to omit implementing it, but #GtkFilterListModel uses it.
|
||||
*
|
||||
* Returns: the strictness of @self
|
||||
**/
|
||||
GtkFilterMatch
|
||||
gtk_filter_get_strictness (GtkFilter *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER (self), GTK_FILTER_MATCH_SOME);
|
||||
|
||||
return GTK_FILTER_GET_CLASS (self)->get_strictness (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_changed:
|
||||
* @self: a #GtkFilter
|
||||
* @change: How the filter changed
|
||||
*
|
||||
* Emits the #GtkFilter:changed signal to notify all users of the filter that
|
||||
* the filter changed. Users of the filter should then check items again via
|
||||
* gtk_filter_match().
|
||||
*
|
||||
* Depending on the @change parameter, not all items need to be changed, but
|
||||
* only some. Refer to the #GtkFilterChange documentation for details.
|
||||
*
|
||||
* This function is intended for implementors of #GtkFilter subclasses and
|
||||
* should not be called from other functions.
|
||||
*/
|
||||
void
|
||||
gtk_filter_changed (GtkFilter *self,
|
||||
GtkFilterChange change)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_FILTER (self));
|
||||
|
||||
g_signal_emit (self, signals[CHANGED], 0, change);
|
||||
}
|
||||
|
||||
129
gtk/gtkfilter.h
Normal file
129
gtk/gtkfilter.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_FILTER_H__
|
||||
#define __GTK_FILTER_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GtkFilterMatch:
|
||||
* @GTK_FILTER_MATCH_SOME: The filter matches some items,
|
||||
* gtk_filter_match() may return %TRUE or %FALSE
|
||||
* @GTK_FILTER_MATCH_NONE: The filter does not match any item,
|
||||
* gtk_filter_match() will always return %FALSE.
|
||||
* @GTK_FILTER_MATCH_ALL: The filter matches all items,
|
||||
* gtk_filter_match() will alays return %TRUE.
|
||||
*
|
||||
* Describes the known strictness of a filter.
|
||||
*
|
||||
* Note that for filters where the strictness is not known,
|
||||
* %@GTK_FILTER_MATCH_SOME is always an acceptable value,
|
||||
* even if a filter does match all or no items.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_FILTER_MATCH_SOME = 0,
|
||||
GTK_FILTER_MATCH_NONE,
|
||||
GTK_FILTER_MATCH_ALL
|
||||
} GtkFilterMatch;
|
||||
|
||||
/**
|
||||
* GtkFilterChange:
|
||||
* @GTK_FILTER_CHANGE_DIFFERENT: The filter change cannot be
|
||||
* described with any of the other enumeration values.
|
||||
* @GTK_FILTER_CHANGE_MATCH_ALL: The filter now matches every
|
||||
* item: gtk_filter_get_strictness() will now return
|
||||
* %GTK_FILTER_MATCH_ALL
|
||||
* @GTK_FILTER_CHANGE_LESS_STRICT: The filter is less strict than
|
||||
* it was before: All items that it used to return %TRUE for
|
||||
* still return %TRUE, others now may, too.
|
||||
* @GTK_FILTER_CHANGE_MORE_STRICT: The filter is more strict than
|
||||
* it was before: All items that it used to return %FALSE for
|
||||
* still return %FALSE, others now may, too.
|
||||
* @GTK_FILTER_CHANGE_MATCH_NONE: The filter now matches no item:
|
||||
* gtk_filter_get_strictness() will now return
|
||||
* %GTK_FILTER_MATCH_NONE
|
||||
*
|
||||
* Describes changes in a filter in more detail and allows objects
|
||||
* using the filter to optimize refiltering items.
|
||||
*
|
||||
* If you are writing an implementation and are not sure which
|
||||
* value to pass, @GTK_FILTER_CHANGE_DIFFERENT is always a correct
|
||||
* choice.
|
||||
*/
|
||||
typedef enum {
|
||||
GTK_FILTER_CHANGE_DIFFERENT = 0,
|
||||
GTK_FILTER_CHANGE_MATCH_ALL,
|
||||
GTK_FILTER_CHANGE_LESS_STRICT,
|
||||
GTK_FILTER_CHANGE_MORE_STRICT,
|
||||
GTK_FILTER_CHANGE_MATCH_NONE
|
||||
} GtkFilterChange;
|
||||
|
||||
#define GTK_TYPE_FILTER (gtk_filter_get_type ())
|
||||
|
||||
/**
|
||||
* GtkFilter:
|
||||
*
|
||||
* The object describing a filter.
|
||||
*/
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_DERIVABLE_TYPE (GtkFilter, gtk_filter, GTK, FILTER, GObject)
|
||||
|
||||
struct _GtkFilterClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
gboolean (* match) (GtkFilter *self,
|
||||
gpointer item);
|
||||
|
||||
/* optional */
|
||||
GtkFilterMatch (* get_strictness) (GtkFilter *self);
|
||||
|
||||
/* Padding for future expansion */
|
||||
void (*_gtk_reserved1) (void);
|
||||
void (*_gtk_reserved2) (void);
|
||||
void (*_gtk_reserved3) (void);
|
||||
void (*_gtk_reserved4) (void);
|
||||
void (*_gtk_reserved5) (void);
|
||||
void (*_gtk_reserved6) (void);
|
||||
void (*_gtk_reserved7) (void);
|
||||
void (*_gtk_reserved8) (void);
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_filter_match (GtkFilter *filter,
|
||||
gpointer item);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterMatch gtk_filter_get_strictness (GtkFilter *filter);
|
||||
|
||||
/* for filter implementations */
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_changed (GtkFilter *filter,
|
||||
GtkFilterChange change);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FILTER_H__ */
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_HAS_FILTER,
|
||||
PROP_FILTER,
|
||||
PROP_ITEM_TYPE,
|
||||
PROP_MODEL,
|
||||
NUM_PROPERTIES
|
||||
@@ -65,11 +65,10 @@ struct _GtkFilterListModel
|
||||
|
||||
GType item_type;
|
||||
GListModel *model;
|
||||
GtkFilterListModelFilterFunc filter_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
GtkFilter *filter;
|
||||
GtkFilterMatch strictness;
|
||||
|
||||
GtkRbTree *items; /* NULL if filter_func == NULL */
|
||||
GtkRbTree *items; /* NULL if strictness != GTK_FILTER_MATCH_SOME */
|
||||
};
|
||||
|
||||
struct _GtkFilterListModelClass
|
||||
@@ -79,6 +78,33 @@ struct _GtkFilterListModelClass
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_augment (GtkRbTree *filter,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FilterNode *node = _node;
|
||||
FilterAugment *aug = _aug;
|
||||
|
||||
aug->n_items = 1;
|
||||
aug->n_visible = node->visible ? 1 : 0;
|
||||
|
||||
if (left)
|
||||
{
|
||||
FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_visible += left_aug->n_visible;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_visible += right_aug->n_visible;
|
||||
}
|
||||
}
|
||||
|
||||
static FilterNode *
|
||||
gtk_filter_list_model_get_nth_filtered (GtkRbTree *tree,
|
||||
guint position,
|
||||
@@ -180,11 +206,20 @@ gtk_filter_list_model_get_n_items (GListModel *list)
|
||||
FilterAugment *aug;
|
||||
FilterNode *node;
|
||||
|
||||
if (self->model == NULL)
|
||||
return 0;
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return 0;
|
||||
|
||||
if (!self->items)
|
||||
return g_list_model_get_n_items (self->model);
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
return g_list_model_get_n_items (self->model);
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
break;
|
||||
}
|
||||
|
||||
node = gtk_rb_tree_get_root (self->items);
|
||||
if (node == NULL)
|
||||
@@ -201,13 +236,22 @@ gtk_filter_list_model_get_item (GListModel *list,
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (list);
|
||||
guint unfiltered;
|
||||
|
||||
if (self->model == NULL)
|
||||
return NULL;
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return NULL;
|
||||
|
||||
if (self->items)
|
||||
gtk_filter_list_model_get_nth_filtered (self->items, position, &unfiltered);
|
||||
else
|
||||
unfiltered = position;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
unfiltered = position;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
gtk_filter_list_model_get_nth_filtered (self->items, position, &unfiltered);
|
||||
break;
|
||||
}
|
||||
|
||||
return g_list_model_get_item (self->model, unfiltered);
|
||||
}
|
||||
@@ -230,8 +274,11 @@ gtk_filter_list_model_run_filter (GtkFilterListModel *self,
|
||||
gpointer item;
|
||||
gboolean visible;
|
||||
|
||||
/* all other cases should have beeen optimized away */
|
||||
g_assert (self->strictness == GTK_FILTER_MATCH_SOME);
|
||||
|
||||
item = g_list_model_get_item (self->model, position);
|
||||
visible = self->filter_func (item, self->user_data);
|
||||
visible = gtk_filter_match (self->filter, item);
|
||||
g_object_unref (item);
|
||||
|
||||
return visible;
|
||||
@@ -269,10 +316,20 @@ gtk_filter_list_model_items_changed_cb (GListModel *model,
|
||||
FilterNode *node;
|
||||
guint i, filter_position, filter_removed, filter_added;
|
||||
|
||||
if (self->items == NULL)
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
return;
|
||||
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
return;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
break;
|
||||
}
|
||||
|
||||
node = gtk_filter_list_model_get_nth (self->items, position, &filter_position);
|
||||
@@ -303,6 +360,10 @@ gtk_filter_list_model_set_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_FILTER:
|
||||
gtk_filter_list_model_set_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
self->item_type = g_value_get_gtype (value);
|
||||
break;
|
||||
@@ -327,8 +388,8 @@ gtk_filter_list_model_get_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_HAS_FILTER:
|
||||
g_value_set_boolean (value, self->items != NULL);
|
||||
case PROP_FILTER:
|
||||
g_value_set_object (value, self->filter);
|
||||
break;
|
||||
|
||||
case PROP_ITEM_TYPE:
|
||||
@@ -357,17 +418,223 @@ gtk_filter_list_model_clear_model (GtkFilterListModel *self)
|
||||
gtk_rb_tree_remove_all (self->items);
|
||||
}
|
||||
|
||||
/*<private>
|
||||
* gtk_filter_list_model_find_filtered:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @start: (out) (caller-allocates): number of unfiltered items
|
||||
* at start of list
|
||||
* @end: (out) (caller-allocates): number of unfiltered items
|
||||
* at end of list
|
||||
* @n_items: (out) (caller-allocates): number of unfiltered items in
|
||||
* list
|
||||
*
|
||||
* Checks if elements in self->items are filtered out and returns
|
||||
* the range that they occupy.
|
||||
* This function is intended to be used for GListModel::items-changed
|
||||
* emissions, so it is called in an intermediate state for @self.
|
||||
*
|
||||
* Returns: %TRUE if elements are filtered out, %FALSE if none are
|
||||
**/
|
||||
static gboolean
|
||||
gtk_filter_list_model_find_filtered (GtkFilterListModel *self,
|
||||
guint *start,
|
||||
guint *end,
|
||||
guint *n_items)
|
||||
{
|
||||
FilterNode *root, *node, *tmp;
|
||||
FilterAugment *aug;
|
||||
|
||||
if (self->items == NULL || self->model == NULL)
|
||||
return FALSE;
|
||||
|
||||
root = gtk_rb_tree_get_root (self->items);
|
||||
if (root == NULL)
|
||||
return FALSE; /* empty parent model */
|
||||
|
||||
aug = gtk_rb_tree_get_augment (self->items, root);
|
||||
if (aug->n_items == aug->n_visible)
|
||||
return FALSE; /* all items visible */
|
||||
|
||||
/* find first filtered */
|
||||
*start = 0;
|
||||
*end = 0;
|
||||
*n_items = aug->n_visible;
|
||||
|
||||
node = root;
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_left (node);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_rb_tree_get_augment (self->items, tmp);
|
||||
if (aug->n_visible < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
*start += aug->n_items;
|
||||
}
|
||||
|
||||
if (!node->visible)
|
||||
break;
|
||||
|
||||
(*start)++;
|
||||
|
||||
node = gtk_rb_tree_node_get_right (node);
|
||||
}
|
||||
|
||||
/* find last filtered by doing everything the opposite way */
|
||||
node = root;
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_rb_tree_node_get_right (node);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_rb_tree_get_augment (self->items, tmp);
|
||||
if (aug->n_visible < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
continue;
|
||||
}
|
||||
*end += aug->n_items;
|
||||
}
|
||||
|
||||
if (!node->visible)
|
||||
break;
|
||||
|
||||
(*end)++;
|
||||
|
||||
node = gtk_rb_tree_node_get_left (node);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self);
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_update_strictness_and_refilter (GtkFilterListModel *self)
|
||||
{
|
||||
GtkFilterMatch new_strictness;
|
||||
|
||||
if (self->model == NULL)
|
||||
new_strictness = GTK_FILTER_MATCH_NONE;
|
||||
else if (self->filter == NULL)
|
||||
new_strictness = GTK_FILTER_MATCH_ALL;
|
||||
else
|
||||
new_strictness = gtk_filter_get_strictness (self->filter);
|
||||
|
||||
/* don't set self->strictness yet so get_n_items() and friends return old values */
|
||||
|
||||
switch (new_strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
{
|
||||
guint n_before = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
self->strictness = new_strictness;
|
||||
if (n_before > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_before, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
self->strictness = new_strictness;
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, g_list_model_get_n_items (self->model));
|
||||
break;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
self->strictness = new_strictness;
|
||||
break;
|
||||
default:
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
{
|
||||
guint start, end, n_before, n_after;
|
||||
self->strictness = new_strictness;
|
||||
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_before))
|
||||
{
|
||||
n_after = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
switch (self->strictness)
|
||||
{
|
||||
case GTK_FILTER_MATCH_NONE:
|
||||
{
|
||||
guint n_after;
|
||||
self->strictness = new_strictness;
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
n_after = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (self->model));
|
||||
if (n_after > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, 0, n_after);
|
||||
}
|
||||
break;
|
||||
case GTK_FILTER_MATCH_ALL:
|
||||
{
|
||||
guint start, end, n_before, n_after;
|
||||
self->strictness = new_strictness;
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
n_before = g_list_model_get_n_items (self->model);
|
||||
gtk_filter_list_model_add_items (self, NULL, 0, n_before);
|
||||
if (gtk_filter_list_model_find_filtered (self, &start, &end, &n_after))
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), start, n_before - end - start, n_after - end - start);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case GTK_FILTER_MATCH_SOME:
|
||||
gtk_filter_list_model_refilter (self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_filter_changed_cb (GtkFilter *filter,
|
||||
GtkFilterChange change,
|
||||
GtkFilterListModel *self)
|
||||
{
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_clear_filter (GtkFilterListModel *self)
|
||||
{
|
||||
if (self->filter == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->filter, gtk_filter_list_model_filter_changed_cb, self);
|
||||
g_clear_object (&self->filter);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_dispose (GObject *object)
|
||||
{
|
||||
GtkFilterListModel *self = GTK_FILTER_LIST_MODEL (object);
|
||||
|
||||
gtk_filter_list_model_clear_model (self);
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
self->filter_func = NULL;
|
||||
self->user_data = NULL;
|
||||
self->user_destroy = NULL;
|
||||
gtk_filter_list_model_clear_filter (self);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_filter_list_model_parent_class)->dispose (object);
|
||||
@@ -383,16 +650,16 @@ gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
|
||||
gobject_class->dispose = gtk_filter_list_model_dispose;
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:has-filter:
|
||||
* GtkFilterListModel:filter:
|
||||
*
|
||||
* If a filter is set for this model
|
||||
* The filter for this model
|
||||
*/
|
||||
properties[PROP_HAS_FILTER] =
|
||||
g_param_spec_boolean ("has-filter",
|
||||
P_("has filter"),
|
||||
P_("If a filter is set for this model"),
|
||||
FALSE,
|
||||
GTK_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
properties[PROP_FILTER] =
|
||||
g_param_spec_object ("filter",
|
||||
P_("Filter"),
|
||||
P_("The filter set for this model"),
|
||||
GTK_TYPE_FILTER,
|
||||
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkFilterListModel:item-type:
|
||||
@@ -424,53 +691,22 @@ gtk_filter_list_model_class_init (GtkFilterListModelClass *class)
|
||||
static void
|
||||
gtk_filter_list_model_init (GtkFilterListModel *self)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_augment (GtkRbTree *filter,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FilterNode *node = _node;
|
||||
FilterAugment *aug = _aug;
|
||||
|
||||
aug->n_items = 1;
|
||||
aug->n_visible = node->visible ? 1 : 0;
|
||||
|
||||
if (left)
|
||||
{
|
||||
FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_visible += left_aug->n_visible;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_visible += right_aug->n_visible;
|
||||
}
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_new:
|
||||
* @model: the model to sort
|
||||
* @filter_func: (allow-none): filter function or %NULL to not filter items
|
||||
* @user_data: user data passed to @filter_func
|
||||
* @user_destroy: destroy notifier for @user_data
|
||||
* @filter: (allow-none): filter or %NULL to not filter items
|
||||
*
|
||||
* Creates a new #GtkFilterListModel that will filter @model using the given
|
||||
* @filter_func.
|
||||
* @filter.
|
||||
*
|
||||
* Returns: a new #GtkFilterListModel
|
||||
**/
|
||||
GtkFilterListModel *
|
||||
gtk_filter_list_model_new (GListModel *model,
|
||||
GtkFilterListModelFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
gtk_filter_list_model_new (GListModel *model,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
GtkFilterListModel *result;
|
||||
|
||||
@@ -479,11 +715,9 @@ gtk_filter_list_model_new (GListModel *model,
|
||||
result = g_object_new (GTK_TYPE_FILTER_LIST_MODEL,
|
||||
"item-type", g_list_model_get_item_type (model),
|
||||
"model", model,
|
||||
"filter", filter,
|
||||
NULL);
|
||||
|
||||
if (filter_func)
|
||||
gtk_filter_list_model_set_filter_func (result, filter_func, user_data, user_destroy);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -508,66 +742,53 @@ gtk_filter_list_model_new_for_type (GType item_type)
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_set_filter_func:
|
||||
* gtk_filter_list_model_set_filter:
|
||||
* @self: a #GtkFilterListModel
|
||||
* @filter_func: (allow-none): filter function or %NULL to not filter items
|
||||
* @user_data: user data passed to @filter_func
|
||||
* @user_destroy: destroy notifier for @user_data
|
||||
* @filter: (allow-none) (transfer none): filter to use or %NULL to not filter items
|
||||
*
|
||||
* Sets the function used to filter items. The function will be called for every
|
||||
* item and if it returns %TRUE the item is considered visible.
|
||||
* Sets the filter used to filter items.
|
||||
**/
|
||||
void
|
||||
gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
|
||||
GtkFilterListModelFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
gtk_filter_list_model_set_filter (GtkFilterListModel *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
gboolean was_filtered, will_be_filtered;
|
||||
|
||||
g_return_if_fail (GTK_IS_FILTER_LIST_MODEL (self));
|
||||
g_return_if_fail (filter_func != NULL || (user_data == NULL && !user_destroy));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
was_filtered = self->filter_func != NULL;
|
||||
will_be_filtered = filter_func != NULL;
|
||||
|
||||
if (!was_filtered && !will_be_filtered)
|
||||
if (self->filter == filter)
|
||||
return;
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
gtk_filter_list_model_clear_filter (self);
|
||||
|
||||
self->filter_func = filter_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
if (!will_be_filtered)
|
||||
if (filter)
|
||||
{
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
self->filter = g_object_ref (filter);
|
||||
g_signal_connect (filter, "changed", G_CALLBACK (gtk_filter_list_model_filter_changed_cb), self);
|
||||
gtk_filter_list_model_filter_changed_cb (filter, GTK_FILTER_CHANGE_DIFFERENT, self);
|
||||
}
|
||||
else if (!was_filtered)
|
||||
else
|
||||
{
|
||||
guint i, n_items;
|
||||
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
if (self->model)
|
||||
{
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
FilterNode *node = gtk_rb_tree_insert_before (self->items, NULL);
|
||||
node->visible = TRUE;
|
||||
}
|
||||
}
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
}
|
||||
|
||||
gtk_filter_list_model_refilter (self);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILTER]);
|
||||
}
|
||||
|
||||
if (was_filtered != will_be_filtered)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HAS_FILTER]);
|
||||
/**
|
||||
* gtk_filter_list_model_get_filter:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Gets the #GtkFilter currently set on @self.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The filter currently in use
|
||||
* or %NULL if the list isn't filtered
|
||||
**/
|
||||
GtkFilter *
|
||||
gtk_filter_list_model_get_filter (GtkFilterListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
|
||||
|
||||
return self->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -579,8 +800,8 @@ gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
|
||||
*
|
||||
* Note that GTK makes no effort to ensure that @model conforms to
|
||||
* the item type of @self. It assumes that the caller knows what they
|
||||
* are doing and have set up an appropriate filter function to ensure
|
||||
* that item types match.
|
||||
* are doing and have set up an appropriate filter to ensure that item
|
||||
* types match.
|
||||
**/
|
||||
void
|
||||
gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
@@ -603,14 +824,23 @@ gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
{
|
||||
self->model = g_object_ref (model);
|
||||
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_filter_list_model_items_changed_cb), self);
|
||||
if (self->items)
|
||||
if (removed == 0)
|
||||
{
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
gtk_filter_list_model_update_strictness_and_refilter (self);
|
||||
added = 0;
|
||||
}
|
||||
else if (self->items)
|
||||
added = gtk_filter_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
|
||||
else
|
||||
added = g_list_model_get_n_items (model);
|
||||
}
|
||||
else
|
||||
added = 0;
|
||||
|
||||
{
|
||||
self->strictness = GTK_FILTER_MATCH_NONE;
|
||||
added = 0;
|
||||
}
|
||||
|
||||
if (removed > 0 || added > 0)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
|
||||
|
||||
@@ -633,32 +863,7 @@ gtk_filter_list_model_get_model (GtkFilterListModel *self)
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_has_filter:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Checks if a filter function is currently set on @self
|
||||
*
|
||||
* Returns: %TRUE if a filter function is set
|
||||
**/
|
||||
gboolean
|
||||
gtk_filter_list_model_has_filter (GtkFilterListModel *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_FILTER_LIST_MODEL (self), FALSE);
|
||||
|
||||
return self->filter_func != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_filter_list_model_refilter:
|
||||
* @self: a #GtkFilterListModel
|
||||
*
|
||||
* Causes @self to refilter all items in the model.
|
||||
*
|
||||
* Calling this function is necessary when data used by the filter
|
||||
* function has changed.
|
||||
**/
|
||||
void
|
||||
static void
|
||||
gtk_filter_list_model_refilter (GtkFilterListModel *self)
|
||||
{
|
||||
FilterNode *node;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#endif
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
|
||||
@@ -51,27 +52,20 @@ typedef gboolean (* GtkFilterListModelFilterFunc) (gpointer item, gpointer user_
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterListModel * gtk_filter_list_model_new (GListModel *model,
|
||||
GtkFilterListModelFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilterListModel * gtk_filter_list_model_new_for_type (GType item_type);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
|
||||
GtkFilterListModelFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
void gtk_filter_list_model_set_filter (GtkFilterListModel *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_filter_list_model_get_filter (GtkFilterListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_set_model (GtkFilterListModel *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_filter_list_model_get_model (GtkFilterListModel *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_filter_list_model_has_filter (GtkFilterListModel *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_filter_list_model_refilter (GtkFilterListModel *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
270
gtk/gtkfilters.c
Normal file
270
gtk/gtkfilters.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkfilters.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
|
||||
/*** CUSTOM FILTER ***/
|
||||
|
||||
struct _GtkCustomFilter
|
||||
{
|
||||
GtkFilter parent_instance;
|
||||
|
||||
GtkCustomFilterFunc match_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkCustomFilter, gtk_custom_filter, GTK_TYPE_FILTER)
|
||||
|
||||
static gboolean
|
||||
gtk_custom_filter_match (GtkFilter *filter,
|
||||
gpointer item)
|
||||
{
|
||||
GtkCustomFilter *self = GTK_CUSTOM_FILTER (filter);
|
||||
|
||||
return self->match_func (item, self->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_dispose (GObject *object)
|
||||
{
|
||||
GtkCustomFilter *self = GTK_CUSTOM_FILTER (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
G_OBJECT_CLASS (gtk_custom_filter_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_class_init (GtkCustomFilterClass *class)
|
||||
{
|
||||
GtkFilterClass *filter_class = GTK_FILTER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
filter_class->match = gtk_custom_filter_match;
|
||||
|
||||
object_class->dispose = gtk_custom_filter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_custom_filter_init (GtkCustomFilter *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_custom_filter_new:
|
||||
* @match_func: Function to filter items
|
||||
* @user_data: (allow none): user data to pass to @match_func
|
||||
* @user_destroy: destory notify
|
||||
*
|
||||
* Creates a new filter using the given @match_func to filter
|
||||
* items.
|
||||
*
|
||||
* If the filter func changes its filtering behavior,
|
||||
* gtk_filter_changed() needs to be called.
|
||||
*
|
||||
* Returns: a new #GtkFilter
|
||||
**/
|
||||
GtkFilter *
|
||||
gtk_custom_filter_new (GtkCustomFilterFunc match_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkCustomFilter *result;
|
||||
|
||||
result = g_object_new (GTK_TYPE_CUSTOM_FILTER, NULL);
|
||||
|
||||
result->match_func = match_func;
|
||||
result->user_data = user_data;
|
||||
result->user_destroy = user_destroy;
|
||||
|
||||
return GTK_FILTER (result);
|
||||
}
|
||||
|
||||
/*** ANY FILTER ***/
|
||||
|
||||
struct _GtkAnyFilter
|
||||
{
|
||||
GtkFilter parent_instance;
|
||||
|
||||
GSequence *filters;
|
||||
};
|
||||
|
||||
static GType
|
||||
gtk_any_filter_get_item_type (GListModel *list)
|
||||
{
|
||||
return GTK_TYPE_FILTER;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_any_filter_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkAnyFilter *self = GTK_ANY_FILTER (list);
|
||||
|
||||
return g_sequence_get_length (self->filters);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_any_filter_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkAnyFilter *self = GTK_ANY_FILTER (list);
|
||||
GSequenceIter *iter;
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (self->filters, position);
|
||||
|
||||
if (g_sequence_iter_is_end (iter))
|
||||
return NULL;
|
||||
else
|
||||
return g_object_ref (g_sequence_get (iter));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_any_filter_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_any_filter_get_item_type;
|
||||
iface->get_n_items = gtk_any_filter_get_n_items;
|
||||
iface->get_item = gtk_any_filter_get_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkAnyFilter, gtk_any_filter, GTK_TYPE_FILTER,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_any_filter_list_model_init))
|
||||
|
||||
static gboolean
|
||||
gtk_any_filter_match (GtkFilter *filter,
|
||||
gpointer item)
|
||||
{
|
||||
GtkAnyFilter *self = GTK_ANY_FILTER (filter);
|
||||
GSequenceIter *iter;
|
||||
|
||||
for (iter = g_sequence_get_begin_iter (self->filters);
|
||||
!g_sequence_iter_is_end (iter);
|
||||
iter = g_sequence_iter_next (iter))
|
||||
{
|
||||
GtkFilter *child = g_sequence_get (iter);
|
||||
|
||||
if (gtk_filter_match (child, item))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_any_filter_dispose (GObject *object)
|
||||
{
|
||||
GtkAnyFilter *self = GTK_ANY_FILTER (object);
|
||||
|
||||
g_clear_pointer (&self->filters, g_sequence_free);
|
||||
|
||||
G_OBJECT_CLASS (gtk_any_filter_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_any_filter_class_init (GtkAnyFilterClass *class)
|
||||
{
|
||||
GtkFilterClass *filter_class = GTK_FILTER_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
filter_class->match = gtk_any_filter_match;
|
||||
|
||||
object_class->dispose = gtk_any_filter_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_any_filter_init (GtkAnyFilter *self)
|
||||
{
|
||||
self->filters = g_sequence_new (g_object_unref);
|
||||
|
||||
gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_MATCH_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_any_filter_new:
|
||||
*
|
||||
* Creates a new "any" filter.
|
||||
*
|
||||
* This filter matches an item if any of the filters added to it
|
||||
* matches the item.
|
||||
* In particular, this means that if no filter has been added to
|
||||
* it, the filter matches no item.
|
||||
*
|
||||
* Returns: a new #GtkFilter
|
||||
**/
|
||||
GtkFilter *
|
||||
gtk_any_filter_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_ANY_FILTER, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_any_filter_append:
|
||||
* @self: a #GtkAnyFilter
|
||||
* @filter: (tranfer none): A new filter to use
|
||||
*
|
||||
* Adds a @filter to @self to use for matching.
|
||||
**/
|
||||
void
|
||||
gtk_any_filter_append (GtkAnyFilter *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_ANY_FILTER (self));
|
||||
g_return_if_fail (GTK_IS_FILTER (filter));
|
||||
|
||||
g_sequence_append (self->filters, g_object_ref (filter));
|
||||
|
||||
gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_LESS_STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_any_filter_remove:
|
||||
* @self: a #GtkAnyFilter
|
||||
* @position: position of filter to remove
|
||||
*
|
||||
* Removes the filter at the given @position from the list of filters used
|
||||
* by @self.
|
||||
* If @position is larger than the number of filters, nothing happens and
|
||||
* the function returns.
|
||||
**/
|
||||
void
|
||||
gtk_any_filter_remove (GtkAnyFilter *self,
|
||||
guint position)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
guint length;
|
||||
|
||||
g_return_if_fail (GTK_IS_ANY_FILTER (self));
|
||||
|
||||
length = g_sequence_get_length (self->filters);
|
||||
if (position >= length)
|
||||
return;
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (self->filters, position);
|
||||
g_sequence_remove (iter);
|
||||
|
||||
gtk_filter_changed (GTK_FILTER (self), length == 1
|
||||
? GTK_FILTER_CHANGE_MATCH_NONE
|
||||
: GTK_FILTER_CHANGE_MORE_STRICT);
|
||||
}
|
||||
69
gtk/gtkfilters.h
Normal file
69
gtk/gtkfilters.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_FILTERS_H__
|
||||
#define __GTK_FILTERS_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GtkCustomFilterFunc:
|
||||
* @item: (type GObject): The item to be matched
|
||||
* @user_data: user data
|
||||
*
|
||||
* User function that is called to determine if the @item should be matched.
|
||||
* If the filter matches the item, this function must return %TRUE. If the
|
||||
* item should be filtered out, %FALSE must be returned.
|
||||
*
|
||||
* Returns: %TRUE to keep the item around
|
||||
*/
|
||||
typedef gboolean (* GtkCustomFilterFunc) (gpointer item, gpointer user_data);
|
||||
|
||||
#define GTK_TYPE_CUSTOM_FILTER (gtk_custom_filter_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCustomFilter, gtk_custom_filter, GTK, CUSTOM_FILTER, GtkFilter)
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_custom_filter_new (GtkCustomFilterFunc filter_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
#define GTK_TYPE_ANY_FILTER (gtk_any_filter_get_type ())
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkAnyFilter, gtk_any_filter, GTK, ANY_FILTER, GtkFilter)
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_any_filter_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_any_filter_append (GtkAnyFilter *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_any_filter_remove (GtkAnyFilter *self,
|
||||
guint position);
|
||||
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FILTERS_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
124
gtk/gtkfunctionslistitemfactory.c
Normal file
124
gtk/gtkfunctionslistitemfactory.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkfunctionslistitemfactory.h"
|
||||
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
struct _GtkFunctionsListItemFactory
|
||||
{
|
||||
GtkListItemFactory parent_instance;
|
||||
|
||||
GtkListItemSetupFunc setup_func;
|
||||
GtkListItemBindFunc bind_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
};
|
||||
|
||||
struct _GtkFunctionsListItemFactoryClass
|
||||
{
|
||||
GtkListItemFactoryClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkFunctionsListItemFactory, gtk_functions_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_setup (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (factory);
|
||||
|
||||
if (self->setup_func)
|
||||
self->setup_func (list_item, self->user_data);
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_functions_list_item_factory_parent_class)->setup (factory, widget, list_item);
|
||||
|
||||
if (gtk_list_item_get_item (list_item) != NULL && self->bind_func)
|
||||
self->bind_func (list_item, self->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_update (GtkListItemFactory *factory,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (factory);
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_CLASS (gtk_functions_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
|
||||
|
||||
if (item != NULL && self->bind_func)
|
||||
self->bind_func (list_item, self->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_finalize (GObject *object)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self = GTK_FUNCTIONS_LIST_ITEM_FACTORY (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
G_OBJECT_CLASS (gtk_functions_list_item_factory_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_class_init (GtkFunctionsListItemFactoryClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_functions_list_item_factory_finalize;
|
||||
|
||||
factory_class->setup = gtk_functions_list_item_factory_setup;
|
||||
factory_class->update = gtk_functions_list_item_factory_update;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_functions_list_item_factory_init (GtkFunctionsListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_functions_list_item_factory_new (GtkListItemSetupFunc setup_func,
|
||||
GtkListItemBindFunc bind_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkFunctionsListItemFactory *self;
|
||||
|
||||
g_return_val_if_fail (setup_func || bind_func, NULL);
|
||||
g_return_val_if_fail (user_data != NULL || user_destroy == NULL, NULL);
|
||||
|
||||
self = g_object_new (GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, NULL);
|
||||
|
||||
self->setup_func = setup_func;
|
||||
self->bind_func = bind_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
return GTK_LIST_ITEM_FACTORY (self);
|
||||
}
|
||||
|
||||
82
gtk/gtkfunctionslistitemfactory.h
Normal file
82
gtk/gtkfunctionslistitemfactory.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistitemfactory.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY (gtk_functions_list_item_factory_get_type ())
|
||||
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactory))
|
||||
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactoryClass))
|
||||
#define GTK_IS_FUNCTIONS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_FUNCTIONS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY))
|
||||
#define GTK_FUNCTIONS_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_FUNCTIONS_LIST_ITEM_FACTORY, GtkFunctionsListItemFactoryClass))
|
||||
|
||||
typedef struct _GtkFunctionsListItemFactory GtkFunctionsListItemFactory;
|
||||
typedef struct _GtkFunctionsListItemFactoryClass GtkFunctionsListItemFactoryClass;
|
||||
|
||||
/**
|
||||
* GtkListItemSetupFunc:
|
||||
* @item: the #GtkListItem to set up
|
||||
* @user_data: (closure): user data
|
||||
*
|
||||
* Called whenever a new list item needs to be setup for managing a row in
|
||||
* the list.
|
||||
*
|
||||
* At this point, the list item is not bound yet, so gtk_list_item_get_item()
|
||||
* will return %NULL.
|
||||
* The list item will later be bound to an item via the #GtkListItemBindFunc.
|
||||
*/
|
||||
typedef void (* GtkListItemSetupFunc) (GtkListItem *item, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GtkListItemBindFunc:
|
||||
* @item: the #GtkListItem to bind
|
||||
* @user_data: (closure): user data
|
||||
*
|
||||
* Binds a#GtkListItem previously set up via a #GtkListItemSetupFunc to
|
||||
* an @item.
|
||||
*
|
||||
* Rebinding a @item to different @items is supported as well as
|
||||
* unbinding it by setting @item to %NULL.
|
||||
*/
|
||||
typedef void (* GtkListItemBindFunc) (GtkListItem *item,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_functions_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_functions_list_item_factory_new (GtkListItemSetupFunc setup_func,
|
||||
GtkListItemBindFunc bind_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FUNCTIONS_LIST_ITEM_FACTORY_H__ */
|
||||
1292
gtk/gtkgridview.c
Normal file
1292
gtk/gtkgridview.c
Normal file
File diff suppressed because it is too large
Load Diff
80
gtk/gtkgridview.h
Normal file
80
gtk/gtkgridview.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_GRID_VIEW_H__
|
||||
#define __GTK_GRID_VIEW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_GRID_VIEW (gtk_grid_view_get_type ())
|
||||
#define GTK_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_GRID_VIEW, GtkGridView))
|
||||
#define GTK_GRID_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_GRID_VIEW, GtkGridViewClass))
|
||||
#define GTK_IS_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_GRID_VIEW))
|
||||
#define GTK_IS_GRID_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_GRID_VIEW))
|
||||
#define GTK_GRID_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_GRID_VIEW, GtkGridViewClass))
|
||||
|
||||
/**
|
||||
* GtkGridView:
|
||||
*
|
||||
* GtkGridView is a list widget implementation that arranges its items in
|
||||
* a grid.
|
||||
*/
|
||||
typedef struct _GtkGridView GtkGridView;
|
||||
typedef struct _GtkGridViewClass GtkGridViewClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_grid_view_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_grid_view_new (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_grid_view_new_with_factory (GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_grid_view_get_model (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_model (GtkGridView *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_factory (GtkGridView *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory *
|
||||
gtk_grid_view_get_factory (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_grid_view_get_min_columns (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_min_columns (GtkGridView *self,
|
||||
guint min_columns);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_grid_view_get_max_columns (GtkGridView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_max_columns (GtkGridView *self,
|
||||
guint max_columns);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_GRID_VIEW_H__ */
|
||||
@@ -3630,14 +3630,24 @@ gtk_label_get_preferred_size (GtkWidget *widget,
|
||||
}
|
||||
else /* GTK_ORIENTATION_VERTICAL */
|
||||
{
|
||||
*minimum_size = MIN (smallest_rect.height, widest_rect.height);
|
||||
*natural_size = MAX (smallest_rect.height, widest_rect.height);
|
||||
|
||||
if (minimum_baseline)
|
||||
*minimum_baseline = smallest_baseline;
|
||||
|
||||
if (natural_baseline)
|
||||
*natural_baseline = widest_baseline;
|
||||
if (smallest_rect.height < widest_rect.height)
|
||||
{
|
||||
*minimum_size = smallest_rect.height;
|
||||
*natural_size = widest_rect.height;
|
||||
if (minimum_baseline)
|
||||
*minimum_baseline = smallest_baseline;
|
||||
if (natural_baseline)
|
||||
*natural_baseline = widest_baseline;
|
||||
}
|
||||
else
|
||||
{
|
||||
*minimum_size = widest_rect.height;
|
||||
*natural_size = smallest_rect.height;
|
||||
if (minimum_baseline)
|
||||
*minimum_baseline = widest_baseline;
|
||||
if (natural_baseline)
|
||||
*natural_baseline = smallest_baseline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1543
gtk/gtklistbase.c
Normal file
1543
gtk/gtklistbase.c
Normal file
File diff suppressed because it is too large
Load Diff
51
gtk/gtklistbase.h
Normal file
51
gtk/gtklistbase.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_BASE_H__
|
||||
#define __GTK_LIST_BASE_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_BASE (gtk_list_base_get_type ())
|
||||
#define GTK_LIST_BASE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_BASE, GtkListBase))
|
||||
#define GTK_LIST_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_BASE, GtkListBaseClass))
|
||||
#define GTK_IS_LIST_BASE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_BASE))
|
||||
#define GTK_IS_LIST_BASE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_BASE))
|
||||
#define GTK_LIST_BASE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_BASE, GtkListBaseClass))
|
||||
|
||||
/**
|
||||
* GtkListBase:
|
||||
*
|
||||
* GtkListBase is the abstract base class for GTK's list widgets.
|
||||
*/
|
||||
typedef struct _GtkListBase GtkListBase;
|
||||
typedef struct _GtkListBaseClass GtkListBaseClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_base_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_BASE_H__ */
|
||||
103
gtk/gtklistbaseprivate.h
Normal file
103
gtk/gtklistbaseprivate.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_BASE_PRIVATE_H__
|
||||
#define __GTK_LIST_BASE_PRIVATE_H__
|
||||
|
||||
#include "gtklistbase.h"
|
||||
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
struct _GtkListBase
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _GtkListBaseClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
|
||||
const char * list_item_name;
|
||||
gsize list_item_size;
|
||||
gsize list_item_augment_size;
|
||||
GtkRbTreeAugmentFunc list_item_augment_func;
|
||||
|
||||
void (* adjustment_value_changed) (GtkListBase *self,
|
||||
GtkOrientation orientation);
|
||||
gboolean (* get_allocation_along) (GtkListBase *self,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size);
|
||||
gboolean (* get_allocation_across) (GtkListBase *self,
|
||||
guint pos,
|
||||
int *offset,
|
||||
int *size);
|
||||
gboolean (* get_position_from_allocation) (GtkListBase *self,
|
||||
int across,
|
||||
int along,
|
||||
guint *pos,
|
||||
cairo_rectangle_int_t *area);
|
||||
guint (* move_focus_along) (GtkListBase *self,
|
||||
guint pos,
|
||||
int steps);
|
||||
guint (* move_focus_across) (GtkListBase *self,
|
||||
guint pos,
|
||||
int steps);
|
||||
};
|
||||
|
||||
GtkOrientation gtk_list_base_get_orientation (GtkListBase *self);
|
||||
#define gtk_list_base_get_opposite_orientation(self) OPPOSITE_ORIENTATION(gtk_list_base_get_orientation(self))
|
||||
guint gtk_list_base_get_focus_position (GtkListBase *self);
|
||||
GtkListItemManager * gtk_list_base_get_manager (GtkListBase *self);
|
||||
GtkScrollablePolicy gtk_list_base_get_scroll_policy (GtkListBase *self,
|
||||
GtkOrientation orientation);
|
||||
guint gtk_list_base_get_n_items (GtkListBase *self);
|
||||
GListModel * gtk_list_base_get_model (GtkListBase *self);
|
||||
gboolean gtk_list_base_set_model (GtkListBase *self,
|
||||
GListModel *model);
|
||||
void gtk_list_base_update_adjustments (GtkListBase *self,
|
||||
int total_across,
|
||||
int total_along,
|
||||
int page_across,
|
||||
int page_along,
|
||||
int *across,
|
||||
int *along);
|
||||
|
||||
guint gtk_list_base_get_anchor (GtkListBase *self);
|
||||
void gtk_list_base_set_anchor (GtkListBase *self,
|
||||
guint anchor_pos,
|
||||
double anchor_align_across,
|
||||
GtkPackType anchor_side_across,
|
||||
double anchor_align_along,
|
||||
GtkPackType anchor_side_along);
|
||||
void gtk_list_base_set_anchor_max_widgets (GtkListBase *self,
|
||||
guint n_center,
|
||||
guint n_above_below);
|
||||
void gtk_list_base_select_item (GtkListBase *self,
|
||||
guint pos,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
gboolean gtk_list_base_grab_focus_on_item (GtkListBase *self,
|
||||
guint pos,
|
||||
gboolean select,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
|
||||
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
|
||||
467
gtk/gtklistitem.c
Normal file
467
gtk/gtklistitem.c
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h" /* for GTK_INVALID_LIST_POSITION */
|
||||
|
||||
/**
|
||||
* SECTION:gtklistitem
|
||||
* @title: GtkListItem
|
||||
* @short_description: Widget used to represent items of a ListModel
|
||||
* @see_also: #GtkListView, #GListModel
|
||||
*
|
||||
* #GtkListItem is the widget that GTK list-handling containers such
|
||||
* as #GtkListView create to represent items in a #GListModel.
|
||||
* They are managed by the container and cannot be created by application
|
||||
* code.
|
||||
*
|
||||
* #GtkListIems need to be populated by application code. This is done by
|
||||
* calling gtk_list_item_set_child().
|
||||
*
|
||||
* #GtkListItems exist in 2 stages:
|
||||
*
|
||||
* 1. The unbound stage where the listitem is not currently connected to
|
||||
* an item in the list. In that case, the GtkListItem:item property is
|
||||
* set to %NULL.
|
||||
*
|
||||
* 2. The bound stage where the listitem references an item from the list.
|
||||
* The GtkListItem:item property is not %NULL.
|
||||
*/
|
||||
|
||||
struct _GtkListItemClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ACTIVATABLE,
|
||||
PROP_CHILD,
|
||||
PROP_ITEM,
|
||||
PROP_POSITION,
|
||||
PROP_SELECTABLE,
|
||||
PROP_SELECTED,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkListItem, gtk_list_item, G_TYPE_OBJECT)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_list_item_dispose (GObject *object)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
g_assert (self->owner == NULL); /* would hold a reference */
|
||||
g_clear_object (&self->child);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_item_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_ACTIVATABLE:
|
||||
g_value_set_boolean (value, self->activatable);
|
||||
break;
|
||||
|
||||
case PROP_CHILD:
|
||||
g_value_set_object (value, self->child);
|
||||
break;
|
||||
|
||||
case PROP_ITEM:
|
||||
if (self->owner)
|
||||
g_value_set_object (value, gtk_list_item_widget_get_item (self->owner));
|
||||
break;
|
||||
|
||||
case PROP_POSITION:
|
||||
if (self->owner)
|
||||
g_value_set_uint (value, gtk_list_item_widget_get_position (self->owner));
|
||||
else
|
||||
g_value_set_uint (value, GTK_INVALID_LIST_POSITION);
|
||||
break;
|
||||
|
||||
case PROP_SELECTABLE:
|
||||
g_value_set_boolean (value, self->selectable);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
if (self->owner)
|
||||
g_value_set_boolean (value, gtk_list_item_widget_get_selected (self->owner));
|
||||
else
|
||||
g_value_set_boolean (value, FALSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_ACTIVATABLE:
|
||||
gtk_list_item_set_activatable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_CHILD:
|
||||
gtk_list_item_set_child (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_SELECTABLE:
|
||||
gtk_list_item_set_selectable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_class_init (GtkListItemClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_list_item_dispose;
|
||||
gobject_class->get_property = gtk_list_item_get_property;
|
||||
gobject_class->set_property = gtk_list_item_set_property;
|
||||
|
||||
/**
|
||||
* GtkListItem:activatable:
|
||||
*
|
||||
* If the item can be activated by the user
|
||||
*/
|
||||
properties[PROP_ACTIVATABLE] =
|
||||
g_param_spec_boolean ("activatable",
|
||||
P_("Activatable"),
|
||||
P_("If the item can be activated by the user"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:child:
|
||||
*
|
||||
* Widget used for display
|
||||
*/
|
||||
properties[PROP_CHILD] =
|
||||
g_param_spec_object ("child",
|
||||
P_("Child"),
|
||||
P_("Widget used for display"),
|
||||
GTK_TYPE_WIDGET,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:item:
|
||||
*
|
||||
* Displayed item
|
||||
*/
|
||||
properties[PROP_ITEM] =
|
||||
g_param_spec_object ("item",
|
||||
P_("Item"),
|
||||
P_("Displayed item"),
|
||||
G_TYPE_OBJECT,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:position:
|
||||
*
|
||||
* Position of the item
|
||||
*/
|
||||
properties[PROP_POSITION] =
|
||||
g_param_spec_uint ("position",
|
||||
P_("Position"),
|
||||
P_("Position of the item"),
|
||||
0, G_MAXUINT, GTK_INVALID_LIST_POSITION,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:selectable:
|
||||
*
|
||||
* If the item can be selected by the user
|
||||
*/
|
||||
properties[PROP_SELECTABLE] =
|
||||
g_param_spec_boolean ("selectable",
|
||||
P_("Selectable"),
|
||||
P_("If the item can be selected by the user"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:selected:
|
||||
*
|
||||
* If the item is currently selected
|
||||
*/
|
||||
properties[PROP_SELECTED] =
|
||||
g_param_spec_boolean ("selected",
|
||||
P_("Selected"),
|
||||
P_("If the item is currently selected"),
|
||||
FALSE,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_init (GtkListItem *self)
|
||||
{
|
||||
self->selectable = TRUE;
|
||||
self->activatable = TRUE;
|
||||
}
|
||||
|
||||
GtkListItem *
|
||||
gtk_list_item_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_LIST_ITEM,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_item:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Gets the item that is currently displayed in model that @self is
|
||||
* currently bound to or %NULL if @self is unbound.
|
||||
*
|
||||
* Returns: (nullable) (transfer none) (type GObject): The item displayed
|
||||
**/
|
||||
gpointer
|
||||
gtk_list_item_get_item (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL);
|
||||
|
||||
if (self->owner == NULL)
|
||||
return NULL;
|
||||
|
||||
return gtk_list_item_widget_get_item (self->owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_child:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Gets the child previously set via gtk_list_item_set_child() or
|
||||
* %NULL if none was set.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The child
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_list_item_get_child (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL);
|
||||
|
||||
return self->child;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_set_child:
|
||||
* @self: a #GtkListItem
|
||||
* @child: (nullable): The list item's child or %NULL to unset
|
||||
*
|
||||
* Sets the child to be used for this listitem.
|
||||
*
|
||||
* This function is typically called by applications when
|
||||
* setting up a listitem so that the widget can be reused when
|
||||
* binding it multiple times.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_set_child (GtkListItem *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
|
||||
|
||||
if (self->child == child)
|
||||
return;
|
||||
|
||||
if (self->child && self->owner)
|
||||
gtk_list_item_widget_remove_child (self->owner, self->child);
|
||||
|
||||
g_clear_object (&self->child);
|
||||
|
||||
if (child)
|
||||
{
|
||||
g_object_ref_sink (child);
|
||||
self->child = child;
|
||||
|
||||
if (self->owner)
|
||||
gtk_list_item_widget_add_child (self->owner, child);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_position:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Gets the position in the model that @self currently displays.
|
||||
* If @self is unbound, 0 is returned.
|
||||
*
|
||||
* Returns: The position of this item
|
||||
**/
|
||||
guint
|
||||
gtk_list_item_get_position (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), GTK_INVALID_LIST_POSITION);
|
||||
|
||||
if (self->owner == NULL)
|
||||
return GTK_INVALID_LIST_POSITION;
|
||||
|
||||
return gtk_list_item_widget_get_position (self->owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_selected:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if the item is displayed as selected. The selected state is
|
||||
* maintained by the container and its list model and cannot be set
|
||||
* otherwise.
|
||||
*
|
||||
* Returns: %TRUE if the item is selected.
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_selected (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
if (self->owner == NULL)
|
||||
return FALSE;
|
||||
|
||||
return gtk_list_item_widget_get_selected (self->owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_selectable:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if a list item has been set to be selectable via
|
||||
* gtk_list_item_set_selectable().
|
||||
*
|
||||
* Do not confuse this function with gtk_list_item_get_selected().
|
||||
*
|
||||
* Returns: %TRUE if the item is selectable
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_selectable (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
return self->selectable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_set_selectable:
|
||||
* @self: a #GtkListItem
|
||||
* @selectable: if the item should be selectable
|
||||
*
|
||||
* Sets @self to be selectable. If an item is selectable, clicking
|
||||
* on the item or using the keyboard will try to select or unselect
|
||||
* the item. If this succeeds is up to the model to determine, as
|
||||
* it is managing the selected state.
|
||||
*
|
||||
* Note that this means that making an item non-selectable has no
|
||||
* influence on the selected state at all. A non-selectable item
|
||||
* may still be selected.
|
||||
*
|
||||
* By default, list items are selectable. When rebinding them to
|
||||
* a new item, they will also be reset to be selectable by GTK.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_set_selectable (GtkListItem *self,
|
||||
gboolean selectable)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->selectable == selectable)
|
||||
return;
|
||||
|
||||
self->selectable = selectable;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_activatable:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if a list item has been set to be activatable via
|
||||
* gtk_list_item_set_activatable().
|
||||
*
|
||||
* Returns: %TRUE if the item is activatable
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_activatable (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
return self->activatable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_set_activatable:
|
||||
* @self: a #GtkListItem
|
||||
* @activatable: if the item should be activatable
|
||||
*
|
||||
* Sets @self to be activatable.
|
||||
*
|
||||
* If an item is activatable, double-clicking on the item, using
|
||||
* the <Return> key or calling gtk_widget_activate() will activate
|
||||
* the item. Activating instructs the containing view to handle
|
||||
* activation. #GtkListView for example will be emitting the
|
||||
* GtkListView::activate signal.
|
||||
*
|
||||
* By default, list items are activatable
|
||||
**/
|
||||
void
|
||||
gtk_list_item_set_activatable (GtkListItem *self,
|
||||
gboolean activatable)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->activatable == activatable)
|
||||
return;
|
||||
|
||||
self->activatable = activatable;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVATABLE]);
|
||||
}
|
||||
70
gtk/gtklistitem.h
Normal file
70
gtk/gtklistitem.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_H__
|
||||
#define __GTK_LIST_ITEM_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM (gtk_list_item_get_type ())
|
||||
#define GTK_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM, GtkListItem))
|
||||
#define GTK_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM, GtkListItemClass))
|
||||
#define GTK_IS_LIST_ITEM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM))
|
||||
#define GTK_IS_LIST_ITEM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM))
|
||||
#define GTK_LIST_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM, GtkListItemClass))
|
||||
|
||||
typedef struct _GtkListItem GtkListItem;
|
||||
typedef struct _GtkListItemClass GtkListItemClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_item_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gpointer gtk_list_item_get_item (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_list_item_get_position (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selected (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selectable (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_selectable (GtkListItem *self,
|
||||
gboolean selectable);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_activatable (GtkListItem *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_activatable (GtkListItem *self,
|
||||
gboolean activatable);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_child (GtkListItem *self,
|
||||
GtkWidget *child);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_list_item_get_child (GtkListItem *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_H__ */
|
||||
118
gtk/gtklistitemfactory.c
Normal file
118
gtk/gtklistitemfactory.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
G_DEFINE_TYPE (GtkListItemFactory, gtk_list_item_factory, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_setup (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gtk_list_item_widget_default_setup (widget, list_item);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_teardown (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gtk_list_item_widget_default_teardown (widget, list_item);
|
||||
|
||||
gtk_list_item_set_child (list_item, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_default_update (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
gtk_list_item_widget_default_update (widget, list_item, position, item, selected);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_class_init (GtkListItemFactoryClass *klass)
|
||||
{
|
||||
klass->setup = gtk_list_item_factory_default_setup;
|
||||
klass->teardown = gtk_list_item_factory_default_teardown;
|
||||
klass->update = gtk_list_item_factory_default_update;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_init (GtkListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_setup (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget)
|
||||
{
|
||||
GtkListItem *list_item;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
|
||||
list_item = gtk_list_item_new ();
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->setup (self, widget, list_item);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_teardown (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget)
|
||||
{
|
||||
GtkListItem *list_item;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
|
||||
list_item = gtk_list_item_widget_get_list_item (widget);
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->teardown (self, widget, list_item);
|
||||
|
||||
g_object_unref (list_item);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_update (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkListItem *list_item;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_WIDGET (widget));
|
||||
|
||||
list_item = gtk_list_item_widget_get_list_item (widget);
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
GTK_LIST_ITEM_FACTORY_GET_CLASS (self)->update (self, widget, list_item, position, item, selected);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (list_item));
|
||||
}
|
||||
|
||||
48
gtk/gtklistitemfactory.h
Normal file
48
gtk/gtklistitemfactory.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright © 2019 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
typedef struct _GtkListItemFactoryClass GtkListItemFactoryClass;
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM_FACTORY (gtk_list_item_factory_get_type ())
|
||||
#define GTK_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactory))
|
||||
#define GTK_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactoryClass))
|
||||
#define GTK_IS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_FACTORY))
|
||||
#define GTK_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactoryClass))
|
||||
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_FACTORY_H__ */
|
||||
71
gtk/gtklistitemfactoryprivate.h
Normal file
71
gtk/gtklistitemfactoryprivate.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_FACTORY_PRIVATE_H__
|
||||
#define __GTK_LIST_ITEM_FACTORY_PRIVATE_H__
|
||||
|
||||
#include <gtk/gtklistitem.h>
|
||||
#include "gtk/gtklistitemwidgetprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GtkListItemFactory
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
struct _GtkListItemFactoryClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* setup @list_item so it can be bound */
|
||||
void (* setup) (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item);
|
||||
/* undo the effects of GtkListItemFactoryClass::setup() */
|
||||
void (* teardown) (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item);
|
||||
|
||||
/* Update properties on @list_item to the given @item, which is in @position and @selected state.
|
||||
* One or more of those properties might be unchanged. */
|
||||
void (* update) (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
};
|
||||
|
||||
void gtk_list_item_factory_setup (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget);
|
||||
void gtk_list_item_factory_teardown (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget);
|
||||
|
||||
void gtk_list_item_factory_update (GtkListItemFactory *self,
|
||||
GtkListItemWidget *widget,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_FACTORY_PRIVATE_H__ */
|
||||
1150
gtk/gtklistitemmanager.c
Normal file
1150
gtk/gtklistitemmanager.c
Normal file
File diff suppressed because it is too large
Load Diff
105
gtk/gtklistitemmanagerprivate.h
Normal file
105
gtk/gtklistitemmanagerprivate.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_MANAGER_H__
|
||||
#define __GTK_LIST_ITEM_MANAGER_H__
|
||||
|
||||
#include "gtk/gtktypes.h"
|
||||
|
||||
#include "gtk/gtklistitemfactory.h"
|
||||
#include "gtk/gtkrbtreeprivate.h"
|
||||
#include "gtk/gtkselectionmodel.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM_MANAGER (gtk_list_item_manager_get_type ())
|
||||
#define GTK_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManager))
|
||||
#define GTK_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass))
|
||||
#define GTK_IS_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_MANAGER))
|
||||
#define GTK_IS_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_MANAGER))
|
||||
#define GTK_LIST_ITEM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass))
|
||||
|
||||
typedef struct _GtkListItemManager GtkListItemManager;
|
||||
typedef struct _GtkListItemManagerClass GtkListItemManagerClass;
|
||||
typedef struct _GtkListItemManagerItem GtkListItemManagerItem; /* sorry */
|
||||
typedef struct _GtkListItemManagerItemAugment GtkListItemManagerItemAugment;
|
||||
typedef struct _GtkListItemTracker GtkListItemTracker;
|
||||
|
||||
struct _GtkListItemManagerItem
|
||||
{
|
||||
GtkWidget *widget;
|
||||
guint n_items;
|
||||
};
|
||||
|
||||
struct _GtkListItemManagerItemAugment
|
||||
{
|
||||
guint n_items;
|
||||
};
|
||||
|
||||
|
||||
GType gtk_list_item_manager_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkListItemManager * gtk_list_item_manager_new_for_size (GtkWidget *widget,
|
||||
const char *item_css_name,
|
||||
gsize element_size,
|
||||
gsize augment_size,
|
||||
GtkRbTreeAugmentFunc augment_func);
|
||||
#define gtk_list_item_manager_new(widget, item_css_name, type, augment_type, augment_func) \
|
||||
gtk_list_item_manager_new_for_size (widget, item_css_name, sizeof (type), sizeof (augment_type), (augment_func))
|
||||
|
||||
void gtk_list_item_manager_augment_node (GtkRbTree *tree,
|
||||
gpointer node_augment,
|
||||
gpointer node,
|
||||
gpointer left,
|
||||
gpointer right);
|
||||
gpointer gtk_list_item_manager_get_root (GtkListItemManager *self);
|
||||
gpointer gtk_list_item_manager_get_first (GtkListItemManager *self);
|
||||
gpointer gtk_list_item_manager_get_nth (GtkListItemManager *self,
|
||||
guint position,
|
||||
guint *offset);
|
||||
guint gtk_list_item_manager_get_item_position (GtkListItemManager *self,
|
||||
gpointer item);
|
||||
gpointer gtk_list_item_manager_get_item_augment (GtkListItemManager *self,
|
||||
gpointer item);
|
||||
|
||||
void gtk_list_item_manager_set_factory (GtkListItemManager *self,
|
||||
GtkListItemFactory *factory);
|
||||
GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self);
|
||||
void gtk_list_item_manager_set_model (GtkListItemManager *self,
|
||||
GtkSelectionModel *model);
|
||||
GtkSelectionModel * gtk_list_item_manager_get_model (GtkListItemManager *self);
|
||||
|
||||
guint gtk_list_item_manager_get_size (GtkListItemManager *self);
|
||||
|
||||
GtkListItemTracker * gtk_list_item_tracker_new (GtkListItemManager *self);
|
||||
void gtk_list_item_tracker_free (GtkListItemManager *self,
|
||||
GtkListItemTracker *tracker);
|
||||
void gtk_list_item_tracker_set_position (GtkListItemManager *self,
|
||||
GtkListItemTracker *tracker,
|
||||
guint position,
|
||||
guint n_before,
|
||||
guint n_after);
|
||||
guint gtk_list_item_tracker_get_position (GtkListItemManager *self,
|
||||
GtkListItemTracker *tracker);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_MANAGER_H__ */
|
||||
46
gtk/gtklistitemprivate.h
Normal file
46
gtk/gtklistitemprivate.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_PRIVATE_H__
|
||||
#define __GTK_LIST_ITEM_PRIVATE_H__
|
||||
|
||||
#include "gtklistitem.h"
|
||||
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GtkListItem
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemWidget *owner; /* has a reference */
|
||||
|
||||
GtkWidget *child;
|
||||
|
||||
guint activatable : 1;
|
||||
guint selectable : 1;
|
||||
};
|
||||
|
||||
GtkListItem * gtk_list_item_new (void);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_PRIVATE_H__ */
|
||||
588
gtk/gtklistitemwidget.c
Normal file
588
gtk/gtklistitemwidget.c
Normal file
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemwidgetprivate.h"
|
||||
|
||||
#include "gtkbindings.h"
|
||||
#include "gtkbinlayout.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkeventcontrollerkey.h"
|
||||
#include "gtkgestureclick.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
#include "gtkwidget.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
typedef struct _GtkListItemWidgetPrivate GtkListItemWidgetPrivate;
|
||||
struct _GtkListItemWidgetPrivate
|
||||
{
|
||||
GtkListItemFactory *factory;
|
||||
GtkListItem *list_item;
|
||||
|
||||
GObject *item;
|
||||
guint position;
|
||||
gboolean selected;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ACTIVATE_SIGNAL,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkListItemWidget, gtk_list_item_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_activate_signal (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->list_item && !priv->list_item->activatable)
|
||||
return;
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.activate-item",
|
||||
"u",
|
||||
priv->position);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_item_widget_focus (GtkWidget *widget,
|
||||
GtkDirectionType direction)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
/* The idea of this function is the following:
|
||||
* 1. If any child can take focus, do not ever attempt
|
||||
* to take focus.
|
||||
* 2. Otherwise, if this item is selectable or activatable,
|
||||
* allow focusing this widget.
|
||||
*
|
||||
* This makes sure every item in a list is focusable for
|
||||
* activation and selection handling, but no useless widgets
|
||||
* get focused and moving focus is as fast as possible.
|
||||
*/
|
||||
if (priv->list_item && priv->list_item->child)
|
||||
{
|
||||
if (gtk_widget_get_focus_child (widget))
|
||||
return FALSE;
|
||||
if (gtk_widget_child_focus (priv->list_item->child, direction))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (gtk_widget_is_focus (widget))
|
||||
return FALSE;
|
||||
|
||||
if (!gtk_widget_get_can_focus (widget) ||
|
||||
!priv->list_item->selectable)
|
||||
return FALSE;
|
||||
|
||||
return gtk_widget_grab_focus (widget);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_item_widget_grab_focus (GtkWidget *widget)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->list_item && priv->list_item->child && gtk_widget_grab_focus (priv->list_item->child))
|
||||
return TRUE;
|
||||
|
||||
return GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_root (GtkWidget *widget)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->root (widget);
|
||||
|
||||
if (priv->factory)
|
||||
gtk_list_item_factory_setup (priv->factory, self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_unroot (GtkWidget *widget)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->unroot (widget);
|
||||
|
||||
if (priv->list_item)
|
||||
gtk_list_item_factory_teardown (priv->factory, self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_list_item_widget_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_dispose (GObject *object)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
g_assert (priv->list_item == NULL);
|
||||
|
||||
g_clear_object (&priv->item);
|
||||
g_clear_object (&priv->factory);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_item_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_select_action (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
gboolean modify, extend;
|
||||
|
||||
if (priv->list_item && !priv->list_item->selectable)
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "(bb)", &modify, &extend);
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.select-item",
|
||||
"(ubb)",
|
||||
priv->position, modify, extend);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_class_init (GtkListItemWidgetClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GtkBindingSet *binding_set;
|
||||
|
||||
klass->activate_signal = gtk_list_item_widget_activate_signal;
|
||||
|
||||
widget_class->focus = gtk_list_item_widget_focus;
|
||||
widget_class->grab_focus = gtk_list_item_widget_grab_focus;
|
||||
widget_class->root = gtk_list_item_widget_root;
|
||||
widget_class->unroot = gtk_list_item_widget_unroot;
|
||||
|
||||
gobject_class->set_property = gtk_list_item_widget_set_property;
|
||||
gobject_class->dispose = gtk_list_item_widget_dispose;
|
||||
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory",
|
||||
"Factory",
|
||||
"Factory managing this list item",
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
signals[ACTIVATE_SIGNAL] =
|
||||
g_signal_new (I_("activate-keybinding"),
|
||||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET (GtkListItemWidgetClass, activate_signal),
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
widget_class->activate_signal = signals[ACTIVATE_SIGNAL];
|
||||
|
||||
/**
|
||||
* GtkListItem|listitem.select:
|
||||
* @modify: %TRUE to toggle the existing selection, %FALSE to select
|
||||
* @extend: %TRUE to extend the selection
|
||||
*
|
||||
* Changes selection if the item is selectable.
|
||||
* If the item is not selectable, nothing happens.
|
||||
*
|
||||
* This function will emit the list.select-item action and the resulting
|
||||
* behavior, in particular the interpretation of @modify and @extend
|
||||
* depends on the view containing this listitem. See for example
|
||||
* GtkListView|list.select-item or GtkGridView|list.select-item.
|
||||
*/
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"listitem.select",
|
||||
"(bb)",
|
||||
gtk_list_item_widget_select_action);
|
||||
|
||||
binding_set = gtk_binding_set_by_class (klass);
|
||||
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
|
||||
"activate-keybinding", 0);
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
|
||||
"activate-keybinding", 0);
|
||||
gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
|
||||
"activate-keybinding", 0);
|
||||
|
||||
/* note that some of these may get overwritten by child widgets,
|
||||
* such as GtkTreeExpander */
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, 0,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, 0,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
gtk_binding_entry_add_action (binding_set, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
||||
"listitem.select", "(bb)", TRUE, FALSE);
|
||||
|
||||
/* This gets overwritten by gtk_list_item_widget_new() but better safe than sorry */
|
||||
gtk_widget_class_set_css_name (widget_class, I_("row"));
|
||||
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
|
||||
if (priv->list_item && !priv->list_item->selectable && !priv->list_item->activatable)
|
||||
{
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!priv->list_item || priv->list_item->selectable)
|
||||
{
|
||||
GdkModifierType state;
|
||||
GdkModifierType mask;
|
||||
gboolean extend = FALSE, modify = FALSE;
|
||||
|
||||
if (gtk_get_current_event_state (&state))
|
||||
{
|
||||
mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
|
||||
if ((state & mask) == mask)
|
||||
modify = TRUE;
|
||||
mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
|
||||
if ((state & mask) == mask)
|
||||
extend = TRUE;
|
||||
}
|
||||
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.select-item",
|
||||
"(ubb)",
|
||||
priv->position, modify, extend);
|
||||
}
|
||||
|
||||
if (!priv->list_item || priv->list_item->activatable)
|
||||
{
|
||||
if (n_press == 2)
|
||||
{
|
||||
gtk_widget_activate_action (GTK_WIDGET (self),
|
||||
"list.activate-item",
|
||||
"u",
|
||||
priv->position);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
|
||||
|
||||
if (gtk_widget_get_focus_on_click (widget))
|
||||
gtk_widget_grab_focus (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_focus_changed_cb (GtkEventControllerKey *controller,
|
||||
GParamSpec *psepc,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (gtk_event_controller_key_contains_focus (controller))
|
||||
{
|
||||
gtk_widget_activate_action (widget,
|
||||
"list.scroll-to-item",
|
||||
"u",
|
||||
priv->position);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_click_gesture_released (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_click_gesture_canceled (GtkGestureClick *gesture,
|
||||
GdkEventSequence *sequence,
|
||||
GtkListItemWidget *self)
|
||||
{
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_widget_init (GtkListItemWidget *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
GtkGesture *gesture;
|
||||
|
||||
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
|
||||
|
||||
gesture = gtk_gesture_click_new ();
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
|
||||
GTK_PHASE_BUBBLE);
|
||||
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
|
||||
FALSE);
|
||||
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
|
||||
GDK_BUTTON_PRIMARY);
|
||||
g_signal_connect (gesture, "pressed",
|
||||
G_CALLBACK (gtk_list_item_widget_click_gesture_pressed), self);
|
||||
g_signal_connect (gesture, "released",
|
||||
G_CALLBACK (gtk_list_item_widget_click_gesture_released), self);
|
||||
g_signal_connect (gesture, "cancel",
|
||||
G_CALLBACK (gtk_list_item_widget_click_gesture_canceled), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "notify::contains-focus", G_CALLBACK (gtk_list_item_widget_focus_changed_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_list_item_widget_new (GtkListItemFactory *factory,
|
||||
const char *css_name)
|
||||
{
|
||||
g_return_val_if_fail (css_name != NULL, NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_LIST_ITEM_WIDGET,
|
||||
"css-name", css_name,
|
||||
"factory", factory,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_update (GtkListItemWidget *self,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->list_item)
|
||||
gtk_list_item_factory_update (priv->factory, self, position, item, selected);
|
||||
else
|
||||
gtk_list_item_widget_default_update (self, NULL, position, item, selected);
|
||||
|
||||
if (selected)
|
||||
gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
|
||||
else
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_default_setup (GtkListItemWidget *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
priv->list_item = list_item;
|
||||
list_item->owner = self;
|
||||
|
||||
if (list_item->child)
|
||||
gtk_list_item_widget_add_child (self, list_item->child);
|
||||
|
||||
if (priv->item)
|
||||
g_object_notify (G_OBJECT (list_item), "item");
|
||||
if (priv->position != GTK_INVALID_LIST_POSITION)
|
||||
g_object_notify (G_OBJECT (list_item), "position");
|
||||
if (priv->selected)
|
||||
g_object_notify (G_OBJECT (list_item), "selected");
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_default_teardown (GtkListItemWidget *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
g_assert (priv->list_item == list_item);
|
||||
|
||||
priv->list_item = NULL;
|
||||
list_item->owner = NULL;
|
||||
|
||||
if (list_item->child)
|
||||
gtk_list_item_widget_remove_child (self, list_item->child);
|
||||
|
||||
if (priv->item)
|
||||
g_object_notify (G_OBJECT (list_item), "item");
|
||||
if (priv->position != GTK_INVALID_LIST_POSITION)
|
||||
g_object_notify (G_OBJECT (list_item), "position");
|
||||
if (priv->selected)
|
||||
g_object_notify (G_OBJECT (list_item), "selected");
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_default_update (GtkListItemWidget *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
/* FIXME: It's kinda evil to notify external objects from here... */
|
||||
|
||||
if (g_set_object (&priv->item, item))
|
||||
{
|
||||
if (list_item)
|
||||
g_object_notify (G_OBJECT (list_item), "item");
|
||||
}
|
||||
|
||||
if (priv->position != position)
|
||||
{
|
||||
priv->position = position;
|
||||
if (list_item)
|
||||
g_object_notify (G_OBJECT (list_item), "position");
|
||||
}
|
||||
|
||||
if (priv->selected != selected)
|
||||
{
|
||||
priv->selected = selected;
|
||||
if (list_item)
|
||||
g_object_notify (G_OBJECT (list_item), "selected");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_set_factory (GtkListItemWidget *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
if (priv->factory == factory)
|
||||
return;
|
||||
|
||||
if (priv->factory)
|
||||
{
|
||||
if (priv->list_item)
|
||||
gtk_list_item_factory_teardown (factory, self);
|
||||
g_clear_object (&priv->factory);
|
||||
}
|
||||
|
||||
if (factory)
|
||||
{
|
||||
priv->factory = g_object_ref (factory);
|
||||
|
||||
if (gtk_widget_get_root (GTK_WIDGET (self)))
|
||||
gtk_list_item_factory_setup (factory, self);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_add_child (GtkListItemWidget *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
gtk_widget_set_parent (child, GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_widget_remove_child (GtkListItemWidget *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
gtk_widget_unparent (child);
|
||||
}
|
||||
|
||||
GtkListItem *
|
||||
gtk_list_item_widget_get_list_item (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
return priv->list_item;
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_list_item_widget_get_position (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
return priv->position;
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_list_item_widget_get_item (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
return priv->item;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_list_item_widget_get_selected (GtkListItemWidget *self)
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
|
||||
return priv->selected;
|
||||
}
|
||||
|
||||
84
gtk/gtklistitemwidgetprivate.h
Normal file
84
gtk/gtklistitemwidgetprivate.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_WIDGET_PRIVATE_H__
|
||||
#define __GTK_LIST_ITEM_WIDGET_PRIVATE_H__
|
||||
|
||||
#include "gtklistitemfactory.h"
|
||||
#include "gtkwidget.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM_WIDGET (gtk_list_item_widget_get_type ())
|
||||
#define GTK_LIST_ITEM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidget))
|
||||
#define GTK_LIST_ITEM_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidgetClass))
|
||||
#define GTK_IS_LIST_ITEM_WIDGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_WIDGET))
|
||||
#define GTK_IS_LIST_ITEM_WIDGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_WIDGET))
|
||||
#define GTK_LIST_ITEM_WIDGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_WIDGET, GtkListItemWidgetClass))
|
||||
|
||||
typedef struct _GtkListItemWidget GtkListItemWidget;
|
||||
typedef struct _GtkListItemWidgetClass GtkListItemWidgetClass;
|
||||
|
||||
struct _GtkListItemWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _GtkListItemWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
|
||||
void (* activate_signal) (GtkListItemWidget *self);
|
||||
};
|
||||
|
||||
GType gtk_list_item_widget_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkWidget * gtk_list_item_widget_new (GtkListItemFactory *factory,
|
||||
const char *css_name);
|
||||
|
||||
void gtk_list_item_widget_update (GtkListItemWidget *self,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
GtkListItem * gtk_list_item_widget_get_list_item (GtkListItemWidget *self);
|
||||
|
||||
void gtk_list_item_widget_default_setup (GtkListItemWidget *self,
|
||||
GtkListItem *list_item);
|
||||
void gtk_list_item_widget_default_teardown (GtkListItemWidget *self,
|
||||
GtkListItem *list_item);
|
||||
void gtk_list_item_widget_default_update (GtkListItemWidget *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
|
||||
void gtk_list_item_widget_set_factory (GtkListItemWidget *self,
|
||||
GtkListItemFactory *factory);
|
||||
void gtk_list_item_widget_add_child (GtkListItemWidget *self,
|
||||
GtkWidget *child);
|
||||
void gtk_list_item_widget_remove_child (GtkListItemWidget *self,
|
||||
GtkWidget *child);
|
||||
|
||||
guint gtk_list_item_widget_get_position (GtkListItemWidget *self);
|
||||
gpointer gtk_list_item_widget_get_item (GtkListItemWidget *self);
|
||||
gboolean gtk_list_item_widget_get_selected (GtkListItemWidget *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_WIDGET_PRIVATE_H__ */
|
||||
@@ -206,15 +206,12 @@ gtk_list_list_model_new_with_size (GType item_type,
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
gpointer item)
|
||||
static guint
|
||||
gtk_list_list_model_find (GtkListListModel *self,
|
||||
gpointer item)
|
||||
{
|
||||
gpointer x;
|
||||
guint position;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != NULL);
|
||||
gpointer x;
|
||||
|
||||
position = 0;
|
||||
for (x = self->get_first (self->data);
|
||||
@@ -222,7 +219,17 @@ gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
x = self->get_next (x, self->data))
|
||||
position++;
|
||||
|
||||
gtk_list_list_model_item_added_at (self, position);
|
||||
return position;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != NULL);
|
||||
|
||||
gtk_list_list_model_item_added_at (self, gtk_list_list_model_find (self, item));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -241,26 +248,49 @@ void
|
||||
gtk_list_list_model_item_removed (GtkListListModel *self,
|
||||
gpointer previous)
|
||||
{
|
||||
gpointer x;
|
||||
guint position;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
|
||||
if (previous == NULL)
|
||||
position = 0;
|
||||
else
|
||||
position = 1 + gtk_list_list_model_find (self, previous);
|
||||
|
||||
gtk_list_list_model_item_removed_at (self, position);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_moved (GtkListListModel *self,
|
||||
gpointer item,
|
||||
gpointer previous_previous)
|
||||
{
|
||||
guint position, previous_position;
|
||||
guint min, max;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != previous_previous);
|
||||
|
||||
position = gtk_list_list_model_find (self, item);
|
||||
|
||||
if (previous_previous == NULL)
|
||||
{
|
||||
position = 0;
|
||||
previous_position = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = 1;
|
||||
|
||||
for (x = self->get_first (self->data);
|
||||
x != previous;
|
||||
x = self->get_next (x, self->data))
|
||||
position++;
|
||||
previous_position = gtk_list_list_model_find (self, previous_previous);
|
||||
if (position > previous_position)
|
||||
previous_position++;
|
||||
}
|
||||
|
||||
gtk_list_list_model_item_removed_at (self, position);
|
||||
/* item didn't move */
|
||||
if (position == previous_position)
|
||||
return;
|
||||
|
||||
min = MIN (position, previous_position);
|
||||
max = MAX (position, previous_position) + 1;
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), min, max - min, max - min);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user