* 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
+/**
+ * SECTION:gtkfilefilter
+ * @Short_description: A filter for selecting a file subset
+ * @Title: GtkFileFilter
+ * @see_also: #GtkFileChooser
+ *
+ * A GtkFileFilter can be used to restrict the files being shown in a
+ * #GtkFileChooser. Files can be filtered based on their name (with
+ * gtk_file_filter_add_pattern()), on their mime type (with
+ * gtk_file_filter_add_mime_type()), or by a custom filter function
+ * (with gtk_file_filter_add_custom()).
+ *
+ * Filtering by mime types handles aliasing and subclassing of mime
+ * types; e.g. a filter for text/plain also matches a file with mime
+ * type application/rtf, since application/rtf is a subclass of
+ * text/plain. Note that #GtkFileFilter allows wildcards for the
+ * subtype of a mime type, so you can e.g. filter for image/*.
+ *
+ * Normally, filters are used by adding them to a #GtkFileChooser,
+ * see gtk_file_chooser_add_filter(), but it is also possible
+ * to manually use a filter on a file with gtk_file_filter_filter().
+ *
+ * <refsect2 id="GtkFileFilter-BUILDER-UI">
+ * <title>GtkFileFilter as GtkBuildable</title>
+ * <para>
+ * The GtkFileFilter implementation of the GtkBuildable interface
+ * supports adding rules using the <mime-types>, <patterns> and
+ * <applications> elements and listing the rules within. Specifying
+ * a <mime-type> or <pattern> is the same
+ * as calling gtk_recent_filter_add_mime_type() or gtk_recent_filter_add_pattern()
+ *
+ * <example>
+ * <title>A UI definition fragment specifying GtkFileFilter rules</title>
+ * <programlisting><![CDATA[
+ * <object class="GtkFileFilter">
+ * <mime-types>
+ * <mime-type>text/plain</mime-type>
+ * <mime-type>image/*</mime-type>
+ * </mime-types>
+ * <patterns>
+ * <pattern>*.txt</pattern>
+ * <pattern>*.png</pattern>
+ * </patterns>
+ * </object>
+ * ]]></programlisting>
+ * </example>
+ * </para>
+ * </refsect2>
+ */
+
+#include "config.h"
#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
#include "gtkfilefilter.h"
-#include "gtkobject.h"
+#include "gtkbuildable.h"
+#include "gtkintl.h"
#include "gtkprivate.h"
typedef struct _GtkFileFilterClass GtkFileFilterClass;
typedef enum {
FILTER_RULE_PATTERN,
FILTER_RULE_MIME_TYPE,
- FILTER_RULE_CUSTOM,
+ FILTER_RULE_PIXBUF_FORMATS,
+ FILTER_RULE_CUSTOM
} FilterRuleType;
struct _GtkFileFilterClass
{
- GtkObjectClass parent_class;
+ GInitiallyUnownedClass parent_class;
};
struct _GtkFileFilter
{
- GtkObject parent_instance;
-
+ GInitiallyUnowned parent_instance;
+
gchar *name;
GSList *rules;
union {
gchar *pattern;
gchar *mime_type;
+ GSList *pixbuf_formats;
struct {
GtkFileFilterFunc func;
gpointer data;
} u;
};
-static void gtk_file_filter_class_init (GtkFileFilterClass *class);
static void gtk_file_filter_finalize (GObject *object);
-static GObjectClass *parent_class;
-GType
-gtk_file_filter_get_type (void)
-{
- static GType file_filter_type = 0;
+static void gtk_file_filter_buildable_init (GtkBuildableIface *iface);
+static gboolean gtk_file_filter_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GMarkupParser *parser,
+ gpointer *data);
+static void gtk_file_filter_buildable_custom_tag_end (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer *data);
- if (!file_filter_type)
- {
- static const GTypeInfo file_filter_info =
- {
- sizeof (GtkFileFilterClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) gtk_file_filter_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GtkFileFilter),
- 0, /* n_preallocs */
- NULL /* init */
- };
-
- file_filter_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkFileFilter",
- &file_filter_info, 0);
- }
+G_DEFINE_TYPE_WITH_CODE (GtkFileFilter, gtk_file_filter, G_TYPE_INITIALLY_UNOWNED,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+ gtk_file_filter_buildable_init))
- return file_filter_type;
+static void
+gtk_file_filter_init (GtkFileFilter *object)
+{
}
static void
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
- parent_class = g_type_class_peek_parent (class);
-
gobject_class->finalize = gtk_file_filter_finalize;
}
if (rule->u.custom.notify)
rule->u.custom.notify (rule->u.custom.data);
break;
+ case FILTER_RULE_PIXBUF_FORMATS:
+ g_slist_free (rule->u.pixbuf_formats);
+ break;
+ default:
+ g_assert_not_reached ();
}
- g_free (rule);
+ g_slice_free (FilterRule, rule);
}
static void
gtk_file_filter_finalize (GObject *object)
{
- GtkFileFilter *filter = GTK_FILE_FILTER (filter);
+ GtkFileFilter *filter = GTK_FILE_FILTER (object);
g_slist_foreach (filter->rules, (GFunc)filter_rule_free, NULL);
+ g_slist_free (filter->rules);
+
+ g_free (filter->name);
+
+ G_OBJECT_CLASS (gtk_file_filter_parent_class)->finalize (object);
+}
+
+/*
+ * GtkBuildable implementation
+ */
+static void
+gtk_file_filter_buildable_init (GtkBuildableIface *iface)
+{
+ iface->custom_tag_start = gtk_file_filter_buildable_custom_tag_start;
+ iface->custom_tag_end = gtk_file_filter_buildable_custom_tag_end;
+}
+
+typedef enum {
+ PARSE_MIME_TYPES,
+ PARSE_PATTERNS
+} ParserType;
- parent_class->finalize (object);
+typedef struct {
+ GtkFileFilter *filter;
+ ParserType type;
+ GString *string;
+ gboolean parsing;
+} SubParserData;
+
+static void
+parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ gpointer user_data,
+ GError **error)
+{
+ SubParserData *parser_data = (SubParserData*)user_data;
+
+ if (strcmp (element_name, "mime-types") == 0)
+ return;
+ else if (strcmp (element_name, "mime-type") == 0)
+ {
+ parser_data->parsing = TRUE;
+ return;
+ }
+ else if (strcmp (element_name, "patterns") == 0)
+ return;
+ else if (strcmp (element_name, "pattern") == 0)
+ {
+ parser_data->parsing = TRUE;
+ return;
+ }
+ else
+ g_warning ("Unsupported tag for GtkFileFilter: %s\n", element_name);
}
+static void
+parser_text_element (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ SubParserData *parser_data = (SubParserData*)user_data;
+
+ if (parser_data->parsing)
+ g_string_append_len (parser_data->string, text, text_len);
+}
+
+static void
+parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ SubParserData *parser_data = (SubParserData*)user_data;
+
+ if (parser_data->string)
+ {
+ switch (parser_data->type)
+ {
+ case PARSE_MIME_TYPES:
+ gtk_file_filter_add_mime_type (parser_data->filter, parser_data->string->str);
+ break;
+ case PARSE_PATTERNS:
+ gtk_file_filter_add_pattern (parser_data->filter, parser_data->string->str);
+ break;
+ default:
+ break;
+ }
+ }
+
+ g_string_set_size (parser_data->string, 0);
+ parser_data->parsing = FALSE;
+}
+
+static const GMarkupParser sub_parser =
+ {
+ parser_start_element,
+ parser_end_element,
+ parser_text_element,
+ };
+
+static gboolean
+gtk_file_filter_buildable_custom_tag_start (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ GMarkupParser *parser,
+ gpointer *data)
+{
+ SubParserData *parser_data = NULL;
+
+ if (strcmp (tagname, "mime-types") == 0)
+ {
+ parser_data = g_slice_new0 (SubParserData);
+ parser_data->string = g_string_new ("");
+ parser_data->type = PARSE_MIME_TYPES;
+ parser_data->filter = GTK_FILE_FILTER (buildable);
+
+ *parser = sub_parser;
+ *data = parser_data;
+ }
+ else if (strcmp (tagname, "patterns") == 0)
+ {
+ parser_data = g_slice_new0 (SubParserData);
+ parser_data->string = g_string_new ("");
+ parser_data->type = PARSE_PATTERNS;
+ parser_data->filter = GTK_FILE_FILTER (buildable);
+
+ *parser = sub_parser;
+ *data = parser_data;
+ }
+
+ return parser_data != NULL;
+}
+
+static void
+gtk_file_filter_buildable_custom_tag_end (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *tagname,
+ gpointer *data)
+{
+ if (strcmp (tagname, "mime-types") == 0 ||
+ strcmp (tagname, "patterns") == 0)
+ {
+ SubParserData *parser_data = (SubParserData*)data;
+
+ g_string_free (parser_data->string, TRUE);
+ g_slice_free (SubParserData, parser_data);
+ }
+}
+
+
/**
* gtk_file_filter_new:
*
* gtk_file_filter_add_mime_type(), gtk_file_filter_add_pattern(),
* or gtk_file_filter_add_custom(). To create a filter
* that accepts any file, use:
- *
- * <informalexample><programlisting>
- * GtkFileFilter *filter = gtk_file_filter_new (<!-- -->);
+ * |[
+ * GtkFileFilter *filter = gtk_file_filter_new ();
* gtk_file_filter_add_pattern (filter, "*");
- * </programlisting></informalexample>
+ * ]|
*
* Return value: a new #GtkFileFilter
*
/**
* gtk_file_filter_set_name:
* @filter: a #GtkFileFilter
- * @name: the human-readable-name for the filter, or %NULL
+ * @name: (allow-none): the human-readable-name for the filter, or %NULL
* to remove any existing name.
*
* Sets the human-readable name of the filter; this is the string
{
g_return_if_fail (GTK_IS_FILE_FILTER (filter));
- if (filter->name)
- g_free (filter->name);
+ g_free (filter->name);
filter->name = g_strdup (name);
}
*
* Since: 2.4
**/
-G_CONST_RETURN gchar *
+const gchar *
gtk_file_filter_get_name (GtkFileFilter *filter)
{
g_return_val_if_fail (GTK_IS_FILE_FILTER (filter), NULL);
g_return_if_fail (GTK_IS_FILE_FILTER (filter));
g_return_if_fail (mime_type != NULL);
- rule = g_new (FilterRule, 1);
+ rule = g_slice_new (FilterRule);
rule->type = FILTER_RULE_MIME_TYPE;
rule->needed = GTK_FILE_FILTER_MIME_TYPE;
rule->u.mime_type = g_strdup (mime_type);
g_return_if_fail (GTK_IS_FILE_FILTER (filter));
g_return_if_fail (pattern != NULL);
- rule = g_new (FilterRule, 1);
+ rule = g_slice_new (FilterRule);
rule->type = FILTER_RULE_PATTERN;
rule->needed = GTK_FILE_FILTER_DISPLAY_NAME;
rule->u.pattern = g_strdup (pattern);
file_filter_add_rule (filter, rule);
}
+/**
+ * gtk_file_filter_add_pixbuf_formats:
+ * @filter: a #GtkFileFilter
+ *
+ * Adds a rule allowing image files in the formats supported
+ * by GdkPixbuf.
+ *
+ * Since: 2.6
+ **/
+void
+gtk_file_filter_add_pixbuf_formats (GtkFileFilter *filter)
+{
+ FilterRule *rule;
+
+ g_return_if_fail (GTK_IS_FILE_FILTER (filter));
+
+ rule = g_slice_new (FilterRule);
+ rule->type = FILTER_RULE_PIXBUF_FORMATS;
+ rule->needed = GTK_FILE_FILTER_MIME_TYPE;
+ rule->u.pixbuf_formats = gdk_pixbuf_get_formats ();
+ file_filter_add_rule (filter, rule);
+}
+
+
/**
* gtk_file_filter_add_custom:
* @filter: a #GtkFileFilter
g_return_if_fail (GTK_IS_FILE_FILTER (filter));
g_return_if_fail (func != NULL);
- rule = g_new (FilterRule, 1);
+ rule = g_slice_new (FilterRule);
rule->type = FILTER_RULE_CUSTOM;
rule->needed = needed;
rule->u.custom.func = func;
*
* Tests whether a file should be displayed according to @filter.
* The #GtkFileFilterInfo structure @filter_info should include
- * the fields returned feom gtk_file_filter_get_needed().
+ * the fields returned from gtk_file_filter_get_needed().
*
* This function will not typically be used by applications; it
* is intended principally for use in the implementation of
switch (rule->type)
{
case FILTER_RULE_MIME_TYPE:
- if (filter_info->mime_type != NULL
- && strcmp (rule->u.mime_type, filter_info->mime_type) == 0)
+ if (filter_info->mime_type != NULL &&
+ g_content_type_is_a (filter_info->mime_type, rule->u.mime_type))
return TRUE;
break;
case FILTER_RULE_PATTERN:
- if (_gtk_fnmatch (rule->u.pattern, filter_info->display_name))
+ if (filter_info->display_name != NULL &&
+ _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE))
return TRUE;
break;
+ case FILTER_RULE_PIXBUF_FORMATS:
+ {
+ GSList *list;
+
+ if (!filter_info->mime_type)
+ break;
+
+ for (list = rule->u.pixbuf_formats; list; list = list->next)
+ {
+ int i;
+ gchar **mime_types;
+
+ mime_types = gdk_pixbuf_format_get_mime_types (list->data);
+
+ for (i = 0; mime_types[i] != NULL; i++)
+ {
+ if (strcmp (mime_types[i], filter_info->mime_type) == 0)
+ {
+ g_strfreev (mime_types);
+ return TRUE;
+ }
+ }
+
+ g_strfreev (mime_types);
+ }
+ break;
+ }
case FILTER_RULE_CUSTOM:
if (rule->u.custom.func (filter_info, rule->u.custom.data))
return TRUE;