mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
source tree cleanup
The lib/ directory was getting a bit out of hand, so this tries to organize things a bit so it is easier going forward to locate the code people want to patch.
This commit is contained in:
@ -35,10 +35,10 @@
|
||||
#include <sys/stat.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "binfile.h"
|
||||
#include "elfparser.h"
|
||||
#include "util/binfile.h"
|
||||
#include "util/elfparser.h"
|
||||
|
||||
#include "../sp-symbol-dirs.h"
|
||||
#include "symbols/sp-symbol-dirs.h"
|
||||
|
||||
struct bin_file_t
|
||||
{
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
#include <elf.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "demangle.h"
|
||||
#include "elfparser.h"
|
||||
#include "util/demangle.h"
|
||||
#include "util/elfparser.h"
|
||||
|
||||
typedef struct Section Section;
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
#define G_LOG_DOMAIN "pointcache"
|
||||
|
||||
#include "pointcache.h"
|
||||
#include "util/pointcache.h"
|
||||
|
||||
struct _PointCache
|
||||
{
|
||||
|
||||
136
lib/util/sp-color-cycle.c
Normal file
136
lib/util/sp-color-cycle.c
Normal file
@ -0,0 +1,136 @@
|
||||
/* sp-color-cycle.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-color-cycle"
|
||||
|
||||
#include "util/sp-color-cycle.h"
|
||||
|
||||
G_DEFINE_BOXED_TYPE (SpColorCycle, sp_color_cycle, sp_color_cycle_ref, sp_color_cycle_unref)
|
||||
|
||||
static const gchar *default_colors[] = {
|
||||
"#73d216",
|
||||
"#f57900",
|
||||
"#3465a4",
|
||||
"#ef2929",
|
||||
"#75507b",
|
||||
"#ce5c00",
|
||||
"#c17d11",
|
||||
"#cc0000",
|
||||
"#edd400",
|
||||
"#555753",
|
||||
"#4e9a06",
|
||||
"#204a87",
|
||||
"#5c3566",
|
||||
"#a40000",
|
||||
"#c4a000",
|
||||
"#8f5902",
|
||||
"#2e3436",
|
||||
"#8ae234",
|
||||
"#729fcf",
|
||||
"#ad7fa8",
|
||||
"#fce94f",
|
||||
"#fcaf3e",
|
||||
"#e9b96e",
|
||||
"#888a85",
|
||||
NULL
|
||||
};
|
||||
|
||||
struct _SpColorCycle
|
||||
{
|
||||
volatile gint ref_count;
|
||||
GdkRGBA *colors;
|
||||
gsize n_colors;
|
||||
guint position;
|
||||
};
|
||||
|
||||
static void
|
||||
sp_color_cycle_destroy (SpColorCycle *self)
|
||||
{
|
||||
g_free (self->colors);
|
||||
g_slice_free (SpColorCycle, self);
|
||||
}
|
||||
|
||||
SpColorCycle *
|
||||
sp_color_cycle_new (void)
|
||||
{
|
||||
SpColorCycle *self;
|
||||
|
||||
self = g_slice_new0 (SpColorCycle);
|
||||
self->ref_count = 1;
|
||||
self->n_colors = g_strv_length ((gchar **)default_colors);
|
||||
self->colors = g_new0 (GdkRGBA, self->n_colors);
|
||||
|
||||
for (guint i = 0; default_colors[i]; i++)
|
||||
{
|
||||
if G_UNLIKELY (!gdk_rgba_parse (&self->colors[i], default_colors[i]))
|
||||
g_warning ("Failed to parse color %s into an RGBA", default_colors[i]);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
SpColorCycle *
|
||||
sp_color_cycle_ref (SpColorCycle *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
g_return_val_if_fail (self->ref_count > 0, NULL);
|
||||
g_atomic_int_inc (&self->ref_count);
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
sp_color_cycle_unref (SpColorCycle *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->ref_count > 0);
|
||||
if (g_atomic_int_dec_and_test (&self->ref_count))
|
||||
sp_color_cycle_destroy (self);
|
||||
}
|
||||
|
||||
void
|
||||
sp_color_cycle_next (SpColorCycle *self,
|
||||
GdkRGBA *rgba)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->position < self->n_colors);
|
||||
|
||||
*rgba = self->colors[self->position];
|
||||
|
||||
/*
|
||||
* TODO: Adjust color HSV/etc
|
||||
*
|
||||
* We could simply adjust the brightness/etc after we dispatch
|
||||
* a color so that we get darker as we go.
|
||||
*/
|
||||
|
||||
self->position = (self->position + 1) % self->n_colors;
|
||||
}
|
||||
|
||||
void
|
||||
sp_color_cycle_reset (SpColorCycle *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
for (guint i = 0; default_colors[i]; i++)
|
||||
{
|
||||
if G_UNLIKELY (!gdk_rgba_parse (&self->colors[i], default_colors[i]))
|
||||
g_warning ("Failed to parse color %s into an RGBA", default_colors[i]);
|
||||
}
|
||||
|
||||
self->position = 0;
|
||||
}
|
||||
40
lib/util/sp-color-cycle.h
Normal file
40
lib/util/sp-color-cycle.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* sp-color-cycle.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_COLOR_CYCLE_H
|
||||
#define SP_COLOR_CYCLE_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_COLOR_CYCLE (sp_color_cycle_get_type())
|
||||
|
||||
typedef struct _SpColorCycle SpColorCycle;
|
||||
|
||||
GType sp_color_cycle_get_type (void);
|
||||
SpColorCycle *sp_color_cycle_ref (SpColorCycle *self);
|
||||
void sp_color_cycle_unref (SpColorCycle *self);
|
||||
SpColorCycle *sp_color_cycle_new (void);
|
||||
void sp_color_cycle_reset (SpColorCycle *self);
|
||||
void sp_color_cycle_next (SpColorCycle *self,
|
||||
GdkRGBA *rgba);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_COLOR_CYCLE_H */
|
||||
118
lib/util/sp-line-reader.c
Normal file
118
lib/util/sp-line-reader.c
Normal file
@ -0,0 +1,118 @@
|
||||
/* sp-line-reader.c
|
||||
*
|
||||
* Copyright (C) 2015 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "util/sp-line-reader.h"
|
||||
|
||||
struct _SpLineReader
|
||||
{
|
||||
const gchar *contents;
|
||||
gsize length;
|
||||
gsize pos;
|
||||
};
|
||||
|
||||
void
|
||||
sp_line_reader_free (SpLineReader *self)
|
||||
{
|
||||
g_slice_free (SpLineReader, self);
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_line_reader_new:
|
||||
* @contents: The buffer to read lines from
|
||||
* @length: the length of @buffer in bytes
|
||||
*
|
||||
* Creates a new #SpLineReader for the contents provided. @contents are not
|
||||
* copied and therefore it is a programming error to free contents before
|
||||
* freeing the #SpLineReader structure.
|
||||
*
|
||||
* Use sp_line_reader_next() to read through the lines of the buffer.
|
||||
*
|
||||
* Returns: (transfer full): A new #SpLineReader that should be freed with
|
||||
* sp_line_reader_free() when no longer in use.
|
||||
*/
|
||||
SpLineReader *
|
||||
sp_line_reader_new (const gchar *contents,
|
||||
gssize length)
|
||||
{
|
||||
SpLineReader *self = g_slice_new (SpLineReader);
|
||||
|
||||
if (contents == NULL)
|
||||
{
|
||||
contents = "";
|
||||
length = 0;
|
||||
}
|
||||
else if (length < 0)
|
||||
{
|
||||
length = strlen (contents);
|
||||
}
|
||||
|
||||
self->contents = contents;
|
||||
self->length = length;
|
||||
self->pos = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_line_reader_next:
|
||||
* @self: the #SpLineReader
|
||||
* @length: a location for the length of the line in bytes
|
||||
*
|
||||
* Moves forward to the beginning of the next line in the buffer. No changes to
|
||||
* the buffer are made, and the result is a pointer within the string passed as
|
||||
* @contents in sp_line_reader_init(). Since the line most likely will not be
|
||||
* terminated with a NULL byte, you must provide @length to determine the
|
||||
* length of the line.
|
||||
*
|
||||
* Using "line[length]" will place you on the \n that was found for the line.
|
||||
* However, to perform this safely, you need to know that your string was
|
||||
* either \0 terminated to begin with, or that your buffer provides enough space
|
||||
* to guarantee you can dereference past the last "textual content" of the
|
||||
* buffer.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The beginning of the line within the buffer
|
||||
*/
|
||||
const gchar *
|
||||
sp_line_reader_next (SpLineReader *self,
|
||||
gsize *length)
|
||||
{
|
||||
const gchar *ret;
|
||||
const gchar *endptr;
|
||||
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
g_return_val_if_fail (length != NULL, NULL);
|
||||
|
||||
if ((self->contents == NULL) || (self->pos >= self->length))
|
||||
{
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = &self->contents [self->pos];
|
||||
|
||||
endptr = memchr (ret, '\n', self->length - self->pos);
|
||||
if (G_UNLIKELY (endptr == NULL))
|
||||
endptr = &self->contents [self->length];
|
||||
|
||||
*length = (endptr - ret);
|
||||
self->pos += *length + 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
38
lib/util/sp-line-reader.h
Normal file
38
lib/util/sp-line-reader.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* sp-line-reader.h
|
||||
*
|
||||
* Copyright (C) 2015 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_LINE_READER_H
|
||||
#define SP_LINE_READER_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _SpLineReader SpLineReader;
|
||||
|
||||
SpLineReader *sp_line_reader_new (const gchar *contents,
|
||||
gssize length);
|
||||
void sp_line_reader_free (SpLineReader *self);
|
||||
const gchar *sp_line_reader_next (SpLineReader *self,
|
||||
gsize *length);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpLineReader, sp_line_reader_free)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_LINE_READER_H */
|
||||
119
lib/util/sp-map-lookaside.c
Normal file
119
lib/util/sp-map-lookaside.c
Normal file
@ -0,0 +1,119 @@
|
||||
/* sp-map-lookaside.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "util/sp-map-lookaside.h"
|
||||
|
||||
struct _SpMapLookaside
|
||||
{
|
||||
GSequence *seq;
|
||||
GStringChunk *chunk;
|
||||
};
|
||||
|
||||
static gint
|
||||
sp_map_compare (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer user_data)
|
||||
{
|
||||
const SpMap *map_a = a;
|
||||
const SpMap *map_b = b;
|
||||
|
||||
return sp_capture_address_compare (map_a->start, map_b->start);
|
||||
}
|
||||
|
||||
static gint
|
||||
sp_map_compare_in_range (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer user_data)
|
||||
{
|
||||
const SpMap *map_a = a;
|
||||
const SpMap *map_b = b;
|
||||
|
||||
/*
|
||||
* map_b is the needle for the search.
|
||||
* Only map_b->start is set.
|
||||
*/
|
||||
|
||||
if ((map_b->start >= map_a->start) && (map_b->start < map_a->end))
|
||||
return 0;
|
||||
|
||||
return sp_capture_address_compare (map_a->start, map_b->start);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_map_free (gpointer data)
|
||||
{
|
||||
SpMap *map = data;
|
||||
|
||||
g_slice_free (SpMap, map);
|
||||
}
|
||||
|
||||
SpMapLookaside *
|
||||
sp_map_lookaside_new (void)
|
||||
{
|
||||
SpMapLookaside *ret;
|
||||
|
||||
ret = g_slice_new (SpMapLookaside);
|
||||
ret->seq = g_sequence_new (sp_map_free);
|
||||
ret->chunk = g_string_chunk_new (4096);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
sp_map_lookaside_free (SpMapLookaside *self)
|
||||
{
|
||||
g_sequence_free (self->seq);
|
||||
g_string_chunk_free (self->chunk);
|
||||
g_slice_free (SpMapLookaside, self);
|
||||
}
|
||||
|
||||
void
|
||||
sp_map_lookaside_insert (SpMapLookaside *self,
|
||||
const SpMap *map)
|
||||
{
|
||||
SpMap *copy;
|
||||
|
||||
g_assert (self != NULL);
|
||||
g_assert (map != NULL);
|
||||
|
||||
copy = g_slice_new (SpMap);
|
||||
copy->start = map->start;
|
||||
copy->end = map->end;
|
||||
copy->offset = map->offset;
|
||||
copy->inode = map->inode;
|
||||
copy->filename = g_string_chunk_insert_const (self->chunk, map->filename);
|
||||
|
||||
g_sequence_insert_sorted (self->seq, copy, sp_map_compare, NULL);
|
||||
}
|
||||
|
||||
const SpMap *
|
||||
sp_map_lookaside_lookup (SpMapLookaside *self,
|
||||
SpCaptureAddress address)
|
||||
{
|
||||
SpMap map = { address };
|
||||
GSequenceIter *iter;
|
||||
|
||||
g_assert (self != NULL);
|
||||
|
||||
iter = g_sequence_lookup (self->seq, &map, sp_map_compare_in_range, NULL);
|
||||
|
||||
if (iter != NULL)
|
||||
return g_sequence_get (iter);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
50
lib/util/sp-map-lookaside.h
Normal file
50
lib/util/sp-map-lookaside.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* sp-map-lookaside.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_MAP_LOOKASIDE_H
|
||||
#define SP_MAP_LOOKASIDE_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "capture/sp-capture-types.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _SpMapLookaside SpMapLookaside;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SpCaptureAddress start;
|
||||
SpCaptureAddress end;
|
||||
off_t offset;
|
||||
ino_t inode;
|
||||
const gchar *filename;
|
||||
} SpMap;
|
||||
|
||||
SpMapLookaside *sp_map_lookaside_new (void);
|
||||
void sp_map_lookaside_insert (SpMapLookaside *self,
|
||||
const SpMap *map);
|
||||
const SpMap *sp_map_lookaside_lookup (SpMapLookaside *self,
|
||||
SpCaptureAddress address);
|
||||
void sp_map_lookaside_free (SpMapLookaside *self);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpMapLookaside, sp_map_lookaside_free)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_MAP_LOOKASIDE_H */
|
||||
472
lib/util/sp-model-filter.c
Normal file
472
lib/util/sp-model-filter.c
Normal file
@ -0,0 +1,472 @@
|
||||
/* sp-model-filter.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "util/sp-model-filter.h"
|
||||
|
||||
/*
|
||||
* This is a simple model filter for GListModel.
|
||||
*
|
||||
* Let me start by saying how it works, and then how I wish it worked.
|
||||
*
|
||||
* This uses 2 GSequence (Treaps) to track the filters. One matches the
|
||||
* underlying listmodel. One matches the filtered set. We hold onto our
|
||||
* own reference to the object in the child list model, and update things
|
||||
* as necessary when ::items-changed is emitted.
|
||||
*
|
||||
* I'd rather see this solved in one of two ways.
|
||||
*
|
||||
* 1) Add filtering support to GListStore
|
||||
*
|
||||
* or
|
||||
*
|
||||
* 2) Create a multi-tree data-structure that contains two tree nodes
|
||||
* in each element. One tree contains all items, one tree contains
|
||||
* the visible items (a subset of the other tree). The nodes might
|
||||
* look something like:
|
||||
*
|
||||
* Item {
|
||||
* TreeNode all_tree;
|
||||
* TreeNode visible_tree;
|
||||
* GObject *item;
|
||||
* }
|
||||
*
|
||||
* But either way, this gets the job done for now. I'd venture a guess
|
||||
* that in many cases (small lists), this is actually slower than just
|
||||
* rechecking a simple GPtrArray, but let's see how it goes.
|
||||
*
|
||||
* -- Christian
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GListModel *child_model;
|
||||
|
||||
GSequence *seq;
|
||||
GSequence *visible_seq;
|
||||
|
||||
SpModelFilterFunc filter_func;
|
||||
gpointer filter_func_data;
|
||||
GDestroyNotify filter_func_data_destroy;
|
||||
|
||||
guint needs_rebuild : 1;
|
||||
} SpModelFilterPrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
GObject *object;
|
||||
} Element;
|
||||
|
||||
static void list_model_iface_init (GListModelInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (SpModelFilter, sp_model_filter, G_TYPE_OBJECT, 0,
|
||||
G_ADD_PRIVATE (SpModelFilter)
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
||||
list_model_iface_init))
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CHILD_MODEL,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
static void
|
||||
element_free (gpointer data)
|
||||
{
|
||||
Element *e = data;
|
||||
|
||||
g_clear_object (&e->object);
|
||||
g_slice_free (Element, e);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_model_filter_default_filter_func (GObject *item,
|
||||
gpointer user_data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_child_model_items_changed (SpModelFilter *self,
|
||||
guint position,
|
||||
guint n_removed,
|
||||
guint n_added,
|
||||
GListModel *child_model)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
GSequenceIter *insert_before = NULL;
|
||||
GSequenceIter *insert_iter;
|
||||
GSequenceIter *lower;
|
||||
GSequenceIter *upper;
|
||||
guint i;
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
g_assert (G_IS_LIST_MODEL (child_model));
|
||||
|
||||
|
||||
for (i = 0; i < n_removed; i++)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
Element *ele;
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (priv->seq, position);
|
||||
ele = g_sequence_get (iter);
|
||||
|
||||
if (ele->iter)
|
||||
{
|
||||
guint visible_position = g_sequence_iter_get_position (ele->iter);
|
||||
|
||||
insert_before = g_sequence_iter_next (ele->iter);
|
||||
g_sequence_remove (ele->iter);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), visible_position, 1, 0);
|
||||
}
|
||||
|
||||
g_sequence_remove (iter);
|
||||
}
|
||||
|
||||
insert_iter = g_sequence_get_iter_at_pos (priv->seq, position + 1);
|
||||
|
||||
if (insert_before != NULL)
|
||||
goto add_items;
|
||||
|
||||
#if GLIB_CHECK_VERSION(2, 48, 0)
|
||||
if (g_sequence_is_empty (priv->visible_seq))
|
||||
#else
|
||||
if (g_sequence_get_begin_iter (priv->visible_seq) == g_sequence_get_end_iter (priv->visible_seq))
|
||||
#endif
|
||||
{
|
||||
insert_before = g_sequence_get_end_iter (priv->visible_seq);
|
||||
goto add_items;
|
||||
}
|
||||
|
||||
lower = g_sequence_get_begin_iter (priv->visible_seq);
|
||||
upper = g_sequence_get_end_iter (priv->visible_seq);
|
||||
|
||||
while (lower != upper)
|
||||
{
|
||||
GSequenceIter *mid;
|
||||
GSequenceIter *iter;
|
||||
guint mid_pos;
|
||||
|
||||
mid = g_sequence_range_get_midpoint (lower, upper);
|
||||
iter = g_sequence_get (mid);
|
||||
mid_pos = g_sequence_iter_get_position (iter);
|
||||
|
||||
if (mid_pos < position)
|
||||
lower = g_sequence_iter_next (mid);
|
||||
else if (mid_pos > position)
|
||||
upper = g_sequence_iter_prev (mid);
|
||||
else
|
||||
upper = lower = mid;
|
||||
}
|
||||
|
||||
if (upper == g_sequence_get_end_iter (priv->visible_seq))
|
||||
insert_before = upper;
|
||||
else
|
||||
insert_before =
|
||||
((guint)g_sequence_iter_get_position (g_sequence_get (upper)) <= position)
|
||||
? upper : g_sequence_iter_next (upper);
|
||||
|
||||
add_items:
|
||||
for (i = 0; i < n_added; i++)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
Element *ele;
|
||||
|
||||
ele = g_slice_new (Element);
|
||||
ele->object = g_list_model_get_item (priv->child_model, position + i);
|
||||
ele->iter = NULL;
|
||||
|
||||
iter = g_sequence_insert_before (insert_iter, ele);
|
||||
|
||||
if (priv->filter_func (ele->object, priv->filter_func_data))
|
||||
{
|
||||
ele->iter = g_sequence_insert_before (insert_before, iter);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self),
|
||||
g_sequence_iter_get_position (ele->iter),
|
||||
0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_finalize (GObject *object)
|
||||
{
|
||||
SpModelFilter *self = (SpModelFilter *)object;
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&priv->seq, g_sequence_free);
|
||||
g_clear_pointer (&priv->visible_seq, g_sequence_free);
|
||||
|
||||
if (priv->filter_func_data_destroy)
|
||||
{
|
||||
g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy);
|
||||
priv->filter_func_data_destroy = NULL;
|
||||
}
|
||||
|
||||
g_clear_object (&priv->child_model);
|
||||
|
||||
G_OBJECT_CLASS (sp_model_filter_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpModelFilter *self = SP_MODEL_FILTER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_CHILD_MODEL:
|
||||
g_value_set_object (value, sp_model_filter_get_child_model (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_class_init (SpModelFilterClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_model_filter_finalize;
|
||||
object_class->get_property = sp_model_filter_get_property;
|
||||
|
||||
properties [PROP_CHILD_MODEL] =
|
||||
g_param_spec_object ("child-model",
|
||||
"Child Model",
|
||||
"The child model being filtered.",
|
||||
G_TYPE_LIST_MODEL,
|
||||
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_init (SpModelFilter *self)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
priv->filter_func = sp_model_filter_default_filter_func;
|
||||
priv->seq = g_sequence_new (element_free);
|
||||
priv->visible_seq = g_sequence_new (NULL);
|
||||
|
||||
priv->needs_rebuild = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_model_filter_rebuild (SpModelFilter *self,
|
||||
gboolean no_emit)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
guint new_n_items = 0;
|
||||
guint old_n_items;
|
||||
guint n_items;
|
||||
guint i;
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
g_assert (priv->needs_rebuild);
|
||||
|
||||
old_n_items = g_sequence_get_length (priv->visible_seq);
|
||||
|
||||
g_clear_pointer (&priv->seq, g_sequence_free);
|
||||
g_clear_pointer (&priv->visible_seq, g_sequence_free);
|
||||
|
||||
priv->seq = g_sequence_new (element_free);
|
||||
priv->visible_seq = g_sequence_new (NULL);
|
||||
|
||||
n_items = g_list_model_get_n_items (priv->child_model);
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
GSequenceIter *iter;
|
||||
Element *ele;
|
||||
|
||||
ele = g_slice_new (Element);
|
||||
ele->object = g_list_model_get_item (priv->child_model, i);
|
||||
ele->iter = NULL;
|
||||
|
||||
iter = g_sequence_append (priv->seq, ele);
|
||||
|
||||
if (priv->filter_func (ele->object, priv->filter_func_data))
|
||||
{
|
||||
ele->iter = g_sequence_append (priv->visible_seq, iter);
|
||||
new_n_items++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!no_emit)
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, old_n_items, new_n_items);
|
||||
|
||||
priv->needs_rebuild = FALSE;
|
||||
}
|
||||
|
||||
static GType
|
||||
sp_model_filter_get_item_type (GListModel *model)
|
||||
{
|
||||
SpModelFilter *self = (SpModelFilter *)model;
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
|
||||
return g_list_model_get_item_type (priv->child_model);
|
||||
}
|
||||
|
||||
static guint
|
||||
sp_model_filter_get_n_items (GListModel *model)
|
||||
{
|
||||
SpModelFilter *self = (SpModelFilter *)model;
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
|
||||
if (priv->needs_rebuild)
|
||||
sp_model_filter_rebuild (self, TRUE);
|
||||
|
||||
return g_sequence_get_length (priv->visible_seq);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
sp_model_filter_get_item (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
SpModelFilter *self = (SpModelFilter *)model;
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
GSequenceIter *iter;
|
||||
Element *ele;
|
||||
|
||||
g_assert (SP_IS_MODEL_FILTER (self));
|
||||
|
||||
if (priv->needs_rebuild)
|
||||
sp_model_filter_rebuild (self, TRUE);
|
||||
|
||||
iter = g_sequence_get_iter_at_pos (priv->visible_seq, position);
|
||||
|
||||
if (!iter || g_sequence_iter_is_end (iter))
|
||||
{
|
||||
g_warning ("invalid position for filter, filter is corrupt");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iter = g_sequence_get (iter);
|
||||
|
||||
if (!iter || g_sequence_iter_is_end (iter))
|
||||
{
|
||||
g_warning ("invalid position for filter, filter is corrupt");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ele = g_sequence_get (iter);
|
||||
|
||||
return g_object_ref (ele->object);
|
||||
}
|
||||
|
||||
static void
|
||||
list_model_iface_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = sp_model_filter_get_item_type;
|
||||
iface->get_n_items = sp_model_filter_get_n_items;
|
||||
iface->get_item = sp_model_filter_get_item;
|
||||
}
|
||||
|
||||
SpModelFilter *
|
||||
sp_model_filter_new (GListModel *child_model)
|
||||
{
|
||||
SpModelFilter *ret;
|
||||
SpModelFilterPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (child_model), NULL);
|
||||
|
||||
ret = g_object_new (SP_TYPE_MODEL_FILTER, NULL);
|
||||
priv = sp_model_filter_get_instance_private (ret);
|
||||
priv->child_model = g_object_ref (child_model);
|
||||
|
||||
g_signal_connect_object (child_model,
|
||||
"items-changed",
|
||||
G_CALLBACK (sp_model_filter_child_model_items_changed),
|
||||
ret,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_model_filter_get_child_model:
|
||||
* @self: A #SpModelFilter
|
||||
*
|
||||
* Gets the child model that is being filtered.
|
||||
*
|
||||
* Returns: (transfer none): A #GListModel.
|
||||
*/
|
||||
GListModel *
|
||||
sp_model_filter_get_child_model (SpModelFilter *self)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (SP_IS_MODEL_FILTER (self), NULL);
|
||||
|
||||
return priv->child_model;
|
||||
}
|
||||
|
||||
void
|
||||
sp_model_filter_invalidate (SpModelFilter *self)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_MODEL_FILTER (self));
|
||||
|
||||
priv->needs_rebuild = TRUE;
|
||||
|
||||
sp_model_filter_rebuild (self, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
sp_model_filter_set_filter_func (SpModelFilter *self,
|
||||
SpModelFilterFunc filter_func,
|
||||
gpointer filter_func_data,
|
||||
GDestroyNotify filter_func_data_destroy)
|
||||
{
|
||||
SpModelFilterPrivate *priv = sp_model_filter_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (SP_IS_MODEL_FILTER (self));
|
||||
g_return_if_fail (filter_func || (!filter_func_data && !filter_func_data_destroy));
|
||||
|
||||
if (priv->filter_func_data_destroy)
|
||||
g_clear_pointer (&priv->filter_func_data, priv->filter_func_data_destroy);
|
||||
|
||||
if (filter_func != NULL)
|
||||
{
|
||||
priv->filter_func = filter_func;
|
||||
priv->filter_func_data = filter_func_data;
|
||||
priv->filter_func_data_destroy = filter_func_data_destroy;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->filter_func = sp_model_filter_default_filter_func;
|
||||
priv->filter_func_data = NULL;
|
||||
priv->filter_func_data_destroy = NULL;
|
||||
}
|
||||
|
||||
sp_model_filter_invalidate (self);
|
||||
}
|
||||
50
lib/util/sp-model-filter.h
Normal file
50
lib/util/sp-model-filter.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* sp-model-filter.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_MODEL_FILTER_H
|
||||
#define SP_MODEL_FILTER_H
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_MODEL_FILTER (sp_model_filter_get_type())
|
||||
|
||||
typedef gboolean (*SpModelFilterFunc) (GObject *object,
|
||||
gpointer user_data);
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (SpModelFilter, sp_model_filter, SP, MODEL_FILTER, GObject)
|
||||
|
||||
struct _SpModelFilterClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
gpointer padding[8];
|
||||
};
|
||||
|
||||
SpModelFilter *sp_model_filter_new (GListModel *child_model);
|
||||
GListModel *sp_model_filter_get_child_model (SpModelFilter *self);
|
||||
void sp_model_filter_invalidate (SpModelFilter *self);
|
||||
void sp_model_filter_set_filter_func (SpModelFilter *self,
|
||||
SpModelFilterFunc filter_func,
|
||||
gpointer filter_func_data,
|
||||
GDestroyNotify filter_func_data_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_MODEL_FILTER_H */
|
||||
65
lib/util/sp-platform.c
Normal file
65
lib/util/sp-platform.c
Normal file
@ -0,0 +1,65 @@
|
||||
/* sp-platform.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util/sp-platform.h"
|
||||
|
||||
/**
|
||||
* sp_memfd_create:
|
||||
* @name: (nullable): A descriptive name for the memfd or %NULL
|
||||
*
|
||||
* Creates a new memfd using the memfd_create syscall if available.
|
||||
* Otherwise, a tmpfile is used. Currently, no attempt is made to
|
||||
* ensure the tmpfile is on a tmpfs backed mount.
|
||||
*
|
||||
* Returns: An fd if successful; otherwise -1 and errno is set.
|
||||
*/
|
||||
int
|
||||
sp_memfd_create (const gchar *name)
|
||||
{
|
||||
#ifdef __NR_memfd_create
|
||||
if (name == NULL)
|
||||
name = "[sysprof]";
|
||||
return syscall (__NR_memfd_create, name, 0);
|
||||
#else
|
||||
gchar *name_used = NULL;
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* TODO: It would be nice to ensure tmpfs
|
||||
*
|
||||
* It is not strictly required that the preferred temporary directory
|
||||
* will be mounted as tmpfs. We should look through the mounts and ensure
|
||||
* that the tmpfile we open is on tmpfs so that we get anonymous backed
|
||||
* pages after unlinking.
|
||||
*/
|
||||
fd = g_file_open_tmp (NULL, &name_used, NULL);
|
||||
|
||||
if (name_used != NULL)
|
||||
{
|
||||
g_unlink (name_used);
|
||||
g_free (name_used);
|
||||
}
|
||||
|
||||
return fd;
|
||||
#endif
|
||||
}
|
||||
30
lib/util/sp-platform.h
Normal file
30
lib/util/sp-platform.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* sp-platform.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_PLATFORM_H
|
||||
#define SP_PLATFORM_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
int sp_memfd_create (const gchar *desc);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_PLATFORM_H */
|
||||
234
lib/util/sp-process-model-item.c
Normal file
234
lib/util/sp-process-model-item.c
Normal file
@ -0,0 +1,234 @@
|
||||
/* sp-process-model-item.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "util/sp-process-model-item.h"
|
||||
#include "sources/sp-proc-source.h"
|
||||
|
||||
struct _SpProcessModelItem
|
||||
{
|
||||
GObject parent_instance;
|
||||
GPid pid;
|
||||
gchar *command_line; /* Short version (first field) */
|
||||
gchar **argv; /* Long version (argv as a strv) */
|
||||
guint is_kernel : 1;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (SpProcessModelItem, sp_process_model_item, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_COMMAND_LINE,
|
||||
PROP_PID,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
|
||||
static void
|
||||
sp_process_model_item_finalize (GObject *object)
|
||||
{
|
||||
SpProcessModelItem *self = (SpProcessModelItem *)object;
|
||||
|
||||
g_clear_pointer (&self->command_line, g_free);
|
||||
g_clear_pointer (&self->argv, g_strfreev);
|
||||
|
||||
G_OBJECT_CLASS (sp_process_model_item_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_item_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpProcessModelItem *self = SP_PROCESS_MODEL_ITEM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_COMMAND_LINE:
|
||||
g_value_set_string (value, self->command_line);
|
||||
break;
|
||||
|
||||
case PROP_PID:
|
||||
g_value_set_int (value, self->pid);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_item_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpProcessModelItem *self = SP_PROCESS_MODEL_ITEM (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_COMMAND_LINE:
|
||||
self->command_line = g_value_dup_string (value);
|
||||
break;
|
||||
|
||||
case PROP_PID:
|
||||
self->pid = g_value_get_int (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_item_class_init (SpProcessModelItemClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_process_model_item_finalize;
|
||||
object_class->get_property = sp_process_model_item_get_property;
|
||||
object_class->set_property = sp_process_model_item_set_property;
|
||||
|
||||
properties [PROP_COMMAND_LINE] =
|
||||
g_param_spec_string ("command-line",
|
||||
"Command Line",
|
||||
"Command Line",
|
||||
NULL,
|
||||
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_PID] =
|
||||
g_param_spec_int ("pid",
|
||||
"Pid",
|
||||
"Pid",
|
||||
-1,
|
||||
G_MAXINT,
|
||||
-1,
|
||||
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_item_init (SpProcessModelItem *self)
|
||||
{
|
||||
}
|
||||
|
||||
SpProcessModelItem *
|
||||
sp_process_model_item_new (GPid pid)
|
||||
{
|
||||
SpProcessModelItem *ret;
|
||||
gchar *cmdline;
|
||||
gboolean is_kernel;
|
||||
|
||||
cmdline = sp_proc_source_get_command_line (pid, &is_kernel);
|
||||
|
||||
ret = g_object_new (SP_TYPE_PROCESS_MODEL_ITEM,
|
||||
"command-line", cmdline,
|
||||
"pid", (int)pid,
|
||||
NULL);
|
||||
ret->is_kernel = is_kernel;
|
||||
|
||||
g_free (cmdline);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
guint
|
||||
sp_process_model_item_hash (SpProcessModelItem *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), 0);
|
||||
|
||||
return self->pid;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_process_model_item_equal (SpProcessModelItem *self,
|
||||
SpProcessModelItem *other)
|
||||
{
|
||||
g_assert (SP_IS_PROCESS_MODEL_ITEM (self));
|
||||
g_assert (SP_IS_PROCESS_MODEL_ITEM (other));
|
||||
|
||||
return ((self->pid == other->pid) &&
|
||||
(g_strcmp0 (self->command_line, other->command_line) == 0));
|
||||
}
|
||||
|
||||
GPid
|
||||
sp_process_model_item_get_pid (SpProcessModelItem *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), 0);
|
||||
|
||||
return self->pid;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
sp_process_model_item_get_command_line (SpProcessModelItem *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), NULL);
|
||||
|
||||
return self->command_line;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_process_model_item_is_kernel (SpProcessModelItem *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), FALSE);
|
||||
|
||||
return self->is_kernel;
|
||||
}
|
||||
|
||||
const gchar * const *
|
||||
sp_process_model_item_get_argv (SpProcessModelItem *self)
|
||||
{
|
||||
g_autofree gchar *contents = NULL;
|
||||
g_autofree gchar *path = NULL;
|
||||
const gchar *pos;
|
||||
const gchar *endptr;
|
||||
GPtrArray *ar;
|
||||
gsize size = 0;
|
||||
GPid pid;
|
||||
|
||||
g_return_val_if_fail (SP_IS_PROCESS_MODEL_ITEM (self), NULL);
|
||||
|
||||
if (self->argv)
|
||||
return (const gchar * const *)self->argv;
|
||||
|
||||
if ((pid = sp_process_model_item_get_pid (self)) < 0)
|
||||
return NULL;
|
||||
|
||||
path = g_strdup_printf ("/proc/%u/cmdline", (guint)pid);
|
||||
if (!g_file_get_contents (path, &contents, &size, NULL))
|
||||
return NULL;
|
||||
|
||||
ar = g_ptr_array_new ();
|
||||
|
||||
/* Each parameter is followed by \0 */
|
||||
for (pos = contents, endptr = contents + size;
|
||||
pos < endptr;
|
||||
pos += strlen (pos) + 1)
|
||||
g_ptr_array_add (ar, g_strdup (pos));
|
||||
g_ptr_array_add (ar, NULL);
|
||||
|
||||
g_clear_pointer (&self->argv, g_strfreev);
|
||||
self->argv = (gchar **)g_ptr_array_free (ar, FALSE);
|
||||
|
||||
return (const gchar * const *)self->argv;
|
||||
}
|
||||
|
||||
41
lib/util/sp-process-model-item.h
Normal file
41
lib/util/sp-process-model-item.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* sp-process-model-item.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_PROCESS_MODEL_ITEM_H
|
||||
#define SP_PROCESS_MODEL_ITEM_H
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_PROCESS_MODEL_ITEM (sp_process_model_item_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpProcessModelItem, sp_process_model_item, SP, PROCESS_MODEL_ITEM, GObject)
|
||||
|
||||
SpProcessModelItem *sp_process_model_item_new (GPid pid);
|
||||
guint sp_process_model_item_hash (SpProcessModelItem *self);
|
||||
gboolean sp_process_model_item_equal (SpProcessModelItem *self,
|
||||
SpProcessModelItem *other);
|
||||
GPid sp_process_model_item_get_pid (SpProcessModelItem *self);
|
||||
const gchar *sp_process_model_item_get_command_line (SpProcessModelItem *self);
|
||||
gboolean sp_process_model_item_is_kernel (SpProcessModelItem *self);
|
||||
const gchar * const *sp_process_model_item_get_argv (SpProcessModelItem *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_PROCESS_MODEL_ITEM_H */
|
||||
293
lib/util/sp-process-model.c
Normal file
293
lib/util/sp-process-model.c
Normal file
@ -0,0 +1,293 @@
|
||||
/* sp-process-model.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util/sp-process-model.h"
|
||||
#include "util/sp-process-model-item.h"
|
||||
|
||||
#define QUEUE_RELOAD_TIMEOUT_MSEC 100
|
||||
|
||||
struct _SpProcessModel
|
||||
{
|
||||
GObject parent_instance;
|
||||
guint reload_source;
|
||||
GPtrArray *items;
|
||||
};
|
||||
|
||||
static void list_model_iface_init (GListModelInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (SpProcessModel, sp_process_model, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
|
||||
|
||||
static void
|
||||
sp_process_model_finalize (GObject *object)
|
||||
{
|
||||
SpProcessModel *self = (SpProcessModel *)object;
|
||||
|
||||
if (self->reload_source)
|
||||
{
|
||||
g_source_remove (self->reload_source);
|
||||
self->reload_source = 0;
|
||||
}
|
||||
|
||||
g_clear_pointer (&self->items, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (sp_process_model_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_class_init (SpProcessModelClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_process_model_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_init (SpProcessModel *self)
|
||||
{
|
||||
self->items = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
sp_process_model_queue_reload (self);
|
||||
}
|
||||
|
||||
static guint
|
||||
find_index (GPtrArray *ar,
|
||||
GPid pid)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_assert (ar != NULL);
|
||||
|
||||
for (i = 0; i < ar->len; i++)
|
||||
{
|
||||
SpProcessModelItem *item = g_ptr_array_index (ar, i);
|
||||
GPid item_pid = sp_process_model_item_get_pid (item);
|
||||
|
||||
g_assert (pid != item_pid);
|
||||
|
||||
if (item_pid > pid)
|
||||
return i;
|
||||
}
|
||||
|
||||
return ar->len;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_merge_cb (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpProcessModel *self = (SpProcessModel *)object;
|
||||
g_autoptr(GPtrArray) ret = NULL;
|
||||
g_autoptr(GHashTable) old_hash = NULL;
|
||||
g_autoptr(GHashTable) new_hash = NULL;
|
||||
GError *error = NULL;
|
||||
guint i;
|
||||
|
||||
g_assert (SP_IS_PROCESS_MODEL (self));
|
||||
g_assert (G_IS_TASK (result));
|
||||
|
||||
ret = g_task_propagate_pointer (G_TASK (result), &error);
|
||||
|
||||
if (ret == NULL)
|
||||
{
|
||||
g_warning ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Clearly this could be optimized to walk both arrays at once
|
||||
* and do a proper 2-way merge.
|
||||
*/
|
||||
|
||||
old_hash = g_hash_table_new ((GHashFunc)sp_process_model_item_hash,
|
||||
(GEqualFunc)sp_process_model_item_equal);
|
||||
new_hash = g_hash_table_new ((GHashFunc)sp_process_model_item_hash,
|
||||
(GEqualFunc)sp_process_model_item_equal);
|
||||
|
||||
for (i = 0; i < self->items->len; i++)
|
||||
{
|
||||
SpProcessModelItem *item = g_ptr_array_index (self->items, i);
|
||||
|
||||
g_hash_table_insert (old_hash, item, NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < ret->len; i++)
|
||||
{
|
||||
SpProcessModelItem *item = g_ptr_array_index (ret, i);
|
||||
|
||||
g_hash_table_insert (new_hash, item, NULL);
|
||||
}
|
||||
|
||||
for (i = self->items->len; i > 0; i--)
|
||||
{
|
||||
guint index = i - 1;
|
||||
SpProcessModelItem *item = g_ptr_array_index (self->items, index);
|
||||
|
||||
if (!g_hash_table_contains (new_hash, item))
|
||||
{
|
||||
g_ptr_array_remove_index (self->items, index);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), index, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ret->len; i++)
|
||||
{
|
||||
SpProcessModelItem *item = g_ptr_array_index (ret, i);
|
||||
GPid pid;
|
||||
guint index;
|
||||
|
||||
if (g_hash_table_contains (old_hash, item))
|
||||
continue;
|
||||
|
||||
pid = sp_process_model_item_get_pid (item);
|
||||
index = find_index (self->items, pid);
|
||||
|
||||
g_ptr_array_insert (self->items, index, g_object_ref (item));
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), index, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_by_pid (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
SpProcessModelItem **aitem = (SpProcessModelItem **)a;
|
||||
SpProcessModelItem **bitem = (SpProcessModelItem **)b;
|
||||
|
||||
return sp_process_model_item_get_pid (*aitem) - sp_process_model_item_get_pid (*bitem);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_process_model_reload_worker (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
g_autoptr(GPtrArray) ret = NULL;
|
||||
const gchar *name;
|
||||
GError *error = NULL;
|
||||
GDir *dir;
|
||||
|
||||
g_assert (SP_IS_PROCESS_MODEL (source_object));
|
||||
g_assert (G_IS_TASK (task));
|
||||
|
||||
dir = g_dir_open ("/proc", 0, &error);
|
||||
|
||||
if (dir == NULL)
|
||||
{
|
||||
g_task_return_error (task, error);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
|
||||
while ((name = g_dir_read_name (dir)))
|
||||
{
|
||||
SpProcessModelItem *item;
|
||||
GPid pid;
|
||||
gchar *end;
|
||||
|
||||
pid = strtol (name, &end, 10);
|
||||
if (pid <= 0 || *end != '\0')
|
||||
continue;
|
||||
|
||||
item = sp_process_model_item_new (pid);
|
||||
|
||||
if (sp_process_model_item_is_kernel (item))
|
||||
{
|
||||
g_object_unref (item);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_ptr_array_add (ret, item);
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
|
||||
g_ptr_array_sort (ret, compare_by_pid);
|
||||
g_task_return_pointer (task, g_ptr_array_ref (ret), (GDestroyNotify)g_ptr_array_unref);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_process_model_do_reload (gpointer user_data)
|
||||
{
|
||||
SpProcessModel *self = user_data;
|
||||
g_autoptr(GTask) task = NULL;
|
||||
|
||||
self->reload_source = 0;
|
||||
|
||||
task = g_task_new (self, NULL, sp_process_model_merge_cb, NULL);
|
||||
g_task_run_in_thread (task, sp_process_model_reload_worker);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
SpProcessModel *
|
||||
sp_process_model_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_PROCESS_MODEL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
sp_process_model_queue_reload (SpProcessModel *self)
|
||||
{
|
||||
g_return_if_fail (SP_IS_PROCESS_MODEL (self));
|
||||
|
||||
if (self->reload_source == 0)
|
||||
self->reload_source = g_timeout_add (QUEUE_RELOAD_TIMEOUT_MSEC,
|
||||
sp_process_model_do_reload,
|
||||
self);
|
||||
}
|
||||
|
||||
static GType
|
||||
sp_process_model_get_item_type (GListModel *model)
|
||||
{
|
||||
return SP_TYPE_PROCESS_MODEL_ITEM;
|
||||
}
|
||||
|
||||
static guint
|
||||
sp_process_model_get_n_items (GListModel *model)
|
||||
{
|
||||
SpProcessModel *self = (SpProcessModel *)model;
|
||||
|
||||
return self->items->len;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
sp_process_model_get_item (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
SpProcessModel *self = (SpProcessModel *)model;
|
||||
|
||||
g_return_val_if_fail (SP_IS_PROCESS_MODEL (self), NULL);
|
||||
g_return_val_if_fail (position < self->items->len, NULL);
|
||||
|
||||
return g_object_ref (g_ptr_array_index (self->items, position));
|
||||
}
|
||||
|
||||
static void
|
||||
list_model_iface_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = sp_process_model_get_item_type;
|
||||
iface->get_n_items = sp_process_model_get_n_items;
|
||||
iface->get_item = sp_process_model_get_item;
|
||||
}
|
||||
35
lib/util/sp-process-model.h
Normal file
35
lib/util/sp-process-model.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* sp-process-model.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <christian@hergert.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_PROCESS_MODEL_H
|
||||
#define SP_PROCESS_MODEL_H
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_PROCESS_MODEL (sp_process_model_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpProcessModel, sp_process_model, SP, PROCESS_MODEL, GObject)
|
||||
|
||||
SpProcessModel *sp_process_model_new (void);
|
||||
void sp_process_model_queue_reload (SpProcessModel *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_PROCESS_MODEL_H */
|
||||
251
lib/util/sp-selection.c
Normal file
251
lib/util/sp-selection.c
Normal file
@ -0,0 +1,251 @@
|
||||
/* sp-selection.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-selection"
|
||||
|
||||
#include "util/sp-selection.h"
|
||||
|
||||
struct _SpSelection
|
||||
{
|
||||
GObject parent_instance;
|
||||
GArray *ranges;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint64 begin;
|
||||
gint64 end;
|
||||
} Range;
|
||||
|
||||
G_DEFINE_TYPE (SpSelection, sp_selection, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_HAS_SELECTION,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
CHANGED,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
static guint signals [N_SIGNALS];
|
||||
|
||||
static inline void
|
||||
int64_swap (gint64 *a,
|
||||
gint64 *b)
|
||||
{
|
||||
if (*a > *b)
|
||||
{
|
||||
gint64 tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_selection_finalize (GObject *object)
|
||||
{
|
||||
SpSelection *self = (SpSelection *)object;
|
||||
|
||||
g_clear_pointer (&self->ranges, g_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (sp_selection_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_selection_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpSelection *self = SP_SELECTION (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_HAS_SELECTION:
|
||||
g_value_set_boolean (value, sp_selection_get_has_selection (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_selection_class_init (SpSelectionClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_selection_finalize;
|
||||
object_class->get_property = sp_selection_get_property;
|
||||
|
||||
properties [PROP_HAS_SELECTION] =
|
||||
g_param_spec_boolean ("has-selection",
|
||||
"Has Selection",
|
||||
"Has Selection",
|
||||
FALSE,
|
||||
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* SpSelection::changed:
|
||||
*
|
||||
* This signal is emitted when the selection has changed.
|
||||
*/
|
||||
signals [CHANGED] =
|
||||
g_signal_new ("changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_selection_init (SpSelection *self)
|
||||
{
|
||||
self->ranges = g_array_new (FALSE, FALSE, sizeof (Range));
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_selection_get_has_selection (SpSelection *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_SELECTION (self), FALSE);
|
||||
|
||||
return self->ranges->len > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_selection_foreach:
|
||||
* @self: A #SpSelection
|
||||
* @foreach_func: (scope call): a callback for each range
|
||||
* @user_data: user data for @foreach_func
|
||||
*
|
||||
* Calls @foreach_func for every selected range.
|
||||
*/
|
||||
void
|
||||
sp_selection_foreach (SpSelection *self,
|
||||
SpSelectionForeachFunc foreach_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_return_if_fail (SP_IS_SELECTION (self));
|
||||
g_return_if_fail (foreach_func != NULL);
|
||||
|
||||
for (guint i = 0; i < self->ranges->len; i++)
|
||||
{
|
||||
const Range *range = &g_array_index (self->ranges, Range, i);
|
||||
foreach_func (self, range->begin, range->end, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_selection_select_range (SpSelection *self,
|
||||
gint64 begin_time,
|
||||
gint64 end_time)
|
||||
{
|
||||
Range range = { 0 };
|
||||
|
||||
g_return_if_fail (SP_IS_SELECTION (self));
|
||||
|
||||
int64_swap (&begin_time, &end_time);
|
||||
|
||||
range.begin = begin_time;
|
||||
range.end = end_time;
|
||||
|
||||
g_array_append_val (self->ranges, range);
|
||||
|
||||
if (self->ranges->len == 1)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
|
||||
g_signal_emit (self, signals [CHANGED], 0);
|
||||
}
|
||||
|
||||
void
|
||||
sp_selection_unselect_range (SpSelection *self,
|
||||
gint64 begin,
|
||||
gint64 end)
|
||||
{
|
||||
g_return_if_fail (SP_IS_SELECTION (self));
|
||||
|
||||
int64_swap (&begin, &end);
|
||||
|
||||
for (guint i = 0; i < self->ranges->len; i++)
|
||||
{
|
||||
const Range *range = &g_array_index (self->ranges, Range, i);
|
||||
|
||||
if (range->begin == begin && range->end == end)
|
||||
{
|
||||
g_array_remove_index (self->ranges, i);
|
||||
if (self->ranges->len == 0)
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
|
||||
g_signal_emit (self, signals [CHANGED], 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_selection_unselect_all (SpSelection *self)
|
||||
{
|
||||
g_return_if_fail (SP_IS_SELECTION (self));
|
||||
|
||||
if (self->ranges->len > 0)
|
||||
{
|
||||
g_array_remove_range (self->ranges, 0, self->ranges->len);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]);
|
||||
g_signal_emit (self, signals [CHANGED], 0);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_selection_contains (SpSelection *self,
|
||||
gint64 time_at)
|
||||
{
|
||||
if (self == NULL || self->ranges->len == 0)
|
||||
return TRUE;
|
||||
|
||||
for (guint i = 0; i < self->ranges->len; i++)
|
||||
{
|
||||
const Range *range = &g_array_index (self->ranges, Range, i);
|
||||
|
||||
if (time_at >= range->begin && time_at <= range->end)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SpSelection *
|
||||
sp_selection_copy (const SpSelection *self)
|
||||
{
|
||||
SpSelection *copy;
|
||||
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
|
||||
copy = g_object_new (SP_TYPE_SELECTION, NULL);
|
||||
|
||||
for (guint i = 0; i < self->ranges->len; i++)
|
||||
{
|
||||
Range range = g_array_index (self->ranges, Range, i);
|
||||
g_array_append_val (copy->ranges, range);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
52
lib/util/sp-selection.h
Normal file
52
lib/util/sp-selection.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* sp-selection.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_SELECTION_H
|
||||
#define SP_SELECTION_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_SELECTION (sp_selection_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpSelection, sp_selection, SP, SELECTION, GObject)
|
||||
|
||||
typedef void (*SpSelectionForeachFunc) (SpSelection *self,
|
||||
gint64 begin_time,
|
||||
gint64 end_time,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean sp_selection_get_has_selection (SpSelection *self);
|
||||
gboolean sp_selection_contains (SpSelection *self,
|
||||
gint64 time_at);
|
||||
void sp_selection_select_range (SpSelection *self,
|
||||
gint64 begin_time,
|
||||
gint64 end_time);
|
||||
void sp_selection_unselect_range (SpSelection *self,
|
||||
gint64 begin,
|
||||
gint64 end);
|
||||
void sp_selection_unselect_all (SpSelection *self);
|
||||
void sp_selection_foreach (SpSelection *self,
|
||||
SpSelectionForeachFunc foreach_func,
|
||||
gpointer user_data);
|
||||
SpSelection *sp_selection_copy (const SpSelection *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_SELECTION_H */
|
||||
264
lib/util/sp-theme-manager.c
Normal file
264
lib/util/sp-theme-manager.c
Normal file
@ -0,0 +1,264 @@
|
||||
/* sp-theme-manager.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-theme-manager"
|
||||
|
||||
#include "util/sp-theme-manager.h"
|
||||
|
||||
struct _SpThemeManager
|
||||
{
|
||||
GObject parent_instance;
|
||||
GHashTable *theme_resources;
|
||||
guint reload_source;
|
||||
guint registered_signals : 1;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint id;
|
||||
gchar *key;
|
||||
gchar *theme_name;
|
||||
gchar *variant;
|
||||
gchar *resource;
|
||||
GtkCssProvider *provider;
|
||||
} ThemeResource;
|
||||
|
||||
G_DEFINE_TYPE (SpThemeManager, sp_theme_manager, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
theme_resource_free (gpointer data)
|
||||
{
|
||||
ThemeResource *theme_resource = data;
|
||||
|
||||
if (theme_resource != NULL)
|
||||
{
|
||||
g_clear_pointer (&theme_resource->key, g_free);
|
||||
g_clear_pointer (&theme_resource->theme_name, g_free);
|
||||
g_clear_pointer (&theme_resource->variant, g_free);
|
||||
g_clear_pointer (&theme_resource->resource, g_free);
|
||||
|
||||
if (theme_resource->provider != NULL)
|
||||
{
|
||||
gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (theme_resource->provider));
|
||||
g_clear_object (&theme_resource->provider);
|
||||
}
|
||||
|
||||
g_slice_free (ThemeResource, theme_resource);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theme_resource_matches (ThemeResource *theme_resource,
|
||||
GtkSettings *settings)
|
||||
{
|
||||
g_autofree gchar *theme_name = NULL;
|
||||
gboolean dark_theme = FALSE;
|
||||
|
||||
g_assert (theme_resource != NULL);
|
||||
g_assert (GTK_IS_SETTINGS (settings));
|
||||
|
||||
if (theme_resource->theme_name == NULL)
|
||||
return TRUE;
|
||||
|
||||
g_object_get (settings,
|
||||
"gtk-theme-name", &theme_name,
|
||||
"gtk-application-prefer-dark-theme", &dark_theme,
|
||||
NULL);
|
||||
|
||||
if (g_strcmp0 (theme_name, theme_resource->theme_name) == 0)
|
||||
{
|
||||
if (dark_theme && g_strcmp0 ("dark", theme_resource->variant) == 0)
|
||||
return TRUE;
|
||||
|
||||
if (!dark_theme && (!theme_resource->variant || g_strcmp0 ("light", theme_resource->variant) == 0))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_theme_manager_do_reload (gpointer data)
|
||||
{
|
||||
SpThemeManager *self = data;
|
||||
ThemeResource *theme_resource;
|
||||
GHashTableIter iter;
|
||||
GtkSettings *settings;
|
||||
|
||||
g_assert (SP_IS_THEME_MANAGER (self));
|
||||
|
||||
self->reload_source = 0;
|
||||
|
||||
settings = gtk_settings_get_default ();
|
||||
|
||||
g_hash_table_iter_init (&iter, self->theme_resources);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&theme_resource))
|
||||
{
|
||||
if (theme_resource_matches (theme_resource, settings))
|
||||
{
|
||||
if (theme_resource->provider == NULL)
|
||||
{
|
||||
theme_resource->provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_resource (theme_resource->provider, theme_resource->resource);
|
||||
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (theme_resource->provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (theme_resource->provider != NULL)
|
||||
{
|
||||
gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (theme_resource->provider));
|
||||
g_clear_object (&theme_resource->provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_theme_manager_queue_reload (SpThemeManager *self)
|
||||
{
|
||||
g_assert (SP_IS_THEME_MANAGER (self));
|
||||
|
||||
if (self->reload_source == 0)
|
||||
self->reload_source = gdk_threads_add_idle_full (G_PRIORITY_LOW,
|
||||
sp_theme_manager_do_reload,
|
||||
self,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_theme_manager_finalize (GObject *object)
|
||||
{
|
||||
SpThemeManager *self = (SpThemeManager *)object;
|
||||
|
||||
if (self->reload_source != 0)
|
||||
{
|
||||
g_source_remove (self->reload_source);
|
||||
self->reload_source = 0;
|
||||
}
|
||||
|
||||
g_clear_pointer (&self->theme_resources, g_hash_table_unref);
|
||||
|
||||
G_OBJECT_CLASS (sp_theme_manager_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_theme_manager_class_init (SpThemeManagerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = sp_theme_manager_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
sp_theme_manager_init (SpThemeManager *self)
|
||||
{
|
||||
self->theme_resources = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, theme_resource_free);
|
||||
|
||||
gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (), "/org/gnome/sysprof/icons");
|
||||
}
|
||||
|
||||
/**
|
||||
* sp_theme_manager_get_default:
|
||||
*
|
||||
* Returns: (transfer none): An #SpThemeManager
|
||||
*/
|
||||
SpThemeManager *
|
||||
sp_theme_manager_get_default (void)
|
||||
{
|
||||
static SpThemeManager *instance;
|
||||
|
||||
if (instance == NULL)
|
||||
instance = g_object_new (SP_TYPE_THEME_MANAGER, NULL);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
guint
|
||||
sp_theme_manager_register_resource (SpThemeManager *self,
|
||||
const gchar *theme_name,
|
||||
const gchar *variant,
|
||||
const gchar *resource)
|
||||
{
|
||||
ThemeResource *theme_resource;
|
||||
static guint counter;
|
||||
guint id;
|
||||
|
||||
g_return_val_if_fail (SP_IS_THEME_MANAGER (self), 0);
|
||||
|
||||
theme_resource = g_slice_new0 (ThemeResource);
|
||||
theme_resource->id = id = ++counter;
|
||||
theme_resource->key = g_strdup_printf ("%s-%s-%d",
|
||||
theme_name ? theme_name : "shared",
|
||||
variant ? variant : "light",
|
||||
theme_resource->id);
|
||||
theme_resource->theme_name = g_strdup (theme_name);
|
||||
theme_resource->variant = g_strdup (variant);
|
||||
theme_resource->resource = g_strdup (resource);
|
||||
theme_resource->provider = NULL;
|
||||
|
||||
g_hash_table_insert (self->theme_resources, theme_resource->key, theme_resource);
|
||||
|
||||
if (!self->registered_signals)
|
||||
{
|
||||
self->registered_signals = TRUE;
|
||||
g_signal_connect_object (gtk_settings_get_default (),
|
||||
"notify::gtk-application-prefer-dark-theme",
|
||||
G_CALLBACK (sp_theme_manager_queue_reload),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
g_signal_connect_object (gtk_settings_get_default (),
|
||||
"notify::gtk-theme-name",
|
||||
G_CALLBACK (sp_theme_manager_queue_reload),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
sp_theme_manager_queue_reload (self);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
sp_theme_manager_unregister (SpThemeManager *self,
|
||||
guint registration_id)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
ThemeResource *theme_resource;
|
||||
|
||||
g_return_if_fail (SP_IS_THEME_MANAGER (self));
|
||||
|
||||
g_hash_table_iter_init (&iter, self->theme_resources);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&theme_resource))
|
||||
{
|
||||
if (theme_resource->id == registration_id)
|
||||
{
|
||||
/* Provider is unregistered during destroy */
|
||||
g_hash_table_iter_remove (&iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
lib/util/sp-theme-manager.h
Normal file
40
lib/util/sp-theme-manager.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* sp-theme-manager.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_THEME_MANAGER_H
|
||||
#define SP_THEME_MANAGER_H
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_THEME_MANAGER (sp_theme_manager_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpThemeManager, sp_theme_manager, SP, THEME_MANAGER, GObject)
|
||||
|
||||
SpThemeManager *sp_theme_manager_get_default (void);
|
||||
void sp_theme_manager_unregister (SpThemeManager *self,
|
||||
guint registration_id);
|
||||
guint sp_theme_manager_register_resource (SpThemeManager *self,
|
||||
const gchar *theme_name,
|
||||
const gchar *variant,
|
||||
const gchar *resource);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_THEME_MANAGER_H */
|
||||
474
lib/util/sp-zoom-manager.c
Normal file
474
lib/util/sp-zoom-manager.c
Normal file
@ -0,0 +1,474 @@
|
||||
/* sp-zoom-manager.c
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "sp-zoom-manager"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "util/sp-zoom-manager.h"
|
||||
|
||||
struct _SpZoomManager
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GSimpleActionGroup *actions;
|
||||
|
||||
gdouble min_zoom;
|
||||
gdouble max_zoom;
|
||||
gdouble zoom;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CAN_ZOOM_IN,
|
||||
PROP_CAN_ZOOM_OUT,
|
||||
PROP_MIN_ZOOM,
|
||||
PROP_MAX_ZOOM,
|
||||
PROP_ZOOM,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static void action_group_iface_init (GActionGroupInterface *iface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (SpZoomManager, sp_zoom_manager, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, action_group_iface_init))
|
||||
|
||||
static GParamSpec *properties [N_PROPS];
|
||||
static gdouble zoom_levels[] = {
|
||||
0.3,
|
||||
0.5,
|
||||
0.67,
|
||||
0.80,
|
||||
0.90,
|
||||
1.0,
|
||||
1.1,
|
||||
1.2,
|
||||
1.33,
|
||||
1.5,
|
||||
1.7,
|
||||
2.0,
|
||||
2.4,
|
||||
3.0,
|
||||
5.0,
|
||||
10.0,
|
||||
20.0,
|
||||
30.0,
|
||||
50.0,
|
||||
};
|
||||
|
||||
static void
|
||||
sp_zoom_manager_zoom_in_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpZoomManager *self = user_data;
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
sp_zoom_manager_zoom_in (self);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_zoom_out_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpZoomManager *self = user_data;
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
sp_zoom_manager_zoom_out (self);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_zoom_one_action (GSimpleAction *action,
|
||||
GVariant *param,
|
||||
gpointer user_data)
|
||||
{
|
||||
SpZoomManager *self = user_data;
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
sp_zoom_manager_reset (self);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpZoomManager *self = SP_ZOOM_MANAGER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MIN_ZOOM:
|
||||
g_value_set_double (value, sp_zoom_manager_get_min_zoom (self));
|
||||
break;
|
||||
|
||||
case PROP_MAX_ZOOM:
|
||||
g_value_set_double (value, sp_zoom_manager_get_max_zoom (self));
|
||||
break;
|
||||
|
||||
case PROP_ZOOM:
|
||||
g_value_set_double (value, sp_zoom_manager_get_zoom (self));
|
||||
break;
|
||||
|
||||
case PROP_CAN_ZOOM_IN:
|
||||
g_value_set_boolean (value, sp_zoom_manager_get_can_zoom_in (self));
|
||||
break;
|
||||
|
||||
case PROP_CAN_ZOOM_OUT:
|
||||
g_value_set_boolean (value, sp_zoom_manager_get_can_zoom_out (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
SpZoomManager *self = SP_ZOOM_MANAGER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MIN_ZOOM:
|
||||
sp_zoom_manager_set_min_zoom (self, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_MAX_ZOOM:
|
||||
sp_zoom_manager_set_max_zoom (self, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
case PROP_ZOOM:
|
||||
sp_zoom_manager_set_zoom (self, g_value_get_double (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_class_init (SpZoomManagerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->get_property = sp_zoom_manager_get_property;
|
||||
object_class->set_property = sp_zoom_manager_set_property;
|
||||
|
||||
properties [PROP_CAN_ZOOM_IN] =
|
||||
g_param_spec_boolean ("can-zoom-in",
|
||||
"Can Zoom In",
|
||||
"Can Zoom In",
|
||||
TRUE,
|
||||
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_CAN_ZOOM_OUT] =
|
||||
g_param_spec_boolean ("can-zoom-out",
|
||||
"Can Zoom Out",
|
||||
"Can Zoom Out",
|
||||
TRUE,
|
||||
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_MIN_ZOOM] =
|
||||
g_param_spec_double ("min-zoom",
|
||||
"Min Zoom",
|
||||
"The minimum zoom to apply",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_MAX_ZOOM] =
|
||||
g_param_spec_double ("max-zoom",
|
||||
"Max Zoom",
|
||||
"The maximum zoom to apply",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
0.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
properties [PROP_ZOOM] =
|
||||
g_param_spec_double ("zoom",
|
||||
"Zoom",
|
||||
"The current zoom level",
|
||||
-G_MAXDOUBLE,
|
||||
G_MAXDOUBLE,
|
||||
1.0,
|
||||
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_init (SpZoomManager *self)
|
||||
{
|
||||
static const GActionEntry entries[] = {
|
||||
{ "zoom-in", sp_zoom_manager_zoom_in_action },
|
||||
{ "zoom-out", sp_zoom_manager_zoom_out_action },
|
||||
{ "zoom-one", sp_zoom_manager_zoom_one_action },
|
||||
};
|
||||
GAction *action;
|
||||
|
||||
self->min_zoom = 0.0;
|
||||
self->max_zoom = 0.0;
|
||||
self->zoom = 1.0;
|
||||
|
||||
self->actions = g_simple_action_group_new ();
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (self->actions),
|
||||
entries,
|
||||
G_N_ELEMENTS (entries),
|
||||
self);
|
||||
|
||||
action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom-in");
|
||||
g_object_bind_property (self, "can-zoom-in", action, "enabled", G_BINDING_SYNC_CREATE);
|
||||
|
||||
action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "zoom-out");
|
||||
g_object_bind_property (self, "can-zoom-out", action, "enabled", G_BINDING_SYNC_CREATE);
|
||||
}
|
||||
|
||||
SpZoomManager *
|
||||
sp_zoom_manager_new (void)
|
||||
{
|
||||
return g_object_new (SP_TYPE_ZOOM_MANAGER, NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_zoom_manager_get_can_zoom_in (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
|
||||
|
||||
return self->max_zoom == 0.0 || self->zoom < self->max_zoom;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_zoom_manager_get_can_zoom_out (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
|
||||
|
||||
return self->min_zoom == 0.0 || self->zoom > self->min_zoom;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_zoom_manager_get_min_zoom (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
|
||||
|
||||
return self->min_zoom;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sp_zoom_manager_get_max_zoom (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), FALSE);
|
||||
|
||||
return self->max_zoom;
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_set_min_zoom (SpZoomManager *self,
|
||||
gdouble min_zoom)
|
||||
{
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
if (min_zoom != self->min_zoom)
|
||||
{
|
||||
self->min_zoom = min_zoom;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MIN_ZOOM]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_OUT]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_set_max_zoom (SpZoomManager *self,
|
||||
gdouble max_zoom)
|
||||
{
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
if (max_zoom != self->max_zoom)
|
||||
{
|
||||
self->max_zoom = max_zoom;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_ZOOM]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_IN]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_zoom_in (SpZoomManager *self)
|
||||
{
|
||||
gdouble zoom;
|
||||
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
if (!sp_zoom_manager_get_can_zoom_in (self))
|
||||
return;
|
||||
|
||||
zoom = self->zoom;
|
||||
|
||||
for (guint i = 0; i < G_N_ELEMENTS (zoom_levels); i++)
|
||||
{
|
||||
if (zoom_levels[i] > zoom)
|
||||
{
|
||||
zoom = zoom_levels[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (zoom == self->zoom)
|
||||
zoom *= 2;
|
||||
|
||||
sp_zoom_manager_set_zoom (self, zoom);
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_zoom_out (SpZoomManager *self)
|
||||
{
|
||||
gdouble zoom;
|
||||
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
if (!sp_zoom_manager_get_can_zoom_out (self))
|
||||
return;
|
||||
|
||||
zoom = self->zoom;
|
||||
|
||||
for (guint i = G_N_ELEMENTS (zoom_levels); i > 0; i--)
|
||||
{
|
||||
if (zoom_levels[i-1] < zoom)
|
||||
{
|
||||
zoom = zoom_levels[i-1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (zoom == self->zoom)
|
||||
zoom /= 2.0;
|
||||
|
||||
sp_zoom_manager_set_zoom (self, zoom);
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_reset (SpZoomManager *self)
|
||||
{
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
sp_zoom_manager_set_zoom (self, 1.0);
|
||||
}
|
||||
|
||||
gdouble
|
||||
sp_zoom_manager_get_zoom (SpZoomManager *self)
|
||||
{
|
||||
g_return_val_if_fail (SP_IS_ZOOM_MANAGER (self), 0.0);
|
||||
|
||||
return self->zoom;
|
||||
}
|
||||
|
||||
void
|
||||
sp_zoom_manager_set_zoom (SpZoomManager *self,
|
||||
gdouble zoom)
|
||||
{
|
||||
gdouble min_zoom;
|
||||
gdouble max_zoom;
|
||||
|
||||
g_return_if_fail (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
min_zoom = (self->min_zoom == 0.0) ? -G_MAXDOUBLE : self->min_zoom;
|
||||
max_zoom = (self->max_zoom == 0.0) ? G_MAXDOUBLE : self->max_zoom;
|
||||
|
||||
zoom = CLAMP (zoom, min_zoom, max_zoom);
|
||||
|
||||
if (zoom == 0.0)
|
||||
zoom = 1.0;
|
||||
|
||||
if (zoom != self->zoom)
|
||||
{
|
||||
self->zoom = zoom;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ZOOM]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_IN]);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_ZOOM_OUT]);
|
||||
}
|
||||
}
|
||||
|
||||
static gchar **
|
||||
sp_zoom_manager_list_actions (GActionGroup *action_group)
|
||||
{
|
||||
SpZoomManager *self = (SpZoomManager *)action_group;
|
||||
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
|
||||
return g_action_group_list_actions (G_ACTION_GROUP (self->actions));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sp_zoom_manager_query_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
gboolean *enabled,
|
||||
const GVariantType **parameter_type,
|
||||
const GVariantType **state_type,
|
||||
GVariant **state_hint,
|
||||
GVariant **state)
|
||||
{
|
||||
SpZoomManager *self = (SpZoomManager *)action_group;
|
||||
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
g_assert (action_name != NULL);
|
||||
|
||||
return g_action_group_query_action (G_ACTION_GROUP (self->actions),
|
||||
action_name,
|
||||
enabled,
|
||||
parameter_type,
|
||||
state_type,
|
||||
state_hint,
|
||||
state);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_change_action_state (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *value)
|
||||
{
|
||||
SpZoomManager *self = (SpZoomManager *)action_group;
|
||||
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
g_assert (action_name != NULL);
|
||||
|
||||
g_action_group_change_action_state (G_ACTION_GROUP (self->actions), action_name, value);
|
||||
}
|
||||
|
||||
static void
|
||||
sp_zoom_manager_activate_action (GActionGroup *action_group,
|
||||
const gchar *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
SpZoomManager *self = (SpZoomManager *)action_group;
|
||||
|
||||
g_assert (SP_IS_ZOOM_MANAGER (self));
|
||||
g_assert (action_name != NULL);
|
||||
|
||||
g_action_group_activate_action (G_ACTION_GROUP (self->actions), action_name, parameter);
|
||||
}
|
||||
|
||||
static void
|
||||
action_group_iface_init (GActionGroupInterface *iface)
|
||||
{
|
||||
iface->list_actions = sp_zoom_manager_list_actions;
|
||||
iface->query_action = sp_zoom_manager_query_action;
|
||||
iface->change_action_state = sp_zoom_manager_change_action_state;
|
||||
iface->activate_action = sp_zoom_manager_activate_action;
|
||||
}
|
||||
48
lib/util/sp-zoom-manager.h
Normal file
48
lib/util/sp-zoom-manager.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* sp-zoom-manager.h
|
||||
*
|
||||
* Copyright (C) 2016 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SP_ZOOM_MANAGER_H
|
||||
#define SP_ZOOM_MANAGER_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define SP_TYPE_ZOOM_MANAGER (sp_zoom_manager_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (SpZoomManager, sp_zoom_manager, SP, ZOOM_MANAGER, GObject)
|
||||
|
||||
SpZoomManager *sp_zoom_manager_new (void);
|
||||
gboolean sp_zoom_manager_get_can_zoom_in (SpZoomManager *self);
|
||||
gboolean sp_zoom_manager_get_can_zoom_out (SpZoomManager *self);
|
||||
gboolean sp_zoom_manager_get_min_zoom (SpZoomManager *self);
|
||||
gboolean sp_zoom_manager_get_max_zoom (SpZoomManager *self);
|
||||
void sp_zoom_manager_set_min_zoom (SpZoomManager *self,
|
||||
gdouble min_zoom);
|
||||
void sp_zoom_manager_set_max_zoom (SpZoomManager *self,
|
||||
gdouble max_zoom);
|
||||
void sp_zoom_manager_zoom_in (SpZoomManager *self);
|
||||
void sp_zoom_manager_zoom_out (SpZoomManager *self);
|
||||
void sp_zoom_manager_reset (SpZoomManager *self);
|
||||
gdouble sp_zoom_manager_get_zoom (SpZoomManager *self);
|
||||
void sp_zoom_manager_set_zoom (SpZoomManager *self,
|
||||
gdouble zoom);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* SP_ZOOM_MANAGER_H */
|
||||
@ -17,7 +17,7 @@
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "stackstash.h"
|
||||
#include "util/stackstash.h"
|
||||
|
||||
struct StackStash
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user