From 91cc097de2a3eb928b1ad3335bba728da17b14b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Wed, 11 Dec 2024 15:21:24 +0100 Subject: [PATCH] projectorganizer: Create a copy of GeanyWrapLabel and use it for preferences The explanation string in preferences is long, separated by \n which makes things hard for translators and makes the dialog wide under some locales. Since GeanyWrapLabel is not exported by Geany to plugins, create its local copy (and rename it to be sure that it doesn't clash with anything). --- projectorganizer/src/Makefile.am | 4 +- projectorganizer/src/prjorg-project.c | 7 +- projectorganizer/src/prjorg-wraplabel.c | 212 ++++++++++++++++++++++++ projectorganizer/src/prjorg-wraplabel.h | 49 ++++++ 4 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 projectorganizer/src/prjorg-wraplabel.c create mode 100644 projectorganizer/src/prjorg-wraplabel.h diff --git a/projectorganizer/src/Makefile.am b/projectorganizer/src/Makefile.am index 91960801c..9ec5fd1b7 100644 --- a/projectorganizer/src/Makefile.am +++ b/projectorganizer/src/Makefile.am @@ -16,7 +16,9 @@ projectorganizer_la_SOURCES = \ prjorg-goto-panel.h \ prjorg-goto-panel.c \ prjorg-goto-anywhere.h \ - prjorg-goto-anywhere.c + prjorg-goto-anywhere.c \ + prjorg-wraplabel.h \ + prjorg-wraplabel.c projectorganizer_la_CPPFLAGS = $(AM_CPPFLAGS) \ -DG_LOG_DOMAIN=\"ProjectOrganizer\" diff --git a/projectorganizer/src/prjorg-project.c b/projectorganizer/src/prjorg-project.c index b21d36436..6fb9fd71f 100644 --- a/projectorganizer/src/prjorg-project.c +++ b/projectorganizer/src/prjorg-project.c @@ -28,6 +28,7 @@ #include "prjorg-utils.h" #include "prjorg-project.h" #include "prjorg-sidebar.h" +#include "prjorg-wraplabel.h" extern GeanyPlugin *geany_plugin; extern GeanyData *geany_data; @@ -736,10 +737,10 @@ GtkWidget *prjorg_project_add_properties_tab(GtkWidget *notebook) gtk_box_pack_start(GTK_BOX(vbox), table_box, FALSE, FALSE, 6); hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - label = gtk_label_new(_("The patterns above affect only sidebar and indexing and are not used in the Find in Files\n" - "dialog. You can further restrict the files belonging to the project by setting the\n" + label = prjorg_wrap_label_new(_("The patterns above affect only sidebar and indexing and are not used in the Find in Files " + "dialog. You can further restrict the files belonging to the project by setting the" "File Patterns under the Project tab (these are also used for the Find in Files dialog).")); - gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 12); + gtk_box_pack_start(GTK_BOX(hbox1), label, TRUE, TRUE, 12); gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 0); hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); diff --git a/projectorganizer/src/prjorg-wraplabel.c b/projectorganizer/src/prjorg-wraplabel.c new file mode 100644 index 000000000..8dca2372c --- /dev/null +++ b/projectorganizer/src/prjorg-wraplabel.c @@ -0,0 +1,212 @@ +/* + * prjorg-wraplabel.c - renamed copy of geanywraplabel.c from Geany + * + * Copyright 2009 The Geany contributors + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * A GtkLabel subclass that can wrap to any width, unlike GtkLabel which has a fixed wrap point. + * (inspired by libview's WrapLabel, https://view.sourceforge.net/) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "prjorg-wraplabel.h" + + +struct _PrjorgWrapLabelClass +{ + GtkLabelClass parent_class; +}; + +typedef struct +{ + gint wrap_width; + gint wrap_height; +} PrjorgWrapLabelPrivate; + +struct _PrjorgWrapLabel +{ + GtkLabel parent; + PrjorgWrapLabelPrivate *priv; +}; + + +static gboolean prjorg_wrap_label_draw(GtkWidget *widget, cairo_t *cr); +static void prjorg_wrap_label_get_preferred_width (GtkWidget *widget, + gint *minimal_width, gint *natural_width); +static void prjorg_wrap_label_get_preferred_height (GtkWidget *widget, + gint *minimal_height, gint *natural_height); +static void prjorg_wrap_label_get_preferred_width_for_height (GtkWidget *widget, + gint height, gint *minimal_width, gint *natural_width); +static void prjorg_wrap_label_get_preferred_height_for_width (GtkWidget *widget, + gint width, gint *minimal_height, gint *natural_height); +static GtkSizeRequestMode prjorg_wrap_label_get_request_mode(GtkWidget *widget); +static void prjorg_wrap_label_size_allocate (GtkWidget *widget, GtkAllocation *alloc); +static void prjorg_wrap_label_set_wrap_width (GtkWidget *widget, gint width); +static void prjorg_wrap_label_label_notify (GObject *object, GParamSpec *pspec, gpointer data); + +G_DEFINE_TYPE(PrjorgWrapLabel, prjorg_wrap_label, GTK_TYPE_LABEL) + + +static void prjorg_wrap_label_class_init(PrjorgWrapLabelClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + widget_class->size_allocate = prjorg_wrap_label_size_allocate; + widget_class->draw = prjorg_wrap_label_draw; + widget_class->get_preferred_width = prjorg_wrap_label_get_preferred_width; + widget_class->get_preferred_width_for_height = prjorg_wrap_label_get_preferred_width_for_height; + widget_class->get_preferred_height = prjorg_wrap_label_get_preferred_height; + widget_class->get_preferred_height_for_width = prjorg_wrap_label_get_preferred_height_for_width; + widget_class->get_request_mode = prjorg_wrap_label_get_request_mode; + + g_type_class_add_private(klass, sizeof (PrjorgWrapLabelPrivate)); +} + + +static void prjorg_wrap_label_init(PrjorgWrapLabel *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + PRJORG_WRAP_LABEL_TYPE, PrjorgWrapLabelPrivate); + + self->priv->wrap_width = 0; + self->priv->wrap_height = 0; + + g_signal_connect(self, "notify::label", G_CALLBACK(prjorg_wrap_label_label_notify), NULL); + gtk_misc_set_alignment(GTK_MISC(self), 0.0, 0.0); +} + + +/* Sets the point at which the text should wrap. */ +static void prjorg_wrap_label_set_wrap_width(GtkWidget *widget, gint width) +{ + PrjorgWrapLabel *self = PRJORG_WRAP_LABEL(widget); + PangoLayout *layout; + + if (width <= 0) + return; + + layout = gtk_label_get_layout(GTK_LABEL(widget)); + + /* + * We may need to reset the wrap width, so do this regardless of whether + * or not we've changed the width. + */ + pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_get_pixel_size(layout, NULL, &self->priv->wrap_height); + + if (self->priv->wrap_width != width) + { + self->priv->wrap_width = width; + gtk_widget_queue_resize(widget); + } +} + + +/* updates the wrap width when the label text changes */ +static void prjorg_wrap_label_label_notify(GObject *object, GParamSpec *pspec, gpointer data) +{ + PrjorgWrapLabel *self = PRJORG_WRAP_LABEL(object); + + prjorg_wrap_label_set_wrap_width(GTK_WIDGET(object), self->priv->wrap_width); +} + + +/* makes sure the layout is setup for rendering and chains to parent renderer */ +static gboolean prjorg_wrap_label_draw(GtkWidget *widget, cairo_t *cr) +{ + PrjorgWrapLabel *self = PRJORG_WRAP_LABEL(widget); + PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(widget)); + + pango_layout_set_width(layout, self->priv->wrap_width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + + return (* GTK_WIDGET_CLASS(prjorg_wrap_label_parent_class)->draw)(widget, cr); +} + + +static void prjorg_wrap_label_get_preferred_width (GtkWidget *widget, + gint *minimal_width, gint *natural_width) +{ + *minimal_width = *natural_width = 0; +} + + +static void prjorg_wrap_label_get_preferred_width_for_height (GtkWidget *widget, + gint height, gint *minimal_width, gint *natural_width) +{ + PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(widget));; + + pango_layout_set_height(layout, height * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_get_pixel_size(layout, natural_width, NULL); + + *minimal_width = 0; +} + + +static void prjorg_wrap_label_get_preferred_height (GtkWidget *widget, + gint *minimal_height, gint *natural_height) +{ + *minimal_height = *natural_height = PRJORG_WRAP_LABEL(widget)->priv->wrap_height; +} + + +static void prjorg_wrap_label_get_preferred_height_for_width (GtkWidget *widget, + gint width, gint *minimal_height, gint *natural_height) +{ + PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(widget)); + + pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_get_pixel_size(layout, NULL, natural_height); + + *minimal_height = *natural_height; +} + + +static GtkSizeRequestMode prjorg_wrap_label_get_request_mode(GtkWidget *widget) +{ + return GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT; +} + + +/* Sets the wrap width to the width allocated to us. */ +static void prjorg_wrap_label_size_allocate(GtkWidget *widget, GtkAllocation *alloc) +{ + GtkWidget *parent; + + (* GTK_WIDGET_CLASS(prjorg_wrap_label_parent_class)->size_allocate)(widget, alloc); + + prjorg_wrap_label_set_wrap_width(widget, alloc->width); + + /* ask the parent to recompute our size, because it seems GTK size + * caching is too aggressive */ + parent = gtk_widget_get_parent(widget); + if (GTK_IS_CONTAINER(parent)) + gtk_container_check_resize(GTK_CONTAINER(parent)); +} + + +GtkWidget *prjorg_wrap_label_new(const gchar *text) +{ + return g_object_new(PRJORG_WRAP_LABEL_TYPE, "label", text, NULL); +} diff --git a/projectorganizer/src/prjorg-wraplabel.h b/projectorganizer/src/prjorg-wraplabel.h new file mode 100644 index 000000000..efd22fdef --- /dev/null +++ b/projectorganizer/src/prjorg-wraplabel.h @@ -0,0 +1,49 @@ +/* + * prjorg- wraplabel.h - renamed copy of geanywraplabel.h from Geany + * + * Copyright 2009 The Geany contributors + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef PRJORG_WRAP_LABEL_H +#define PRJORG_WRAP_LABEL_H 1 + +#include "gtkcompat.h" + +G_BEGIN_DECLS + + +#define PRJORG_WRAP_LABEL_TYPE (prjorg_wrap_label_get_type()) +#define PRJORG_WRAP_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + PRJORG_WRAP_LABEL_TYPE, PrjorgWrapLabel)) +#define PRJORG_WRAP_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ + PRJORG_WRAP_LABEL_TYPE, PrjorgWrapLabelClass)) +#define IS_PRJORG_WRAP_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + PRJORG_WRAP_LABEL_TYPE)) +#define IS_PRJORG_WRAP_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \ + PRJORG_WRAP_LABEL_TYPE)) + + +typedef struct _PrjorgWrapLabel PrjorgWrapLabel; +typedef struct _PrjorgWrapLabelClass PrjorgWrapLabelClass; + +GType prjorg_wrap_label_get_type (void); +GtkWidget* prjorg_wrap_label_new (const gchar *text); + + +G_END_DECLS + +#endif /* PRJORG_WRAP_LABEL_H */