aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--TODO25
-rw-r--r--char-table.c192
-rw-r--r--char-table.h2
-rw-r--r--common.h7
-rw-r--r--gcmap.c34
-rw-r--r--gcmap.desktop8
-rw-r--r--logical-scroll-pane.c579
-rw-r--r--logical-scroll-pane.h59
9 files changed, 805 insertions, 107 deletions
diff --git a/Makefile b/Makefile
index 0725b98..25034c9 100644
--- a/Makefile
+++ b/Makefile
@@ -6,11 +6,13 @@ include $(CONFIGFILE)
OBJ =\
gcmap.o\
char-table.o\
+ logical-scroll-pane.o\
colour.o
HDR =\
common.h\
- char-table.h
+ char-table.h\
+ logical-scroll-pane.h
all: gcmap
$(OBJ): $(HDR)
@@ -26,10 +28,12 @@ install: gcmap
mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1/"
cp -- gcmap "$(DESTDIR)$(PREFIX)/bin/"
cp -- gcmap.1 "$(DESTDIR)$(MANPREFIX)/man1/"
+ cp -- gcmap.desktop "$(DESTDIR)$(PREFIX)/share/applications/"
uninstall:
-rm -f -- "$(DESTDIR)$(PREFIX)/bin/gcmap"
-rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/gcmap.1"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/share/applications/gcmap.desktop"
clean:
-rm -f -- *.o *.a *.lo *.su *.so *.so.* *.gch *.gcov *.gcno *.gcda
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..07fc25d
--- /dev/null
+++ b/TODO
@@ -0,0 +1,25 @@
+Add bookmarks expander under the tabbed pane
+
+Mark whenever a fallback font is used for a glyph
+
+Mark reserved, non-printable, and unassigned characters
+
+Add handheld-layout (with compile-time opt-in as default):
+ alternative 1: put group list, as combo box, above table
+ alternative 2: put group list as tab
+ also: skip the menubar (but add the accelerators)
+
+Remember some settings
+
+Add font blacklist
+
+Add support for command line operands: select listed fonts by path
+
+Add editor to font drop-down to make it easier to select a font
+
+Scrolling inside a GtkScrolledWindow or LogicalScrollPane shall steal focus
+
+Scrolling with Control held down shall change zoom. The hovered cell shall
+remain visible, preferably at the same Y position as the pointer. The pointer
+shall jump to the new position of the cell so account for it being impossible
+to ensure that cell remains at the same X position as the pointer.
diff --git a/char-table.c b/char-table.c
index d49ef19..4aec952 100644
--- a/char-table.c
+++ b/char-table.c
@@ -7,6 +7,12 @@
#define CELL_X_CLEARANCE 2
#define CELL_Y_CLEARANCE 2
+struct cells {
+ int r0, c0; /* inclusive */
+ int r1, c1; /* exclusive */
+ int r_off;
+};
+
G_DEFINE_TYPE(CharTable, gcmap_char_table, GTK_TYPE_DRAWING_AREA)
@@ -97,30 +103,47 @@ fit_text(CharTable *this, const char *text, int *text_w_out, int *text_h_out)
}
+static void
+get_exposed_cells(GtkWidget *widget, GdkEventExpose *event, struct cells *cells_out)
+{
+ CharTable *this = GCMAP_CHAR_TABLE(widget);
+ GdkRectangle area = event->area;
+ LogicalScrollPane *scrolls = LOGICAL_SCROLL_PANE(gtk_widget_get_parent(widget));
+ GtkAdjustment *vadjustment = logical_scroll_pane_get_vadjustment(scrolls);
+
+ cells_out->r0 = area.y * this->nrows / this->viewport_h;
+ cells_out->c0 = area.x * this->ncols / this->viewport_w;
+ cells_out->r1 = (area.y + area.height - 1) * this->nrows / this->viewport_h + 1;
+ cells_out->c1 = (area.x + area.width - 1) * this->ncols / this->viewport_w + 1;
+
+ cells_out->r0 = MAX(cells_out->r0, 0);
+ cells_out->c0 = MAX(cells_out->c0, 0);
+ cells_out->r1 = MIN(cells_out->r1, cells_out->r0 + this->nrows);
+ cells_out->c1 = MIN(cells_out->c1, cells_out->c0 + this->ncols);
+
+ cells_out->r_off = MAX((int)vadjustment->value, 0);
+}
+
+
static gboolean
gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
{
+ struct cells cells;
CharTable *this = GCMAP_CHAR_TABLE(widget);
GdkRectangle area = event->area;
- int r0 = MAX(area.y * this->nrows / this->viewport_h, 0);
- int c0 = MAX(area.x * this->ncols / this->viewport_w, 0);
- int r1 = MIN((area.y + area.height - 1) * this->nrows / this->viewport_h + 1, r0 + this->nrows);
- int c1 = MIN((area.x + area.width - 1) * this->ncols / this->viewport_w + 1, c0 + this->ncols);
GtkStyle *style = gtk_widget_get_style(widget);
cairo_t *g;
char text[16];
- int r, c, x0, x1, y0, y1;
- int text_w, text_h;
- size_t skip, range, row_skip;
+ int r, c, x0, x1, y0, y1, text_w, text_h;
+ size_t skip, range, row_skip, max_jump, jump;
uint32_t cp;
+ get_exposed_cells(widget, event, &cells);
+
g = gdk_cairo_create(widget->window);
if (!g)
eprintf("gdk_cairo_create:");
- /* Make sure it still looks good even if starting in the middle of row */
- r1 += 1;
-
/* Configure drawing area */
gdk_cairo_rectangle(g, &area);
cairo_clip(g);
@@ -129,44 +152,47 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
set_source_colour(g, &style->base[GTK_STATE_NORMAL]);
cairo_paint(g);
+ r = cells.r0;
+ c = cells.c0;
+ if (this->nranges == 0u)
+ goto out;
+
/* Draw grid lines */
- set_source_colour_blend(g, &style->base[GTK_STATE_NORMAL], 0.10, &style->text[GTK_STATE_NORMAL]);
+ set_source_colour_blend(g, &style->base[GTK_STATE_NORMAL], 0.05, &style->text[GTK_STATE_NORMAL]);
cairo_set_line_width(g, 1.0);
/* horizontal */
- y0 = this->viewport_h * (r0 + 1) / this->nrows;
- x0 = this->viewport_w * c0 / this->ncols;
- x1 = this->viewport_w * c1 / this->ncols;
- for (r = r0 + (area.y > 0); r < r1; r++, y0 = y1) {
- y1 = this->viewport_h * (r + 1) / this->nrows;
+ y0 = (this->viewport_h + 1) * (cells.r0 + 1) / this->nrows;
+ x0 = (this->viewport_w + 1) * cells.c0 / this->ncols;
+ x1 = (this->viewport_w + 1) * cells.c1 / this->ncols;
+ for (r = cells.r0 + (area.y > 0); r < cells.r1; r++, y0 = y1) {
+ y1 = (this->viewport_h + 1) * (r + 1) / this->nrows;
cairo_move_to(g, x0, y0 + 0.5);
cairo_line_to(g, x1, y0 + 0.5);
cairo_stroke(g);
}
/* vertical */
- x0 = this->viewport_w * (c0 + 1) / this->ncols;
- y0 = this->viewport_h * r0 / this->nrows;
- y1 = this->viewport_h * r1 / this->nrows;
- for (c = c0 + (area.x > 0); c < c1; c++, x0 = x1) {
- x1 = this->viewport_w * (c + 1) / this->ncols;
+ x0 = (this->viewport_w + 1) * (cells.c0 + 1) / this->ncols;
+ y0 = (this->viewport_h + 1) * cells.r0 / this->nrows;
+ y1 = (this->viewport_h + 1) * cells.r1 / this->nrows;
+ for (c = cells.c0 + (area.x > 0); c < cells.c1; c++, x0 = x1) {
+ x1 = (this->viewport_w + 1) * (c + 1) / this->ncols;
cairo_move_to(g, x0 + 0.5, y0);
cairo_line_to(g, x0 + 0.5, y1);
cairo_stroke(g);
}
/* Draw gryphs */
- if (this->nranges == 0u)
- goto out;
set_source_colour(g, &style->text[GTK_STATE_NORMAL]);
- y0 = this->viewport_h * r0 / this->nrows;
+ y0 = (this->viewport_h + 1) * cells.r0 / this->nrows;
range = 0u;
cp = this->ranges[range].first;
- skip = (size_t)r0 * (size_t)this->ncols + (size_t)c0;
- row_skip = (size_t)this->ncols - (size_t)(c1 - c0);
- /* TODO scroll makes the lines jump by a pixel */
- for (r = r0; r < r1; r++, y0 = y1, skip = row_skip) {
+ skip = (size_t)cells.r0 + (size_t)cells.r_off;
+ skip = skip * (size_t)this->ncols + (size_t)cells.c0;
+ row_skip = (size_t)this->ncols - (size_t)(cells.c1 - cells.c0);
+ for (r = cells.r0; r < cells.r1; r++, y0 = y1, skip = row_skip) {
while (skip) {
- size_t max_jump = (size_t)(this->ranges[range].last - cp + 1u);
- size_t jump = MIN(skip, max_jump);
+ max_jump = (size_t)(this->ranges[range].last - cp + 1u);
+ jump = MIN(skip, max_jump);
cp += (uint32_t)jump;
skip -= jump;
if (cp > this->ranges[range].last) {
@@ -175,15 +201,15 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
cp = this->ranges[range].first;
}
}
- x0 = this->viewport_w * c0 / this->ncols;
- y1 = this->viewport_h * (r + 1) / this->nrows;
- for (c = c0; c < c1; c++, cp++, x0 = x1) {
+ x0 = (this->viewport_w + 1) * cells.c0 / this->ncols;
+ y1 = (this->viewport_h + 1) * (r + 1) / this->nrows;
+ for (c = cells.c0; c < cells.c1; c++, cp++, x0 = x1) {
if (cp > this->ranges[range].last) {
if (++range == this->nranges)
goto out;
cp = this->ranges[range].first;
}
- x1 = this->viewport_w * (c + 1) / this->ncols;
+ x1 = (this->viewport_w + 1) * (c + 1) / this->ncols;
format_character((gunichar)cp, text);
fit_text(this, text, &text_w, &text_h);
@@ -193,40 +219,29 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
}
out:
+ /* Grey out unused area */
+ set_source_colour_blend(g, &style->base[GTK_STATE_NORMAL], 0.05, &style->text[GTK_STATE_NORMAL]);
+ y0 = (this->viewport_h + 1) * r / this->nrows;
+ x0 = (this->viewport_w + 1) * c / this->ncols;
+ y1 = (this->viewport_h + 1) * (r + 1) / this->nrows;
+ cairo_rectangle(g, x0, y0, (double)this->viewport_w - x0, y1 - y0);
+ cairo_fill(g);
+ cairo_rectangle(g, 0.0, y1, (double)this->viewport_w, (double)this->viewport_h - y1);
+ cairo_fill(g);
+
cairo_destroy(g);
return FALSE;
}
static void
-update_adjustment(CharTable *this)
-{
- uint32_t nrows_total;
- nrows_total = this->nchars + (uint32_t)this->ncols - 1u;
- nrows_total /= (uint32_t)this->nrows;
- /* TODO
- if (!this->adjustment)
- return;
- gtk_adjustment_set_upper(this->adjustment, (double)nrows_total);
- gtk_adjustment_set_page_increment(this->adjustment, this->nrows);
- gtk_adjustment_set_page_size(this->adjustment, this->nrows);
- */
-}
-
-
-static int
update_cell_size(GtkWidget *widget, int ranges_changed)
{
CharTable *this = GCMAP_CHAR_TABLE(widget);
- int text_w, text_h;
- int cell_w, cell_h;
+ int text_w, text_h, cell_w, cell_h;
uint32_t nchars;
- int table_h;
size_t i;
- int old_ncols = this->ncols;
- int old_cell_h = this->cell_h;
-
pango_layout_set_text(this->layout, REFERENCE_CHARACTER, -1);
pango_font_description_set_weight(this->font, PANGO_WEIGHT_NORMAL);
@@ -237,43 +252,50 @@ update_cell_size(GtkWidget *widget, int ranges_changed)
cell_w = MAX(text_w, 1) * CELL_WIDTH_FACTOR;
cell_h = MAX(text_h, 1) * CELL_HEIGHT_FACTOR;
- this->ncols = MAX(this->viewport_w / cell_w, 1);
- this->nrows = MAX(this->viewport_h / cell_h, 1);
- cell_w = MAX(this->viewport_w / this->ncols - 1, 1);
- cell_h = MAX(this->viewport_h / this->nrows - 1, 1);
+ this->ncols = (this->viewport_w + 1) / (cell_w + 1);
+ this->nrows = (this->viewport_h + 1) / (cell_h + 1);
+ this->ncols = MAX(this->ncols, 1);
+ this->nrows = MAX(this->nrows, 1);
+
+ cell_w = (this->viewport_w + 1) / this->ncols - 1;
+ cell_h = (this->viewport_h + 1) / this->nrows - 1;
+ cell_w = MAX(cell_w, 1);
+ cell_h = MAX(cell_h, 1);
this->max_w = cell_w - CELL_X_CLEARANCE * 2;
this->max_h = cell_h - CELL_Y_CLEARANCE * 2;
this->max_w = this->max_w < 1 ? cell_w : this->max_w;
this->max_h = this->max_h < 1 ? cell_h : this->max_h;
this->cell_h = cell_h;
- if (old_ncols == this->ncols && old_cell_h == this->cell_h && !ranges_changed)
- return 0;
-
- nchars = (uint32_t)this->nranges;
- for (i = 0u; i < this->nranges; i++)
- nchars += this->ranges[i].last - this->ranges[i].first;
- if (this->nchars != nchars) {
+ if (ranges_changed) {
+ nchars = (uint32_t)this->nranges;
+ for (i = 0u; i < this->nranges; i++)
+ nchars += this->ranges[i].last - this->ranges[i].first;
this->nchars = nchars;
- update_adjustment(this);
}
- table_h = (int)(nchars / (uint32_t)this->ncols);
- if (nchars % (uint32_t)this->ncols)
- table_h += 1;
- table_h = table_h > INT_MAX / this->cell_h ? INT_MAX : table_h * this->cell_h;
- if (this->table_h == table_h)
- return 0;
- this->table_h = table_h;
- return 1;
+}
+
+
+static void
+update_content_size(CharTable *this)
+{
+ GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(this));
+ int nrows;
+ if (parent && LOGICAL_IS_SCROLL_PANE(parent)) {
+ nrows = (int)(this->nchars / (uint32_t)this->ncols);
+ nrows += this->nchars % (uint32_t)this->ncols > 0u;
+ logical_scroll_pane_set_content_size(LOGICAL_SCROLL_PANE(parent), this->ncols, nrows);
+ logical_scroll_pane_set_viewport_size(LOGICAL_SCROLL_PANE(parent), this->ncols, this->nrows);
+ }
}
static void
gcmap_char_table_size_request(GtkWidget *widget, GtkRequisition *requisition)
{
- CharTable *this = GCMAP_CHAR_TABLE(widget);
+ (void) widget;
requisition->width = 1;
- requisition->height = this->table_h;
+ requisition->height = 1;
}
static gboolean
@@ -294,7 +316,6 @@ gcmap_char_table_init(CharTable *this)
this->max_w = 1;
this->max_h = 1;
this->cell_h = 1;
- this->table_h = 1;
this->nchars = 0u;
this->ranges = NULL;
this->nranges = 0u;
@@ -311,8 +332,6 @@ gcmap_char_table_init(CharTable *this)
g_signal_connect(widget, "button-press-event",
G_CALLBACK(&gcmap_char_table_button_press), NULL);
-
- /* TODO scrolling with control held should change font size (attempt to keep hover glyph in place) */
}
@@ -352,7 +371,8 @@ gcmap_char_table_set_font_family(CharTable *this, const char *family)
pango_font_description_set_family(this->font, family);
pango_layout_set_font_description(this->layout, this->font);
pango_layout_context_changed(this->layout);
- gtk_widget_queue_resize(GTK_WIDGET(this));
+ update_cell_size(GTK_WIDGET(this), 0);
+ update_content_size(this);
gtk_widget_queue_draw(GTK_WIDGET(this));
}
@@ -394,7 +414,8 @@ gcmap_char_table_set_font_size(CharTable *this, int size)
pango_layout_set_font_description(this->layout, this->font);
pango_layout_context_changed(this->layout);
widget = GTK_WIDGET(this);
- gtk_widget_queue_resize(widget);
+ update_cell_size(widget, 0);
+ update_content_size(this);
gtk_widget_queue_draw(widget);
}
@@ -408,8 +429,8 @@ gcmap_char_table_set_ranges(CharTable *this, const struct libcmap_range *ranges,
this->ranges = ranges;
this->nranges = nranges;
widget = GTK_WIDGET(this);
- if (update_cell_size(widget, 1))
- gtk_widget_queue_resize(widget);
+ update_cell_size(widget, 1);
+ update_content_size(this);
gtk_widget_queue_draw(widget);
}
@@ -420,6 +441,7 @@ gcmap_char_table_viewport_updated(CharTable *this, int viewport_w, int viewport_
this->viewport_w = viewport_w;
this->viewport_h = viewport_h;
update_cell_size(GTK_WIDGET(this), 0);
+ update_content_size(this);
}
diff --git a/char-table.h b/char-table.h
index 33999b2..1663c9c 100644
--- a/char-table.h
+++ b/char-table.h
@@ -14,7 +14,7 @@ typedef struct _CharTable {
GtkDrawingArea parent_instance;
int nrows, ncols;
int max_w, max_h;
- int cell_h, table_h;
+ int cell_h;
uint32_t nchars;
int viewport_w, viewport_h;
PangoFontDescription *font;
diff --git a/common.h b/common.h
index a30d4c7..f54e8ed 100644
--- a/common.h
+++ b/common.h
@@ -25,9 +25,16 @@
# define HAVE_ITEM_TOOLTIPS
#endif
+#if defined(__GNUC__)
+# define PURE __attribute__((__pure__))
+#else
+# define PURE
+#endif
+
#define COMMA ,
#include "char-table.h"
+#include "logical-scroll-pane.h"
/* colour.c */
double srgb_encode_unsigned(double t);
diff --git a/gcmap.c b/gcmap.c
index ac82d0d..665229c 100644
--- a/gcmap.c
+++ b/gcmap.c
@@ -728,17 +728,17 @@ vtabtext_changed(GtkWidget *menu_item, void *user_data)
static void
-chartable_viewport_size_allocate(GtkWidget *viewport, GtkAllocation *allocation, void *user_data) {
+chartable_size_allocate(GtkWidget *widget, GtkAllocation *allocation, void *user_data)
+{
(void) user_data;
- (void) viewport;
- gcmap_char_table_viewport_updated(GCMAP_CHAR_TABLE(chartable), allocation->width, allocation->height);
+ gcmap_char_table_viewport_updated(GCMAP_CHAR_TABLE(widget), allocation->width, allocation->height);
}
static void
create_window(const char *default_font, PangoWeight default_weight, PangoStyle default_style)
{
- GtkWidget *vbox, *hbox, *left, *scrolled, *viewport;
+ GtkWidget *vbox, *hbox, *left, *scrolled;
GtkWidget *label, *copybutton, *align, *menubar, *menu;
GtkListStore *store;
GtkCellRenderer *renderer;
@@ -877,21 +877,18 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
/* TODO character map tab pane (should have focus: gtk_widget_grab_focus(GTK_WIDGET(...)) */
- scrolled = gtk_scrolled_window_new(NULL, NULL);
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
- gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
+ scrolled = logical_scroll_pane_new();
+ logical_scroll_pane_set_hscrollbar_visible(LOGICAL_SCROLL_PANE(scrolled), FALSE);
chartable = gcmap_char_table_new(default_font, (int)default_font_size, default_weight, default_style);
- gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), chartable);
- viewport = gtk_bin_get_child(GTK_BIN(scrolled));
- g_signal_connect(viewport, "size-allocate", G_CALLBACK(&chartable_viewport_size_allocate), NULL);
- /* TODO scroll shall snap to beginning of a row */
+ logical_scroll_pane_set_child(LOGICAL_SCROLL_PANE(scrolled), chartable);
+ g_signal_connect(chartable, "size-allocate", G_CALLBACK(&chartable_size_allocate), NULL);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, gtk_label_new_with_mnemonic("Character _table"));
#ifdef TODO
scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
- gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO")); // gtk_scrolled_window_add_with_viewport
+ gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO"));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, gtk_label_new_with_mnemonic("Character _details"));
#endif
@@ -899,7 +896,7 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
- gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO")); // gtk_scrolled_window_add_with_viewport
+ gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO"));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, gtk_label_new_with_mnemonic("Glyph v_ariants"));
#endif
@@ -907,7 +904,7 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
- gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO")); // gtk_scrolled_window_add_with_viewport
+ gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO"));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, gtk_label_new_with_mnemonic("Font c_omparison"));
#endif
@@ -1009,9 +1006,6 @@ main(int argc, char *argv[])
{
GtkIconTheme *theme;
const char *icon;
- int xargc = 1;
- char *xargva[] = {argv[0], NULL};
- char **xargv = xargva;
enum listing listing = (enum listing)0;
int use_bold = 0;
int use_italic = 0;
@@ -1022,10 +1016,10 @@ main(int argc, char *argv[])
ARGBEGIN {
case 'B':
- listing = 1;
+ listing = BY_BLOCK;
break;
case 'S':
- listing = 0;
+ listing = BY_SCRIPT;
break;
case 'b':
use_bold = 1;
@@ -1077,7 +1071,7 @@ main(int argc, char *argv[])
g_set_prgname(argv0);
g_set_application_name("gcmap");
- gtk_init(&xargc, &xargv);
+ gtk_init(NULL, NULL);
theme = gtk_icon_theme_get_default();
if (!gtk_icon_theme_has_icon(theme, icon = "apps/gcmap") &&
!gtk_icon_theme_has_icon(theme, icon = "gcmap") &&
diff --git a/gcmap.desktop b/gcmap.desktop
new file mode 100644
index 0000000..e11c21d
--- /dev/null
+++ b/gcmap.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=Application
+Name=gcmap
+Comment=Find and insert unusual characters
+Exec=gcmap %F
+Icon=accessories-character-map
+Terminal=false
+Categories=Accessories;Utility;
diff --git a/logical-scroll-pane.c b/logical-scroll-pane.c
new file mode 100644
index 0000000..207d34e
--- /dev/null
+++ b/logical-scroll-pane.c
@@ -0,0 +1,579 @@
+#include "common.h"
+
+G_DEFINE_TYPE(LogicalScrollPane, logical_scroll_pane, GTK_TYPE_BIN)
+
+static void
+logical_scroll_pane_adjustment_value_changed(GtkAdjustment *adjustment, LogicalScrollPane *this)
+{
+#if defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+
+ double value;
+ if (this->viewport_size_is_logical) {
+ value = round(adjustment->value);
+ if (value != adjustment->value) {
+ gtk_adjustment_set_value(adjustment, value);
+ return;
+ }
+ }
+ if (this->child)
+ gtk_widget_queue_draw(this->child);
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+}
+
+static void
+logical_scroll_pane_configure_adjustment(LogicalScrollPane *this, GtkAdjustment *adjustment, int content_size, int page_size)
+{
+ double value, upper, pg;
+ page_size = MAX(page_size, 1);
+ pg = (double)page_size;
+ upper = (double)MAX(content_size - page_size, 0);
+ value = this->viewport_size_is_logical ? round(adjustment->value) : adjustment->value;
+ value = MAX(value, 0.0);
+ value = MIN(value, upper);
+ upper = (double)MAX(content_size, page_size);
+ if (this->viewport_size_is_logical)
+ gtk_adjustment_configure(adjustment, value, 0.0, upper, 1.0, pg, pg);
+ else
+ gtk_adjustment_configure(adjustment, value, 0.0, upper, 0.1 * pg, 0.9 * pg, pg);
+}
+
+static void
+logical_scroll_pane_configure_hadjustment(LogicalScrollPane *this)
+{
+ logical_scroll_pane_configure_adjustment(this, this->hadjustment, this->content_width, this->page_width);
+}
+
+static void
+logical_scroll_pane_configure_vadjustment(LogicalScrollPane *this)
+{
+ logical_scroll_pane_configure_adjustment(this, this->vadjustment, this->content_height, this->page_height);
+}
+
+static void
+logical_scroll_pane_configure_adjustments(LogicalScrollPane *this)
+{
+ logical_scroll_pane_configure_hadjustment(this);
+ logical_scroll_pane_configure_vadjustment(this);
+}
+
+static void
+logical_scroll_pane_destroy(GtkObject *this_)
+{
+ LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_);
+
+ if (this->child) {
+ g_signal_handlers_disconnect_by_data(this->child, this);
+ gtk_widget_unparent(this->child);
+ }
+ if (this->hscrollbar) gtk_widget_unparent(this->hscrollbar);
+ if (this->vscrollbar) gtk_widget_unparent(this->vscrollbar);
+ if (this->hadjustment) g_object_unref(this->hadjustment);
+ if (this->vadjustment) g_object_unref(this->vadjustment);
+
+ this->child = NULL;
+ this->hscrollbar = NULL;
+ this->vscrollbar = NULL;
+ this->hadjustment = NULL;
+ this->vadjustment = NULL;
+
+ GTK_OBJECT_CLASS(logical_scroll_pane_parent_class)->destroy(this_);
+}
+
+static void
+logical_scroll_pane_size_request(GtkWidget *this_, GtkRequisition *requisition)
+{
+ LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_);
+ GtkRequisition hreq;
+ GtkRequisition vreq;
+
+ requisition->width = hreq.width = vreq.width = 0;
+ requisition->height = hreq.height = vreq.height = 0;
+
+ if (this->hscrollbar && this->hscrollbar_visible) gtk_widget_size_request(this->hscrollbar, &hreq);
+ if (this->vscrollbar && this->vscrollbar_visible) gtk_widget_size_request(this->vscrollbar, &vreq);
+
+ if (this->vscrollbar_visible) requisition->width += vreq.width;
+ if (this->hscrollbar_visible) requisition->height += hreq.height;
+
+ requisition->width = MAX(requisition->width, hreq.width);
+ requisition->height = MAX(requisition->height, vreq.height);
+}
+
+static void
+logical_scroll_pane_size_allocate(GtkWidget *this_, GtkAllocation *allocation)
+{
+ LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_);
+
+ GtkRequisition hreq;
+ GtkRequisition vreq;
+
+ GtkAllocation child_allocation;
+ GtkAllocation hscrollbar_allocation;
+ GtkAllocation vscrollbar_allocation;
+
+ int content_width;
+ int content_height;
+ double x, y, max;
+
+ this_->allocation = *allocation;
+
+ x = this->hadjustment->value + 0.5 * this->hadjustment->page_size;
+ y = this->vadjustment->value + 0.5 * this->vadjustment->page_size;
+
+ hreq.width = hreq.height = 0;
+ vreq.width = vreq.height = 0;
+
+ if (this->hscrollbar && this->hscrollbar_visible) gtk_widget_size_request(this->hscrollbar, &hreq);
+ if (this->vscrollbar && this->vscrollbar_visible) gtk_widget_size_request(this->vscrollbar, &vreq);
+
+ content_width = allocation->width;
+ if (this->vscrollbar_visible)
+ content_width -= vreq.width;
+ content_width = MAX(content_width, 1);
+
+ content_height = allocation->height;
+ if (this->hscrollbar_visible)
+ content_height -= hreq.height;
+ content_height = MAX(content_height, 1);
+
+ if (!this->viewport_size_is_logical) {
+ this->page_width = content_width;
+ this->page_height = content_height;
+ }
+ logical_scroll_pane_configure_adjustments(this);
+
+ max = this->hadjustment->upper - this->hadjustment->page_size;
+ x -= 0.5 * this->hadjustment->page_size;
+ x = MIN(MAX(this->hadjustment->lower, x), max);
+ gtk_adjustment_set_value(this->hadjustment, x);
+
+ max = this->vadjustment->upper - this->vadjustment->page_size;
+ y -= 0.5 * this->vadjustment->page_size;
+ y = MIN(MAX(this->vadjustment->lower, y), max);
+ gtk_adjustment_set_value(this->vadjustment, y);
+
+ if (this->child && GTK_WIDGET_VISIBLE(this->child)) {
+ child_allocation.x = allocation->x;
+ child_allocation.y = allocation->y;
+ child_allocation.width = content_width;
+ child_allocation.height = content_height;
+ gtk_widget_size_allocate(this->child, &child_allocation);
+ }
+
+ if (this->hscrollbar && this->hscrollbar_visible) {
+ hscrollbar_allocation.x = allocation->x;
+ hscrollbar_allocation.y = allocation->y + content_height;
+ hscrollbar_allocation.width = content_width;
+ hscrollbar_allocation.height = hreq.height;
+ gtk_widget_size_allocate(this->hscrollbar, &hscrollbar_allocation);
+ }
+
+ if (this->vscrollbar && this->vscrollbar_visible) {
+ vscrollbar_allocation.x = allocation->x + content_width;
+ vscrollbar_allocation.y = allocation->y;
+ vscrollbar_allocation.width = vreq.width;
+ vscrollbar_allocation.height = content_height;
+ gtk_widget_size_allocate(this->vscrollbar, &vscrollbar_allocation);
+ }
+}
+
+static void
+logical_scroll_pane_scroll_adjustment(GtkAdjustment *adjustment, double amount)
+{
+ double value = adjustment->value;
+ double lower = adjustment->lower;
+ double upper = adjustment->upper - adjustment->page_size;
+ double new_value = value + amount;
+ if (amount < 0.0 && value > lower)
+ gtk_adjustment_set_value(adjustment, MAX(new_value, lower));
+ else if (amount > 0.0 && value < upper)
+ gtk_adjustment_set_value(adjustment, MIN(new_value, upper));
+}
+
+static gboolean
+logical_scroll_pane_child_scroll(GtkWidget *widget, GdkEventScroll *event, LogicalScrollPane *this)
+{
+ GtkAdjustment *adjustment = this->vadjustment;
+
+ (void) widget;
+
+ switch (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_META_MASK)) {
+ case 0:
+ switch (event->direction) {
+ case GDK_SCROLL_LEFT:
+ case GDK_SCROLL_RIGHT:
+ goto horizontal;
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_DOWN:
+ default:
+ break;
+ }
+ break;
+ case GDK_SHIFT_MASK:
+ horizontal:
+ adjustment = this->hadjustment;
+ break;
+ default:
+ return FALSE;
+ }
+
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_LEFT:
+ logical_scroll_pane_scroll_adjustment(adjustment, -adjustment->step_increment);
+ return TRUE;
+ case GDK_SCROLL_DOWN:
+ case GDK_SCROLL_RIGHT:
+ logical_scroll_pane_scroll_adjustment(adjustment, +adjustment->step_increment);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static gboolean
+logical_scroll_pane_child_key_press(GtkWidget *widget, GdkEventKey *event, LogicalScrollPane *this)
+{
+ GtkAdjustment *adjustment = this->vadjustment;
+
+ (void) widget;
+
+ switch (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_META_MASK)) {
+ case 0:
+ break;
+ case GDK_SHIFT_MASK:
+ adjustment = this->hadjustment;
+ break;
+ default:
+ return FALSE;
+ }
+
+ switch (event->keyval) {
+ case GDK_Page_Up:
+ case GDK_KP_Page_Up:
+ logical_scroll_pane_scroll_adjustment(adjustment, -adjustment->page_increment);
+ return TRUE;
+ case GDK_Page_Down:
+ case GDK_KP_Page_Down:
+ logical_scroll_pane_scroll_adjustment(adjustment, +adjustment->page_increment);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static void
+logical_scroll_pane_add(GtkContainer *this_, GtkWidget *widget)
+{
+ LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_);
+ if (this->child) {
+ g_warning("LogicalScrollPane already has a child");
+ return;
+ }
+ logical_scroll_pane_set_child(this, widget);
+}
+
+static void
+logical_scroll_pane_remove(GtkContainer *this_, GtkWidget *widget)
+{
+ LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_);
+
+ if (widget == this->child) {
+ g_signal_handlers_disconnect_by_data(widget, this);
+ this->child = NULL;
+ GTK_BIN(this)->child = NULL;
+ } else if (widget == this->hscrollbar) {
+ this->hscrollbar = NULL;
+ } else if (widget == this->vscrollbar) {
+ this->vscrollbar = NULL;
+ } else {
+ g_warning("Widget is not a child of LogicalScrollPane");
+ return;
+ }
+
+ gtk_widget_unparent(widget);
+ gtk_widget_queue_resize(GTK_WIDGET(this));
+}
+
+static void
+logical_scroll_pane_forall(GtkContainer *this_, int include_internals, GtkCallback callback, void *callback_data)
+{
+ LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_);
+
+ if (this->child) (*callback)(this->child, callback_data);
+
+ if (!include_internals)
+ return;
+
+ if (this->hscrollbar) (*callback)(this->hscrollbar, callback_data);
+ if (this->vscrollbar) (*callback)(this->vscrollbar, callback_data);
+}
+
+static void
+set_adjustment(LogicalScrollPane *this, GtkAdjustment *adjustment, GtkAdjustment **slot, GtkWidget *scrollbar)
+{
+ if (!adjustment)
+ adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 1.0, 1.0, 0.0));
+
+ if (*slot != adjustment) {
+ g_object_ref_sink(adjustment);
+
+ if (*slot) {
+ g_signal_handlers_disconnect_by_data(*slot, this);
+ g_object_unref(*slot);
+ }
+
+ *slot = adjustment;
+ g_signal_connect(*slot, "value-changed",
+ G_CALLBACK(&logical_scroll_pane_adjustment_value_changed), this);
+ }
+
+ if (scrollbar)
+ gtk_range_set_adjustment(GTK_RANGE(scrollbar), adjustment);
+}
+
+static void
+logical_scroll_pane_class_init(LogicalScrollPaneClass *class)
+{
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS(class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS(class);
+
+ object_class->destroy = &logical_scroll_pane_destroy;
+
+ widget_class->size_request = &logical_scroll_pane_size_request;
+ widget_class->size_allocate = &logical_scroll_pane_size_allocate;
+
+ container_class->add = &logical_scroll_pane_add;
+ container_class->remove = &logical_scroll_pane_remove;
+ container_class->forall = &logical_scroll_pane_forall;
+}
+
+static void
+logical_scroll_pane_init(LogicalScrollPane *this)
+{
+ this->child = NULL;
+ this->content_width = 1;
+ this->content_height = 1;
+ this->page_width = 1;
+ this->page_height = 1;
+ this->viewport_size_is_logical = 0;
+ this->hscrollbar_visible = 1;
+ this->vscrollbar_visible = 1;
+
+ this->hadjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, /* value */
+ 0.0, /* lower */
+ 0.0, /* upper */
+ 1.0, /* step increment */
+ 1.0, /* page increment */
+ 0.0)); /* page size */
+
+ this->vadjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 1.0, 1.0, 0.0));
+
+ g_object_ref_sink(this->hadjustment);
+ g_object_ref_sink(this->vadjustment);
+ g_signal_connect(this->hadjustment, "value-changed",
+ G_CALLBACK(&logical_scroll_pane_adjustment_value_changed), this);
+ g_signal_connect(this->vadjustment, "value-changed",
+ G_CALLBACK(&logical_scroll_pane_adjustment_value_changed), this);
+
+ this->hscrollbar = gtk_hscrollbar_new(this->hadjustment);
+ this->vscrollbar = gtk_vscrollbar_new(this->vadjustment);
+
+ gtk_widget_set_parent(this->hscrollbar, GTK_WIDGET(this));
+ gtk_widget_set_parent(this->vscrollbar, GTK_WIDGET(this));
+
+ gtk_widget_show(this->hscrollbar);
+ gtk_widget_show(this->vscrollbar);
+}
+
+GtkWidget *
+logical_scroll_pane_new(void)
+{
+ return GTK_WIDGET(g_object_new(LOGICAL_TYPE_SCROLL_PANE, NULL));
+}
+
+void
+logical_scroll_pane_set_child(LogicalScrollPane *this, GtkWidget *child)
+{
+ if (child == this->child)
+ return;
+
+ if (this->child) {
+ g_signal_handlers_disconnect_by_data(this->child, this);
+ gtk_widget_unparent(this->child);
+ this->child = NULL;
+ GTK_BIN(this)->child = NULL;
+ }
+
+ if (child) {
+ this->child = child;
+ GTK_BIN(this)->child = child;
+ gtk_widget_add_events(child, GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK);
+ g_signal_connect(child, "scroll-event", G_CALLBACK(&logical_scroll_pane_child_scroll), this);
+ g_signal_connect(child, "key-press-event", G_CALLBACK(&logical_scroll_pane_child_key_press), this);
+ gtk_widget_set_parent(child, GTK_WIDGET(this));
+ if (GTK_WIDGET_VISIBLE(this))
+ gtk_widget_show(child);
+ }
+
+ gtk_widget_queue_resize(GTK_WIDGET(this));
+}
+
+GtkWidget *
+logical_scroll_pane_get_child(LogicalScrollPane *this)
+{
+ return this->child;
+}
+
+void
+logical_scroll_pane_set_content_size(LogicalScrollPane *this, int width, int height)
+{
+ width = MAX(1, width);
+ height = MAX(1, height);
+ if (this->content_width == width && this->content_height == height)
+ return;
+
+ this->content_width = width;
+ this->content_height = height;
+ logical_scroll_pane_configure_adjustments(this);
+ gtk_widget_queue_resize(GTK_WIDGET(this));
+ if (this->child)
+ gtk_widget_queue_draw(this->child);
+}
+
+void
+logical_scroll_pane_set_viewport_size(LogicalScrollPane *this, int width, int height)
+{
+ width = MAX(1, width);
+ height = MAX(1, height);
+ this->viewport_size_is_logical = 1;
+ if (this->page_width == width && this->page_height == height)
+ return;
+
+ this->page_width = width;
+ this->page_height = height;
+ logical_scroll_pane_configure_adjustments(this);
+ if (this->child)
+ gtk_widget_queue_draw(this->child);
+}
+
+static void
+set_scrollbar(LogicalScrollPane *this, GtkWidget *scrollbar, GtkWidget **slot, int *scrollbar_visible)
+{
+ if (scrollbar == *slot)
+ return;
+
+ if (scrollbar) {
+ if (*slot)
+ gtk_widget_unparent(*slot);
+ *slot = scrollbar;
+ gtk_widget_set_parent(scrollbar, GTK_WIDGET(this));
+
+ if (*scrollbar_visible)
+ gtk_widget_show(scrollbar);
+ } else {
+ if (*slot) {
+ gtk_widget_unparent(*slot);
+ *slot = NULL;
+ }
+ }
+
+ gtk_widget_queue_resize(GTK_WIDGET(this));
+}
+
+void
+logical_scroll_pane_set_hscrollbar(LogicalScrollPane *this, GtkWidget *scrollbar)
+{
+ set_scrollbar(this, scrollbar, &this->hscrollbar, &this->hscrollbar_visible);
+}
+
+void
+logical_scroll_pane_set_vscrollbar(LogicalScrollPane *this, GtkWidget *scrollbar)
+{
+ set_scrollbar(this, scrollbar, &this->vscrollbar, &this->vscrollbar_visible);
+}
+
+GtkWidget *
+logical_scroll_pane_get_hscrollbar(LogicalScrollPane *this)
+{
+ return this->hscrollbar;
+}
+
+GtkWidget *
+logical_scroll_pane_get_vscrollbar(LogicalScrollPane *this)
+{
+ return this->vscrollbar;
+}
+
+static void
+set_scrollbar_visible(LogicalScrollPane *this, int visible, GtkWidget **scrollbar, int *scrollbar_visible)
+{
+ visible = visible != 0;
+
+ if (*scrollbar_visible == visible)
+ return;
+
+ *scrollbar_visible = visible;
+
+ if (*scrollbar)
+ gtk_widget_set_visible(*scrollbar, visible);
+
+ gtk_widget_queue_resize(GTK_WIDGET(this));
+}
+
+void
+logical_scroll_pane_set_hscrollbar_visible(LogicalScrollPane *this, int visible)
+{
+ set_scrollbar_visible(this, visible, &this->hscrollbar, &this->hscrollbar_visible);
+}
+
+void
+logical_scroll_pane_set_vscrollbar_visible(LogicalScrollPane *this, int visible)
+{
+ set_scrollbar_visible(this, visible, &this->vscrollbar, &this->vscrollbar_visible);
+}
+
+int
+logical_scroll_pane_get_hscrollbar_visible(LogicalScrollPane *this)
+{
+ return this->hscrollbar_visible;
+}
+
+int
+logical_scroll_pane_get_vscrollbar_visible(LogicalScrollPane *this)
+{
+ return this->vscrollbar_visible;
+}
+
+void
+logical_scroll_pane_set_hadjustment(LogicalScrollPane *this, GtkAdjustment *adjustment)
+{
+ set_adjustment(this, adjustment, &this->hadjustment, this->hscrollbar);
+ logical_scroll_pane_configure_hadjustment(this);
+}
+
+void
+logical_scroll_pane_set_vadjustment(LogicalScrollPane *this, GtkAdjustment *adjustment)
+{
+ set_adjustment(this, adjustment, &this->vadjustment, this->vscrollbar);
+ logical_scroll_pane_configure_vadjustment(this);
+}
+
+GtkAdjustment *
+logical_scroll_pane_get_hadjustment(LogicalScrollPane *this)
+{
+ return this->hadjustment;
+}
+
+GtkAdjustment *
+logical_scroll_pane_get_vadjustment(LogicalScrollPane *this)
+{
+ return this->vadjustment;
+}
diff --git a/logical-scroll-pane.h b/logical-scroll-pane.h
new file mode 100644
index 0000000..7401d94
--- /dev/null
+++ b/logical-scroll-pane.h
@@ -0,0 +1,59 @@
+#include <gtk/gtk.h>
+
+#define LOGICAL_TYPE_SCROLL_PANE (logical_scroll_pane_get_type())
+#define LOGICAL_SCROLL_PANE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), LOGICAL_TYPE_SCROLL_PANE, LogicalScrollPane))
+#define LOGICAL_IS_SCROLL_PANE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LOGICAL_TYPE_SCROLL_PANE))
+
+typedef struct _LogicalScrollPane LogicalScrollPane;
+typedef struct _LogicalScrollPaneClass LogicalScrollPaneClass;
+
+struct _LogicalScrollPane {
+ GtkBin parent_instance;
+
+ GtkWidget *child;
+ GtkWidget *hscrollbar;
+ GtkWidget *vscrollbar;
+
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+
+ int content_width;
+ int content_height;
+ int page_width;
+ int page_height;
+ int viewport_size_is_logical;
+ int hscrollbar_visible;
+ int vscrollbar_visible;
+};
+
+struct _LogicalScrollPaneClass {
+ GtkBinClass parent_class;
+};
+
+PURE GType logical_scroll_pane_get_type(void);
+
+GtkWidget *logical_scroll_pane_new(void);
+
+void logical_scroll_pane_set_child(LogicalScrollPane *this, GtkWidget *child);
+PURE GtkWidget *logical_scroll_pane_get_child(LogicalScrollPane *this);
+
+void logical_scroll_pane_set_content_size(LogicalScrollPane *this, int width, int height);
+void logical_scroll_pane_set_viewport_size(LogicalScrollPane *this, int width, int height);
+
+void logical_scroll_pane_set_hscrollbar(LogicalScrollPane *this, GtkWidget *scrollbar);
+void logical_scroll_pane_set_vscrollbar(LogicalScrollPane *this, GtkWidget *scrollbar);
+
+PURE GtkWidget *logical_scroll_pane_get_hscrollbar(LogicalScrollPane *this);
+PURE GtkWidget *logical_scroll_pane_get_vscrollbar(LogicalScrollPane *this);
+
+void logical_scroll_pane_set_hscrollbar_visible(LogicalScrollPane *this, int visible);
+void logical_scroll_pane_set_vscrollbar_visible(LogicalScrollPane *this, int visible);
+
+PURE int logical_scroll_pane_get_hscrollbar_visible(LogicalScrollPane *this);
+PURE int logical_scroll_pane_get_vscrollbar_visible(LogicalScrollPane *this);
+
+void logical_scroll_pane_set_hadjustment(LogicalScrollPane *this, GtkAdjustment *adjustment);
+void logical_scroll_pane_set_vadjustment(LogicalScrollPane *this, GtkAdjustment *adjustment);
+
+PURE GtkAdjustment *logical_scroll_pane_get_hadjustment(LogicalScrollPane *this);
+PURE GtkAdjustment *logical_scroll_pane_get_vadjustment(LogicalScrollPane *this);