[glabels/vala] Initial import of vala port work.
- From: Jim Evins <jimevins src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glabels/vala] Initial import of vala port work.
- Date: Sun, 4 Mar 2012 05:35:34 +0000 (UTC)
commit cfb682d6f2b802a776e9c607176b90b2580a086d
Author: Jim Evins <evins snaught com>
Date: Sun Mar 4 00:31:54 2012 -0500
Initial import of vala port work.
.gitignore | 11 +
Makefile.am | 59 +-
autogen.sh | 15 +-
configure.ac | 373 +---
configure.ac.old | 378 ++++
data/Makefile.am | 9 +-
data/icons/22x22/Makefile.am | 9 +-
.../22x22/actions/glabels-align-text-bottom.png | Bin 429 -> 0 bytes
.../22x22/actions/glabels-align-text-middle.png | Bin 418 -> 0 bytes
.../icons/22x22/actions/glabels-align-text-top.png | Bin 396 -> 0 bytes
data/icons/24x24/Makefile.am | 3 -
.../24x24/actions/glabels-align-text-bottom.png | Bin 481 -> 0 bytes
.../24x24/actions/glabels-align-text-middle.png | Bin 639 -> 0 bytes
.../icons/24x24/actions/glabels-align-text-top.png | Bin 398 -> 0 bytes
data/icons/Makefile.am | 2 +-
data/schemas/org.gnome.glabels-3.gschema.xml.in.in | 8 +-
data/ui/Makefile.am | 10 +-
data/ui/new-label-dialog.ui | 486 -----
data/ui/new_label_dialog.ui | 135 ++
data/ui/object-editor.ui | 424 +----
data/ui/property-bar.ui | 103 +-
glabels/Makefile.am | 87 +
glabels/TMP_gdk_key.vala | 19 +
glabels/color.vala | 136 ++
glabels/color_button.vala | 181 ++
glabels/color_history.vala | 127 ++
glabels/color_menu.vala | 253 +++
glabels/color_menu_item.vala | 66 +
glabels/color_node.vala | 93 +
glabels/color_swatch.vala | 107 +
glabels/enum_util.vala | 106 +
glabels/file.vala | 483 +++++
glabels/file_util.vala | 58 +
glabels/font_button.vala | 154 ++
glabels/font_families.vala | 107 +
glabels/font_history.vala | 101 +
glabels/font_menu.vala | 138 ++
glabels/font_menu_item.vala | 85 +
glabels/font_sample.vala | 140 ++
glabels/glabels.vala | 95 +
glabels/handle.vala | 243 +++
glabels/help.vala | 99 +
glabels/label.vala | 1470 ++++++++++++++
glabels/label_object.vala | 725 +++++++
glabels/label_object_box.vala | 176 ++
glabels/label_region.vala | 35 +
glabels/label_state.vala | 93 +
glabels/merge.vala | 87 +
glabels/merge_factory.vala | 78 +
glabels/merge_field.vala | 33 +
glabels/merge_none.vala | 72 +
glabels/merge_record.vala | 47 +
glabels/merge_text.vala | 395 ++++
glabels/mini_preview.vala | 417 ++++
glabels/new_label_dialog.vala | 218 ++
glabels/prefs.vala | 376 ++++
glabels/print.vala | 217 ++
glabels/template_history.vala | 94 +
glabels/test.vala | 221 ++
glabels/ui.vala | 1391 +++++++++++++
glabels/units_util.vala | 96 +
glabels/view.vala | 1232 ++++++++++++
glabels/window.vala | 279 +++
glabels/xml_label.vala | 504 +++++
libglabels/Makefile.am | 122 +-
libglabels/{lgl-str.h => category.vala} | 44 +-
libglabels/db.vala | 788 ++++++++
libglabels/lgl-category.c | 132 --
libglabels/lgl-category.h | 62 -
libglabels/lgl-db.c | 2123 --------------------
libglabels/lgl-db.h | 197 --
libglabels/lgl-paper.c | 147 --
libglabels/lgl-paper.h | 70 -
libglabels/lgl-str.c | 284 ---
libglabels/lgl-template.c | 1438 -------------
libglabels/lgl-template.h | 424 ----
libglabels/lgl-units.c | 261 ---
libglabels/lgl-units.h | 70 -
libglabels/lgl-vendor.c | 128 --
libglabels/lgl-vendor.h | 63 -
libglabels/lgl-xml-category.c | 185 --
libglabels/lgl-xml-category.h | 51 -
libglabels/lgl-xml-paper.c | 192 --
libglabels/lgl-xml-template.c | 1207 -----------
libglabels/lgl-xml-template.h | 61 -
libglabels/lgl-xml-vendor.c | 183 --
libglabels/lgl-xml-vendor.h | 51 -
libglabels/lgl-xml.c | 524 -----
libglabels/lgl-xml.h | 119 --
libglabels/libglabels-3.0.pc.in | 12 -
libglabels/libglabels.h | 46 -
libglabels/mini_preview_pixbuf.vala | 156 ++
libglabels/paper.vala | 51 +
libglabels/str_util.vala | 213 ++
libglabels/template.vala | 252 +++
libglabels/template_coord.vala | 70 +
libglabels/template_frame.vala | 123 ++
libglabels/template_frame_cd.vala | 182 ++
libglabels/template_frame_ellipse.vala | 157 ++
libglabels/template_frame_rect.vala | 160 ++
libglabels/template_frame_round.vala | 128 ++
libglabels/template_layout.vala | 86 +
.../{libglabels-private.h => template_markup.vala} | 40 +-
libglabels/template_markup_circle.vala | 63 +
libglabels/template_markup_ellipse.vala | 83 +
libglabels/template_markup_line.vala | 66 +
libglabels/template_markup_margin.vala | 171 ++
libglabels/template_markup_rect.vala | 82 +
libglabels/units.vala | 137 ++
libglabels/{lgl-xml-paper.h => vendor.vala} | 45 +-
libglabels/xml_category.vala | 104 +
libglabels/xml_paper.vala | 109 +
libglabels/xml_template.vala | 744 +++++++
libglabels/xml_util.vala | 244 +++
libglabels/xml_vendor.vala | 104 +
po/POTFILES.in | 267 +---
po/POTFILES.skip | 3 -
vapi/Makefile.am | 5 +
vapi/config.vapi | 18 +
119 files changed, 16255 insertions(+), 9751 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 897e589..11de99a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ glabels-*.tar.gz
/aclocal.m4
/autom4te.cache
/autoscan.log
+/compile
/config.guess
/config.h
/config.h.in
@@ -42,9 +43,19 @@ glabels-*.tar.gz
/stamp-h1
/xmldocs.make
+/libglabels/*.c
+/libglabels/*.h
+/libglabels/*.vapi
+/libglabels/*.stamp
/libglabels/libglabels*.pc
+
/libglbarcode/libglbarcode*.pc
+/glabels/*.c
+/glabels/*.stamp
+/glabels/test
+/glabels/glabels
+
/src/marshal.[ch]
/src/stock-pixmaps/stockpixbufs.h
/src/cursors/cursor_pixdata.h
diff --git a/Makefile.am b/Makefile.am
index 0aec6e1..b23c823 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,50 +1,31 @@
-## Process this file with automake to produce Makefile.in
+NULL =
+
+#Build in these directories:
SUBDIRS = \
- po \
libglabels \
- libglbarcode \
- src \
- data \
+ glabels \
+ po \
templates \
+ data \
help \
- docs
+ $(NULL)
+
+glabelsdocdir = ${prefix}/doc/glabels
+glabelsdoc_DATA = \
+ $(NULL)
EXTRA_DIST = \
- README \
- COPYING.README_FIRST \
- COPYING \
- COPYING-DOCS \
- COPYING-LIBS \
- COPYING-TEMPLATES \
- AUTHORS \
- ChangeLog \
- INSTALL \
- NEWS \
- TODO \
+ $(glabelsdoc_DATA) \
intltool-extract.in \
intltool-merge.in \
- intltool-update.in \
- gnome-doc-utils.make \
- glabels.spec.in \
- glabels.spec
-
-DISTCLEANFILES = gnome-doc-utils.make
+ intltool-update.in\
+ $(NULL)
-DISTCHECK_CONFIGURE_FLAGS = \
- --disable-scrollkeeper \
- --enable-gtk-doc
+DISTCLEANFILES = \
+ intltool-extract \
+ intltool-merge \
+ intltool-update \
+ po/.intltool-merge-cache \
+ $(NULL)
-dist-hook:
- @if test -d "$(srcdir)/.git"; \
- then \
- echo Creating ChangeLog && \
- (GIT_DIR=$(top_srcdir)/.git \
- ./missing --run git log -M -C --name-status --date=short --no-color) | \
- fmt --split-only > ChangeLog.tmp \
- && mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \
- || ( rm -f ChangeLog.tmp ; \
- echo Failed to generate ChangeLog >&2 ); \
- else \
- echo A git clone is required to generate a ChangeLog >&2; \
- fi
diff --git a/autogen.sh b/autogen.sh
index 307c331..fe21110 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -6,17 +6,4 @@ test -z "$srcdir" && srcdir=.
PKG_NAME="glabels"
-(test -f $srcdir/configure.ac \
- && test -f $srcdir/README \
- && test -d $srcdir/src) || {
- echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
- echo " top-level $PKG_NAME directory"
- exit 1
-}
-
-which gnome-autogen.sh || {
- echo "You need to install gnome-common from the GNOME CVS"
- exit 1
-}
-USE_GNOME2_MACROS=1 USE_COMMON_DOC_BUILD=yes . gnome-autogen.sh
-
+. gnome-autogen.sh
diff --git a/configure.ac b/configure.ac
index f5d92c7..6e39130 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@ dnl ---------------------------------------------------------------------------
dnl - GLABELS version
dnl ---------------------------------------------------------------------------
m4_define([glabels_major_version], [3])
-m4_define([glabels_minor_version], [0])
+m4_define([glabels_minor_version], [99])
m4_define([glabels_micro_version], [0])
m4_define([glabels_version],
@@ -14,367 +14,104 @@ m4_define([glabels_version],
dnl ---------------------------------------------------------------------------
-AC_PREREQ(2.64)
-AC_INIT([glabels],[glabels_version],
+AC_INIT([glabels], [glabels_version],
[http://bugzilla.gnome.org/enter_bug.cgi?product=glabels],
- [glabels])
-
-AC_CONFIG_SRCDIR(src/glabels.c)
-
-AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+ [glabels])
+AC_CONFIG_SRCDIR([Makefile.am])
+AC_CONFIG_HEADERS(config.h)
+AM_INIT_AUTOMAKE([dist-bzip2])
AM_MAINTAINER_MODE
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
-AC_CONFIG_HEADERS(config.h)
-
-IT_PROG_INTLTOOL([0.21])
-
-PKG_PROG_PKG_CONFIG
-
-LT_INIT
-
-GNOME_DOC_INIT
-GTK_DOC_CHECK(1.0)
-
-GLIB_GSETTINGS
-
-AC_SEARCH_LIBS([strerror],[cposix])
AC_PROG_CC
-AC_PROG_INSTALL
+AM_PROG_CC_C_O
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
-GNOME_COMPILE_WARNINGS
+GNOME_DOC_INIT
-AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
-AC_PATH_PROG(GDK_PIXBUF_CSOURCE, gdk-pixbuf-csource)
AC_PATH_PROG(GTK_UPDATE_ICON_CACHE, gtk-update-icon-cache)
-
dnl ---------------------------------------------------------------------------
dnl - GLABELS branch
dnl ---------------------------------------------------------------------------
-GLABELS_BRANCH=glabels-3.0
+GLABELS_BRANCH=glabels-4.0
AC_SUBST(GLABELS_BRANCH)
dnl ---------------------------------------------------------------------------
dnl - LIBGLABELS branch
dnl ---------------------------------------------------------------------------
-LIBGLABELS_BRANCH=libglabels-3.0
+LIBGLABELS_BRANCH=libglabels-4.0
AC_SUBST(LIBGLABELS_BRANCH)
dnl ---------------------------------------------------------------------------
dnl - LIBGLBARCODE branch
dnl ---------------------------------------------------------------------------
-LIBGLBARCODE_BRANCH=libglbarcode-3.0
+LIBGLBARCODE_BRANCH=libglbarcode-4.0
AC_SUBST(LIBGLBARCODE_BRANCH)
-dnl ---------------------------------------------------------------------------
-dnl - LIBGLABELS API versioning
-dnl ---------------------------------------------------------------------------
-dnl From the libtool manual:
-dnl 1. Start with version information of `0:0:0' for each libtool library.
-dnl 2. Update the version information only immediately before a public release.
-dnl More frequent updates are unnecessary, and only guarantee that the current
-dnl interface number gets larger faster.
-dnl 3. If the library source code has changed at all since the last update, then increment
-dnl revision (`c:r:a' becomes `c:r+1:a').
-dnl 4. If any interfaces have been added, removed, or changed since the last update,
-dnl increment current, and set revision to 0.
-dnl 5. If any interfaces have been added since the last public release, then increment age.
-dnl 6. If any interfaces have been removed since the last public release, then set age
-dnl to 0.
-LIBGLABELS_C=8
-LIBGLABELS_R=0
-LIBGLABELS_A=0
-LIBGLABELS_API_VERSION=${LIBGLABELS_C}:${LIBGLABELS_R}:${LIBGLABELS_A}
-AC_SUBST(LIBGLABELS_API_VERSION)
+AC_PATH_PROG(VALAC, valac, valac)
+AC_SUBST(VALAC)
-dnl ---------------------------------------------------------------------------
-dnl - LIBGLBARCODE API versioning
-dnl ---------------------------------------------------------------------------
-dnl From the libtool manual:
-dnl 1. Start with version information of `0:0:0' for each libtool library.
-dnl 2. Update the version information only immediately before a public release.
-dnl More frequent updates are unnecessary, and only guarantee that the current
-dnl interface number gets larger faster.
-dnl 3. If the library source code has changed at all since the last update, then increment
-dnl revision (`c:r:a' becomes `c:r+1:a').
-dnl 4. If any interfaces have been added, removed, or changed since the last update,
-dnl increment current, and set revision to 0.
-dnl 5. If any interfaces have been added since the last public release, then increment age.
-dnl 6. If any interfaces have been removed since the last public release, then set age
-dnl to 0.
-LIBGLBARCODE_C=0
-LIBGLBARCODE_R=0
-LIBGLBARCODE_A=0
+AH_TEMPLATE([GETTEXT_PACKAGE], [Package name for gettext])
+GETTEXT_PACKAGE=glabels-4.0
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE")
+AC_SUBST(GETTEXT_PACKAGE)
+AM_GLIB_GNU_GETTEXT
+IT_PROG_INTLTOOL([0.35.0])
-LIBGLBARCODE_API_VERSION=${LIBGLBARCODE_C}:${LIBGLBARCODE_R}:${LIBGLBARCODE_A}
-AC_SUBST(LIBGLBARCODE_API_VERSION)
+AC_SUBST(CFLAGS)
+AC_SUBST(CPPFLAGS)
+AC_SUBST(LDFLAGS)
-dnl ---------------------------------------------------------------------------
-dnl - Library dependencies
-dnl ---------------------------------------------------------------------------
-dnl Required dependencies
-GLIB_REQUIRED=2.28.2
+GLIB_REQUIRED=2.28.0
GTK_REQUIRED=3.0.9
LIBXML_REQUIRED=2.7.8
-LIBRSVG_REQUIRED=2.32.0
-CAIRO_REQUIRED=1.10.0
-PANGO_REQUIRED=1.28.1
-
-dnl Optional dependencies
-LIBEBOOK_REQUIRED=2.30.3
-LIBBARCODE_REQUIRED=0.98
-LIBQRENCODE_REQUIRED=3.1.0
-LIBIEC16022_REQUIRED=0.2.4
-LIBZINT_REQUIRED=2.4.0
+GEE_REQUIRED=0.6.2.1
+CAIRO_REQUIRED=1.10.2
+GDK_PIXBUF_REQUIRED=2.24.0
-dnl Make above strings available for packaging files (e.g. rpm spec files)
-AC_SUBST(GLIB_REQUIRED)
-AC_SUBST(GTK_REQUIRED)
-AC_SUBST(LIBXML_REQUIRED)
-AC_SUBST(LIBRSVG_REQUIRED)
-AC_SUBST(CAIRO_REQUIRED)
-AC_SUBST(PANGO_REQUIRED)
-AC_SUBST(LIBEBOOK_REQUIRED)
-AC_SUBST(LIBBARCODE_REQUIRED)
-AC_SUBST(LIBQRENCODE_REQUIRED)
-AC_SUBST(LIBIEC16022_REQUIRED)
-AC_SUBST(LIBZINT_REQUIRED)
-
-
-dnl ---------------------------------------------------------------------------
-dnl - GLABELS prerequisites
-dnl ---------------------------------------------------------------------------
PKG_CHECK_MODULES(GLABELS, [\
- glib-2.0 >= $GLIB_REQUIRED \
- gtk+-3.0 >= $GTK_REQUIRED \
- libxml-2.0 >= $LIBXML_REQUIRED \
- librsvg-2.0 >= $LIBRSVG_REQUIRED \
+ glib-2.0 >= $GLIB_REQUIRED \
+ gobject-2.0 >= $GLIB_REQUIRED \
+ gtk+-3.0 >= $GTK_REQUIRED \
+ libxml-2.0 >= $LIBXML_REQUIRED \
+ gee-1.0 >= $GEE_REQUIRED \
])
AC_SUBST(GLABELS_CFLAGS)
AC_SUBST(GLABELS_LIBS)
-
-dnl ---------------------------------------------------------------------------
-dnl - LIBGLABELS more modest prerequisites
-dnl ---------------------------------------------------------------------------
PKG_CHECK_MODULES(LIBGLABELS, [\
- glib-2.0 >= $GLIB_REQUIRED \
- gobject-2.0 >= $GLIB_REQUIRED \
- libxml-2.0 >= $LIBXML_REQUIRED \
+ glib-2.0 >= $GLIB_REQUIRED \
+ gobject-2.0 >= $GLIB_REQUIRED \
+ libxml-2.0 >= $LIBXML_REQUIRED \
+ gee-1.0 >= $GEE_REQUIRED \
+ cairo >= $CAIRO_REQUIRED \
+ gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED \
])
AC_SUBST(LIBGLABELS_CFLAGS)
AC_SUBST(LIBGLABELS_LIBS)
-dnl ---------------------------------------------------------------------------
-dnl - LIBGLBARCODE more modest prerequisites
-dnl ---------------------------------------------------------------------------
-PKG_CHECK_MODULES(LIBGLBARCODE, [\
- glib-2.0 >= $GLIB_REQUIRED \
- cairo >= $CAIRO_REQUIRED \
- pango >= $PANGO_REQUIRED \
- pangocairo >= $PANGO_REQUIRED \
-])
-
-AC_SUBST(LIBGLBARCODE_CFLAGS)
-AC_SUBST(LIBGLBARCODE_LIBS)
-
-
-dnl ---------------------------------------------------------------------------
-dnl - Check for optional evolution data server
-dnl ---------------------------------------------------------------------------
-AC_ARG_WITH(libebook,
- [AS_HELP_STRING([--without-libebook],[build without Evolution Data Server support])])
-have_libebook=no
-if test "x$with_libebook" != xno; then
- PKG_CHECK_MODULES(LIBEBOOK, libebook-1.2 >= $LIBEBOOK_REQUIRED,
- [have_libebook=yes], [have_libebook=no])
-fi
-
-if test "x$have_libebook" = "xyes"; then
- AC_DEFINE(HAVE_LIBEBOOK,1,[Define to 1 for EDS support])
- AC_SUBST(LIBEBOOK_CFLAGS)
- AC_SUBST(LIBEBOOK_LIBS)
-fi
-
+AC_CONFIG_FILES([Makefile
+ vapi/Makefile
+ libglabels/Makefile
+ glabels/Makefile
+ po/Makefile.in
+ templates/Makefile
+ data/Makefile
+ data/icons/Makefile
+ data/icons/16x16/Makefile
+ data/icons/22x22/Makefile
+ data/icons/24x24/Makefile
+ data/icons/32x32/Makefile
+ data/icons/48x48/Makefile
+ data/pixmaps/Makefile
+ data/ui/Makefile
+ help/Makefile])
-dnl ---------------------------------------------------------------------------
-dnl - Check for optional GNU Barcode backend
-dnl ---------------------------------------------------------------------------
-AC_ARG_WITH(libbarcode,
- [AS_HELP_STRING([--without-libbarcode],[build without GNU Barcode support])])
-have_libbarcode=no
-if test "x$with_libbarcode" != xno; then
- AC_CHECK_LIB(barcode, Barcode_Create,
- [have_libbarcode=yes], [have_libbarcode=no])
-fi
-
-if test "x$have_libbarcode" = "xyes"; then
- AC_DEFINE(HAVE_LIBBARCODE,1,[Define to 1 for GNU Barcode support])
- LIBBARCODE_CFLAGS=""
- LIBBARCODE_LIBS="-lbarcode"
- AC_SUBST(LIBBARCODE_CFLAGS)
- AC_SUBST(LIBBARCODE_LIBS)
-else
- help_libbarcode="(See http://www.gnu.org/software/barcode/barcode.html)"
-fi
-
-dnl ---------------------------------------------------------------------------
-dnl - Check for optional Zint backend
-dnl ---------------------------------------------------------------------------
-AC_ARG_WITH(libzint,
- [AS_HELP_STRING([--without-libzint],[build without Zint Barcode support])])
-have_libzint=no
-if test "x$with_libzint" != xno; then
- AC_CHECK_LIB(zint, ZBarcode_Render,
- [have_libzint=yes], [have_libzint=no])
-fi
-
-if test "x$have_libzint" = "xyes"; then
- AC_DEFINE(HAVE_LIBZINT,1,[Define to 1 for Zint Barcode support])
- LIBZINT_CFLAGS=""
- LIBZINT_LIBS="-lzint"
- AC_SUBST(LIBZINT_CFLAGS)
- AC_SUBST(LIBZINT_LIBS)
-else
- help_libzint="(See http://www.zint.org.uk)"
-fi
-
-
-dnl ---------------------------------------------------------------------------
-dnl - Check for optional QRencode Barcode backend
-dnl ---------------------------------------------------------------------------
-AC_ARG_WITH(libqrencode,
- [AS_HELP_STRING([--without-libqrencode],[build without QR code support])])
-have_libqrencode=no
-if test "x$with_libqrencode" != xno; then
- PKG_CHECK_MODULES(LIBQRENCODE, libqrencode >= $LIBQRENCODE_REQUIRED,
- [have_libqrencode=yes], [have_libqrencode=no])
-fi
-
-if test "x$have_libqrencode" = "xyes"; then
- AC_DEFINE(HAVE_LIBQRENCODE,1,[Define to 1 for QR code support])
- AC_SUBST(LIBQRENCODE_CFLAGS)
- AC_SUBST(LIBQRENCODE_LIBS)
-else
- help_libqrencode="(See http://megaui.net/fukuchi/works/qrencode/index.en.html)"
-fi
-
-
-dnl ---------------------------------------------------------------------------
-dnl - Check for IEC16022 Barcode backend
-dnl ---------------------------------------------------------------------------
-AC_ARG_WITH(libiec16022,
- [AS_HELP_STRING([--without-libiec16022],[build without IEC 16022 support])])
-have_libiec16022=no
-if test "x$with_libiec16022" != xno; then
- PKG_CHECK_MODULES(LIBIEC16022, libiec16022 >= $LIBIEC16022_REQUIRED,
- [have_libiec16022=yes], [have_libiec16022=no])
-fi
-
-if test "x$have_libiec16022" = "xyes"; then
- AC_DEFINE(HAVE_LIBIEC16022,1,[Define to 1 for IEC 16022 support])
- AC_SUBST(LIBIEC16022_CFLAGS)
- AC_SUBST(LIBIEC16022_LIBS)
-else
- help_libiec16022="(See http://datenfreihafen.org/projects/iec16022.html)"
-fi
-
-
-dnl ---------------------------------------------------------------------------
-dnl - Enable deprecation testing
-dnl ---------------------------------------------------------------------------
-AC_ARG_ENABLE(deprecations,
- [AS_HELP_STRING([--enable-deprecations],[warn about deprecated usages [default=no]])],,
- [enable_deprecations=no])
-
-if test "x$enable_deprecations" = "xyes"; then
- DISABLE_DEPRECATED_CFLAGS="\
--DG_DISABLE_DEPRECATED \
--DGDK_DISABLE_DEPRECATED \
--DGTK_DISABLE_DEPRECATED \
--DGDK_PIXBUF_DISABLE_DEPRECATED \
-"
- AC_SUBST(DISABLE_DEPRECATED_CFLAGS)
-fi
-
-
-dnl ---------------------------------------------------------------------------
-dnl - i18n support
-dnl ---------------------------------------------------------------------------
-GETTEXT_PACKAGE=${GLABELS_BRANCH}
-AC_SUBST(GETTEXT_PACKAGE)
-AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Gettext package])
-
-AM_GLIB_GNU_GETTEXT
-
-
-dnl ---------------------------------------------------------------------------
-dnl - Makefiles, etc.
-dnl ---------------------------------------------------------------------------
-AC_CONFIG_FILES([
-Makefile
-libglabels/Makefile
-libglabels/${LIBGLABELS_BRANCH}.pc
-libglbarcode/Makefile
-libglbarcode/${LIBGLBARCODE_BRANCH}.pc
-src/Makefile
-src/cursors/Makefile
-src/pixmaps/Makefile
-data/Makefile
-data/desktop/Makefile
-data/icons/Makefile
-data/icons/16x16/Makefile
-data/icons/22x22/Makefile
-data/icons/24x24/Makefile
-data/icons/32x32/Makefile
-data/icons/48x48/Makefile
-data/man/Makefile
-data/mime/Makefile
-data/pixmaps/Makefile
-data/schemas/Makefile
-data/schemas/org.gnome.glabels-3.gschema.xml.in
-data/ui/Makefile
-templates/Makefile
-po/Makefile.in
-help/Makefile
-docs/Makefile
-docs/libglabels/Makefile
-docs/libglbarcode/Makefile
-glabels.spec
-])
AC_OUTPUT
-
-
-dnl ---------------------------------------------------------------------------
-dnl - Print configuration information
-dnl ---------------------------------------------------------------------------
-echo "
-
-Configuration:
-
- Package ................. ${PACKAGE}-${VERSION}
- Installation prefix ..... ${prefix}
- Source code location .... ${srcdir}
- Compiler ................ ${CC}
-
-
-Optional data merge backends:
-
- Evolution Data Server ... ${have_libebook}
-
-
-Optional barcode backends:
-
- GNU Barcode ............. ${have_libbarcode} ${help_libbarcode}
- QR Code ................. ${have_libqrencode} ${help_libqrencode}
- IEC 16022 ............... ${have_libiec16022} ${help_libiec16022}
- Zint .................... ${have_libzint} ${help_libzint}
-
-
-"
diff --git a/configure.ac.old b/configure.ac.old
new file mode 100644
index 0000000..a52cd1a
--- /dev/null
+++ b/configure.ac.old
@@ -0,0 +1,378 @@
+dnl Process this file with autoconf to produce a configure script.
+
+
+dnl ---------------------------------------------------------------------------
+dnl - GLABELS version
+dnl ---------------------------------------------------------------------------
+m4_define([glabels_major_version], [3])
+m4_define([glabels_minor_version], [0])
+m4_define([glabels_micro_version], [0])
+
+m4_define([glabels_version],
+ [glabels_major_version.glabels_minor_version.glabels_micro_version])
+
+dnl ---------------------------------------------------------------------------
+
+
+AC_PREREQ(2.64)
+AC_INIT([glabels],[glabels_version],
+ [http://bugzilla.gnome.org/enter_bug.cgi?product=glabels],
+ [glabels])
+
+AC_CONFIG_SRCDIR(src/glabels.c)
+
+AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
+
+AM_MAINTAINER_MODE
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+
+AC_CONFIG_HEADERS(config.h)
+
+IT_PROG_INTLTOOL([0.21])
+
+PKG_PROG_PKG_CONFIG
+
+LT_INIT
+
+GNOME_DOC_INIT
+GTK_DOC_CHECK(1.0)
+
+GLIB_GSETTINGS
+
+AC_SEARCH_LIBS([strerror],[cposix])
+AC_PROG_CC
+AC_PROG_INSTALL
+
+GNOME_COMPILE_WARNINGS
+
+AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
+AC_PATH_PROG(GDK_PIXBUF_CSOURCE, gdk-pixbuf-csource)
+AC_PATH_PROG(GTK_UPDATE_ICON_CACHE, gtk-update-icon-cache)
+
+
+dnl ---------------------------------------------------------------------------
+dnl - GLABELS branch
+dnl ---------------------------------------------------------------------------
+GLABELS_BRANCH=glabels-4.0
+AC_SUBST(GLABELS_BRANCH)
+
+dnl ---------------------------------------------------------------------------
+dnl - LIBGLABELS branch
+dnl ---------------------------------------------------------------------------
+LIBGLABELS_BRANCH=libglabels-4.0
+AC_SUBST(LIBGLABELS_BRANCH)
+
+dnl ---------------------------------------------------------------------------
+dnl - LIBGLBARCODE branch
+dnl ---------------------------------------------------------------------------
+LIBGLBARCODE_BRANCH=libglbarcode-4.0
+AC_SUBST(LIBGLBARCODE_BRANCH)
+
+dnl ---------------------------------------------------------------------------
+dnl - LIBGLABELS API versioning
+dnl ---------------------------------------------------------------------------
+dnl From the libtool manual:
+dnl 1. Start with version information of `0:0:0' for each libtool library.
+dnl 2. Update the version information only immediately before a public release.
+dnl More frequent updates are unnecessary, and only guarantee that the current
+dnl interface number gets larger faster.
+dnl 3. If the library source code has changed at all since the last update, then increment
+dnl revision (`c:r:a' becomes `c:r+1:a').
+dnl 4. If any interfaces have been added, removed, or changed since the last update,
+dnl increment current, and set revision to 0.
+dnl 5. If any interfaces have been added since the last public release, then increment age.
+dnl 6. If any interfaces have been removed since the last public release, then set age
+dnl to 0.
+LIBGLABELS_C=8
+LIBGLABELS_R=0
+LIBGLABELS_A=0
+
+LIBGLABELS_API_VERSION=${LIBGLABELS_C}:${LIBGLABELS_R}:${LIBGLABELS_A}
+AC_SUBST(LIBGLABELS_API_VERSION)
+
+dnl ---------------------------------------------------------------------------
+dnl - LIBGLBARCODE API versioning
+dnl ---------------------------------------------------------------------------
+dnl From the libtool manual:
+dnl 1. Start with version information of `0:0:0' for each libtool library.
+dnl 2. Update the version information only immediately before a public release.
+dnl More frequent updates are unnecessary, and only guarantee that the current
+dnl interface number gets larger faster.
+dnl 3. If the library source code has changed at all since the last update, then increment
+dnl revision (`c:r:a' becomes `c:r+1:a').
+dnl 4. If any interfaces have been added, removed, or changed since the last update,
+dnl increment current, and set revision to 0.
+dnl 5. If any interfaces have been added since the last public release, then increment age.
+dnl 6. If any interfaces have been removed since the last public release, then set age
+dnl to 0.
+LIBGLBARCODE_C=0
+LIBGLBARCODE_R=0
+LIBGLBARCODE_A=0
+
+LIBGLBARCODE_API_VERSION=${LIBGLBARCODE_C}:${LIBGLBARCODE_R}:${LIBGLBARCODE_A}
+AC_SUBST(LIBGLBARCODE_API_VERSION)
+
+dnl ---------------------------------------------------------------------------
+dnl - Library dependencies
+dnl ---------------------------------------------------------------------------
+dnl Required dependencies
+GLIB_REQUIRED=2.28.2
+GTK_REQUIRED=3.0.9
+LIBXML_REQUIRED=2.7.8
+LIBRSVG_REQUIRED=2.32.0
+CAIRO_REQUIRED=1.10.0
+PANGO_REQUIRED=1.28.1
+
+dnl Optional dependencies
+LIBEBOOK_REQUIRED=2.30.3
+LIBBARCODE_REQUIRED=0.98
+LIBQRENCODE_REQUIRED=3.1.0
+LIBIEC16022_REQUIRED=0.2.4
+LIBZINT_REQUIRED=2.4.0
+
+dnl Make above strings available for packaging files (e.g. rpm spec files)
+AC_SUBST(GLIB_REQUIRED)
+AC_SUBST(GTK_REQUIRED)
+AC_SUBST(LIBXML_REQUIRED)
+AC_SUBST(LIBRSVG_REQUIRED)
+AC_SUBST(CAIRO_REQUIRED)
+AC_SUBST(PANGO_REQUIRED)
+AC_SUBST(LIBEBOOK_REQUIRED)
+AC_SUBST(LIBBARCODE_REQUIRED)
+AC_SUBST(LIBQRENCODE_REQUIRED)
+AC_SUBST(LIBIEC16022_REQUIRED)
+AC_SUBST(LIBZINT_REQUIRED)
+
+
+dnl ---------------------------------------------------------------------------
+dnl - GLABELS prerequisites
+dnl ---------------------------------------------------------------------------
+PKG_CHECK_MODULES(GLABELS, [\
+ glib-2.0 >= $GLIB_REQUIRED \
+ gtk+-3.0 >= $GTK_REQUIRED \
+ libxml-2.0 >= $LIBXML_REQUIRED \
+ librsvg-2.0 >= $LIBRSVG_REQUIRED \
+])
+
+AC_SUBST(GLABELS_CFLAGS)
+AC_SUBST(GLABELS_LIBS)
+
+
+dnl ---------------------------------------------------------------------------
+dnl - LIBGLABELS more modest prerequisites
+dnl ---------------------------------------------------------------------------
+PKG_CHECK_MODULES(LIBGLABELS, [\
+ glib-2.0 >= $GLIB_REQUIRED \
+ libxml-2.0 >= $LIBXML_REQUIRED \
+])
+
+AC_SUBST(LIBGLABELS_CFLAGS)
+AC_SUBST(LIBGLABELS_LIBS)
+
+
+dnl ---------------------------------------------------------------------------
+dnl - LIBGLBARCODE more modest prerequisites
+dnl ---------------------------------------------------------------------------
+PKG_CHECK_MODULES(LIBGLBARCODE, [\
+ glib-2.0 >= $GLIB_REQUIRED \
+ cairo >= $CAIRO_REQUIRED \
+ pango >= $PANGO_REQUIRED \
+])
+
+AC_SUBST(LIBGLBARCODE_CFLAGS)
+AC_SUBST(LIBGLBARCODE_LIBS)
+
+
+dnl ---------------------------------------------------------------------------
+dnl - Check for optional evolution data server
+dnl ---------------------------------------------------------------------------
+AC_ARG_WITH(libebook,
+ [AS_HELP_STRING([--without-libebook],[build without Evolution Data Server support])])
+have_libebook=no
+if test "x$with_libebook" != xno; then
+ PKG_CHECK_MODULES(LIBEBOOK, libebook-1.2 >= $LIBEBOOK_REQUIRED,
+ [have_libebook=yes], [have_libebook=no])
+fi
+
+if test "x$have_libebook" = "xyes"; then
+ AC_DEFINE(HAVE_LIBEBOOK,1,[Define to 1 for EDS support])
+ AC_SUBST(LIBEBOOK_CFLAGS)
+ AC_SUBST(LIBEBOOK_LIBS)
+fi
+
+
+dnl ---------------------------------------------------------------------------
+dnl - Check for optional GNU Barcode backend
+dnl ---------------------------------------------------------------------------
+AC_ARG_WITH(libbarcode,
+ [AS_HELP_STRING([--without-libbarcode],[build without GNU Barcode support])])
+have_libbarcode=no
+if test "x$with_libbarcode" != xno; then
+ AC_CHECK_LIB(barcode, Barcode_Create,
+ [have_libbarcode=yes], [have_libbarcode=no])
+fi
+
+if test "x$have_libbarcode" = "xyes"; then
+ AC_DEFINE(HAVE_LIBBARCODE,1,[Define to 1 for GNU Barcode support])
+ LIBBARCODE_CFLAGS=""
+ LIBBARCODE_LIBS="-lbarcode"
+ AC_SUBST(LIBBARCODE_CFLAGS)
+ AC_SUBST(LIBBARCODE_LIBS)
+else
+ help_libbarcode="(See http://www.gnu.org/software/barcode/barcode.html)"
+fi
+
+dnl ---------------------------------------------------------------------------
+dnl - Check for optional Zint backend
+dnl ---------------------------------------------------------------------------
+AC_ARG_WITH(libzint,
+ [AS_HELP_STRING([--without-libzint],[build without Zint Barcode support])])
+have_libzint=no
+if test "x$with_libzint" != xno; then
+ AC_CHECK_LIB(zint, ZBarcode_Render,
+ [have_libzint=yes], [have_libzint=no])
+fi
+
+if test "x$have_libzint" = "xyes"; then
+ AC_DEFINE(HAVE_LIBZINT,1,[Define to 1 for Zint Barcode support])
+ LIBZINT_CFLAGS=""
+ LIBZINT_LIBS="-lzint"
+ AC_SUBST(LIBZINT_CFLAGS)
+ AC_SUBST(LIBZINT_LIBS)
+else
+ help_libzint="(See http://www.zint.org.uk)"
+fi
+
+
+dnl ---------------------------------------------------------------------------
+dnl - Check for optional QRencode Barcode backend
+dnl ---------------------------------------------------------------------------
+AC_ARG_WITH(libqrencode,
+ [AS_HELP_STRING([--without-libqrencode],[build without QR code support])])
+have_libqrencode=no
+if test "x$with_libqrencode" != xno; then
+ PKG_CHECK_MODULES(LIBQRENCODE, libqrencode >= $LIBQRENCODE_REQUIRED,
+ [have_libqrencode=yes], [have_libqrencode=no])
+fi
+
+if test "x$have_libqrencode" = "xyes"; then
+ AC_DEFINE(HAVE_LIBQRENCODE,1,[Define to 1 for QR code support])
+ AC_SUBST(LIBQRENCODE_CFLAGS)
+ AC_SUBST(LIBQRENCODE_LIBS)
+else
+ help_libqrencode="(See http://megaui.net/fukuchi/works/qrencode/index.en.html)"
+fi
+
+
+dnl ---------------------------------------------------------------------------
+dnl - Check for IEC16022 Barcode backend
+dnl ---------------------------------------------------------------------------
+AC_ARG_WITH(libiec16022,
+ [AS_HELP_STRING([--without-libiec16022],[build without IEC 16022 support])])
+have_libiec16022=no
+if test "x$with_libiec16022" != xno; then
+ PKG_CHECK_MODULES(LIBIEC16022, libiec16022 >= $LIBIEC16022_REQUIRED,
+ [have_libiec16022=yes], [have_libiec16022=no])
+fi
+
+if test "x$have_libiec16022" = "xyes"; then
+ AC_DEFINE(HAVE_LIBIEC16022,1,[Define to 1 for IEC 16022 support])
+ AC_SUBST(LIBIEC16022_CFLAGS)
+ AC_SUBST(LIBIEC16022_LIBS)
+else
+ help_libiec16022="(See http://datenfreihafen.org/projects/iec16022.html)"
+fi
+
+
+dnl ---------------------------------------------------------------------------
+dnl - Enable deprecation testing
+dnl ---------------------------------------------------------------------------
+AC_ARG_ENABLE(deprecations,
+ [AS_HELP_STRING([--enable-deprecations],[warn about deprecated usages [default=no]])],,
+ [enable_deprecations=no])
+
+if test "x$enable_deprecations" = "xyes"; then
+ DISABLE_DEPRECATED_CFLAGS="\
+-DG_DISABLE_DEPRECATED \
+-DGDK_DISABLE_DEPRECATED \
+-DGTK_DISABLE_DEPRECATED \
+-DGDK_PIXBUF_DISABLE_DEPRECATED \
+"
+ AC_SUBST(DISABLE_DEPRECATED_CFLAGS)
+fi
+
+
+dnl ---------------------------------------------------------------------------
+dnl - i18n support
+dnl ---------------------------------------------------------------------------
+GETTEXT_PACKAGE=${GLABELS_BRANCH}
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Gettext package])
+
+AM_GLIB_GNU_GETTEXT
+
+
+dnl ---------------------------------------------------------------------------
+dnl - Makefiles, etc.
+dnl ---------------------------------------------------------------------------
+AC_CONFIG_FILES([
+Makefile
+libglabels/Makefile
+libglabels/${LIBGLABELS_BRANCH}.pc
+libglbarcode/Makefile
+libglbarcode/${LIBGLBARCODE_BRANCH}.pc
+src/Makefile
+src/cursors/Makefile
+src/pixmaps/Makefile
+data/Makefile
+data/desktop/Makefile
+data/icons/Makefile
+data/icons/16x16/Makefile
+data/icons/22x22/Makefile
+data/icons/24x24/Makefile
+data/icons/32x32/Makefile
+data/icons/48x48/Makefile
+data/man/Makefile
+data/mime/Makefile
+data/pixmaps/Makefile
+data/schemas/Makefile
+data/schemas/org.gnome.glabels-3.gschema.xml.in
+data/ui/Makefile
+templates/Makefile
+po/Makefile.in
+help/Makefile
+docs/Makefile
+docs/libglabels/Makefile
+docs/libglbarcode/Makefile
+glabels.spec
+])
+AC_OUTPUT
+
+
+dnl ---------------------------------------------------------------------------
+dnl - Print configuration information
+dnl ---------------------------------------------------------------------------
+echo "
+
+Configuration:
+
+ Package ................. ${PACKAGE}-${VERSION}
+ Installation prefix ..... ${prefix}
+ Source code location .... ${srcdir}
+ Compiler ................ ${CC}
+
+
+Optional data merge backends:
+
+ Evolution Data Server ... ${have_libebook}
+
+
+Optional barcode backends:
+
+ GNU Barcode ............. ${have_libbarcode} ${help_libbarcode}
+ QR Code ................. ${have_libqrencode} ${help_libqrencode}
+ IEC 16022 ............... ${have_libiec16022} ${help_libiec16022}
+ Zint .................... ${have_libzint} ${help_libzint}
+
+
+"
diff --git a/data/Makefile.am b/data/Makefile.am
index bc73c75..2eb6b7c 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,12 +1,13 @@
## Process this file with automake to produce Makefile.in
SUBDIRS = \
- desktop \
icons \
- man \
- mime \
pixmaps \
- schemas \
ui
+# desktop \
+# man \
+# mime \
+# schemas
+
diff --git a/data/icons/22x22/Makefile.am b/data/icons/22x22/Makefile.am
index 1587bb6..955d860 100644
--- a/data/icons/22x22/Makefile.am
+++ b/data/icons/22x22/Makefile.am
@@ -1,18 +1,11 @@
size = 22x22
appiconsdir = $(datadir)/icons/hicolor/$(size)/apps
-actioniconsdir = $(datadir)/$(GLABELS_BRANCH)/icons/hicolor/$(size)/actions
appicons_DATA = \
apps/glabels-3.0.png
-actionicons_DATA = \
- actions/glabels-align-text-bottom.png \
- actions/glabels-align-text-middle.png \
- actions/glabels-align-text-top.png
-
noinst_DATA =
-EXTRA_DIST = $(appicons_DATA) \
- $(actionicons_DATA) \
+EXTRA_DIST = $(appicons_DATA) \
$(noinst_DATA)
diff --git a/data/icons/24x24/Makefile.am b/data/icons/24x24/Makefile.am
index 4af195e..4dd62a0 100644
--- a/data/icons/24x24/Makefile.am
+++ b/data/icons/24x24/Makefile.am
@@ -7,9 +7,6 @@ appicons_DATA = \
apps/glabels-3.0.png
actionicons_DATA = \
- actions/glabels-align-text-bottom.png \
- actions/glabels-align-text-middle.png \
- actions/glabels-align-text-top.png \
actions/glabels-arrow.png \
actions/glabels-barcode.png \
actions/glabels-box.png \
diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am
index 7a9e4f2..1d25fa2 100644
--- a/data/icons/Makefile.am
+++ b/data/icons/Makefile.am
@@ -1,7 +1,7 @@
SUBDIRS = 16x16 22x22 24x24 32x32 48x48
-gtk_update_icon_cache = $(GTK_UPDATE_ICON_CACHE) -f -t $(datadir)/icons/hicolor
+gtk_update_icon_cache = $(GTK_UPDATE_ICON_CACHE) -f -t $(datadir)/$(GLABELS_BRANCH)/icons/hicolor
install-data-hook: update-icon-cache
uninstall-hook: update-icon-cache
diff --git a/data/schemas/org.gnome.glabels-3.gschema.xml.in.in b/data/schemas/org.gnome.glabels-3.gschema.xml.in.in
index 065fe00..c7e7ea9 100644
--- a/data/schemas/org.gnome.glabels-3.gschema.xml.in.in
+++ b/data/schemas/org.gnome.glabels-3.gschema.xml.in.in
@@ -2,10 +2,10 @@
<schema id="org.gnome.glabels-3" path="/apps/glabels-3/" gettext-domain="@GETTEXT_PACKAGE@">
- <child name="ui" schema="org.gnome.glabels-3.ui"/>
- <child name="locale" schema="org.gnome.glabels-3.locale"/>
- <child name="objects" schema="org.gnome.glabels-3.objects"/>
- <child name="history" schema="org.gnome.glabels-3.history"/>
+ <child name="ui" schema="org.gnome.glabels.ui"/>
+ <child name="locale" schema="org.gnome.glabels.locale"/>
+ <child name="objects" schema="org.gnome.glabels.objects"/>
+ <child name="history" schema="org.gnome.glabels.history"/>
</schema>
diff --git a/data/ui/Makefile.am b/data/ui/Makefile.am
index 833eb26..70b2f69 100644
--- a/data/ui/Makefile.am
+++ b/data/ui/Makefile.am
@@ -3,13 +3,7 @@
builderdir = $(datadir)/$(GLABELS_BRANCH)/ui/
builder_DATA = \
- property-bar.ui \
- print-op-dialog-custom-widget.ui \
- media-select.ui \
- new-label-dialog.ui \
- merge-properties-dialog.ui \
- template-designer.ui \
- prefs-dialog.ui \
- object-editor.ui
+ new_label_dialog.ui
+
EXTRA_DIST = $(builder_DATA)
diff --git a/data/ui/new_label_dialog.ui b/data/ui/new_label_dialog.ui
new file mode 100644
index 0000000..014a523
--- /dev/null
+++ b/data/ui/new_label_dialog.ui
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.24"/>
+ <object class="GtkWindow" id="new_label_dialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">New Label</property>
+ <property name="icon_name">preferences-desktop</property>
+ <child>
+ <object class="GtkVBox" id="main_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkToolItem" id="toolbutton1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">5</property>
+ <property name="bottom_padding">5</property>
+ <property name="left_padding">10</property>
+ <property name="right_padding">5</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkAlignment" id="entry-alignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="xscale">0</property>
+ <child>
+ <object class="GtkEntry" id="search_entry">
+ <property name="width_request">210</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">â</property>
+ <property name="secondary_icon_name">edit-find-symbolic</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="secondary_icon_sensitive">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="info_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">10</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">10</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkIconView" id="icon_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="selection_mode">browse</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/data/ui/object-editor.ui b/data/ui/object-editor.ui
index 422bcca..582a19e 100644
--- a/data/ui/object-editor.ui
+++ b/data/ui/object-editor.ui
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.20"/>
+ <!-- interface-naming-policy toplevel-contextual -->
<object class="GtkAdjustment" id="adjustment1">
<property name="lower">1</property>
<property name="upper">250</property>
@@ -8,29 +9,6 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
- <object class="GtkAdjustment" id="adjustment10">
- <property name="upper">100</property>
- <property name="step_increment">0.01</property>
- <property name="page_increment">1</property>
- </object>
- <object class="GtkAdjustment" id="adjustment11">
- <property name="upper">100</property>
- <property name="step_increment">0.01</property>
- <property name="page_increment">1</property>
- </object>
- <object class="GtkAdjustment" id="adjustment12">
- <property name="upper">100</property>
- <property name="value">1</property>
- <property name="step_increment">1</property>
- <property name="page_increment">10</property>
- </object>
- <object class="GtkAdjustment" id="adjustment13">
- <property name="lower">1</property>
- <property name="upper">100</property>
- <property name="value">1</property>
- <property name="step_increment">1</property>
- <property name="page_increment">10</property>
- </object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">5</property>
<property name="value">1</property>
@@ -78,60 +56,70 @@
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
+ <object class="GtkAdjustment" id="adjustment10">
+ <property name="upper">100</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">1</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment11">
+ <property name="upper">100</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">1</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment12">
+ <property name="upper">100</property>
+ <property name="value">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment13">
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="value">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkSizeGroup" id="page_sizegroup">
+ <property name="mode">both</property>
+ <widgets>
+ <widget name="shadow_page_vbox"/>
+ <widget name="lsize_page_vbox"/>
+ <widget name="size_page_vbox"/>
+ <widget name="bc_page_vbox"/>
+ <widget name="data_page_vbox"/>
+ <widget name="img_page_vbox"/>
+ <widget name="fill_page_vbox"/>
+ <widget name="line_page_vbox"/>
+ <widget name="text_page_vbox"/>
+ <widget name="edit_page_vbox"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup" id="width_sizegroup">
+ <widgets>
+ <widget name="notebook"/>
+ <widget name="title_hbox"/>
+ </widgets>
+ </object>
<object class="GtkDialog" id="dialog">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">6</property>
<property name="title" translatable="yes">dialog1</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
- <object class="GtkBox" id="dialog-vbox1">
+ <object class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="dialog-action_area1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="closebutton1">
- <property name="label">gtk-close</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
<child>
<object class="GtkVBox" id="editor_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">6</property>
<property name="spacing">12</property>
<child>
<object class="GtkHBox" id="title_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage" id="title_image">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -142,7 +130,6 @@
<child>
<object class="GtkLabel" id="title_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label"><span weight="bold" size="larger">Xxx object properties</span></property>
<property name="use_markup">True</property>
</object>
@@ -167,13 +154,11 @@
<child>
<object class="GtkVBox" id="edit_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="hbox30">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
@@ -202,12 +187,10 @@
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="edit_insert_field_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -233,7 +216,6 @@
<child type="tab">
<object class="GtkLabel" id="edit_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Text</property>
</object>
<packing>
@@ -243,18 +225,15 @@
<child>
<object class="GtkVBox" id="text_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkHBox" id="hbox65">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="text_family_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Family:</property>
</object>
@@ -267,7 +246,6 @@
<child>
<object class="GtkHBox" id="text_family_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -288,12 +266,10 @@
<child>
<object class="GtkHBox" id="hbox66">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="text_size_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Size:</property>
</object>
@@ -306,7 +282,6 @@
<child>
<object class="GtkHBox" id="hbox28">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="text_size_spin">
@@ -339,12 +314,10 @@
<child>
<object class="GtkHBox" id="hbox67">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="text_style_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Style:</property>
</object>
@@ -357,18 +330,15 @@
<child>
<object class="GtkHBox" id="hbox29">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkToggleButton" id="text_bold_toggle">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-bold</property>
</object>
</child>
@@ -384,11 +354,9 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-italic</property>
</object>
</child>
@@ -416,12 +384,10 @@
<child>
<object class="GtkHBox" id="hbox68">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="text_color_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0.14000000059604645</property>
<property name="label" translatable="yes">Color:</property>
@@ -435,12 +401,10 @@
<child>
<object class="GtkVBox" id="vbox3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="hbox42">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="text_color_radio">
@@ -448,7 +412,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -462,7 +425,6 @@
<child>
<object class="GtkHBox" id="text_color_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -484,7 +446,6 @@
<child>
<object class="GtkHBox" id="hbox44">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="text_color_key_radio">
@@ -492,7 +453,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="xalign">0.54000002145767212</property>
@@ -508,7 +468,6 @@
<child>
<object class="GtkHBox" id="text_color_key_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -544,12 +503,10 @@
<child>
<object class="GtkHBox" id="hbox69">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="text_align_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Alignment:</property>
</object>
@@ -562,18 +519,15 @@
<child>
<object class="GtkHBox" id="hbox4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkToggleButton" id="text_left_toggle">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkImage" id="image3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-justify-left</property>
</object>
</child>
@@ -589,11 +543,9 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-justify-center</property>
</object>
</child>
@@ -609,11 +561,9 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkImage" id="image5">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-justify-right</property>
</object>
</child>
@@ -639,115 +589,12 @@
</packing>
</child>
<child>
- <object class="GtkHBox" id="hbox69v">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="text_valign_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Vertical alignment:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkHBox" id="hbox4v">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkToggleButton" id="text_top_toggle">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
- <child>
- <object class="GtkImage" id="image3v">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="pixel_size">22</property>
- <property name="icon_name">glabels-align-text-top</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="text_vcenter_toggle">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
- <property name="xalign">0.47999998927116394</property>
- <child>
- <object class="GtkImage" id="image4v">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="pixel_size">22</property>
- <property name="icon_name">glabels-align-text-middle</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="text_bottom_toggle">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
- <child>
- <object class="GtkImage" id="image5v">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="pixel_size">22</property>
- <property name="icon_name">glabels-align-text-bottom</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">5</property>
- </packing>
- </child>
- <child>
<object class="GtkHBox" id="hbox70">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="text_line_spacing_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Line Spacing:</property>
</object>
@@ -760,7 +607,6 @@
<child>
<object class="GtkHBox" id="hbox31">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="text_line_spacing_spin">
@@ -787,7 +633,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">6</property>
+ <property name="position">5</property>
</packing>
</child>
<child>
@@ -796,14 +642,13 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">7</property>
+ <property name="position">6</property>
</packing>
</child>
</object>
@@ -815,7 +660,6 @@
<child type="tab">
<object class="GtkLabel" id="text_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0.46000000834465027</property>
<property name="label" translatable="yes">Style</property>
</object>
@@ -827,18 +671,15 @@
<child>
<object class="GtkVBox" id="line_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkHBox" id="hbox71">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="line_w_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Width:</property>
</object>
@@ -851,7 +692,6 @@
<child>
<object class="GtkHBox" id="hbox7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="line_width_spin">
@@ -871,7 +711,6 @@
<child>
<object class="GtkLabel" id="label21">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">points</property>
</object>
<packing>
@@ -897,12 +736,10 @@
<child>
<object class="GtkHBox" id="hbox72">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="line_color_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0.14000000059604645</property>
<property name="label" translatable="yes">Color:</property>
@@ -916,12 +753,11 @@
<child>
<object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
+ <property name="vexpand">False</property>
<child>
<object class="GtkHBox" id="hbox38">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="line_color_radio">
@@ -929,7 +765,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -943,7 +778,6 @@
<child>
<object class="GtkHBox" id="line_color_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -965,7 +799,6 @@
<child>
<object class="GtkHBox" id="hbox40">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="line_key_radio">
@@ -973,7 +806,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -988,7 +820,6 @@
<child>
<object class="GtkHBox" id="line_key_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -1029,7 +860,6 @@
<child type="tab">
<object class="GtkLabel" id="line_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Line</property>
</object>
<packing>
@@ -1040,17 +870,15 @@
<child>
<object class="GtkVBox" id="fill_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<child>
<object class="GtkHBox" id="hbox73">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
+ <property name="vexpand">False</property>
<child>
<object class="GtkLabel" id="fill_color_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0.14000000059604645</property>
<property name="label" translatable="yes">Color:</property>
@@ -1064,12 +892,10 @@
<child>
<object class="GtkVBox" id="vbox5">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="hbox50">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="fill_color_radio">
@@ -1077,7 +903,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="active">True</property>
@@ -1092,7 +917,6 @@
<child>
<object class="GtkHBox" id="fill_color_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -1114,7 +938,6 @@
<child>
<object class="GtkHBox" id="hbox52">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="fill_key_radio">
@@ -1122,7 +945,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -1137,7 +959,6 @@
<child>
<object class="GtkHBox" id="fill_key_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -1178,7 +999,6 @@
<child type="tab">
<object class="GtkLabel" id="fill_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Fill</property>
</object>
<packing>
@@ -1189,13 +1009,11 @@
<child>
<object class="GtkVBox" id="img_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkHBox" id="hbox74">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkRadioButton" id="img_file_radio">
@@ -1203,7 +1021,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -1217,7 +1034,6 @@
<child>
<object class="GtkFileChooserButton" id="img_file_button">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="show_hidden">True</property>
</object>
<packing>
@@ -1236,7 +1052,6 @@
<child>
<object class="GtkHBox" id="hbox75">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkRadioButton" id="img_key_radio">
@@ -1245,7 +1060,6 @@
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -1260,7 +1074,6 @@
<child>
<object class="GtkHBox" id="img_key_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -1286,7 +1099,6 @@
<child type="tab">
<object class="GtkLabel" id="img_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Image</property>
</object>
<packing>
@@ -1297,13 +1109,11 @@
<child>
<object class="GtkVBox" id="data_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkHBox" id="hbox76">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkRadioButton" id="data_literal_radio">
@@ -1311,7 +1121,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
@@ -1342,7 +1151,6 @@
<child>
<object class="GtkHBox" id="hbox77">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkRadioButton" id="data_key_radio">
@@ -1350,7 +1158,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<property name="group">data_literal_radio</property>
@@ -1364,7 +1171,6 @@
<child>
<object class="GtkHBox" id="data_key_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -1385,12 +1191,10 @@
<child>
<object class="GtkHBox" id="hbox78">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="data_fill_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label"> </property>
</object>
<packing>
@@ -1402,7 +1206,6 @@
<child>
<object class="GtkTable" id="table9">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="column_spacing">6</property>
@@ -1410,7 +1213,6 @@
<child>
<object class="GtkLabel" id="data_format_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">format:</property>
</object>
@@ -1422,7 +1224,6 @@
<child>
<object class="GtkLabel" id="data_ex_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label">00000000000 00000</property>
</object>
@@ -1436,7 +1237,6 @@
<child>
<object class="GtkLabel" id="data_digits_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">digits:</property>
</object>
@@ -1450,7 +1250,6 @@
<child>
<object class="GtkHBox" id="hbox32">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkSpinButton" id="data_digits_spin">
<property name="visible">True</property>
@@ -1497,7 +1296,6 @@
<child type="tab">
<object class="GtkLabel" id="data_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Data</property>
</object>
<packing>
@@ -1508,21 +1306,19 @@
<child>
<object class="GtkVBox" id="bc_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkTable" id="table1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="column_spacing">6</property>
<property name="row_spacing">6</property>
+ <property name="vexpand">False</property>
<child>
<object class="GtkLabel" id="bc_be_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Backend:</property>
</object>
@@ -1534,7 +1330,6 @@
<child>
<object class="GtkLabel" id="bc_style_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Style:</property>
</object>
@@ -1548,7 +1343,6 @@
<child>
<object class="GtkHBox" id="bc_backend_combo_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -1561,7 +1355,6 @@
<child>
<object class="GtkHBox" id="bc_style_combo_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -1586,7 +1379,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
@@ -1602,7 +1394,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
@@ -1615,12 +1406,11 @@
<child>
<object class="GtkHBox" id="hbox80">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
+ <property name="vexpand">False</property>
<child>
<object class="GtkLabel" id="bc_color_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0.14000000059604645</property>
<property name="label" translatable="yes">Color:</property>
@@ -1634,12 +1424,10 @@
<child>
<object class="GtkVBox" id="vbox4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="hbox46">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="bc_color_radio">
@@ -1647,7 +1435,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="active">True</property>
@@ -1662,7 +1449,6 @@
<child>
<object class="GtkHBox" id="bc_color_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -1684,7 +1470,6 @@
<child>
<object class="GtkHBox" id="hbox48">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="bc_key_radio">
@@ -1692,7 +1477,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -1707,7 +1491,6 @@
<child>
<object class="GtkHBox" id="bc_key_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -1748,7 +1531,6 @@
<child type="tab">
<object class="GtkLabel" id="bc_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Style</property>
</object>
<packing>
@@ -1759,13 +1541,11 @@
<child>
<object class="GtkVBox" id="size_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkTable" id="table6">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="column_spacing">12</property>
@@ -1773,7 +1553,6 @@
<child>
<object class="GtkLabel" id="size_w_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Width:</property>
</object>
@@ -1785,7 +1564,6 @@
<child>
<object class="GtkLabel" id="size_h_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Height:</property>
</object>
@@ -1799,7 +1577,6 @@
<child>
<object class="GtkHBox" id="hbox11">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="size_h_spin">
@@ -1820,7 +1597,6 @@
<child>
<object class="GtkLabel" id="size_h_units_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">inches</property>
</object>
<packing>
@@ -1842,7 +1618,6 @@
<child>
<object class="GtkHBox" id="hbox13">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkButton" id="size_reset_image_button">
@@ -1850,7 +1625,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
</object>
<packing>
@@ -1871,7 +1645,6 @@
<child>
<object class="GtkHBox" id="hbox10">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="size_w_spin">
@@ -1892,7 +1665,6 @@
<child>
<object class="GtkLabel" id="size_w_units_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">inches</property>
</object>
<packing>
@@ -1912,7 +1684,6 @@
<child>
<object class="GtkVBox" id="size_aspect_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -1943,7 +1714,6 @@
<child type="tab">
<object class="GtkLabel" id="size_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Size</property>
</object>
<packing>
@@ -1954,13 +1724,11 @@
<child>
<object class="GtkVBox" id="lsize_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkTable" id="table7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="column_spacing">12</property>
@@ -1968,7 +1736,6 @@
<child>
<object class="GtkLabel" id="lsize_r_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Length:</property>
</object>
@@ -1980,7 +1747,6 @@
<child>
<object class="GtkLabel" id="lsize_theta_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Angle:</property>
</object>
@@ -1994,7 +1760,6 @@
<child>
<object class="GtkHBox" id="hbox25">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="lsize_r_spin">
@@ -2015,7 +1780,6 @@
<child>
<object class="GtkLabel" id="lsize_r_units_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">inches</property>
</object>
<packing>
@@ -2034,7 +1798,6 @@
<child>
<object class="GtkHBox" id="hbox26">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="lsize_theta_spin">
@@ -2055,7 +1818,6 @@
<child>
<object class="GtkLabel" id="label38">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">degrees</property>
</object>
<packing>
@@ -2089,7 +1851,6 @@
<child type="tab">
<object class="GtkLabel" id="lsize_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Size</property>
</object>
<packing>
@@ -2100,13 +1861,11 @@
<child>
<object class="GtkVBox" id="pos_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<object class="GtkTable" id="table8">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">3</property>
<property name="column_spacing">12</property>
@@ -2115,7 +1874,6 @@
<object class="GtkLabel" id="pos_x_label">
<property name="width_request">50</property>
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">X:</property>
</object>
@@ -2127,7 +1885,6 @@
<child>
<object class="GtkLabel" id="pos_y_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Y:</property>
</object>
@@ -2141,7 +1898,6 @@
<child>
<object class="GtkLabel" id="pos_x_units_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">inches</property>
</object>
@@ -2155,7 +1911,6 @@
<child>
<object class="GtkLabel" id="pos_y_units_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">inches</property>
</object>
@@ -2219,7 +1974,6 @@
<child type="tab">
<object class="GtkLabel" id="pos_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Position</property>
</object>
<packing>
@@ -2230,7 +1984,6 @@
<child>
<object class="GtkVBox" id="shadow_page_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
@@ -2239,7 +1992,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
@@ -2252,17 +2004,15 @@
<child>
<object class="GtkVBox" id="shadow_controls_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
+ <property name="vexpand">False</property>
<child>
<object class="GtkHBox" id="hbox63">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label40">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">X Offset:</property>
</object>
@@ -2275,7 +2025,6 @@
<child>
<object class="GtkHBox" id="hbox54">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="shadow_x_spin">
@@ -2296,7 +2045,6 @@
<child>
<object class="GtkLabel" id="shadow_x_units_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">inches</property>
</object>
<packing>
@@ -2322,12 +2070,10 @@
<child>
<object class="GtkHBox" id="hbox64">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label41">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Y Offset:</property>
</object>
@@ -2340,7 +2086,6 @@
<child>
<object class="GtkHBox" id="hbox55">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="shadow_y_spin">
@@ -2361,7 +2106,6 @@
<child>
<object class="GtkLabel" id="shadow_y_units_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">inches</property>
</object>
<packing>
@@ -2387,12 +2131,10 @@
<child>
<object class="GtkHBox" id="hbox61">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label45">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0.14000000059604645</property>
<property name="label" translatable="yes">Color:</property>
@@ -2406,12 +2148,10 @@
<child>
<object class="GtkVBox" id="vbox7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="hbox57">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="shadow_color_radio">
@@ -2419,7 +2159,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -2433,7 +2172,6 @@
<child>
<object class="GtkHBox" id="shadow_color_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -2455,7 +2193,6 @@
<child>
<object class="GtkHBox" id="hbox59">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="shadow_key_radio">
@@ -2463,7 +2200,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
<property name="draw_indicator">True</property>
@@ -2478,7 +2214,6 @@
<child>
<object class="GtkHBox" id="shadow_key_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<placeholder/>
@@ -2514,12 +2249,10 @@
<child>
<object class="GtkHBox" id="hbox62">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label46">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Opacity:</property>
</object>
@@ -2532,7 +2265,6 @@
<child>
<object class="GtkHBox" id="hbox56">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkSpinButton" id="shadow_opacity_spin">
@@ -2550,7 +2282,6 @@
<child>
<object class="GtkLabel" id="label47">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">%</property>
</object>
<packing>
@@ -2588,7 +2319,6 @@
<child type="tab">
<object class="GtkLabel" id="shadow_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Shadow</property>
</object>
<packing>
@@ -2610,31 +2340,37 @@
<property name="position">2</property>
</packing>
</child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="closebutton1">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
</object>
</child>
<action-widgets>
<action-widget response="-7">closebutton1</action-widget>
</action-widgets>
</object>
- <object class="GtkSizeGroup" id="page_sizegroup">
- <property name="mode">both</property>
- <widgets>
- <widget name="shadow_page_vbox"/>
- <widget name="lsize_page_vbox"/>
- <widget name="size_page_vbox"/>
- <widget name="bc_page_vbox"/>
- <widget name="data_page_vbox"/>
- <widget name="img_page_vbox"/>
- <widget name="fill_page_vbox"/>
- <widget name="line_page_vbox"/>
- <widget name="text_page_vbox"/>
- <widget name="edit_page_vbox"/>
- </widgets>
- </object>
- <object class="GtkSizeGroup" id="width_sizegroup">
- <widgets>
- <widget name="notebook"/>
- <widget name="title_hbox"/>
- </widgets>
- </object>
</interface>
diff --git a/data/ui/property-bar.ui b/data/ui/property-bar.ui
index 2bb07e6..0d3914a 100644
--- a/data/ui/property-bar.ui
+++ b/data/ui/property-bar.ui
@@ -1,13 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.20"/>
- <object class="GtkAdjustment" id="adjustment1">
- <property name="lower">1</property>
- <property name="upper">250</property>
- <property name="value">1</property>
- <property name="step_increment">1</property>
- <property name="page_increment">10</property>
- </object>
+ <!-- interface-naming-policy toplevel-contextual -->
<object class="GtkAdjustment" id="adjustment2">
<property name="lower">0.25</property>
<property name="upper">4</property>
@@ -15,25 +9,27 @@
<property name="step_increment">0.25</property>
<property name="page_increment">1</property>
</object>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="lower">1</property>
+ <property name="upper">250</property>
+ <property name="value">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
<object class="GtkWindow" id="window1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="title" translatable="yes">window1</property>
<child>
<object class="GtkToolbar" id="property_toolbar">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="toolbar_style">icons</property>
<property name="show_arrow">False</property>
<child>
<object class="GtkToolItem" id="toolitem1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkEventBox" id="font_family_eventbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Font family</property>
<property name="visible_window">False</property>
<child>
@@ -49,12 +45,9 @@
<child>
<object class="GtkToolItem" id="toolitem2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="left_padding">6</property>
<child>
<object class="GtkSpinButton" id="font_size_spin">
@@ -75,7 +68,6 @@
<child>
<object class="GtkSeparatorToolItem" id="separatortoolitem1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -84,9 +76,7 @@
<child>
<object class="GtkToggleToolButton" id="font_bold_toggle">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Bold</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-bold</property>
</object>
@@ -98,9 +88,7 @@
<child>
<object class="GtkToggleToolButton" id="font_italic_toggle">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Italic</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-italic</property>
</object>
@@ -112,7 +100,6 @@
<child>
<object class="GtkSeparatorToolItem" id="separatortoolitem2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -121,9 +108,7 @@
<child>
<object class="GtkRadioToolButton" id="text_align_left_radio">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Left align</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-justify-left</property>
</object>
@@ -135,9 +120,7 @@
<child>
<object class="GtkRadioToolButton" id="text_align_center_radio">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Center align</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-justify-center</property>
<property name="group">text_align_left_radio</property>
@@ -150,9 +133,7 @@
<child>
<object class="GtkRadioToolButton" id="text_align_right_radio">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Right align</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-justify-right</property>
<property name="group">text_align_left_radio</property>
@@ -165,60 +146,6 @@
<child>
<object class="GtkSeparatorToolItem" id="separatortoolitem3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioToolButton" id="text_valign_top_radio">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Top vertical align</property>
- <property name="use_action_appearance">False</property>
- <property name="use_underline">True</property>
- <property name="icon_name">glabels-align-text-top</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioToolButton" id="text_valign_vcenter_radio">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Center vertical align</property>
- <property name="use_action_appearance">False</property>
- <property name="use_underline">True</property>
- <property name="icon_name">glabels-align-text-middle</property>
- <property name="group">text_valign_top_radio</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkRadioToolButton" id="text_valign_bottom_radio">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Bottom vertical align</property>
- <property name="use_action_appearance">False</property>
- <property name="use_underline">True</property>
- <property name="icon_name">glabels-align-text-bottom</property>
- <property name="group">text_valign_top_radio</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparatorToolItem" id="separatortoolitem4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -227,12 +154,9 @@
<child>
<object class="GtkToolItem" id="toolitem3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkEventBox" id="text_color_eventbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Text color</property>
<property name="visible_window">False</property>
<child>
@@ -248,12 +172,9 @@
<child>
<object class="GtkToolItem" id="toolitem4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkEventBox" id="fill_color_eventbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Fill color</property>
<property name="visible_window">False</property>
<child>
@@ -269,12 +190,9 @@
<child>
<object class="GtkToolItem" id="toolitem5">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkEventBox" id="line_color_eventbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Line color</property>
<property name="visible_window">False</property>
<child>
@@ -288,9 +206,8 @@
</packing>
</child>
<child>
- <object class="GtkSeparatorToolItem" id="separatortoolitem5">
+ <object class="GtkSeparatorToolItem" id="separatortoolitem4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -299,8 +216,6 @@
<child>
<object class="GtkToolItem" id="toolitem6">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="use_action_appearance">False</property>
<child>
<object class="GtkSpinButton" id="line_width_spin">
<property name="visible">True</property>
diff --git a/glabels/Makefile.am b/glabels/Makefile.am
new file mode 100644
index 0000000..e7ed4cf
--- /dev/null
+++ b/glabels/Makefile.am
@@ -0,0 +1,87 @@
+NULL =
+
+bin_PROGRAMS = glabels-4
+
+glabels_4_SOURCES = \
+ glabels.vala \
+ TMP_gdk_key.vala \
+ color.vala \
+ color_button.vala \
+ color_history.vala \
+ color_menu.vala \
+ color_menu_item.vala \
+ color_node.vala \
+ color_swatch.vala \
+ enum_util.vala \
+ file.vala \
+ file_util.vala \
+ font_button.vala \
+ font_families.vala \
+ font_history.vala \
+ font_menu.vala \
+ font_menu_item.vala \
+ font_sample.vala \
+ help.vala \
+ handle.vala \
+ label.vala \
+ label_object.vala \
+ label_object_box.vala \
+ label_region.vala \
+ label_state.vala \
+ merge.vala \
+ merge_factory.vala \
+ merge_field.vala \
+ merge_none.vala \
+ merge_record.vala \
+ merge_text.vala \
+ mini_preview.vala \
+ new_label_dialog.vala \
+ prefs.vala \
+ print.vala \
+ template_history.vala \
+ ui.vala \
+ units_util.vala \
+ view.vala \
+ window.vala \
+ xml_label.vala \
+ $(NULL)
+
+
+INCLUDES = \
+ -include config.h \
+ $(GLABELS_CFLAGS) \
+ -I../libglabels \
+ -DLOCALEDIR=\""$(localedir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DGLABELS_BRANCH=\""$(GLABELS_BRANCH)"\" \
+ $(NULL)
+
+VALAFLAGS = \
+ --vapidir=$(srcdir)/../vapi \
+ --vapidir=$(srcdir)/../libglabels \
+ --pkg config \
+ --pkg posix \
+ --pkg gtk+-3.0 \
+ --pkg libxml-2.0 \
+ --pkg gee-1.0 \
+ --pkg libglabels-4 \
+ $(NULL)
+
+
+glabels_4_LDADD = \
+ -L../libglabels -lglabels-4.0 \
+ $(GLABELS_LIBS) \
+ $(NULL)
+
+
+EXTRA_DIST = \
+ $(NULL)
+
+DISTCLEANFILES = \
+ $(NULL)
+
+
+$(bin_PROGRAMS): libglabels
+
+libglabels:
+ cd ../libglabels; $(MAKE)
diff --git a/glabels/TMP_gdk_key.vala b/glabels/TMP_gdk_key.vala
new file mode 100644
index 0000000..85aa8c1
--- /dev/null
+++ b/glabels/TMP_gdk_key.vala
@@ -0,0 +1,19 @@
+namespace Gdk {
+ namespace Key {
+
+ public const int Left = 0xff51;
+ public const int Up = 0xff52;
+ public const int Right = 0xff53;
+ public const int Down = 0xff54;
+
+ public const int KP_Left = 0xff96;
+ public const int KP_Up = 0xff97;
+ public const int KP_Right = 0xff98;
+ public const int KP_Down = 0xff99;
+
+ public const int KP_Delete = 0xff9f;
+
+ public const int Delete = 0xffff;
+
+ }
+}
\ No newline at end of file
diff --git a/glabels/color.vala b/glabels/color.vala
new file mode 100644
index 0000000..5b5e0af
--- /dev/null
+++ b/glabels/color.vala
@@ -0,0 +1,136 @@
+/* color.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public struct Color
+ {
+ public double r { get; set; }
+ public double g { get; set; }
+ public double b { get; set; }
+ public double a { get; set; }
+
+ public Color( double r, double g, double b, double a )
+ {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ public Color.from_rgba( double r, double g, double b, double a )
+ {
+ this( r, g, b, a );
+ }
+
+ public Color.from_rgb( double r, double g, double b )
+ {
+ this( r, g, b, 1.0 );
+ }
+
+ public Color.from_byte_rgba( uchar r, uchar g, uchar b, uchar a )
+ {
+ this( r/255.0, g/255.0, b/255.0, a/255.0 );
+ }
+
+ public Color.from_byte_rgb( uchar r, uchar g, uchar b )
+ {
+ this( r/255.0, g/255.0, b/255.0, 1.0 );
+ }
+
+ public Color.from_gdk_color( Gdk.Color c )
+ {
+ this( c.red/65535.0, c.green/65535.0, c.blue/65535.0, 1.0 );
+ }
+
+ public Color.from_legacy_color( uint32 c )
+ {
+ this( ((c>>24) & 0xff) / 255.0,
+ ((c>>16) & 0xff) / 255.0,
+ ((c>>8) & 0xff) / 255.0,
+ ((c) & 0xff) / 255.0 );
+ }
+
+ public Color.none()
+ {
+ this( 0, 0, 0, 0 );
+ }
+
+ public Color.black()
+ {
+ this( 0, 0, 0, 1 );
+ }
+
+ public Color.white()
+ {
+ this( 1, 1, 1, 1 );
+ }
+
+ public Color.from_color_and_opacity( Color color,
+ double opacity )
+ {
+ this( color.r, color.g, color.b, opacity * color.a );
+ }
+
+ public Gdk.Color to_gdk_color()
+ {
+ Gdk.Color c = Gdk.Color();
+
+ c.red = (uint16) (r * 65535);
+ c.green = (uint16) (g * 65535);
+ c.blue = (uint16) (b * 65535);
+
+ return c;
+ }
+
+ public uint32 to_legacy_color()
+ {
+ uint32 c;
+
+ c = (((uint32)(r*255) & 0xff) << 24) |
+ (((uint32)(g*255) & 0xff) << 16) |
+ (((uint32)(b*255) & 0xff) << 8) |
+ (((uint32)(a*255) & 0xff));
+
+ return c;
+ }
+
+ public void set_opacity( double opacity )
+ {
+ a *= opacity;
+ }
+
+ public bool equal( Color c )
+ {
+ return ( (this.r == c.r) && (this.g == c.g) && (this.b == c.b) && (this.a == c.a) );
+ }
+
+ public bool has_alpha()
+ {
+ return ( a != 0 );
+ }
+
+ }
+
+}
diff --git a/glabels/color_button.vala b/glabels/color_button.vala
new file mode 100644
index 0000000..3129d9b
--- /dev/null
+++ b/glabels/color_button.vala
@@ -0,0 +1,181 @@
+/* color_button.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using Gtk;
+
+namespace glabels
+{
+
+ public class ColorButton : Gtk.ToggleButton
+ {
+ private const int SWATCH_W = 24;
+ private const int SWATCH_H = 24;
+
+ public signal void color_changed( Color color,
+ bool is_default );
+
+ private bool is_default_flag;
+ private Color color;
+
+ private Color default_color;
+
+ private ColorSwatch swatch;
+ private ColorMenu menu;
+
+
+ public ColorButton( string? default_label,
+ Color default_color,
+ Color color )
+ {
+ if ( default_label == null )
+ {
+ default_label = _("Default color");
+ }
+
+ Gtk.HBox hbox = new Gtk.HBox( false, 3 );
+ this.add( hbox );
+
+ swatch = new ColorSwatch( SWATCH_W, SWATCH_H, color );
+ hbox.pack_start( swatch, true, true, 0 );
+
+ Gtk.Arrow arrow = new Gtk.Arrow( Gtk.ArrowType.DOWN, Gtk.ShadowType.IN );
+ hbox.pack_end( arrow, false, false, 0 );
+
+ this.default_color = default_color;
+ this.color = color;
+
+ menu = new ColorMenu( default_label, color );
+ menu.show_all();
+
+ menu.color_changed.connect( on_menu_color_changed );
+ menu.selection_done.connect( on_menu_selection_done );
+
+ this.button_press_event.connect( on_button_press_event );
+ }
+
+ public void set_color( Color color )
+ {
+ is_default_flag = false;
+ this.color = color;
+ swatch.set_color( color );
+ }
+
+ public void set_to_default()
+ {
+ is_default_flag = true;
+ color = default_color;
+ swatch.set_color( color );
+ }
+
+ public Color get_color( out bool is_default )
+ {
+ is_default = is_default_flag;
+ return color;
+ }
+
+ private void menu_position_function( Gtk.Menu menu,
+ out int x,
+ out int y,
+ out bool push_in )
+ {
+ Gdk.Screen screen = this.get_screen();
+ int w_screen = screen.get_width();
+ int h_screen = screen.get_height();
+
+ Gdk.Window window = this.get_window();
+ int x_window, y_window;
+ window.get_origin( out x_window, out y_window );
+
+ Gtk.Allocation allocation;
+ this.get_allocation( out allocation );
+ int x_this = allocation.x;
+ int y_this = allocation.y;
+ int h_this = allocation.height;
+
+ int w_menu, h_menu;
+ menu.get_size_request( out w_menu, out h_menu );
+
+ x = x_window + x_this;
+ y = y_window + y_this + h_this;
+
+ if ( (y + h_menu) > h_screen )
+ {
+ y = y_window + y_this - h_menu;
+
+ if ( y < 0 )
+ {
+ y = h_screen - h_menu;
+ }
+ }
+
+ if ( (x + w_menu) > w_screen )
+ {
+ x = w_screen - w_menu;
+ }
+
+ push_in = true;
+ }
+
+ private bool on_button_press_event( Gdk.EventButton event )
+ {
+ switch (event.button)
+ {
+
+ case 1:
+ this.set_active( true );
+ menu.popup( null, null, menu_position_function, event.button, event.time );
+ break;
+
+ default:
+ break;
+
+ }
+
+ return false;
+ }
+
+ private void on_menu_color_changed( Color color, bool is_default )
+ {
+
+ if (is_default)
+ {
+ this.color = default_color;
+ }
+ else
+ {
+ this.color = color;
+ }
+ this.is_default_flag = is_default;
+
+ swatch.set_color( color );
+
+ color_changed( color, is_default );
+ }
+
+ private void on_menu_selection_done()
+ {
+ this.set_active( false );
+ }
+
+ }
+
+}
diff --git a/glabels/color_history.vala b/glabels/color_history.vala
new file mode 100644
index 0000000..097ccb8
--- /dev/null
+++ b/glabels/color_history.vala
@@ -0,0 +1,127 @@
+/* color_history.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class ColorHistory : Object
+ {
+
+ public signal void changed();
+
+ private static GLib.Settings history;
+ private int max_n;
+
+ static construct
+ {
+ history = new GLib.Settings( "org.gnome.glabels-3.history" );
+ }
+
+ public ColorHistory( int n )
+ {
+ max_n = n;
+
+ history.changed["recent-colors"].connect( on_history_changed );
+ }
+
+ public void add_color( Color color )
+ {
+ size_t n;
+ uint[] old_colors = get_color_array( out n );
+ uint[] new_colors = new uint[max_n];
+ size_t i;
+
+ new_colors[0] = color.to_legacy_color();
+
+ for ( i = 0; (i < (max_n-1)) && (i < n); i++ )
+ {
+ new_colors[i+1] = old_colors[i];
+ }
+
+ set_color_array( new_colors, i+1 );
+ }
+
+ public Color get_color( int i )
+ {
+ size_t n;
+ uint[] colors = get_color_array( out n );
+ Color color;
+
+ if ( (n > 0) && (i < n))
+ {
+ color = Color.from_legacy_color( colors[i] );
+ }
+ else
+ {
+ color = Color.none();
+ }
+
+ return color;
+ }
+
+ private void on_history_changed()
+ {
+ changed();
+ }
+
+ private uint[] get_color_array( out size_t n )
+ {
+ GLib.Variant value;
+ GLib.Variant child_value;
+ size_t i;
+
+ value = history.get_value( "recent-colors" );
+ n = value.n_children();
+
+ uint[] array = new uint[n];
+
+ for ( i = 0; i < n; i++ )
+ {
+ child_value = value.get_child_value( i );
+ array[i] = child_value.get_uint32();
+ }
+
+ return array;
+ }
+
+ private void set_color_array( uint[] array, size_t n )
+ {
+ GLib.Variant value;
+ GLib.Variant[] child_values;
+ size_t i;
+
+ child_values = new GLib.Variant[n];
+
+ for ( i = 0; i < n; i++ )
+ {
+ child_values[i] = new Variant.uint32( array[i] );
+ }
+
+ value = new Variant.array( GLib.VariantType.UINT32, child_values );
+
+ history.set_value( "recent-colors", value );
+ }
+
+ }
+
+}
diff --git a/glabels/color_menu.vala b/glabels/color_menu.vala
new file mode 100644
index 0000000..d721373
--- /dev/null
+++ b/glabels/color_menu.vala
@@ -0,0 +1,253 @@
+/* color_menu.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class ColorMenu : Gtk.Menu
+ {
+ public signal void color_changed( Color color,
+ bool is_default );
+
+ private Color default_color;
+ private Color color;
+
+ private struct ColorTableEntry {
+ uchar r;
+ uchar g;
+ uchar b;
+ string name;
+ }
+
+ private const ColorTableEntry color_table[] = {
+
+ { 139, 0, 0, N_("Dark Red") },
+ { 165, 42, 42, N_("Brown") },
+ { 205, 149, 12, N_("Dark Goldenrod") },
+ { 0, 100, 0, N_("Dark Green") },
+ { 0, 139, 139, N_("Dark Cyan") },
+ { 0, 0, 128, N_("Navy Blue") },
+ { 148, 0, 211, N_("Dark Violet") },
+
+ { 255, 0, 0, N_("Red") },
+ { 255, 165, 0, N_("Orange") },
+ { 205, 205, 0, N_("Dark Yellow") },
+ { 0, 205, 0, N_("Medium green") },
+ { 64, 224, 208, N_("Turquoise") },
+ { 0, 0, 255, N_("Blue") },
+ { 160, 32, 240, N_("Purple") },
+
+ { 250, 128, 114, N_("Salmon") },
+ { 255, 215, 0, N_("Gold") },
+ { 255, 255, 0, N_("Yellow") },
+ { 0, 255, 0, N_("Green") },
+ { 0, 255, 255, N_("Cyan") },
+ { 135, 206, 235, N_("SkyBlue") },
+ { 238, 130, 238, N_("Violet") },
+
+ { 255, 192, 203, N_("Pink") },
+ { 255, 246, 143, N_("Khaki") },
+ { 255, 255, 224, N_("Light Yellow") },
+ { 144, 238, 144, N_("Light Green") },
+ { 224, 255, 255, N_("Light Cyan") },
+ { 198, 226, 255, N_("Slate Gray") },
+ { 216, 191, 216, N_("Thistle") },
+
+ { 255, 255, 255, N_("White") },
+ /* xgettext: no-c-format */
+ { 230, 230, 230, N_("10% Gray") },
+ /* xgettext: no-c-format */
+ { 192, 192, 192, N_("25% Gray") },
+ /* xgettext: no-c-format */
+ { 153, 153, 153, N_("40% Gray") },
+ /* xgettext: no-c-format */
+ { 128, 128, 128, N_("50% Gray") },
+ /* xgettext: no-c-format */
+ { 102, 102, 102, N_("60% Gray") },
+ { 0, 0, 0, N_("Black") }
+
+ };
+
+ private const int PALETTE_COLS = 7;
+ private const int PALETTE_ROWS = color_table.length/PALETTE_COLS + 1;
+
+ private const int ROW_DEFAULT = 0;
+ private const int ROW_SEP_1 = ROW_DEFAULT + 1;
+ private const int ROW_PALETTE = ROW_SEP_1 + 1;
+ private const int ROW_SEP_2 = ROW_PALETTE + PALETTE_ROWS;
+ private const int ROW_HISTORY = ROW_SEP_2 + 1;
+ private const int ROW_SEP_3 = ROW_HISTORY + 1;
+ private const int ROW_CUSTOM = ROW_SEP_3 + 1;
+
+ private Gtk.MenuItem default_menu_item;
+
+ private ColorHistory custom_color_history;
+ private ColorMenuItem history_menu_item[7];
+
+ private Gtk.MenuItem custom_menu_item;
+
+ public ColorMenu( string default_label,
+ Color color )
+ {
+ Gtk.SeparatorMenuItem separator_menu_item;
+
+ this.default_color = this.color = color;
+
+ this.default_menu_item = new Gtk.MenuItem.with_label( default_label );
+ this.attach( this.default_menu_item, 0, PALETTE_COLS, ROW_DEFAULT, ROW_DEFAULT+1 );
+ this.default_menu_item.activate.connect( on_default_menu_item_activate );
+
+ separator_menu_item = new Gtk.SeparatorMenuItem();
+ this.attach( separator_menu_item, 0, PALETTE_COLS, ROW_SEP_1, ROW_SEP_1+1 );
+
+ for ( int i=0; i < color_table.length; i++ )
+ {
+ int i_row = i / PALETTE_COLS;
+ int i_col = i % PALETTE_COLS;
+
+ uchar r = color_table[i].r;
+ uchar g = color_table[i].g;
+ uchar b = color_table[i].b;
+
+ var palette_menu_item = new ColorMenuItem( i,
+ Color.from_byte_rgb(r, g, b),
+ dgettext(null, color_table[i].name) );
+
+ palette_menu_item.activated.connect( on_palette_menu_item_activated );
+
+ this.attach( palette_menu_item, i_col, i_col+1, ROW_PALETTE+i_row, ROW_PALETTE+i_row+1 );
+ }
+
+ separator_menu_item = new Gtk.SeparatorMenuItem();
+ this.attach( separator_menu_item, 0, PALETTE_COLS, ROW_SEP_2, ROW_SEP_2+1 );
+
+ for ( int i=0; i < PALETTE_COLS; i++ )
+ {
+
+ history_menu_item[i] = new ColorMenuItem( i, Color.none(), null );
+ history_menu_item[i].set_sensitive( false );
+
+ history_menu_item[i].activated.connect( on_history_menu_item_activated );
+
+ this.attach( history_menu_item[i], i, i+1, ROW_HISTORY, ROW_HISTORY+1 );
+ }
+
+ separator_menu_item = new Gtk.SeparatorMenuItem();
+ this.attach( separator_menu_item, 0, PALETTE_COLS, ROW_SEP_3, ROW_SEP_3+1 );
+
+ custom_menu_item = new Gtk.MenuItem.with_label( _("Custom color") );
+ custom_menu_item.activate.connect( on_custom_menu_item_activate );
+
+ this.attach( custom_menu_item, 0, PALETTE_COLS, ROW_CUSTOM, ROW_CUSTOM+1 );
+
+ custom_color_history = new ColorHistory( PALETTE_COLS );
+ this.map_event.connect( on_map_event );
+ }
+
+ private void on_default_menu_item_activate()
+ {
+ color = default_color;
+
+ color_changed( color, true );
+ }
+
+ private void on_palette_menu_item_activated( int id )
+ {
+ color = Color.from_byte_rgb( color_table[id].r,
+ color_table[id].g,
+ color_table[id].b );
+
+ color_changed( color, false );
+ }
+
+ private void on_history_menu_item_activated( int id )
+ {
+
+ color = custom_color_history.get_color( id );
+
+ color_changed( color, false );
+ }
+
+ private void on_custom_menu_item_activate()
+ {
+ Gtk.ColorSelectionDialog dialog = new Gtk.ColorSelectionDialog( _("Custom Color") );
+ Gtk.ColorSelection colorsel = dialog.get_color_selection() as Gtk.ColorSelection;
+ Gdk.Color gdk_color;
+ int response;
+
+ gdk_color = color.to_gdk_color();
+ colorsel.set_current_color( gdk_color );
+
+ response = dialog.run();
+
+ switch (response) {
+ case Gtk.ResponseType.OK:
+ colorsel.get_current_color( out gdk_color );
+
+ color = Color.from_gdk_color( gdk_color );
+ custom_color_history.add_color( color );
+
+ color_changed( color, false );
+
+ dialog.destroy();
+ break;
+
+ default:
+ dialog.destroy();
+ break;
+ }
+
+ }
+
+ private void load_custom_color_history()
+ {
+ int i;
+
+ for ( i = 0; i < PALETTE_COLS; i++ )
+ {
+ Color color = custom_color_history.get_color( i );
+
+ if (color.a != 0)
+ {
+ string tip = _("Custom color #%d").printf( i+1 );
+
+ history_menu_item[i].set_color( i, color, tip );
+ history_menu_item[i].set_sensitive( true );
+ }
+ }
+
+ }
+
+ private bool on_map_event()
+ {
+ load_custom_color_history();
+ return false;
+ }
+
+ }
+
+}
+
+
+
+
diff --git a/glabels/color_menu_item.vala b/glabels/color_menu_item.vala
new file mode 100644
index 0000000..e216305
--- /dev/null
+++ b/glabels/color_menu_item.vala
@@ -0,0 +1,66 @@
+/* color_menu_item.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class ColorMenuItem : Gtk.MenuItem
+ {
+ private const int SIZE = 20;
+
+ public signal void activated( int id );
+
+ private int id;
+ private ColorSwatch swatch;
+
+ public ColorMenuItem( int id,
+ Color color,
+ string? tip )
+ {
+ this.id = id;
+ this.swatch = new ColorSwatch( SIZE, SIZE, color );
+
+ this.add( this.swatch );
+
+ this.set_tooltip_text( tip );
+
+ this.activate.connect( on_activate );
+ }
+
+ public void set_color( int id,
+ Color color,
+ string tip )
+ {
+ this.swatch.set_color( color );
+ this.set_tooltip_text( tip );
+ }
+
+ private void on_activate()
+ {
+ activated( id );
+ }
+
+ }
+
+}
+
diff --git a/glabels/color_node.vala b/glabels/color_node.vala
new file mode 100644
index 0000000..f92bb21
--- /dev/null
+++ b/glabels/color_node.vala
@@ -0,0 +1,93 @@
+/* color_node.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public struct ColorNode
+ {
+ public bool field_flag { get; set; }
+ public Color color { get; set; }
+ public string? key { get; set; }
+
+
+ public ColorNode( bool field_flag, Color color, string? key )
+ {
+ this.field_flag = field_flag;
+ this.color = color;
+ this.key = key;
+ }
+
+
+ public ColorNode.from_color( Color color )
+ {
+ this( false, color, null );
+ }
+
+
+ public bool equal( ColorNode cn )
+ {
+ return ( (this.field_flag == cn.field_flag) && this.color.equal( cn.color ) && (this.key == cn.key) );
+ }
+
+
+ public Color expand( MergeRecord? record )
+ {
+ if ( field_flag )
+ {
+ if ( record == null )
+ {
+ return Color.none();
+ }
+ else
+ {
+ string? text = record.eval_key( key );
+ if ( text != null )
+ {
+ Gdk.Color gdk_color = Gdk.Color();
+ if ( Gdk.Color.parse( text, out gdk_color ) )
+ {
+ Color color = Color.from_gdk_color( gdk_color );
+ return color;
+ }
+ else
+ {
+ return Color.none();
+ }
+ }
+ else
+ {
+ return Color.none();
+ }
+ }
+ }
+ else
+ {
+ return color;
+ }
+ }
+
+
+ }
+
+}
diff --git a/glabels/color_swatch.vala b/glabels/color_swatch.vala
new file mode 100644
index 0000000..ce763e1
--- /dev/null
+++ b/glabels/color_swatch.vala
@@ -0,0 +1,107 @@
+/* color_swatch.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class ColorSwatch : Gtk.DrawingArea
+ {
+ private Color color;
+
+ public ColorSwatch( int w,
+ int h,
+ Color color )
+ {
+ this.set_has_window( false );
+
+ this.set_size_request( w, h );
+ this.color = color;
+ }
+
+ public override bool draw( Cairo.Context cr )
+ {
+ Gtk.Style style;
+ double w, h;
+ Color fill_color, line_color;
+
+ cr.set_antialias( Cairo.Antialias.NONE );
+
+ w = get_allocated_width();
+ h = get_allocated_height();
+
+ style = this.get_style();
+ if ( this.is_sensitive() )
+ {
+ fill_color = color;
+ line_color = Color.from_gdk_color( style.fg[Gtk.StateType.NORMAL] );
+ }
+ else
+ {
+ fill_color = Color.none();
+ line_color = Color.from_gdk_color( style.fg[Gtk.StateType.INSENSITIVE] );
+ }
+
+ cr.rectangle( 1, 1, w-2, h-2 );
+
+ cr.set_source_rgba( fill_color.r, fill_color.g, fill_color.b, fill_color.a );
+ cr.fill_preserve();
+
+ cr.set_source_rgb( line_color.r, line_color.g, line_color.b );
+ cr.set_line_width( 1.0 );
+ cr.stroke();
+
+ return false;
+ }
+
+ public override void style_set( Gtk.Style? style )
+ {
+ redraw_canvas();
+ }
+
+ public void set_color( Color color )
+ {
+ if ( !this.color.equal( color ) )
+ {
+ this.color = color;
+ redraw_canvas();
+ }
+ }
+
+ private void redraw_canvas()
+ {
+ var window = get_window ();
+ if (null == window)
+ {
+ return;
+ }
+
+ unowned Cairo.Region region = window.get_clip_region ();
+ // redraw the cairo canvas completely by exposing it
+ window.invalidate_region (region, true);
+ window.process_updates (true);
+ }
+
+
+ }
+
+}
diff --git a/glabels/enum_util.vala b/glabels/enum_util.vala
new file mode 100644
index 0000000..ce2cf52
--- /dev/null
+++ b/glabels/enum_util.vala
@@ -0,0 +1,106 @@
+/* enum_util.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ namespace EnumUtil
+ {
+
+ /****************************************************************************/
+ /* Utilities to deal with PangoAlignment types. */
+ /****************************************************************************/
+
+ public string align_to_string( Pango.Alignment align )
+ {
+ switch (align) {
+ case Pango.Alignment.LEFT:
+ return "left";
+ case Pango.Alignment.CENTER:
+ return "center";
+ case Pango.Alignment.RIGHT:
+ return "right";
+ default:
+ return "?";
+ }
+ }
+
+ public Pango.Alignment string_to_align( string align_string )
+ {
+ switch (align_string)
+ {
+ case "left":
+ case "Left":
+ case "LEFT":
+ return Pango.Alignment.LEFT;
+ case "center":
+ case "Center":
+ case "CENTER":
+ return Pango.Alignment.CENTER;
+ case "right":
+ case "Right":
+ case "RIGHT":
+ return Pango.Alignment.RIGHT;
+ default:
+ return Pango.Alignment.LEFT;
+ }
+ }
+
+
+ /****************************************************************************/
+ /* Utilities to deal with PangoWeight types */
+ /****************************************************************************/
+
+ public string weight_to_string( Pango.Weight weight )
+ {
+ switch (weight) {
+ case Pango.Weight.NORMAL:
+ return "regular";
+ case Pango.Weight.BOLD:
+ return "bold";
+ default:
+ return "?";
+ }
+ }
+
+ public Pango.Weight string_to_weight( string weight_string )
+ {
+ switch (weight_string)
+ {
+ case "regular":
+ case "Regular":
+ case "REGULAR":
+ return Pango.Weight.NORMAL;
+ case "bold":
+ case "Bold":
+ case "BOLD":
+ return Pango.Weight.BOLD;
+ default:
+ return Pango.Weight.NORMAL;
+ }
+ }
+
+
+ }
+
+}
diff --git a/glabels/file.vala b/glabels/file.vala
new file mode 100644
index 0000000..4f5b662
--- /dev/null
+++ b/glabels/file.vala
@@ -0,0 +1,483 @@
+/* file.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ namespace File
+ {
+ private static string? previous_open_path = null;
+ private static string? previous_save_path = null;
+
+
+ public void new_label( Window window )
+ {
+ NewLabelDialog dialog = new NewLabelDialog( window );
+ dialog.set_title( _("New Label or Card") );
+
+ dialog.show_all();
+ int response = dialog.run();
+
+ if ( response == Gtk.ResponseType.OK )
+ {
+ Label label = new Label();
+ label.template = libglabels.Db.lookup_template_from_name( dialog.template_name );
+
+ if ( window.is_empty() )
+ {
+ window.set_label( label );
+ }
+ else
+ {
+ Window new_window = new Window.from_label( label );
+ new_window.show_all();
+ }
+
+ dialog.hide();
+ }
+ }
+
+
+ public void open( Window window )
+ {
+ OpenDialog chooser = new OpenDialog( window );
+
+ chooser.response.connect( on_open_response );
+
+ chooser.show();
+ }
+
+
+ private void on_open_response( Gtk.Dialog gtk_dialog, int response )
+ {
+ OpenDialog dialog = (OpenDialog)gtk_dialog;
+
+ switch (response)
+ {
+
+ case Gtk.ResponseType.ACCEPT:
+ string raw_filename = dialog.get_filename();
+
+ string? filename = null;
+ try
+ {
+ filename = Filename.to_utf8( raw_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ message( "Utf8 filename conversion: %s", e.message );
+ }
+
+ if ( (raw_filename == null) || (filename == null) ||
+ FileUtils.test( filename, FileTest.IS_DIR ) )
+ {
+ var md = new Gtk.MessageDialog( dialog,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.CLOSE,
+ _("Empty file name selection") );
+ md.format_secondary_text( _("Please select a file or supply a valid file name") );
+
+ md.run();
+ md.destroy();
+ }
+ else
+ {
+
+ if ( !FileUtils.test( filename, FileTest.IS_REGULAR ) )
+ {
+ var md = new Gtk.MessageDialog( dialog,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.CLOSE,
+ _("File does not exist") );
+ md.format_secondary_text( _("Please select a file or supply a valid file name") );
+
+ md.run();
+ md.destroy();
+ }
+ else
+ {
+ if ( open_real( filename, dialog.parent_window ) )
+ {
+ dialog.destroy();
+ }
+ }
+
+ }
+ break;
+
+ default:
+ dialog.destroy();
+ break;
+
+ }
+ }
+
+
+ private bool open_real( string filename, Window parent )
+ {
+ string abs_filename = FileUtil.make_absolute( filename );
+ Label? label = null;
+ try {
+ label = XmlLabel.open_file( abs_filename );
+ }
+ catch ( XmlLabel.XmlError e )
+ {
+ var md = new Gtk.MessageDialog( parent,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.CLOSE,
+ _("Could not open file \"%s\""), filename );
+ md.format_secondary_text( _("Not a supported file format") );
+
+ md.run();
+ md.destroy();
+
+ return false;
+ }
+
+ if ( parent.is_empty() )
+ {
+ parent.set_label( label );
+ }
+ else
+ {
+ Window new_window = new Window.from_label( label );
+ new_window.show_all();
+ }
+
+ /* TODO: add abs_filename to recents. */
+
+ previous_open_path = Path.get_dirname( abs_filename );
+
+ return true;
+ }
+
+
+ private class OpenDialog : Gtk.FileChooserDialog
+ {
+ public Window parent_window { get; private set; }
+
+ public OpenDialog( Window window )
+ {
+ parent_window = window;
+
+ set_title( _("Open label") );
+ action = Gtk.FileChooserAction.OPEN;
+ add_button( Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL );
+ add_button( Gtk.Stock.OPEN, Gtk.ResponseType.ACCEPT );
+
+ set_transient_for( parent_window );
+
+ /* Recover state of open dialog */
+ if ( previous_open_path != null )
+ {
+ set_current_folder( previous_open_path );
+ }
+
+ Gtk.FileFilter filter_all = new Gtk.FileFilter();
+ filter_all.add_pattern( "*" );
+ filter_all.set_name( _("All files") );
+ add_filter( filter_all );
+
+ Gtk.FileFilter filter_glabels = new Gtk.FileFilter();
+ filter_glabels.add_pattern( "*.glabels" );
+ filter_glabels.set_name( _("gLabels documents") );
+ add_filter( filter_glabels );
+
+ set_filter( filter_glabels );
+
+ }
+
+ }
+
+
+ public bool save( Label label, Window parent )
+ {
+
+ if ( label.is_untitled() )
+ {
+ return save_as( label, parent );
+ }
+
+ if ( !label.modified )
+ {
+ return true;
+ }
+
+ try
+ {
+ XmlLabel.save_file( label, label.filename );
+ }
+ catch ( XmlLabel.XmlError e )
+ {
+ var md = new Gtk.MessageDialog( parent,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.CLOSE,
+ _("Could not save file \"%s\""), label.filename );
+ md.format_secondary_text( _("Error encountered during save. The file is still not saved.") );
+
+ md.run();
+ md.destroy();
+
+ return false;
+ }
+
+ /* TODO: add filename to recents. */
+
+ return true;
+ }
+
+
+ public bool save_as( Label label, Window parent )
+ {
+ SaveAsDialog chooser = new SaveAsDialog( label, parent );
+ chooser.response.connect( on_save_as_response );
+
+ chooser.show();
+
+ /* Hold here and process events until we are done with this dialog. */
+ /* This is so we can return a bollean result of our save attempt. */
+ Gtk.main();
+
+ bool ret = chooser.saved_flag;
+
+ chooser.destroy();
+
+ return ret;
+ }
+
+
+ private void on_save_as_response( Gtk.Dialog gtk_dialog, int response )
+ {
+ SaveAsDialog dialog = (SaveAsDialog)gtk_dialog;
+
+ switch (response)
+ {
+
+ case Gtk.ResponseType.ACCEPT:
+ string raw_filename = dialog.get_filename();
+
+ if ( (raw_filename == null) || FileUtils.test( raw_filename, FileTest.IS_DIR ) )
+ {
+ var md = new Gtk.MessageDialog( dialog,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.CLOSE,
+ _("Empty file name selection") );
+ md.format_secondary_text( _("Please select a file or supply a valid file name") );
+
+ md.run();
+ md.destroy();
+ }
+ else
+ {
+ bool cancel_flag = false;
+
+ string full_filename = FileUtil.add_extension( raw_filename );
+
+ string? filename = null;
+ try
+ {
+ filename = Filename.to_utf8( full_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ message( "Utf8 filename conversion: %s", e.message );
+ }
+
+ if ( FileUtils.test( filename, FileTest.IS_REGULAR ) )
+ {
+ var md = new Gtk.MessageDialog( dialog,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.YES_NO,
+ _("Overwrite file \"%s\"?"), filename );
+ md.format_secondary_text( _("File already exists.") );
+
+ int ret = md.run();
+ if ( ret == Gtk.ResponseType.NO )
+ {
+ cancel_flag = true;
+ }
+
+ md.destroy();
+ }
+
+ if ( !cancel_flag )
+ {
+ try
+ {
+ XmlLabel.save_file( dialog.label, filename );
+ }
+ catch ( XmlLabel.XmlError e )
+ {
+ var md = new Gtk.MessageDialog( dialog,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.CLOSE,
+ _("Could not save file \"%s\""), filename );
+ md.format_secondary_text( _("Error encountered during save. The file is still not saved.") );
+
+ md.run();
+ md.destroy();
+
+ return;
+ }
+
+ dialog.saved_flag = true;
+
+ /* TODO: add filename to recents. */
+
+ previous_save_path = Path.get_dirname( filename );
+
+ Gtk.main_quit();
+
+ }
+
+ }
+ break;
+
+ default:
+ Gtk.main_quit();
+ break;
+
+ }
+ }
+
+
+ private class SaveAsDialog : Gtk.FileChooserDialog
+ {
+ public Label label { get; private set; }
+ public Window parent_window { get; private set; }
+ public bool saved_flag { get; set; default = false; }
+
+ public SaveAsDialog( Label label, Window window )
+ {
+ this.label = label;
+ this.parent_window = window;
+
+ string title = _("Save \"%s\" as").printf( label.get_short_name() );
+
+ set_title( title );
+ action = Gtk.FileChooserAction.SAVE;
+ add_button( Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL );
+ add_button( Gtk.Stock.SAVE, Gtk.ResponseType.ACCEPT );
+
+ set_transient_for( parent_window );
+
+ /* Recover state of open dialog */
+ if ( previous_save_path != null )
+ {
+ set_current_folder( previous_save_path );
+ }
+
+ Gtk.FileFilter filter_all = new Gtk.FileFilter();
+ filter_all.add_pattern( "*" );
+ filter_all.set_name( _("All files") );
+ add_filter( filter_all );
+
+ Gtk.FileFilter filter_glabels = new Gtk.FileFilter();
+ filter_glabels.add_pattern( "*.glabels" );
+ filter_glabels.set_name( _("gLabels documents") );
+ add_filter( filter_glabels );
+
+ set_filter( filter_glabels );
+
+ }
+
+ }
+
+
+ private void close( Window window )
+ {
+ bool close_flag = true;
+
+ if ( !window.is_empty() )
+ {
+ if ( window.view.label.modified )
+ {
+ var md = new Gtk.MessageDialog( window,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.NONE,
+ _("Save changes to document \"%s\" before closing?"),
+ window.view.label.get_short_name() );
+ md.format_secondary_text( _("Your changes will be lost if you don't save them.") );
+
+ md.add_button( _("Close without saving"), Gtk.ResponseType.NO );
+ md.add_button( Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL );
+ md.add_button( Gtk.Stock.SAVE, Gtk.ResponseType.YES );
+ md.set_default_response( Gtk.ResponseType.YES );
+
+ md.set_resizable( false );
+
+ int ret = md.run();
+
+ md.destroy();
+
+ switch (ret)
+ {
+ case Gtk.ResponseType.YES:
+ close_flag = save( window.view.label, window );
+ break;
+ case Gtk.ResponseType.NO:
+ close_flag = true;
+ break;
+ default:
+ close_flag = false;
+ break;
+ }
+
+
+ }
+ }
+
+ if ( close_flag )
+ {
+ window.destroy();
+ }
+
+ }
+
+
+ private void exit()
+ {
+ unowned List<Window> p;
+ unowned List<Window> p_next = null;
+
+ for ( p = Window.window_list; p != null; p = p_next )
+ {
+ p_next = p.next; /* Squirrel away next pointer since close may be destructive to list. */
+
+ close( p.data );
+ }
+ }
+
+
+ }
+
+}
+
diff --git a/glabels/file_util.vala b/glabels/file_util.vala
new file mode 100644
index 0000000..03ebf97
--- /dev/null
+++ b/glabels/file_util.vala
@@ -0,0 +1,58 @@
+/* file_util.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ namespace FileUtil
+ {
+
+ public string make_absolute( string filename )
+ {
+ if ( Path.is_absolute( filename ) )
+ {
+ return filename;
+ }
+ else
+ {
+ string pwd = Environment.get_current_dir();
+ return Path.build_filename( pwd, filename, null );
+ }
+ }
+
+
+ public string add_extension( string filename )
+ {
+ if ( filename.has_suffix( ".glabels" ) )
+ {
+ return filename;
+ }
+ else
+ {
+ return filename.concat( ".glabels", null );
+ }
+ }
+
+ }
+
+}
diff --git a/glabels/font_button.vala b/glabels/font_button.vala
new file mode 100644
index 0000000..6d85ba3
--- /dev/null
+++ b/glabels/font_button.vala
@@ -0,0 +1,154 @@
+/* font_button.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class FontButton : Gtk.ToggleButton
+ {
+
+ public signal void changed();
+
+ private string font_family;
+
+ private Gtk.Label font_label;
+ private FontMenu menu;
+
+
+ public FontButton( string? font_family )
+ {
+ this.font_family = font_family;
+
+ Gtk.HBox hbox = new Gtk.HBox( false, 3 );
+ this.add( hbox );
+
+ font_label = new Gtk.Label( font_family );
+ font_label.set_alignment( 0.0f, 0.5f );
+ font_label.set_size_request( 180, -1 );
+ hbox.pack_start( font_label, true, true, 0 );
+
+ Gtk.Arrow arrow = new Gtk.Arrow( Gtk.ArrowType.DOWN, Gtk.ShadowType.IN );
+ hbox.pack_end( arrow, false, false, 0 );
+
+ this.button_press_event.connect( on_button_press_event );
+
+ menu = new FontMenu();
+ menu.show_all();
+
+ menu.font_changed.connect( on_menu_font_changed );
+ menu.selection_done.connect( on_menu_selection_done );
+
+ this.button_press_event.connect( on_button_press_event );
+ }
+
+ public void set_family( string family )
+ {
+ this.font_family = family;
+ this.font_label.set_text( family );
+ }
+
+ public string get_family()
+ {
+ return this.font_family;
+ }
+
+ private void menu_position_function( Gtk.Menu menu,
+ out int x,
+ out int y,
+ out bool push_in )
+ {
+ Gdk.Screen screen = this.get_screen();
+ int w_screen = screen.get_width();
+ int h_screen = screen.get_height();
+
+ Gdk.Window window = this.get_window();
+ int x_window, y_window;
+ window.get_origin( out x_window, out y_window );
+
+ Gtk.Allocation allocation;
+ this.get_allocation( out allocation );
+ int x_this = allocation.x;
+ int y_this = allocation.y;
+ int h_this = allocation.height;
+
+ int w_menu, h_menu;
+ menu.get_size_request( out w_menu, out h_menu );
+
+ x = x_window + x_this;
+ y = y_window + y_this + h_this;
+
+ if ( (y + h_menu) > h_screen )
+ {
+ y = y_window + y_this - h_menu;
+
+ if ( y < 0 )
+ {
+ y = h_screen - h_menu;
+ }
+ }
+
+ if ( (x + w_menu) > w_screen )
+ {
+ x = w_screen - w_menu;
+ }
+
+ push_in = true;
+ }
+
+ private bool on_button_press_event( Gdk.EventButton event )
+ {
+ switch (event.button)
+ {
+
+ case 1:
+ this.set_active( true );
+ menu.popup( null, null, menu_position_function, event.button, event.time );
+ break;
+
+ default:
+ break;
+
+ }
+
+ return false;
+ }
+
+ private void on_menu_font_changed( string family )
+ {
+ this.font_family = family;
+ this.font_label.set_text( family );
+ changed();
+ }
+
+ private void on_menu_selection_done()
+ {
+ this.set_active( false );
+ }
+
+ }
+
+}
+
+
+
+
diff --git a/glabels/font_families.vala b/glabels/font_families.vala
new file mode 100644
index 0000000..b4830f7
--- /dev/null
+++ b/glabels/font_families.vala
@@ -0,0 +1,107 @@
+/* font_families.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ class FontFamilies
+ {
+
+ public static List<string> all;
+ public static List<string> proportional;
+ public static List<string> fixed_width;
+
+
+ static construct
+ {
+ Pango.FontMap fontmap;
+ Pango.Context context;
+ Pango.FontFamily[] families;
+ int i;
+ string name;
+
+ fontmap = Pango.CairoFontMap.new();
+ context = fontmap.create_context();
+
+ context.list_families( out families );
+
+ for ( i = 0; i < families.length; i++ )
+ {
+ name = families[i].get_name();
+
+ all.insert_sorted( name, string.collate );
+
+ if ( families[i].is_monospace() )
+ {
+ fixed_width.insert_sorted( name, string.collate );
+ }
+ else
+ {
+ proportional.insert_sorted( name, string.collate );
+ }
+
+ }
+
+ }
+
+
+ public string validate_family( string family )
+ {
+ string good_family;
+
+ if ( all.find_custom( family, string.collate ) != null )
+ {
+ good_family = family;
+ }
+ else if ( all.find_custom( "Sans", string.collate ) != null )
+ {
+ good_family = "Sans";
+ }
+ else if ( all != null )
+ {
+ good_family = all.data;
+ }
+ else
+ {
+ good_family = null;
+ }
+
+ return good_family;
+ }
+
+
+ public bool is_family_installed( string family )
+ {
+ unowned List<string> p;
+
+ p = all.find_custom( family, string.collate );
+
+ return ( p != null );
+ }
+
+
+ }
+
+}
+
+
diff --git a/glabels/font_history.vala b/glabels/font_history.vala
new file mode 100644
index 0000000..15ee153
--- /dev/null
+++ b/glabels/font_history.vala
@@ -0,0 +1,101 @@
+/* font_history.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class FontHistory : Object
+ {
+
+ public signal void changed();
+
+ private static GLib.Settings history;
+ private int max_n;
+
+ private FontFamilies font_families;
+
+ static construct
+ {
+ history = new GLib.Settings( "org.gnome.glabels-3.history" );
+ }
+
+ public FontHistory( int n )
+ {
+ max_n = n;
+
+ history.changed["recent-fonts"].connect( on_history_changed );
+
+ font_families = new FontFamilies();
+ }
+
+ public void add_familty( string family )
+ {
+ string[] old_families;
+ string[] new_families;
+ int i, j;
+
+ old_families = history.get_strv( "recent-fonts" );
+
+ new_families = new string[1];
+ new_families += family;
+
+ for ( i = 0, j = 1; (j < (max_n-1)) && (i < old_families.length); i++ )
+ {
+ if ( family != old_families[i] )
+ {
+ new_families += old_families[i];
+ }
+ }
+
+ history.set_strv( "recent-fonts", new_families );
+ }
+
+ public List<string> get_family_list()
+ {
+ string[] families;
+ List<string> family_list = new List<string>();
+ int i;
+
+ families = history.get_strv( "recent-fonts" );
+
+ for ( i = 0; i < families.length; i++ )
+ {
+ if ( font_families.is_family_installed( families[i] ) )
+ {
+ family_list.append( families[i] );
+ }
+ }
+
+ return family_list;
+ }
+
+ private void on_history_changed()
+ {
+ changed();
+ }
+
+ }
+
+}
+
+
diff --git a/glabels/font_menu.vala b/glabels/font_menu.vala
new file mode 100644
index 0000000..a68438f
--- /dev/null
+++ b/glabels/font_menu.vala
@@ -0,0 +1,138 @@
+/* font_menu.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class FontMenu : Gtk.Menu
+ {
+ public signal void font_changed( string family );
+
+ private Gtk.MenuItem recent_menu_item;
+ private Gtk.Menu recent_sub_menu;
+
+ private const string[] standard_families = { "Sans", "Serif", "Monospace" };
+
+ private FontHistory font_history;
+ private FontFamilies font_families;
+
+ public FontMenu()
+ {
+ int i;
+ FontMenuItem font_menu_item;
+ Gtk.MenuItem separator_item;
+ Gtk.MenuItem menu_item;
+ List<string> list;
+
+
+ font_families = new FontFamilies();
+
+
+ for ( i = 0; i < standard_families.length; i++ )
+ {
+ font_menu_item = new FontMenuItem( standard_families[i] );
+ this.append( font_menu_item );
+ font_menu_item.activated.connect( on_menu_item_activated );
+ }
+
+
+ separator_item = new Gtk.SeparatorMenuItem();
+ this.append( separator_item );
+
+
+ recent_menu_item = new Gtk.MenuItem.with_label( "Recent fonts" );
+ this.append( recent_menu_item );
+
+ font_history = new FontHistory( 10 );
+ list = font_history.get_family_list();
+ recent_sub_menu = create_font_sub_menu( list );
+ recent_menu_item.set_submenu( recent_sub_menu );
+ recent_menu_item.set_sensitive( list != null );
+
+
+ menu_item = new Gtk.MenuItem.with_label( "Proportional fonts" );
+ this.append( menu_item );
+ menu_item.set_submenu( create_font_sub_menu( font_families.proportional ) );
+ menu_item.set_sensitive( font_families.proportional != null );
+
+
+ menu_item = new Gtk.MenuItem.with_label( "Fixed-width fonts" );
+ this.append( menu_item );
+ menu_item.set_submenu( create_font_sub_menu( font_families.fixed_width ) );
+ menu_item.set_sensitive( font_families.fixed_width != null );
+
+
+ menu_item = new Gtk.MenuItem.with_label( "All fonts" );
+ this.append( menu_item );
+ menu_item.set_submenu( create_font_sub_menu( font_families.all ) );
+ menu_item.set_sensitive( font_families.all != null );
+
+
+ this.show_all();
+
+ font_history.changed.connect( on_font_history_changed );
+
+ }
+
+
+ private void on_menu_item_activated( string family )
+ {
+ font_changed( family );
+ }
+
+
+ private void on_font_history_changed()
+ {
+ List<string> list;
+
+ list = font_history.get_family_list();
+ recent_sub_menu = create_font_sub_menu( list );
+
+ recent_menu_item.set_submenu( recent_sub_menu );
+ recent_menu_item.set_sensitive( list != null );
+ }
+
+
+ private Gtk.Menu create_font_sub_menu( List<string> list )
+ {
+ Gtk.Menu menu = new Gtk.Menu();
+
+ foreach ( string s in list )
+ {
+ FontMenuItem menu_item = new FontMenuItem( s );
+ menu_item.show_all();
+ menu_item.activated.connect( on_menu_item_activated );
+ menu.append( menu_item );
+ }
+
+ return menu;
+ }
+
+ }
+
+}
+
+
+
+
+
diff --git a/glabels/font_menu_item.vala b/glabels/font_menu_item.vala
new file mode 100644
index 0000000..a3628b3
--- /dev/null
+++ b/glabels/font_menu_item.vala
@@ -0,0 +1,85 @@
+/* font_menu_item.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class FontMenuItem : Gtk.MenuItem
+ {
+ public signal void activated( string family );
+
+ private const int SAMPLE_W = 32;
+ private const int SAMPLE_H = 24;
+
+ /* Translators: very short sample text, used in building font menu item icons */
+ private const string short_sample_text = _("Aa");
+ /* Translators: lower case sample text */
+ private const string lower_case_text = _("abcdefghijklmnopqrstuvwxyz");
+ /* Translators: upper case sample text */
+ private const string upper_case_text = _("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ /* Translators: numbers and special characters sample text */
+ private const string numbers_special_text = _("0123456789 .:,;(*!?)");
+
+ private string font_family;
+
+ public FontMenuItem( string font_family )
+ {
+ this.font_family = font_family;
+
+ Gtk.HBox hbox = new Gtk.HBox( false, 6 );
+ this.add( hbox );
+
+ FontSample sample = new FontSample( SAMPLE_W, SAMPLE_H, short_sample_text, font_family );
+ hbox.pack_start( sample, false, false, 0 );
+
+ Gtk.Label label = new Gtk.Label( font_family );
+ hbox.pack_start( label, false, false, 0 );
+
+ unowned Pango.Language language = Pango.Language.get_default();
+ string sample_text = language.get_sample_string();
+
+ string tip = "<span font_family=\"%s\" size=\"x-large\" weight=\"bold\">%s\n</span>%s:\n\n<span font_family=\"%s\" size=\"large\">%s\n%s\n%s\n\n%s</span>".printf(
+ font_family, font_family,
+ _("Sample text"),
+ font_family,
+ lower_case_text,
+ upper_case_text,
+ numbers_special_text,
+ sample_text );
+
+ this.set_tooltip_markup( tip );
+
+ this.activate.connect( on_menu_item_activate );
+ }
+
+
+ private void on_menu_item_activate()
+ {
+ activated( font_family );
+ }
+
+
+ }
+
+}
+
diff --git a/glabels/font_sample.vala b/glabels/font_sample.vala
new file mode 100644
index 0000000..508e338
--- /dev/null
+++ b/glabels/font_sample.vala
@@ -0,0 +1,140 @@
+/* font_sample.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class FontSample : Gtk.DrawingArea
+ {
+ private string sample_text;
+ private string font_family;
+
+ public FontSample( int w,
+ int h,
+ string sample_text,
+ string font_family )
+ {
+ this.set_has_window( false );
+
+ this.sample_text = sample_text;
+ this.font_family = font_family;
+
+ this.set_size_request( w, h );
+ }
+
+ public override bool draw( Cairo.Context cr )
+ {
+ Gtk.Style style;
+ double w, h;
+ Color fill_color, line_color;
+ Pango.Layout layout;
+ Pango.FontDescription desc;
+ Pango.Rectangle ink_rect, logical_rect;
+ double layout_x, layout_y, layout_width, layout_height;
+
+ w = get_allocated_width();
+ h = get_allocated_height();
+
+ style = this.get_style();
+ if ( this.is_sensitive() )
+ {
+ fill_color = Color.from_gdk_color( style.light[Gtk.StateType.NORMAL] );
+ line_color = Color.from_gdk_color( style.fg[Gtk.StateType.NORMAL] );
+ }
+ else
+ {
+ fill_color = Color.none();
+ line_color = Color.from_gdk_color( style.fg[Gtk.StateType.INSENSITIVE] );
+ }
+
+ cr.set_antialias( Cairo.Antialias.NONE );
+
+ cr.rectangle( 1, 1, w-2, h-2 );
+
+ cr.set_source_rgba( fill_color.r, fill_color.g, fill_color.b, fill_color.a );
+ cr.fill_preserve();
+
+ cr.set_source_rgb( line_color.r, line_color.g, line_color.b );
+ cr.set_line_width( 1.0 );
+ cr.stroke();
+
+ cr.set_antialias( Cairo.Antialias.DEFAULT );
+
+ layout = Pango.cairo_create_layout( cr );
+
+ desc = new Pango.FontDescription();
+ desc.set_family( font_family );
+ desc.set_weight( Pango.Weight.NORMAL );
+ desc.set_style( Pango.Style.NORMAL );
+ desc.set_size( (int)(0.6 * (h-1) * Pango.SCALE) );
+
+ layout.set_font_description( desc );
+ layout.set_text( sample_text, -1 );
+ layout.set_width( -1 );
+ layout.get_pixel_extents( out ink_rect, out logical_rect );
+ layout_width = double.max( logical_rect.width, ink_rect.width );
+ layout_height = double.max( logical_rect.height, ink_rect.height );
+
+ layout_x = (w - layout_width) / 2.0;
+ layout_y = (h - layout_height) / 2.0;
+
+ if (ink_rect.x < logical_rect.x)
+ {
+ layout_x += logical_rect.x - ink_rect.x;
+ }
+
+ if (ink_rect.y < logical_rect.y)
+ {
+ layout_y += logical_rect.y - ink_rect.y;
+ }
+
+ cr.set_source_rgb( line_color.r, line_color.g, line_color.b );
+ cr.move_to( layout_x, layout_y );
+ Pango.cairo_show_layout( cr, layout );
+
+ return false;
+ }
+
+ public override void style_set( Gtk.Style? style )
+ {
+ redraw_canvas();
+ }
+
+ private void redraw_canvas()
+ {
+ var window = get_window ();
+ if (null == window)
+ {
+ return;
+ }
+
+ unowned Cairo.Region region = window.get_clip_region ();
+ // redraw the cairo canvas completely by exposing it
+ window.invalidate_region (region, true);
+ window.process_updates (true);
+ }
+
+
+ }
+
+}
diff --git a/glabels/glabels.vala b/glabels/glabels.vala
new file mode 100644
index 0000000..ca05103
--- /dev/null
+++ b/glabels/glabels.vala
@@ -0,0 +1,95 @@
+/* glabels.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ class Main
+ {
+
+ private static string[] filenames;
+ private const OptionEntry[] option_entries = {
+ { "", 0, 0, OptionArg.FILENAME_ARRAY, ref filenames, null, N_("[FILE...]") },
+ { null }
+ };
+
+
+ internal static int main( string[] args )
+ {
+ OptionContext option_context = new OptionContext("");
+ option_context.set_summary( _("Launch gLabels label and business card designer.") );
+ option_context.add_main_entries( option_entries, Config.GETTEXT_PACKAGE );
+
+ Gtk.init( ref args );
+
+ try
+ {
+ option_context.parse( ref args );
+ }
+ catch ( OptionError e )
+ {
+ stderr.printf( _("%s\nRun '%s --help' to see a full list of available command line options.\n"),
+ e.message, args[0] );
+ return 0;
+ }
+
+ libglabels.Db.init();
+ libglabels.XmlUtil.init();
+ libglabels.XmlUtil.default_units = libglabels.Units.inch();
+
+ Gtk.IconTheme icon_theme = Gtk.IconTheme.get_default();
+ icon_theme.append_search_path( Path.build_filename( Config.DATADIR, Config.GLABELS_BRANCH, "icons", null ) );
+
+
+ if ( filenames != null )
+ {
+ for ( int i = 0; filenames[i] != null; i++ )
+ {
+ try
+ {
+ Label label = XmlLabel.open_file( filenames[i] );
+ Window window = new Window.from_label( label );
+ window.show_all();
+ }
+ catch ( XmlLabel.XmlError e )
+ {
+ message( "Error opening file: %s", e.message );
+ }
+ }
+ }
+ else
+ {
+ Window window = new Window();
+ window.show_all();
+ }
+
+ Gtk.main();
+
+ return 0;
+ }
+
+ }
+
+}
+
+
diff --git a/glabels/handle.vala b/glabels/handle.vala
new file mode 100644
index 0000000..8e9121b
--- /dev/null
+++ b/glabels/handle.vala
@@ -0,0 +1,243 @@
+/* label_object_box.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ private const double HANDLE_PIXELS = 7;
+ private const double HANDLE_OUTLINE_WIDTH_PIXELS = 1;
+
+ private const Color HANDLE_FILL_COLOR = { 0.0, 0.75, 0.0, 0.4 };
+ private const Color HANDLE_OUTLINE_COLOR = { 0.0, 0.0, 0.0, 0.8 };
+
+
+ public abstract class Handle
+ {
+ public LabelObject owner { get; protected set; }
+
+ public abstract void draw( Cairo.Context cr );
+ public abstract void cairo_path( Cairo.Context cr );
+
+
+ protected static void draw_at( Cairo.Context cr,
+ double x_handle,
+ double y_handle )
+ {
+ cr.save();
+
+ cr.translate( x_handle, y_handle );
+
+ double scale_x = 1;
+ double scale_y = 1;
+ cr.device_to_user_distance( ref scale_x, ref scale_y );
+ cr.scale( scale_x, scale_y );
+
+ cr.rectangle( -HANDLE_PIXELS/2, -HANDLE_PIXELS/2, HANDLE_PIXELS, HANDLE_PIXELS );
+
+ cr.set_source_rgba( HANDLE_FILL_COLOR.r, HANDLE_FILL_COLOR.g, HANDLE_FILL_COLOR.b, HANDLE_FILL_COLOR.a );
+ cr.fill_preserve();
+
+ cr.set_line_width( HANDLE_OUTLINE_WIDTH_PIXELS );
+ cr.set_source_rgba( HANDLE_OUTLINE_COLOR.r, HANDLE_OUTLINE_COLOR.g, HANDLE_OUTLINE_COLOR.b,
+ HANDLE_OUTLINE_COLOR.a );
+ cr.stroke();
+
+ cr.restore();
+ }
+
+
+ protected static void cairo_path_at( Cairo.Context cr,
+ double x_handle,
+ double y_handle )
+ {
+ cr.save();
+
+ cr.translate( x_handle, y_handle );
+
+ double scale_x = 1;
+ double scale_y = 1;
+ cr.device_to_user_distance( ref scale_x, ref scale_y );
+ cr.scale( scale_x, scale_y );
+
+ cr.rectangle( -HANDLE_PIXELS/2, -HANDLE_PIXELS/2, HANDLE_PIXELS, HANDLE_PIXELS );
+
+ cr.restore();
+ }
+
+ }
+
+
+ public class HandleNorth : Handle
+ {
+ public HandleNorth( LabelObject owner )
+ {
+ this.owner = owner;
+ }
+
+ public override void draw( Cairo.Context cr )
+ {
+ draw_at( cr, owner.w/2, 0 );
+ }
+
+ public override void cairo_path( Cairo.Context cr )
+ {
+ cairo_path_at( cr, owner.w/2, 0 );
+ }
+ }
+
+
+ public class HandleNorthEast : Handle
+ {
+ public HandleNorthEast( LabelObject owner )
+ {
+ this.owner = owner;
+ }
+
+ public override void draw( Cairo.Context cr )
+ {
+ draw_at( cr, owner.w, 0 );
+ }
+
+ public override void cairo_path( Cairo.Context cr )
+ {
+ cairo_path_at( cr, owner.w, 0 );
+ }
+ }
+
+
+ public class HandleEast : Handle
+ {
+ public HandleEast( LabelObject owner )
+ {
+ this.owner = owner;
+ }
+
+ public override void draw( Cairo.Context cr )
+ {
+ draw_at( cr, owner.w, owner.h/2 );
+ }
+
+ public override void cairo_path( Cairo.Context cr )
+ {
+ cairo_path_at( cr, owner.w, owner.h/2 );
+ }
+ }
+
+
+ public class HandleSouthEast : Handle
+ {
+ public HandleSouthEast( LabelObject owner )
+ {
+ this.owner = owner;
+ }
+
+ public override void draw( Cairo.Context cr )
+ {
+ draw_at( cr, owner.w, owner.h );
+ }
+
+ public override void cairo_path( Cairo.Context cr )
+ {
+ cairo_path_at( cr, owner.w, owner.h );
+ }
+ }
+
+
+ public class HandleSouth : Handle
+ {
+ public HandleSouth( LabelObject owner )
+ {
+ this.owner = owner;
+ }
+
+ public override void draw( Cairo.Context cr )
+ {
+ draw_at( cr, owner.w/2, owner.h );
+ }
+
+ public override void cairo_path( Cairo.Context cr )
+ {
+ cairo_path_at( cr, owner.w/2, owner.h );
+ }
+ }
+
+
+ public class HandleSouthWest : Handle
+ {
+ public HandleSouthWest( LabelObject owner )
+ {
+ this.owner = owner;
+ }
+
+ public override void draw( Cairo.Context cr )
+ {
+ draw_at( cr, 0, owner.h );
+ }
+
+ public override void cairo_path( Cairo.Context cr )
+ {
+ cairo_path_at( cr, 0, owner.h );
+ }
+ }
+
+
+ public class HandleWest : Handle
+ {
+ public HandleWest( LabelObject owner )
+ {
+ this.owner = owner;
+ }
+
+ public override void draw( Cairo.Context cr )
+ {
+ draw_at( cr, 0, owner.h/2 );
+ }
+
+ public override void cairo_path( Cairo.Context cr )
+ {
+ cairo_path_at( cr, 0, owner.h/2 );
+ }
+ }
+
+
+ public class HandleNorthWest : Handle
+ {
+ public HandleNorthWest( LabelObject owner )
+ {
+ this.owner = owner;
+ }
+
+ public override void draw( Cairo.Context cr )
+ {
+ draw_at( cr, 0, 0 );
+ }
+
+ public override void cairo_path( Cairo.Context cr )
+ {
+ cairo_path_at( cr, 0, 0 );
+ }
+ }
+
+
+}
+
diff --git a/glabels/help.vala b/glabels/help.vala
new file mode 100644
index 0000000..63d9254
--- /dev/null
+++ b/glabels/help.vala
@@ -0,0 +1,99 @@
+/* help.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ namespace Help
+ {
+
+ public void display_contents( Gtk.Window window )
+ {
+ try {
+ Gtk.show_uri( window.get_screen(), "ghelp:glabels-4.0", Gtk.get_current_event_time() );
+ }
+ catch ( Error e )
+ {
+ message( "%s", e.message );
+ }
+ }
+
+
+ public void display_about_dialog( Gtk.Window window )
+ {
+ string[] authors = {
+ "Jim Evins",
+ _("See the file AUTHORS for additional credits,"),
+ null
+ };
+
+ string[] documenters = {
+ "Jim Evins",
+ "Mario BlÃttermann",
+ null
+ };
+
+ string[] artists = {
+ "Jim Evins",
+ null
+ };
+
+ string pixbuf_filename = Path.build_filename( Config.DATADIR, Config.GLABELS_BRANCH, "pixmaps", "glabels-logo.png", null );
+ Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.from_file( pixbuf_filename );
+
+ Gtk.AboutDialog about = new Gtk.AboutDialog();
+ about.title = _("About glabels");
+ about.program_name = Config.PACKAGE_NAME;
+ about.version = Config.PACKAGE_VERSION;
+ about.copyright = "Copyright \xc2\xa9 2001-2012 Jim Evins";
+ about.comments = _("A label and business card creation program.\n");
+ about.website = "http://glabels.org";
+ about.logo = pixbuf;
+
+ about.authors = authors;
+ about.documenters = documenters;
+ about.artists = artists;
+ about.translator_credits = _("translator-credits");
+ about.license = _(
+"""gLabels 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.
+
+gLabels 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."""
+ );
+
+ about.set_destroy_with_parent( true );
+
+ about.run();
+ about.hide();
+
+ }
+
+ }
+
+}
+
diff --git a/glabels/label.vala b/glabels/label.vala
new file mode 100644
index 0000000..693447b
--- /dev/null
+++ b/glabels/label.vala
@@ -0,0 +1,1470 @@
+/* label.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+// ****************************************************************************************
+// TODO: do checkpointing in UI code before invoking changes to Label or LabelObject*s.
+// ****************************************************************************************
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ public class Label : Object
+ {
+ public signal void name_changed();
+ public signal void selection_changed();
+ public signal void modified_changed();
+ public signal void size_changed();
+ public signal void changed();
+ public signal void merge_changed();
+
+
+ public unowned List<LabelObject> object_list { get; private set; }
+
+
+ private TemplateHistory template_history;
+
+ private static int untitled_count;
+ private int untitled_instance;
+
+ private bool selection_op_flag;
+ private bool delayed_change_flag;
+
+
+ // TODO: Pixbuf cache
+ // TODO: SVG cache
+
+
+ /* Clipboard storage. */
+ private string? clipboard_xml_buffer;
+ private string? clipboard_text;
+ private Gdk.Pixbuf? clipboard_pixbuf;
+
+
+ /* Undo/Redo state */
+ private Queue<LabelState?> undo_stack;
+ private Queue<LabelState?> redo_stack;
+ private bool cp_cleared_flag;
+ private string cp_desc;
+
+
+ /**
+ * Filename
+ */
+ public string? filename
+ {
+ get { return _filename; }
+
+ set
+ {
+ if ( _filename != value )
+ {
+ _filename = value;
+ name_changed();
+ }
+ }
+ }
+ private string? _filename;
+
+
+ /**
+ * Compression mode ( 0 = no compression, 9 = max compression )
+ */
+ public int compression
+ {
+ get { return _compression; }
+
+ set
+ {
+ if ( (value < 0) && (value > 9) )
+ {
+ warning( "Compression mode out of range." );
+ _compression = 9;
+ }
+ else
+ {
+ _compression = value;
+ }
+ }
+ }
+ private int _compression = 9;
+
+
+ /**
+ * Modified flag
+ */
+ public bool modified
+ {
+ get { return _modified; }
+
+ set
+ {
+ if ( _modified != value )
+ {
+ _modified = value;
+ if ( !_modified )
+ {
+ time_stamp.get_current_time();
+ }
+ modified_changed();
+ }
+ }
+ }
+ private bool _modified;
+
+ public TimeVal time_stamp { get; private set; }
+
+
+ /**
+ * Template
+ */
+ public Template template
+ {
+ get { return _template; }
+
+ set
+ {
+ if ( _template != value )
+ {
+ _template = value;
+ changed();
+ size_changed();
+ template_history.add_name( template.name );
+ modified = true;
+ }
+ }
+ }
+ private Template _template;
+
+
+ /**
+ * Rotate
+ */
+ public bool rotate
+ {
+ get { return _rotate; }
+
+ set
+ {
+ if ( _rotate != value )
+ {
+ _rotate = value;
+ changed();
+ size_changed();
+ modified = true;
+ }
+ }
+ }
+ private bool _rotate;
+
+
+ /**
+ * Merge
+ */
+ public Merge merge
+ {
+ get { return _merge; }
+
+ set
+ {
+ if ( _merge != value )
+ {
+ _merge = value;
+ changed();
+ merge_changed();
+ modified = true;
+ }
+ }
+ }
+ private Merge _merge;
+
+
+ /* Default object text properties */
+ public string default_font_family { get; set; }
+ public double default_font_size { get; set; }
+ public Pango.Weight default_font_weight { get; set; default=Pango.Weight.NORMAL; }
+ public bool default_font_italic_flag { get; set; }
+ public Color default_text_color { get; set; }
+ public Pango.Alignment default_text_alignment { get; set; }
+ public double default_text_line_spacing { get; set; }
+
+ /* Default object line properties */
+ public double default_line_width { get; set; }
+ public Color default_line_color { get; set; }
+
+ /* Default object fill properties */
+ public Color default_fill_color { get; set; }
+
+
+
+ public Label()
+ {
+ _merge = new MergeNone();
+
+ template_history = new TemplateHistory( 5 );
+
+ undo_stack = new Queue<LabelState?>();
+ redo_stack = new Queue<LabelState?>();
+
+ // TODO: Set default properties from user prefs
+ }
+
+
+
+ public string get_short_name()
+ {
+ if ( filename == null )
+ {
+
+ if ( untitled_instance == 0 )
+ {
+ untitled_instance = ++untitled_count;
+ }
+
+ return "%s %d".printf( _("Untitled"), untitled_instance );
+
+ }
+ else
+ {
+
+ string base_name = Path.get_basename( filename );
+ try
+ {
+ Regex ext_pattern = new Regex( "\\.glabels$" );
+ string short_name = ext_pattern.replace( base_name, -1, 0, "" );
+ return short_name;
+ }
+ catch ( RegexError e )
+ {
+ warning( "%s", e.message );
+ return base_name;
+ }
+
+
+ }
+ }
+
+
+ public bool is_untitled()
+ {
+ return filename == null;
+ }
+
+
+ public void get_size( out double w,
+ out double h )
+ {
+ if ( template == null )
+ {
+ w = 0;
+ h = 0;
+ return;
+ }
+
+ TemplateFrame frame = template.frames.first().data;
+
+ if ( !rotate )
+ {
+ frame.get_size( out w, out h );
+ }
+ else
+ {
+ frame.get_size( out h, out w );
+ }
+ }
+
+
+ public void add_object( LabelObject object )
+ {
+ object.parent = this;
+ object_list.append( object );
+
+ object.changed.connect( on_object_changed );
+ object.moved.connect( on_object_moved );
+
+ changed();
+ modified = true;
+ }
+
+
+ public void delete_object( LabelObject object )
+ {
+ object_list.remove( object );
+
+ object.changed.disconnect( on_object_changed );
+ object.moved.disconnect( on_object_moved );
+
+ changed();
+ modified = true;
+ }
+
+
+ private void on_object_changed()
+ {
+ schedule_or_emit_changed_signal();
+ }
+
+
+ private void on_object_moved()
+ {
+ schedule_or_emit_changed_signal();
+ }
+
+
+ public void draw( Cairo.Context cr,
+ bool in_editor,
+ MergeRecord? record )
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ object.draw( cr, in_editor, record );
+ }
+ }
+
+
+ public LabelObject? object_at( Cairo.Context cr,
+ double x_pixels,
+ double y_pixels )
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_located_at( cr, x_pixels, y_pixels ) )
+ {
+ return object;
+ }
+ }
+
+ return null;
+ }
+
+
+ public Handle? handle_at( Cairo.Context cr,
+ double x_pixels,
+ double y_pixels )
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ Handle? handle = object.handle_at( cr, x_pixels, y_pixels );
+
+ if ( handle != null )
+ {
+ return handle;
+ }
+ }
+
+ return null;
+ }
+
+
+ public void select_object( LabelObject object )
+ {
+ object.select();
+ cp_cleared_flag = true;
+ selection_changed();
+ }
+
+
+ public void unselect_object( LabelObject object )
+ {
+ object.unselect();
+ cp_cleared_flag = true;
+ selection_changed();
+ }
+
+
+ public void select_all()
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ object.select();
+ }
+ cp_cleared_flag = true;
+ selection_changed();
+ }
+
+
+ public void unselect_all()
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ object.unselect();
+ }
+ cp_cleared_flag = true;
+ selection_changed();
+ }
+
+
+ public void select_region( LabelRegion region )
+ {
+ double r_x1 = double.min( region.x1, region.x2 );
+ double r_y1 = double.min( region.y1, region.y2 );
+ double r_x2 = double.max( region.x1, region.x2 );
+ double r_y2 = double.max( region.y1, region.y2 );
+
+ foreach ( LabelObject object in object_list )
+ {
+ LabelRegion obj_extent = object.get_extent();
+
+ if ( (obj_extent.x1 >= r_x1) &&
+ (obj_extent.x2 <= r_x2) &&
+ (obj_extent.y1 >= r_y1) &&
+ (obj_extent.y2 <= r_y2) )
+ {
+ object.select();
+ }
+ }
+ cp_cleared_flag = true;
+ selection_changed();
+ }
+
+
+ public bool is_selection_empty()
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ public bool is_selection_atomic()
+ {
+ int n_selected = 0;
+
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ n_selected++;
+ if ( n_selected > 1 )
+ {
+ return false;
+ }
+ }
+ }
+ return (n_selected == 1);
+ }
+
+
+ public LabelObject? get_1st_selected_object()
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ return object;
+ }
+ }
+ return null;
+ }
+
+
+ public List<LabelObject> get_selection_list()
+ {
+ List<LabelObject> selection_list = null;
+
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ selection_list.append( object );
+ }
+ }
+ return selection_list;
+ }
+
+
+ public bool can_selection_text()
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() && object.can_text() )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public bool can_selection_fill()
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() && object.can_fill() )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public bool can_selection_line_color()
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() && object.can_line_color() )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public bool can_selection_line_width()
+ {
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() && object.can_line_width() )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private void schedule_or_emit_changed_signal()
+ {
+ if ( selection_op_flag )
+ {
+ delayed_change_flag = true;
+ }
+ else
+ {
+ modified = true;
+ changed();
+ }
+ }
+
+
+ private void begin_selection_op()
+ {
+ selection_op_flag = true;
+ }
+
+
+ private void end_selection_op()
+ {
+ selection_op_flag = false;
+ if ( delayed_change_flag )
+ {
+ delayed_change_flag = false;
+ changed();
+ modified = true;
+ }
+ }
+
+
+ public void delete_selection()
+ {
+ List<LabelObject> selection_list = get_selection_list();
+
+ foreach ( LabelObject object in selection_list )
+ {
+ delete_object( object );
+ }
+
+ changed();
+ modified = true;
+ }
+
+
+ public void raise_selection_to_top()
+ {
+ List<LabelObject> selection_list = get_selection_list();
+
+ foreach ( LabelObject object in selection_list )
+ {
+ object_list.remove( object );
+ }
+
+ /* Move to end of list, representing front most object */
+ foreach ( LabelObject object in selection_list )
+ {
+ object_list.append( object );
+ }
+
+ changed();
+ modified = true;
+ }
+
+
+ public void lower_selection_to_bottom()
+ {
+ List<LabelObject> selection_list = get_selection_list();
+
+ foreach ( LabelObject object in selection_list )
+ {
+ object_list.remove( object );
+ }
+
+ /* Move to end of list, representing front most object */
+ foreach ( LabelObject object in object_list )
+ {
+ selection_list.append( object );
+ }
+ object_list = selection_list;
+
+ changed();
+ modified = true;
+ }
+
+
+ public void rotate_selection( double theta_degs )
+ {
+ begin_selection_op();
+
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.rotate( theta_degs );
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void rotate_selection_left()
+ {
+ begin_selection_op();
+
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.rotate( -90.0 );
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void rotate_selection_right()
+ {
+ begin_selection_op();
+
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.rotate( 90.0 );
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void flip_selection_horiz()
+ {
+ begin_selection_op();
+
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.flip_horiz();
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void flip_selection_vert()
+ {
+ begin_selection_op();
+
+ foreach ( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.flip_vert();
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void align_selection_left()
+ {
+ if ( is_selection_empty() || is_selection_atomic() )
+ {
+ return;
+ }
+
+ begin_selection_op();
+
+ List<LabelObject> selection_list = get_selection_list();
+
+ /* Find left most edge. */
+ LabelRegion obj_extent = selection_list.first().data.get_extent();
+ double x1_min = obj_extent.x1;
+ foreach ( LabelObject object in selection_list.nth(1) )
+ {
+ obj_extent = object.get_extent();
+ if ( obj_extent.x1 < x1_min ) x1_min = obj_extent.x1;
+ }
+
+ /* Now adjust the object positions to line up the left edges. */
+ foreach ( LabelObject object in selection_list )
+ {
+ obj_extent = object.get_extent();
+ double dx = x1_min - obj_extent.x1;
+ object.set_position_relative( dx, 0 );
+ }
+
+ end_selection_op();
+ }
+
+
+ public void align_selection_right()
+ {
+ if ( is_selection_empty() || is_selection_atomic() )
+ {
+ return;
+ }
+
+ begin_selection_op();
+
+ List<LabelObject> selection_list = get_selection_list();
+
+ /* Find right most edge. */
+ LabelRegion obj_extent = selection_list.first().data.get_extent();
+ double x2_max = obj_extent.x2;
+ foreach ( LabelObject object in selection_list.nth(1) )
+ {
+ obj_extent = object.get_extent();
+ if ( obj_extent.x2 > x2_max ) x2_max = obj_extent.x2;
+ }
+
+ /* Now adjust the object positions to line up the right edges. */
+ foreach ( LabelObject object in selection_list )
+ {
+ obj_extent = object.get_extent();
+ double dx = x2_max - obj_extent.x2;
+ object.set_position_relative( dx, 0 );
+ }
+
+ end_selection_op();
+ }
+
+
+ public void align_selection_hcenter()
+ {
+ if ( is_selection_empty() || is_selection_atomic() )
+ {
+ return;
+ }
+
+ begin_selection_op();
+
+ List<LabelObject> selection_list = get_selection_list();
+ LabelRegion obj_extent;
+
+ /* Find average center of objects. */
+ double xsum = 0;
+ int n = 0;
+ foreach ( LabelObject object in selection_list )
+ {
+ obj_extent = object.get_extent();
+ xsum += (obj_extent.x1 + obj_extent.x2) / 2.0;
+ n++;
+ }
+ double xavg = xsum / n;
+
+ /* find center of object closest to average center */
+ obj_extent = selection_list.first().data.get_extent();
+ double xcenter = (obj_extent.x1 + obj_extent.x2) / 2.0;
+ double dxmin = Math.fabs( xavg - xcenter );
+ foreach ( LabelObject object in selection_list.nth(1) )
+ {
+ obj_extent = object.get_extent();
+ double dx = Math.fabs( xavg - (obj_extent.x1 + obj_extent.x2)/2.0 );
+ if ( dx < dxmin )
+ {
+ dxmin = dx;
+ xcenter = (obj_extent.x1 + obj_extent.x2) / 2.0;
+ }
+ }
+
+ /* Now adjust the object positions to line up with this center. */
+ foreach ( LabelObject object in selection_list )
+ {
+ obj_extent = object.get_extent();
+ double dx = xcenter - (obj_extent.x1 + obj_extent.x2)/2.0;
+ object.set_position_relative( dx, 0 );
+ }
+
+ end_selection_op();
+ }
+
+
+ public void align_selection_top()
+ {
+ if ( is_selection_empty() || is_selection_atomic() )
+ {
+ return;
+ }
+
+ begin_selection_op();
+
+ List<LabelObject> selection_list = get_selection_list();
+
+ /* Find top most edge. */
+ LabelRegion obj_extent = selection_list.first().data.get_extent();
+ double y1_min = obj_extent.y1;
+ foreach ( LabelObject object in selection_list.nth(1) )
+ {
+ obj_extent = object.get_extent();
+ if ( obj_extent.y1 < y1_min ) y1_min = obj_extent.y1;
+ }
+
+ /* Now adjust the object positions to line up the top edges. */
+ foreach ( LabelObject object in selection_list )
+ {
+ obj_extent = object.get_extent();
+ double dy = y1_min - obj_extent.y1;
+ object.set_position_relative( 0, dy );
+ }
+
+ end_selection_op();
+ }
+
+
+ public void align_selection_bottom()
+ {
+ if ( is_selection_empty() || is_selection_atomic() )
+ {
+ return;
+ }
+
+ begin_selection_op();
+
+ List<LabelObject> selection_list = get_selection_list();
+
+ /* Find bottom most edge. */
+ LabelRegion obj_extent = selection_list.first().data.get_extent();
+ double y2_max = obj_extent.y2;
+ foreach ( LabelObject object in selection_list.nth(1) )
+ {
+ obj_extent = object.get_extent();
+ if ( obj_extent.y2 > y2_max ) y2_max = obj_extent.y2;
+ }
+
+ /* Now adjust the object positions to line up the bottom edges. */
+ foreach ( LabelObject object in selection_list )
+ {
+ obj_extent = object.get_extent();
+ double dy = y2_max - obj_extent.y2;
+ object.set_position_relative( 0, dy );
+ }
+
+ end_selection_op();
+ }
+
+
+ public void align_selection_vcenter()
+ {
+ if ( is_selection_empty() || is_selection_atomic() )
+ {
+ return;
+ }
+
+ begin_selection_op();
+
+ List<LabelObject> selection_list = get_selection_list();
+ LabelRegion obj_extent;
+
+ /* Find average center of objects. */
+ double ysum = 0;
+ int n = 0;
+ foreach ( LabelObject object in selection_list )
+ {
+ obj_extent = object.get_extent();
+ ysum += (obj_extent.y1 + obj_extent.y2) / 2.0;
+ n++;
+ }
+ double yavg = ysum / n;
+
+ /* find center of object closest to average center */
+ obj_extent = selection_list.first().data.get_extent();
+ double ycenter = (obj_extent.y1 + obj_extent.y2) / 2.0;
+ double dymin = Math.fabs( yavg - ycenter );
+ foreach ( LabelObject object in selection_list.nth(1) )
+ {
+ obj_extent = object.get_extent();
+ double dy = Math.fabs( yavg - (obj_extent.y1 + obj_extent.y2)/2.0 );
+ if ( dy < dymin )
+ {
+ dymin = dy;
+ ycenter = (obj_extent.y1 + obj_extent.y2) / 2.0;
+ }
+ }
+
+ /* Now adjust the object positions to line up with this center. */
+ foreach ( LabelObject object in selection_list )
+ {
+ obj_extent = object.get_extent();
+ double dy = ycenter - (obj_extent.y1 + obj_extent.y2)/2.0;
+ object.set_position_relative( 0, dy );
+ }
+
+ end_selection_op();
+ }
+
+
+ public void center_selection_horiz()
+ {
+ begin_selection_op();
+
+ double w, h;
+ get_size( out w, out h );
+ double x_label_center = w / 2.0;
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ LabelRegion obj_extent = object.get_extent();
+ double x_obj_center = (obj_extent.x1 + obj_extent.x2) / 2.0;
+ double dx = x_label_center - x_obj_center;
+ object.set_position_relative( dx, 0 );
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void center_selection_vert()
+ {
+ begin_selection_op();
+
+ double w, h;
+ get_size( out w, out h );
+ double y_label_center = h / 2.0;
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ LabelRegion obj_extent = object.get_extent();
+ double y_obj_center = (obj_extent.y1 + obj_extent.y2) / 2.0;
+ double dy = y_label_center - y_obj_center;
+ object.set_position_relative( 0, dy );
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void move_selection( double dx,
+ double dy )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.set_position_relative( dx, dy );
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_font_family( string font_family )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.font_family = font_family;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_font_size( double font_size )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.font_size = font_size;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_font_weight( Pango.Weight font_weight )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.font_weight = font_weight;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_font_italic_flag( bool font_italic_flag )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.font_italic_flag = font_italic_flag;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_text_alignment( Pango.Alignment text_alignment )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.text_alignment = text_alignment;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_text_line_spacing( double text_line_spacing )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.text_line_spacing = text_line_spacing;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_text_color( ColorNode text_color_node )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.text_color_node = text_color_node;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_line_width( double line_width )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.line_width = line_width;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_line_color( ColorNode line_color_node )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.line_color_node = line_color_node;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void set_selection_fill_color( ColorNode fill_color_node )
+ {
+ begin_selection_op();
+
+ foreach( LabelObject object in object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.fill_color_node = fill_color_node;
+ }
+ }
+
+ end_selection_op();
+ }
+
+
+ public void cut_selection()
+ {
+ copy_selection();
+ delete_selection();
+ }
+
+
+ public void copy_selection()
+ {
+ const Gtk.TargetEntry glabels_targets[] = {
+ { "application/glabels", 0, 0 },
+ { "text/xml", 0, 0 }
+ };
+
+ Gtk.Clipboard clipboard = Gtk.Clipboard.get( Gdk.SELECTION_CLIPBOARD );
+
+ List<LabelObject> selection_list = get_selection_list();
+
+ if ( selection_list != null )
+ {
+
+ Gtk.TargetList target_list = new Gtk.TargetList( glabels_targets );
+
+ /*
+ * Serialize selection by encoding as an XML label document.
+ */
+ Label label_copy = new Label();
+
+ label_copy.template = template;
+ label_copy.rotate = rotate;
+
+ foreach ( LabelObject object in object_list )
+ {
+ label_copy.add_object( object );
+ }
+
+ // TODO: set clipboard_xml_buffer from label_copy
+
+ /*
+ * Is it an atomic text selection? If so, also make available as text.
+ */
+ if ( is_selection_atomic() /* && TODO: first object is LabelObjectText */ )
+ {
+ target_list.add_text_targets( 1 );
+ // TODO: set clipboard_text from LabelObjectText get_text()
+ }
+
+ /*
+ * Is it an atomic image selection? If so, also make available as pixbuf.
+ */
+ if ( is_selection_atomic() /* && TODO: first object is LabelObjectImage */ )
+ {
+ // TODO: pixbuf = LabelObjectImage get_pixbuf
+ // TODO: if ( pixbuf != null )
+ {
+ target_list.add_image_targets( 2, true );
+ // TODO: set clipboard_pixbuf = pixbuf
+ }
+ }
+
+ Gtk.TargetEntry[] target_table = Gtk.target_table_new_from_list( target_list );
+
+ clipboard.set_with_owner( target_table,
+ (Gtk.ClipboardGetFunc)clipboard_get_cb,
+ (Gtk.ClipboardClearFunc)clipboard_clear_cb, this );
+
+ }
+
+ }
+
+
+ public void paste()
+ {
+ Gtk.Clipboard clipboard = Gtk.Clipboard.get( Gdk.SELECTION_CLIPBOARD );
+
+ clipboard.request_targets( clipboard_receive_targets_cb );
+ }
+
+
+ public bool can_paste()
+ {
+ Gtk.Clipboard clipboard = Gtk.Clipboard.get( Gdk.SELECTION_CLIPBOARD );
+
+ return ( clipboard.wait_is_target_available( Gdk.Atom.intern("application/glabels", true) ) ||
+ clipboard.wait_is_text_available() ||
+ clipboard.wait_is_image_available() );
+ }
+
+
+ private void clipboard_get_cb( Gtk.Clipboard clipboard,
+ Gtk.SelectionData selection_data,
+ uint info,
+ void* user_data )
+ {
+ switch (info)
+ {
+ case 0:
+ selection_data.set( selection_data.get_target(),
+ 8,
+ (uchar[])clipboard_xml_buffer );
+ break;
+
+ case 1:
+ selection_data.set_text( clipboard_text, -1 );
+ break;
+
+ case 2:
+ selection_data.set_pixbuf( clipboard_pixbuf );
+ break;
+
+ default:
+ assert_not_reached();
+
+ }
+ }
+
+
+ private void clipboard_clear_cb( Gtk.Clipboard clipboard,
+ void* user_data )
+ {
+ clipboard_xml_buffer = null;
+ clipboard_text = null;
+ clipboard_pixbuf = null;
+ }
+
+
+ private void clipboard_receive_targets_cb( Gtk.Clipboard clipboard,
+ Gdk.Atom[] targets )
+ {
+
+ /*
+ * Application/glabels
+ */
+ for ( int i = 0; i < targets.length; i++ )
+ {
+ if ( targets[i].name() == "application/glabels" )
+ {
+ clipboard.request_contents( targets[i], paste_xml_received_cb );
+ return;
+ }
+ }
+
+ /*
+ * Text
+ */
+ if ( Gtk.targets_include_text( targets ) )
+ {
+ clipboard.request_text( paste_text_received_cb );
+ return;
+ }
+
+ /*
+ * Image
+ */
+ if ( Gtk.targets_include_image( targets, true ) )
+ {
+ clipboard.request_image( paste_image_received_cb );
+ return;
+ }
+
+ }
+
+
+ private void paste_xml_received_cb( Gtk.Clipboard clipboard,
+ Gtk.SelectionData selection_data )
+ {
+ string xml_buffer = (string)selection_data.get_data();
+
+ /*
+ * Deserialize XML label document and extract objects.
+ */
+ // TODO: label_copy = xml_label_open_buffer( xml_buffer )
+ // unselect all
+ // foreach object in label copy, add to this, select each object as added.
+
+ }
+
+
+ private void paste_text_received_cb( Gtk.Clipboard clipboard,
+ string? text )
+ {
+ unselect_all();
+ // TODO: create new LabelObjectText object from text. set to a default location, select.
+ }
+
+
+ private void paste_image_received_cb( Gtk.Clipboard clipboard,
+ Gdk.Pixbuf pixbuf )
+ {
+ unselect_all();
+ // TODO: create new LabelObjectImage object from pixbuf. set to a default location, select.
+ }
+
+
+ public void checkpoint( string description )
+ {
+ /*
+ * Do not perform consecutive checkpoints that are identical.
+ * E.g. moving an object by dragging, would produce a large number
+ * of incremental checkpoints -- what we really want is a single
+ * checkpoint so that we can undo the entire dragging effort with
+ * one "undo"
+ */
+ if ( cp_cleared_flag || (cp_desc == null) || ( description != cp_desc) )
+ {
+
+ /* Sever old redo "thread" */
+ stack_clear(redo_stack);
+
+ /* Save state onto undo stack. */
+ LabelState state = new LabelState( description, this );
+ undo_stack.push_head( state );
+
+ /* Track consecutive checkpoints. */
+ cp_cleared_flag = false;
+ cp_desc = description;
+ }
+
+ }
+
+
+ public void undo()
+ {
+ LabelState state_old = undo_stack.pop_head();
+ LabelState state_now = new LabelState( state_old.description, this );
+
+ redo_stack.push_head( state_now );
+
+ state_old.restore( this );
+
+ cp_cleared_flag = true;
+
+ selection_changed();
+ }
+
+
+ public void redo()
+ {
+ LabelState state_old = redo_stack.pop_head();
+ LabelState state_now = new LabelState( state_old.description, this );
+
+ undo_stack.push_head( state_now );
+
+ state_old.restore( this );
+
+ cp_cleared_flag = true;
+
+ selection_changed();
+ }
+
+
+ public bool can_undo()
+ {
+ return ( !undo_stack.is_empty() );
+ }
+
+
+ public bool can_redo()
+ {
+ return ( !redo_stack.is_empty() );
+ }
+
+
+ public string get_undo_description()
+ {
+ LabelState state = undo_stack.peek_head();
+ if ( state != null )
+ {
+ return state.description;
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+
+ public string get_redo_description()
+ {
+ LabelState state = redo_stack.peek_head();
+ if ( state != null )
+ {
+ return state.description;
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+
+ private void stack_clear( Queue<LabelState> stack )
+ {
+ while ( stack.pop_head() != null ) {}
+ }
+
+
+ }
+
+}
diff --git a/glabels/label_object.vala b/glabels/label_object.vala
new file mode 100644
index 0000000..51fce90
--- /dev/null
+++ b/glabels/label_object.vala
@@ -0,0 +1,725 @@
+/* label_object.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public abstract class LabelObject : Object
+ {
+ public signal void changed();
+ public signal void moved();
+
+
+ private bool selected;
+ protected List<Handle> handles;
+
+ private double aspect_ratio;
+
+
+ /**
+ * Parent label
+ */
+ public Label parent { get; set; }
+
+
+ /**
+ * X coordinate of origin
+ */
+ public double x0
+ {
+ get { return _x0; }
+
+ set
+ {
+ if ( _x0 != value )
+ {
+ _x0 = value;
+ moved();
+ }
+ }
+ }
+ private double _x0;
+
+
+ /**
+ * Y coordinate of origin
+ */
+ public double y0
+ {
+ get { return _y0; }
+
+ set
+ {
+ if ( _y0 != value )
+ {
+ _y0 = value;
+ moved();
+ }
+ }
+ }
+ private double _y0;
+
+
+ /**
+ * Width of bounding box
+ */
+ public double w
+ {
+ get { return _w; }
+
+ set
+ {
+ if ( _w != value )
+ {
+ _w = value;
+ aspect_ratio = _h / _w;
+ changed();
+ }
+ }
+ }
+ private double _w;
+
+
+ /**
+ * Height of bounding box
+ */
+ public double h
+ {
+ get { return _h; }
+
+ set
+ {
+ if ( _h != value )
+ {
+ _h = value;
+ aspect_ratio = _h / _w;
+ changed();
+ }
+ }
+ }
+ private double _h;
+
+
+ /**
+ * Transformation matrix
+ */
+ public Cairo.Matrix matrix
+ {
+ get { return _matrix; }
+
+ set
+ {
+ if ( _matrix != value )
+ {
+ _matrix = value;
+ changed();
+ }
+ }
+ }
+ private Cairo.Matrix _matrix;
+
+
+ /**
+ * Font family
+ */
+ public string font_family
+ {
+ get { return _font_family; }
+
+ set
+ {
+ if ( _font_family != value )
+ {
+ _font_family = value;
+ changed();
+ }
+ }
+ }
+ private string _font_family;
+
+
+ /**
+ * Font size
+ */
+ public double font_size
+ {
+ get { return _font_size; }
+
+ set
+ {
+ if ( _font_size != value )
+ {
+ _font_size = value;
+ changed();
+ }
+ }
+ }
+ private double _font_size;
+
+
+ /**
+ * Font weight
+ */
+ public Pango.Weight font_weight
+ {
+ get { return _font_weight; }
+
+ set
+ {
+ if ( _font_weight != value )
+ {
+ _font_weight = value;
+ changed();
+ }
+ }
+ }
+ private Pango.Weight _font_weight = Pango.Weight.NORMAL;
+
+
+ /**
+ * Font italic flag
+ */
+ public bool font_italic_flag
+ {
+ get { return _font_italic_flag; }
+
+ set
+ {
+ if ( _font_italic_flag != value )
+ {
+ _font_italic_flag = value;
+ changed();
+ }
+ }
+ }
+ private bool _font_italic_flag;
+
+
+ /**
+ * Text color node
+ */
+ public ColorNode text_color_node
+ {
+ get { return _text_color_node; }
+
+ set
+ {
+ if ( _text_color_node != value )
+ {
+ _text_color_node = value;
+ changed();
+ }
+ }
+ }
+ private ColorNode _text_color_node;
+
+
+ /**
+ * Text alignment
+ */
+ public Pango.Alignment text_alignment
+ {
+ get { return _text_alignment; }
+
+ set
+ {
+ if ( _text_alignment != value )
+ {
+ _text_alignment = value;
+ changed();
+ }
+ }
+ }
+ private Pango.Alignment _text_alignment;
+
+
+ /**
+ * Text line spacing
+ */
+ public double text_line_spacing
+ {
+ get { return _text_line_spacing; }
+
+ set
+ {
+ if ( _text_line_spacing != value )
+ {
+ _text_line_spacing = value;
+ changed();
+ }
+ }
+ }
+ private double _text_line_spacing;
+
+
+ /**
+ * Line width
+ */
+ public double line_width
+ {
+ get { return _line_width; }
+
+ set
+ {
+ if ( _line_width != value )
+ {
+ _line_width = value;
+ changed();
+ }
+ }
+ }
+ private double _line_width;
+
+
+ /**
+ * Line color node
+ */
+ public ColorNode line_color_node
+ {
+ get { return _line_color_node; }
+
+ set
+ {
+ if ( _line_color_node != value )
+ {
+ _line_color_node = value;
+ changed();
+ }
+ }
+ }
+ private ColorNode _line_color_node;
+
+
+ /**
+ * Fill color node
+ */
+ public ColorNode fill_color_node
+ {
+ get { return _fill_color_node; }
+
+ set
+ {
+ if ( _fill_color_node != value )
+ {
+ _fill_color_node = value;
+ changed();
+ }
+ }
+ }
+ private ColorNode _fill_color_node;
+
+
+ /**
+ * Shadow state
+ */
+ public bool shadow_state
+ {
+ get { return _shadow_state; }
+
+ set
+ {
+ if ( _shadow_state != value )
+ {
+ _shadow_state = value;
+ changed();
+ }
+ }
+ }
+ private bool _shadow_state;
+
+
+ /**
+ * Shadow x offset
+ */
+ public double shadow_x
+ {
+ get { return _shadow_x; }
+
+ set
+ {
+ if ( _shadow_x != value )
+ {
+ _shadow_x = value;
+ changed();
+ }
+ }
+ }
+ private double _shadow_x;
+
+
+ /**
+ * Shadow y offset
+ */
+ public double shadow_y
+ {
+ get { return _shadow_y; }
+
+ set
+ {
+ if ( _shadow_y != value )
+ {
+ _shadow_y = value;
+ changed();
+ }
+ }
+ }
+ private double _shadow_y;
+
+
+ /**
+ * Shadow color node
+ */
+ public ColorNode shadow_color_node
+ {
+ get { return _shadow_color_node; }
+
+ set
+ {
+ if ( _shadow_color_node != value )
+ {
+ _shadow_color_node = value;
+ changed();
+ }
+ }
+ }
+ private ColorNode _shadow_color_node;
+
+
+ /**
+ * Shadow opacity
+ */
+ public double shadow_opacity
+ {
+ get { return _shadow_opacity; }
+
+ set
+ {
+ if ( _shadow_opacity != value )
+ {
+ _shadow_opacity = value;
+ changed();
+ }
+ }
+ }
+ private double _shadow_opacity;
+
+
+
+ /*
+ * Methods that concrete LabelObjects must implement.
+ */
+
+ public abstract LabelObject dup();
+
+ protected abstract void draw_object( Cairo.Context cr, bool in_editor, MergeRecord? record );
+ protected abstract void draw_shadow( Cairo.Context cr, bool in_editor, MergeRecord? record );
+
+ protected abstract bool is_object_located_at( Cairo.Context cr, double x, double y );
+
+
+
+ public LabelObject()
+ {
+ _x0 = 0;
+ _y0 = 0;
+ _matrix = Cairo.Matrix.identity();
+
+ _line_width = 0;
+
+ _shadow_state = false;
+ _shadow_x = 3.6;
+ _shadow_y = 3.6;
+ _shadow_color_node = ColorNode.from_color( Color.black() );
+ _shadow_opacity = 0.5;
+
+ selected = false;
+ }
+
+
+ protected void set_common_properties_from_object( LabelObject src_object )
+ {
+ parent = src_object.parent;
+ x0 = src_object.x0;
+ y0 = src_object.y0;
+ matrix = src_object.matrix;
+
+ w = src_object.w;
+ h = src_object.h;
+
+ shadow_state = src_object.shadow_state;
+ shadow_x = src_object.shadow_x;
+ shadow_y = src_object.shadow_y;
+ shadow_color_node = src_object.shadow_color_node;
+ shadow_opacity = src_object.shadow_opacity;
+ }
+
+
+ public void select()
+ {
+ selected = true;
+ }
+
+
+ public void unselect()
+ {
+ selected = false;
+ }
+
+
+ public bool is_selected()
+ {
+ return selected;
+ }
+
+
+ /*
+ * Virtual method defaults, these methods are used to determine if generic properties, such as those
+ * controlled by the PropertyBar, are available for the object.
+ */
+
+ public virtual bool can_text()
+ {
+ return false;
+ }
+
+ public virtual bool can_fill()
+ {
+ return false;
+ }
+
+ public virtual bool can_line_color()
+ {
+ return false;
+ }
+
+ public virtual bool can_line_width()
+ {
+ return false;
+ }
+
+
+ public void set_position( double x0,
+ double y0 )
+ {
+ if ( ( _x0 != x0 ) || ( _y0 != y0 ) )
+ {
+ _x0 = x0;
+ _y0 = y0;
+
+ moved();
+ }
+ }
+
+
+ public void set_position_relative( double dx,
+ double dy )
+ {
+ if ( ( dx != 0 ) || ( dy != 0 ) )
+ {
+ _x0 += dx;
+ _y0 += dy;
+
+ moved();
+ }
+ }
+
+
+ public void set_size( double w,
+ double h )
+ {
+ if ( ( _w != w ) || ( _h != h ) )
+ {
+ _w = w;
+ _h = h;
+
+ aspect_ratio = _h / _w;
+
+ changed();
+ }
+ }
+
+
+ public void set_size_honor_aspect( double w,
+ double h )
+ {
+ if ( h > (w * aspect_ratio) )
+ {
+ h = w * aspect_ratio;
+ }
+ else
+ {
+ w = h / aspect_ratio;
+ }
+
+ if ( ( _w != w ) || ( _h != h ) )
+ {
+ _w = w;
+ _h = h;
+
+ changed();
+ }
+ }
+
+
+ public LabelRegion get_extent()
+ {
+ double xa1 = - line_width/2;
+ double ya1 = - line_width/2;
+ double xa2 = w + line_width/2;
+ double ya2 = - line_width/2;
+ double xa3 = w + line_width/2;
+ double ya3 = h + line_width/2;
+ double xa4 = - line_width/2;
+ double ya4 = h + line_width/2;
+
+ matrix.transform_point( ref xa1, ref ya1 );
+ matrix.transform_point( ref xa2, ref ya2 );
+ matrix.transform_point( ref xa3, ref ya3 );
+ matrix.transform_point( ref xa4, ref ya4 );
+
+ LabelRegion region = LabelRegion();
+ region.x1 = double.min( xa1, double.min( xa2, double.min( xa3, xa4 ) ) ) + x0;
+ region.y1 = double.min( ya1, double.min( ya2, double.min( ya3, ya4 ) ) ) + y0;
+ region.x2 = double.max( xa1, double.max( xa2, double.max( xa3, xa4 ) ) ) + x0;
+ region.y2 = double.max( ya1, double.max( ya2, double.max( ya3, ya4 ) ) ) + y0;
+
+ return region;
+ }
+
+
+ public void rotate( double theta_degs )
+ {
+ if ( theta_degs != 0 )
+ {
+ _matrix.rotate( theta_degs * (Math.PI / 180) );
+ changed();
+ }
+ }
+
+
+ public void flip_horiz()
+ {
+ _matrix.scale( -1, 1 );
+ changed();
+ }
+
+
+ public void flip_vert()
+ {
+ _matrix.scale( 1, -1 );
+ changed();
+ }
+
+
+ public void draw( Cairo.Context cr,
+ bool in_editor,
+ MergeRecord? record )
+ {
+ cr.save();
+ cr.translate( x0, y0 );
+
+ if ( shadow_state )
+ {
+ cr.save();
+ cr.translate( shadow_x, shadow_y );
+ cr.transform( matrix );
+ draw_shadow( cr, in_editor, record );
+ cr.restore();
+ }
+
+ cr.transform( matrix );
+ draw_object( cr, in_editor, record );
+
+ cr.restore();
+ }
+
+
+ public void draw_selection_highlight( Cairo.Context cr )
+ {
+ cr.save();
+ cr.translate( x0, y0 );
+ cr.transform( matrix );
+
+ foreach( Handle handle in handles )
+ {
+ handle.draw( cr );
+ }
+
+ cr.restore();
+ }
+
+
+ public bool is_located_at( Cairo.Context cr,
+ double x_pixels,
+ double y_pixels )
+ {
+ cr.save();
+ cr.translate( x0, y0 );
+ cr.transform( matrix );
+
+ double x = x_pixels;
+ double y = y_pixels;
+ cr.device_to_user( ref x, ref y );
+
+ bool ret_val = is_object_located_at( cr, x, y );
+
+ cr.restore();
+
+ return ret_val;
+ }
+
+
+ public Handle? handle_at( Cairo.Context cr,
+ double x_pixels,
+ double y_pixels )
+ {
+ Handle ret_val = null;
+
+ cr.save();
+ cr.translate( x0, y0 );
+ cr.transform( matrix );
+
+ double x = x_pixels;
+ double y = y_pixels;
+ cr.device_to_user( ref x, ref y );
+
+ foreach( Handle handle in handles )
+ {
+ handle.cairo_path( cr );
+
+ if ( cr.in_fill( x, y ) )
+ {
+ ret_val = handle;
+ break;
+ }
+ }
+
+ cr.restore();
+
+ return ret_val;
+ }
+
+
+ }
+
+}
diff --git a/glabels/label_object_box.vala b/glabels/label_object_box.vala
new file mode 100644
index 0000000..107934a
--- /dev/null
+++ b/glabels/label_object_box.vala
@@ -0,0 +1,176 @@
+/* label_object_box.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class LabelObjectBox : LabelObject
+ {
+
+ public LabelObjectBox()
+ {
+ handles.append( new HandleSouthEast( this ) );
+ handles.append( new HandleSouthWest( this ) );
+ handles.append( new HandleNorthEast( this ) );
+ handles.append( new HandleNorthWest( this ) );
+ handles.append( new HandleEast( this ) );
+ handles.append( new HandleSouth( this ) );
+ handles.append( new HandleWest( this ) );
+ handles.append( new HandleNorth( this ) );
+ }
+
+
+ public override bool can_fill()
+ {
+ return true;
+ }
+
+ public override bool can_line_color()
+ {
+ return true;
+ }
+
+ public override bool can_line_width()
+ {
+ return true;
+ }
+
+
+ public override LabelObject dup()
+ {
+ LabelObjectBox copy = new LabelObjectBox();
+
+ copy.set_common_properties_from_object( this );
+
+ return copy;
+ }
+
+
+ public override void draw_object( Cairo.Context cr, bool in_editor, MergeRecord? record )
+ {
+ Color line_color = line_color_node.expand( record );
+ Color fill_color = fill_color_node.expand( record );
+
+ if ( in_editor && line_color_node.field_flag )
+ {
+ line_color = Color.from_rgba( 0, 0, 0, 0.5 );
+ }
+
+ if ( in_editor && fill_color_node.field_flag )
+ {
+ fill_color = Color.from_rgba( 0.5, 0.5, 0.5, 0.5 );
+ }
+
+ cr.rectangle( 0, 0, w, h );
+
+ /* Paint fill color */
+ cr.set_source_rgba( fill_color.r, fill_color.g, fill_color.b, fill_color.a );
+ cr.fill_preserve();
+
+ /* Draw outline */
+ cr.set_source_rgba( line_color.r, line_color.g, line_color.b, line_color.a );
+ cr.set_line_width( line_width );
+ cr.stroke();
+ }
+
+
+ public override void draw_shadow( Cairo.Context cr, bool in_editor, MergeRecord? record )
+ {
+ Color line_color = line_color_node.expand( record );
+ Color fill_color = fill_color_node.expand( record );
+ Color shadow_color = shadow_color_node.expand( record );
+
+ if ( in_editor && line_color_node.field_flag )
+ {
+ line_color = Color.from_rgba( 0, 0, 0, 0.5 );
+ }
+
+ if ( in_editor && fill_color_node.field_flag )
+ {
+ fill_color = Color.from_rgba( 0.5, 0.5, 0.5, 0.5 );
+ }
+
+ if ( in_editor && shadow_color_node.field_flag )
+ {
+ shadow_color = Color.black();
+ }
+
+ shadow_color.set_opacity( shadow_opacity );
+
+ if ( fill_color.has_alpha() )
+ {
+ if ( line_color.has_alpha() )
+ {
+ /* Has FILL and OUTLINE: adjust size to account for line width. */
+ cr.rectangle( -line_width/2, -line_width/2, w+line_width, h+line_width );
+ }
+ else
+ {
+ /* Has FILL, but no OUTLINE. */
+ cr.rectangle( 0, 0, w, h );
+ }
+
+ /* Draw shadow */
+ cr.fill();
+ }
+ else
+ {
+ if ( line_color.has_alpha() )
+ {
+ /* Has only OUTLINE. */
+ cr.rectangle( 0, 0, w, h );
+
+ /* Draw shdow of OUTLINE. */
+ cr.set_line_width( line_width );
+ cr.stroke();
+ }
+ }
+ }
+
+
+ public override bool is_object_located_at( Cairo.Context cr, double x, double y )
+ {
+ cr.new_path();
+ cr.rectangle( 0, 0, w, h );
+
+ if ( cr.in_fill( x, y ) )
+ {
+ return true;
+ }
+
+ cr.set_line_width( line_width );
+ if ( cr.in_stroke( x, y ) )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ // TODO: get_handle_at method.
+
+
+ }
+
+}
diff --git a/glabels/label_region.vala b/glabels/label_region.vala
new file mode 100644
index 0000000..f45ba52
--- /dev/null
+++ b/glabels/label_region.vala
@@ -0,0 +1,35 @@
+/* label_region.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public struct LabelRegion
+ {
+ public double x1 { get; set; }
+ public double y1 { get; set; }
+ public double x2 { get; set; }
+ public double y2 { get; set; }
+ }
+
+}
diff --git a/glabels/label_state.vala b/glabels/label_state.vala
new file mode 100644
index 0000000..a687dc9
--- /dev/null
+++ b/glabels/label_state.vala
@@ -0,0 +1,93 @@
+/* label_state.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ public class LabelState
+ {
+ public string description { get; private set; }
+
+ public bool modified { get; private set; }
+ public TimeVal time_stamp { get; private set; }
+
+ public Template template { get; private set; }
+ public bool rotate { get; private set; }
+
+ // TODO: Merge
+
+ public unowned List<LabelObject> object_list { get; private set; }
+
+
+ public LabelState( string description,
+ Label label )
+ {
+ this.description = description;
+
+ modified = label.modified;
+ time_stamp = label.time_stamp;
+
+ template = label.template;
+ rotate = label.rotate;
+
+ // TODO: Merge
+
+ foreach ( LabelObject object in label.object_list )
+ {
+ object_list.append( object.dup() );
+ }
+
+ }
+
+
+ public void restore( Label label )
+ {
+ label.rotate = rotate;
+ label.template = template;
+
+ foreach ( LabelObject object in label.object_list )
+ {
+ label.delete_object( object );
+ }
+
+ foreach ( LabelObject object in object_list )
+ {
+ label.add_object( object.dup() );
+ }
+
+ // TODO: Merge
+
+ if ( !modified &&
+ (time_stamp.tv_sec == label.time_stamp.tv_sec) &&
+ (time_stamp.tv_usec == label.time_stamp.tv_usec) )
+ {
+ label.modified = false;
+ }
+ }
+
+
+ }
+
+}
+
diff --git a/glabels/merge.vala b/glabels/merge.vala
new file mode 100644
index 0000000..63d4a27
--- /dev/null
+++ b/glabels/merge.vala
@@ -0,0 +1,87 @@
+/* merge.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public enum MergeSrcType { NONE, FIXED, FILE }
+
+ public abstract class Merge : Object
+ {
+ public string name { get; construct; }
+ public string description { get; construct; }
+ public MergeSrcType src_type { get; protected set; }
+
+
+ public unowned List<MergeRecord> record_list
+ {
+ get { return _record_list; }
+ }
+ private List<MergeRecord> _record_list;
+
+
+ public uint record_count
+ {
+ get { return _record_list.length(); }
+ }
+
+
+ public string? src
+ {
+ get { return _src; }
+
+ set
+ {
+ _record_list = null;
+ _src = value;
+
+ if ( _src != null )
+ {
+ MergeRecord? record;
+
+ this.open();
+ while ( (record = this.get_record()) != null )
+ {
+ _record_list.append( record );
+ }
+ this.close();
+ }
+
+ }
+
+ }
+ private string? _src;
+
+
+ public abstract List<string> get_key_list();
+ public abstract string get_primary_key();
+ protected abstract void open();
+ protected abstract void close();
+ protected abstract MergeRecord? get_record();
+ public abstract Merge dup();
+
+
+
+ }
+
+}
diff --git a/glabels/merge_factory.vala b/glabels/merge_factory.vala
new file mode 100644
index 0000000..20da5ca
--- /dev/null
+++ b/glabels/merge_factory.vala
@@ -0,0 +1,78 @@
+/* merge_factory.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class MergeFactory
+ {
+
+ /* TODO: register backends rather than hard code. Problem gathering varargs of unknown
+ * type to set Values. */
+
+ public static Merge create_merge( string name )
+ {
+ switch (name)
+ {
+
+ case "Text/Comma":
+ return new MergeText( name, _("Text: Comma Separated Values (CSV)"),
+ ',', false );
+
+ case "Text/Comma/Line1Keys":
+ return new MergeText( name, _("Text: Comma Separated Values (CSV) with keys on line 1"),
+ ',', true );
+
+ case "Text/Tab":
+ return new MergeText( name, _("Text: Tab Separated Values (TSV)"),
+ '\t', false );
+
+ case "Text/Tab/Line1Keys":
+ return new MergeText( name, _("Text: Tab Separated Values (TSV) with keys on line 1"),
+ '\t', true );
+
+ case "Text/Colon":
+ return new MergeText( name, _("Text: Colon Separated Values"),
+ ':', false );
+
+ case "Text/Colon/Line1Keys":
+ return new MergeText( name, _("Text: Colon Separated Values with keys on line 1"),
+ ':', true );
+
+ case "Text/Semicolon":
+ return new MergeText( name, _("Text: Semicolon Separated Values"),
+ ';', false );
+
+ case "Text/Semicolon/Line1Keys":
+ return new MergeText( name, _("Text: Semicolon Separated Values with keys on line 1"),
+ ';', true );
+
+ default:
+ return new MergeNone();
+
+ }
+ }
+
+ }
+
+}
diff --git a/glabels/merge_field.vala b/glabels/merge_field.vala
new file mode 100644
index 0000000..87d6037
--- /dev/null
+++ b/glabels/merge_field.vala
@@ -0,0 +1,33 @@
+/* merge_field.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public struct MergeField
+ {
+ public string key { get; set; }
+ public string value { get; set; }
+ }
+
+}
diff --git a/glabels/merge_none.vala b/glabels/merge_none.vala
new file mode 100644
index 0000000..dc9ea62
--- /dev/null
+++ b/glabels/merge_none.vala
@@ -0,0 +1,72 @@
+/* merge_none.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class MergeNone : Merge
+ {
+
+ public MergeNone()
+ {
+ src_type = MergeSrcType.NONE;
+ }
+
+
+ public override List<string> get_key_list()
+ {
+ return new List<string>();
+ }
+
+
+ public override string get_primary_key()
+ {
+ return "";
+ }
+
+
+ protected override void open()
+ {
+ }
+
+
+ protected override void close()
+ {
+ }
+
+
+ protected override MergeRecord? get_record()
+ {
+ return null;
+ }
+
+
+ public override Merge dup()
+ {
+ MergeNone copy = new MergeNone();
+ return copy;
+ }
+
+ }
+
+}
diff --git a/glabels/merge_record.vala b/glabels/merge_record.vala
new file mode 100644
index 0000000..cf4c73a
--- /dev/null
+++ b/glabels/merge_record.vala
@@ -0,0 +1,47 @@
+/* merge_record.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class MergeRecord
+ {
+ public bool selected { get; set; }
+ public unowned List<MergeField?> field_list { get; set; }
+
+
+ public string? eval_key( string key )
+ {
+ foreach ( MergeField field in field_list )
+ {
+ if ( field.key == key )
+ {
+ return field.value;
+ }
+ }
+
+ return null;
+ }
+ }
+
+}
diff --git a/glabels/merge_text.vala b/glabels/merge_text.vala
new file mode 100644
index 0000000..a5d18c3
--- /dev/null
+++ b/glabels/merge_text.vala
@@ -0,0 +1,395 @@
+/* merge_text.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class MergeText : Merge
+ {
+ public int delim { get; construct; default=','; }
+ public bool line1_has_keys { get; construct; default=false; }
+
+
+ private FileStream fp;
+
+ private string[] keys;
+ private int n_fields_max;
+
+
+ public MergeText( string name = "text/csv",
+ string description = "",
+ int delim = ',',
+ bool line1_has_keys = false )
+ {
+ Object( name:name, description:description, delim:delim, line1_has_keys:line1_has_keys );
+
+ src_type = MergeSrcType.FILE;
+ }
+
+
+ public override List<string> get_key_list()
+ {
+ int n_fields;
+ if ( line1_has_keys )
+ {
+ n_fields = keys.length;
+ }
+ else
+ {
+ n_fields = n_fields_max;
+ }
+
+ List<string> key_list = new List<string>();
+ for ( int i_field = 0; i_field < n_fields; i_field++ )
+ {
+ key_list.append( key_from_index( i_field ) );
+ }
+
+ return key_list;
+ }
+
+
+ private string key_from_index( int i_field )
+ {
+ if ( line1_has_keys && (i_field < keys.length) )
+ {
+ return keys[ i_field ];
+ }
+ else
+ {
+ return "%d".printf( i_field+1 );
+ }
+ }
+
+
+ public override string get_primary_key()
+ {
+ /* For now, let's always assume the first column is the primary key. */
+ return key_from_index( 0 );
+ }
+
+
+ protected override void open()
+ {
+ if ( src == "-" )
+ {
+ fp = FileStream.fdopen( Posix.STDIN_FILENO, "r" );
+ }
+ else
+ {
+ fp = FileStream.open( src, "r" );
+ }
+
+ keys = null;
+ n_fields_max = 0;
+
+ if ( line1_has_keys )
+ {
+ /*
+ * Extract keys from first line and discard line.
+ */
+ List<string> line1_fields = parse_line();
+
+ foreach (string field in line1_fields)
+ {
+ keys += field;
+ }
+ }
+ }
+
+
+ protected override void close()
+ {
+ fp = null;
+ }
+
+
+ protected override MergeRecord? get_record()
+ {
+ List<string?> values = parse_line();
+ if ( values == null )
+ {
+ return null;
+ }
+
+ MergeRecord record = new MergeRecord();
+
+ int i_field = 0;
+ foreach ( string value in values )
+ {
+ MergeField field = MergeField();
+ field.key = key_from_index( i_field );
+ field.value = value;
+ i_field++;
+
+ record.field_list.append( field );
+ }
+
+ if ( i_field > n_fields_max )
+ {
+ n_fields_max = i_field;
+ }
+ return record;
+ }
+
+
+ public override Merge dup()
+ {
+ MergeText copy = new MergeText();
+ return copy;
+ }
+
+
+ private enum State { DELIM,
+ QUOTED, QUOTED_QUOTE1, QUOTED_ESCAPED,
+ SIMPLE, SIMPLE_ESCAPED,
+ DONE }
+
+ /*---------------------------------------------------------------------------
+ * PRIVATE. Parse line.
+ *
+ * Attempt to be a robust parser of various CSV (and similar) formats.
+ *
+ * Based on CSV format described in RFC 4180 section 2.
+ *
+ * Additions to RFC 4180 rules:
+ * - delimeters and other special characters may be "escaped" by a leading
+ * backslash (\)
+ * - C escape sequences for newline (\n) and tab (\t) are also translated.
+ * - if quoted text is not followed by a delimeter, any additional text is
+ * concatenated with quoted portion.
+ *
+ * Returns a list of fields. A blank line is considered a line with one
+ * empty field. Returns empty (NULL) when done.
+ *--------------------------------------------------------------------------*/
+ private List<string> parse_line()
+ {
+ List<string> list = new List<string>();
+
+ if ( fp == null )
+ {
+ return list;
+ }
+
+ State state = State.DELIM;
+ StringBuilder field = new StringBuilder();
+
+ while ( state != State.DONE ) {
+ int c = fp.getc();
+
+ switch (state) {
+
+ case State.DELIM:
+ switch (c) {
+ case '\n':
+ /* last field is empty. */
+ list.append("");
+ state = State.DONE;
+ break;
+ case '\r':
+ /* ignore */
+ state = State.DELIM;
+ break;
+ case FileStream.EOF:
+ /* end of file, no more lines. */
+ state = State.DONE;
+ break;
+ case '"':
+ /* start a quoted field. */
+ state = State.QUOTED;
+ break;
+ case '\\':
+ /* simple field, but 1st character is an escape. */
+ state = State.SIMPLE_ESCAPED;
+ break;
+ default:
+ if ( c == delim )
+ {
+ /* field is empty. */
+ list.append("");
+ state = State.DELIM;
+ }
+ else
+ {
+ /* begining of a simple field. */
+ field.append_c( (char)c );
+ state = State.SIMPLE;
+ }
+ break;
+ }
+ break;
+
+ case State.QUOTED:
+ switch (c) {
+ case FileStream.EOF:
+ /* File ended mid way through quoted item, truncate field. */
+ list.append( field.str );
+ state = State.DONE;
+ break;
+ case '"':
+ /* Possible end of field, but could be 1st of a pair. */
+ state = State.QUOTED_QUOTE1;
+ break;
+ case '\\':
+ /* Escape next character, or special escape, e.g. \n. */
+ state = State.QUOTED_ESCAPED;
+ break;
+ default:
+ /* Use character literally. */
+ field.append_c( (char)c );
+ break;
+ }
+ break;
+
+ case State.QUOTED_QUOTE1:
+ switch (c) {
+ case '\n':
+ case FileStream.EOF:
+ /* line or file ended after quoted item */
+ list.append( field.str );
+ state = State.DONE;
+ break;
+ case '"':
+ /* second quote, insert and stay quoted. */
+ field.append_c( (char)c );
+ state = State.QUOTED;
+ break;
+ case '\r':
+ /* ignore and go to fallback */
+ state = State.SIMPLE;
+ break;
+ default:
+ if ( c == delim )
+ {
+ /* end of field. */
+ list.append( field.str );
+ field.truncate( 0 );
+ state = State.DELIM;
+ }
+ else
+ {
+ /* fallback if not a delim or another quote. */
+ field.append_c( (char)c );
+ state = State.SIMPLE;
+ }
+ break;
+ }
+ break;
+
+ case State.QUOTED_ESCAPED:
+ switch (c) {
+ case FileStream.EOF:
+ /* File ended mid way through quoted item */
+ list.append( field.str );
+ state = State.DONE;
+ break;
+ case 'n':
+ /* Decode "\n" as newline. */
+ field.append_c( (char)'\n' );
+ state = State.QUOTED;
+ break;
+ case 't':
+ /* Decode "\t" as tab. */
+ field.append_c( (char)'\t' );
+ state = State.QUOTED;
+ break;
+ default:
+ /* Use character literally. */
+ field.append_c( (char)c );
+ state = State.QUOTED;
+ break;
+ }
+ break;
+
+ case State.SIMPLE:
+ switch (c) {
+ case '\n':
+ case FileStream.EOF:
+ /* line or file ended */
+ list.append( field.str );
+ state = State.DONE;
+ break;
+ case '\r':
+ /* ignore */
+ state = State.SIMPLE;
+ break;
+ case '\\':
+ /* Escape next character, or special escape, e.g. \n. */
+ state = State.SIMPLE_ESCAPED;
+ break;
+ default:
+ if ( c == delim )
+ {
+ /* end of field. */
+ list.append( field.str );
+ field.truncate( 0 );
+ state = State.DELIM;
+ }
+ else
+ {
+ /* Use character literally. */
+ field.append_c( (char)c );
+ state = State.SIMPLE;
+ }
+ break;
+ }
+ break;
+
+ case State.SIMPLE_ESCAPED:
+ switch (c) {
+ case FileStream.EOF:
+ /* File ended mid way through quoted item */
+ list.append( field.str );
+ state = State.DONE;
+ break;
+ case 'n':
+ /* Decode "\n" as newline. */
+ field.append_c( (char)'\n' );
+ state = State.SIMPLE;
+ break;
+ case 't':
+ /* Decode "\t" as tab. */
+ field.append_c( (char)'\t' );
+ state = State.SIMPLE;
+ break;
+ default:
+ /* Use character literally. */
+ field.append_c( (char)c );
+ state = State.SIMPLE;
+ break;
+ }
+ break;
+
+ default:
+ assert_not_reached();
+ }
+
+ }
+
+ return list;
+ }
+
+
+ }
+
+}
diff --git a/glabels/mini_preview.vala b/glabels/mini_preview.vala
new file mode 100644
index 0000000..49298e6
--- /dev/null
+++ b/glabels/mini_preview.vala
@@ -0,0 +1,417 @@
+/* mini_preview.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ public class MiniPreview : Gtk.EventBox
+ {
+
+ public signal void clicked( int index );
+
+ public signal void pressed( int index1,
+ int index2 );
+
+ public signal void released( int index1,
+ int index2 );
+
+
+ private const double MARGIN = 2;
+ private const double SHADOW_OFFSET = 3;
+
+
+ private Gtk.DrawingArea canvas;
+
+ private Template? template;
+ private Gee.ArrayList<TemplateCoord?> origins;
+ private Gee.ArrayList<TemplateCoord?> centers;
+
+ private int highlight_first;
+ private int highlight_last;
+
+ private bool dragging;
+ private int first_i;
+ private int last_i;
+ private int prev_i;
+
+ private bool update_scheduled_flag;
+
+ private Label? label;
+
+
+
+ public MiniPreview( int height,
+ int width )
+ {
+ add_events( Gdk.EventMask.BUTTON_PRESS_MASK |
+ Gdk.EventMask.BUTTON_RELEASE_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK );
+
+ set_visible_window( false );
+
+ canvas = new Gtk.DrawingArea();
+ canvas.set_has_window( false );
+ add( canvas );
+
+ canvas.set_size_request( width, height );
+
+ canvas.draw.connect( on_draw );
+ canvas.style_set.connect( on_style_set );
+ }
+
+
+ public void set_label( Label label )
+ {
+ this.label = label;
+ set_template( label.template );
+ }
+
+
+ public void set_template_by_name( string name )
+ {
+ Template? template = Db.lookup_template_from_name( name );
+ set_template( template );
+ }
+
+
+ public void set_template( Template? template )
+ {
+ this.template = template;
+
+ if ( template != null )
+ {
+ TemplateFrame frame = template.frames.first().data;
+
+ /*
+ * Cache list of origins.
+ */
+ origins = frame.get_origins();
+
+ /*
+ * Also build list of label centers from origins and cache.
+ */
+ double w, h;
+ frame.get_size( out w, out h );
+
+ centers = frame.get_origins();
+
+ foreach ( TemplateCoord center in centers )
+ {
+ center.x += w/2;
+ center.y += h/2;
+ }
+
+
+ redraw_canvas();
+ }
+ }
+
+
+ public void highlight_range( int first_label,
+ int last_label )
+ {
+ if ( ( first_label != highlight_first ) ||
+ ( last_label != highlight_last ) )
+ {
+ highlight_first = first_label;
+ highlight_last = last_label;
+
+ redraw_canvas();
+ }
+ }
+
+
+ public override bool button_press_event( Gdk.EventButton event )
+ {
+ if ( event.button == 1 )
+ {
+ Cairo.Context cr = Gdk.cairo_create( canvas.get_window() );
+
+ set_transform_and_get_scale( cr );
+
+ double x = event.x;
+ double y = event.y;
+ cr.device_to_user( ref x, ref y );
+
+ int i = find_closest_label( x, y );
+ clicked( i );
+
+ first_i = i;
+ last_i = i;
+ pressed( first_i, last_i );
+
+ dragging = true;
+ prev_i = i;
+ }
+
+ return false;
+ }
+
+
+ public override bool button_release_event( Gdk.EventButton event )
+ {
+ if ( event.button == 1 )
+ {
+ dragging = false;
+ }
+ released( first_i, last_i );
+
+ return false;
+ }
+
+
+ public override bool motion_notify_event( Gdk.EventMotion event )
+ {
+ if ( dragging )
+ {
+ Cairo.Context cr = Gdk.cairo_create( canvas.get_window() );
+
+ set_transform_and_get_scale( cr );
+
+ double x = event.x;
+ double y = event.y;
+ cr.device_to_user( ref x, ref y );
+
+ int i = find_closest_label( x, y );
+
+ if ( i != prev_i )
+ {
+ last_i = prev_i = i;
+ pressed( int.min( first_i, last_i ), int.max( first_i, last_i ) );
+ }
+ }
+
+ return false;
+ }
+
+
+ private void on_style_set( Gtk.Style? previous_style )
+ {
+ redraw_canvas();
+ }
+
+
+ private void redraw_canvas()
+ {
+ var window = get_window ();
+ if (null == window)
+ {
+ return;
+ }
+
+ if ( !update_scheduled_flag )
+ {
+ update_scheduled_flag = true;
+
+ unowned Cairo.Region region = window.get_clip_region ();
+ // redraw the cairo canvas completely by exposing it
+ window.invalidate_region (region, true);
+ }
+
+ }
+
+
+ private double set_transform_and_get_scale(Cairo.Context cr )
+ {
+
+ /* Establish scale and origin. */
+ double w = get_allocated_width();
+ double h = get_allocated_height();
+
+ /* establish scale. */
+ double scale = double.min( (w - 2*MARGIN - 2*SHADOW_OFFSET)/template.page_width,
+ (h - 2*MARGIN - 2*SHADOW_OFFSET)/template.page_height );
+
+ /* Find offset to center preview. */
+ double offset_x = (w/scale - template.page_width) / 2.0;
+ double offset_y = (h/scale - template.page_height) / 2.0;
+
+ /* Set transformation. */
+ cr.scale( scale, scale );
+ cr.translate( offset_x, offset_y );
+
+ return scale;
+ }
+
+
+ private int find_closest_label( double x,
+ double y )
+ {
+ double dx = x - centers[0].x;
+ double dy = y - centers[0].y;
+ double min_d2 = dx*dx + dy*dy;
+
+ int i = 0;
+ int min_i = 0;
+
+ foreach ( TemplateCoord center in centers )
+ {
+ dx = x - center.x;
+ dy = y - center.y;
+ double d2 = dx*dx + dy*dy;
+
+ if ( d2 < min_d2 )
+ {
+ min_d2 = d2;
+ min_i = i;
+ }
+ i++;
+ }
+
+ return min_i + 1;
+ }
+
+
+ private bool on_draw( Cairo.Context cr )
+ {
+ update_scheduled_flag = false;
+
+ if ( template != null )
+ {
+ double scale = set_transform_and_get_scale( cr );
+
+ /* update shadow */
+ double shadow_x = SHADOW_OFFSET/scale;
+ double shadow_y = SHADOW_OFFSET/scale;
+
+ draw_shadow( cr, shadow_x, shadow_y );
+
+ draw_paper( cr, 1.0/scale );
+
+ draw_labels( cr, 2.0/scale );
+
+ if ( label != null )
+ {
+ draw_rich_preview( cr );
+ }
+
+ }
+
+ return false;
+ }
+
+
+ private void draw_shadow( Cairo.Context cr,
+ double x,
+ double y )
+ {
+ cr.save();
+
+ cr.rectangle( x, y, template.page_width, template.page_height );
+
+ Gtk.Style style = get_style();
+ Color shadow_color = Color.from_gdk_color( style.dark[Gtk.StateType.NORMAL] );
+ cr.set_source_rgb( shadow_color.r, shadow_color.g, shadow_color.b );
+
+ cr.fill();
+
+ cr.restore();
+ }
+
+
+ private void draw_paper( Cairo.Context cr,
+ double line_width )
+ {
+ cr.save();
+
+ cr.rectangle( 0, 0, template.page_width, template.page_height );
+
+ Gtk.Style style = get_style();
+ Color paper_color = Color.from_gdk_color( style.light[Gtk.StateType.NORMAL] );
+ Color outline_color = Color.from_gdk_color( style.fg[Gtk.StateType.NORMAL] );
+
+ cr.set_source_rgb( paper_color.r, paper_color.g, paper_color.b );
+ cr.fill_preserve();
+
+ cr.set_source_rgb( outline_color.r, outline_color.g, outline_color.b );
+ cr.set_line_width( line_width );
+ cr.stroke();
+
+ cr.restore();
+ }
+
+
+ private void draw_labels( Cairo.Context cr,
+ double line_width )
+ {
+ TemplateFrame frame = template.frames.first().data;
+
+ int n_labels = frame.get_n_labels();
+
+ Gtk.Style style = get_style();
+ Color base_color = Color.from_gdk_color( style.base[Gtk.StateType.SELECTED] );
+ Color highlight_color = Color.from_color_and_opacity( base_color, 0.10 );
+
+ Color outline_color;
+ if ( label != null )
+ {
+ /* Outlines are more subtle when doing a rich preview. */
+ outline_color = Color.from_color_and_opacity( base_color, 0.25 );
+ }
+ else
+ {
+ outline_color = Color.from_color_and_opacity( base_color, 1.00 );
+ }
+
+ for ( int i = 0; i < n_labels; i++ )
+ {
+ cr.save();
+
+ cr.translate( origins[i].x, origins[i].y );
+ frame.cairo_path( cr, false );
+
+ if ( ((i+1) >= highlight_first) &&
+ ((i+1) <= highlight_last) )
+ {
+ cr.set_source_rgba( highlight_color.r, highlight_color.g, highlight_color.b, highlight_color.a );
+ cr.set_fill_rule( Cairo.FillRule.EVEN_ODD );
+ cr.fill_preserve();
+ }
+
+ cr.set_line_width( line_width );
+ cr.set_source_rgba( outline_color.r, outline_color.g, outline_color.b, outline_color.a );
+ cr.stroke();
+
+ cr.restore();
+ }
+ }
+
+
+ private void draw_rich_preview( Cairo.Context cr )
+ {
+ Print print = new Print();
+
+ print.label = label;
+ print.outline_flag = false;
+ print.reverse_flag = false;
+ print.crop_marks_flag = false;
+
+ /* TODO: test for merge. */
+ print.print_simple_sheet( cr );
+ }
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/glabels/new_label_dialog.vala b/glabels/new_label_dialog.vala
new file mode 100644
index 0000000..a7a66a5
--- /dev/null
+++ b/glabels/new_label_dialog.vala
@@ -0,0 +1,218 @@
+/* new_label_dialog.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ class NewLabelDialog : Gtk.Dialog
+ {
+ private Prefs prefs;
+
+
+ private Gtk.IconView icon_view;
+
+ private Gtk.Entry search_entry;
+ private string search_string = "";
+ private Gtk.TreeModelFilter search_filtered_model;
+
+ private Gtk.ListStore model;
+
+
+ public string? template_name { get; private set; }
+
+
+ public NewLabelDialog( Window window )
+ {
+ prefs = new Prefs();
+
+ this.set_transient_for( window );
+ this.set_destroy_with_parent( true );
+
+ this.set_default_size( 788, 600 );
+
+
+ /* Load the user interface. */
+ Gtk.Builder builder = new Gtk.Builder();
+ try
+ {
+ string file = GLib.Path.build_filename( Config.DATADIR, Config.GLABELS_BRANCH, "ui", "new_label_dialog.ui" );
+ string[] objects = { "main_vbox", null };
+ builder.add_objects_from_file( file, objects );
+ }
+ catch ( Error err )
+ {
+ error( "Error: %s", err.message );
+ }
+
+ Gtk.VBox main_vbox = builder.get_object( "main_vbox" ) as Gtk.VBox;
+ ((Gtk.Box)get_content_area()).pack_start( main_vbox );
+
+ search_entry = builder.get_object( "search_entry" ) as Gtk.Entry;
+ icon_view = builder.get_object( "icon_view" ) as Gtk.IconView;
+
+
+ /* Create and set icon view model. */
+ model = new Gtk.ListStore( 3, typeof(string), typeof(Gdk.Pixbuf), typeof(string) );
+
+ search_filtered_model = new Gtk.TreeModelFilter( model, null );
+ search_filtered_model.set_visible_func( search_filter_func );
+
+ icon_view.set_model( search_filtered_model );
+ icon_view.set_text_column( 0 );
+ icon_view.set_pixbuf_column( 1 );
+ icon_view.set_tooltip_column( 2 );
+
+ /* Set "follow-state" property of pixbuf renderer. (pre-light) */
+ List<weak Gtk.CellRenderer> renderer_list = icon_view.cell_area.get_cells();
+ foreach ( Gtk.CellRenderer renderer in renderer_list )
+ {
+ if ( renderer is Gtk.CellRendererPixbuf )
+ {
+ ((Gtk.CellRendererPixbuf)renderer).follow_state = true;
+ }
+ }
+
+ /* Intialize model. */
+ foreach ( libglabels.Template template in libglabels.Db.templates )
+ {
+ Gtk.TreeIter iter;
+ model.append( out iter );
+
+ string tooltip = build_tooltip( template );
+
+ model.set( iter,
+ 0, template.name,
+ 1, template.preview_pixbuf,
+ 2, tooltip,
+ -1);
+ }
+
+ /* Connect signals. */
+ search_entry.changed.connect( on_search_entry_changed );
+ search_entry.icon_release.connect( on_search_entry_clear );
+ icon_view.selection_changed.connect( on_icon_view_selection_changed );
+ icon_view.button_release_event.connect( on_icon_view_button_release_event );
+ }
+
+
+ private string build_tooltip( libglabels.Template template )
+ {
+ libglabels.Units units = prefs.units;
+
+ libglabels.TemplateFrame frame = template.frames.first().data;
+ string size_string = frame.get_size_description( units );
+ string count_string = frame.get_layout_description();
+
+ string tip = "<span weight=\"bold\">%s: %s\n</span>%s\n%s".printf(
+ template.name, template.description,
+ size_string,
+ count_string );
+
+ return tip;
+ }
+
+
+ private bool search_filter_func( Gtk.TreeModel model, Gtk.TreeIter iter )
+ {
+ if ( search_string == "" )
+ {
+ return true;
+ }
+
+ Value value;
+ model.get_value( iter, 0, out value );
+ string name = value.get_string();
+
+ string needle = search_string.casefold();
+ string haystack = name.casefold();
+
+ if ( needle in haystack )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ private void on_search_entry_clear()
+ {
+ search_entry.set_text( "" );
+ }
+
+
+ private void on_search_entry_changed()
+ {
+ string new_search_string = search_entry.get_text();
+
+ new_search_string.strip();
+ if ( search_string == new_search_string )
+ {
+ return;
+ }
+ search_string = new_search_string;
+
+
+ if ( search_string == "" )
+ {
+ search_entry.secondary_icon_name = "edit-find-symbolic";
+ search_entry.secondary_icon_activatable = false;
+ search_entry.secondary_icon_sensitive = false;
+ }
+ else
+ {
+ search_entry.secondary_icon_name = "edit-clear-symbolic";
+ search_entry.secondary_icon_activatable = true;
+ search_entry.secondary_icon_sensitive = true;
+ }
+
+ search_filtered_model.refilter();
+ }
+
+
+ private void on_icon_view_selection_changed()
+ {
+ List<Gtk.TreePath> list = icon_view.get_selected_items();
+
+ Gtk.TreeIter iter;
+ if ( search_filtered_model.get_iter( out iter, list.first().data ) )
+ {
+ Value value;
+ search_filtered_model.get_value( iter, 0, out value );
+
+ template_name = value.get_string();
+ }
+ }
+
+
+ private bool on_icon_view_button_release_event( Gdk.EventButton event )
+ {
+ response( Gtk.ResponseType.OK );
+ return true;
+ }
+
+
+ }
+
+}
+
diff --git a/glabels/prefs.vala b/glabels/prefs.vala
new file mode 100644
index 0000000..eaa3f63
--- /dev/null
+++ b/glabels/prefs.vala
@@ -0,0 +1,376 @@
+/* prefs.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ public class Prefs : Object
+ {
+ private const string DEFAULT_UNITS_STRING_US = "in";
+ private const string DEFAULT_PAGE_SIZE_US = "US-Letter";
+
+ private const string DEFAULT_UNITS_STRING_METRIC = "mm";
+ private const string DEFAULT_PAGE_SIZE_METRIC = "A4";
+
+
+ public signal void changed();
+
+
+ private static GLib.Settings locale;
+ private static GLib.Settings objects;
+ private static GLib.Settings ui;
+
+
+ static construct
+ {
+ locale = new GLib.Settings( "org.gnome.glabels-3.locale" );
+ objects = new GLib.Settings( "org.gnome.glabels-3.objects" );
+ ui = new GLib.Settings( "org.gnome.glabels-3.ui" );
+ }
+
+ public Prefs()
+ {
+ locale.changed.connect( on_changed );
+ objects.changed.connect( on_changed );
+ ui.changed.connect( on_changed );
+ }
+
+
+ private void on_changed()
+ {
+ changed();
+ }
+
+
+ public Units units
+ {
+ get
+ {
+ string? units_string = locale.get_string( "units" );
+
+ /* If not set, make educated guess about locale default. */
+ if ( (units_string == null) || (units_string == "") )
+ {
+ string pgsize = Gtk.PaperSize.get_default();
+ if ( pgsize == Gtk.PAPER_NAME_LETTER )
+ {
+ units_string = DEFAULT_UNITS_STRING_US;
+ }
+ else
+ {
+ units_string = DEFAULT_UNITS_STRING_METRIC;
+ }
+ }
+
+ _units = Units.from_id( units_string );
+
+ /* If invalid, make an educated guess from locale. */
+ if ( _units.id == "pt" )
+ {
+ string pgsize = Gtk.PaperSize.get_default();
+ if ( pgsize == Gtk.PAPER_NAME_LETTER )
+ {
+ _units = Units.inch();
+ }
+ else
+ {
+ _units = Units.mm();
+ }
+ }
+
+ return _units;
+ }
+
+ set
+ {
+ locale.set_string( "units", value.id );
+ }
+ }
+ private Units _units;
+
+
+ public string default_page_size
+ {
+ get
+ {
+ _default_page_size = locale.get_string( "default-page-size" );
+
+ /* If not set, make educated guess about locale default. */
+ /* Also proof read the default page size -- it must be a valid id. */
+ /* (For compatability with older versions.) */
+ if ( (_default_page_size == null) ||
+ (_default_page_size == "") ||
+ ( Db.lookup_paper_from_id( _default_page_size ) == null ) )
+ {
+ string pgsize = Gtk.PaperSize.get_default();
+ if ( pgsize == Gtk.PAPER_NAME_LETTER )
+ {
+ _default_page_size = DEFAULT_PAGE_SIZE_US;
+ }
+ else
+ {
+ _default_page_size = DEFAULT_PAGE_SIZE_METRIC;
+ }
+ }
+
+ return _default_page_size;
+ }
+
+ set
+ {
+ locale.set_string( "default-page-size", value );
+ }
+ }
+ private string? _default_page_size;
+
+
+ public string default_font_family
+ {
+ get
+ {
+ _default_font_family = objects.get_string( "default-font-family" );
+ return _default_font_family;
+ }
+
+ set
+ {
+ objects.set_string( "default-font-family", value );
+ }
+ }
+ private string? _default_font_family;
+
+
+ public double default_font_size
+ {
+ get
+ {
+ return objects.get_double( "default-font-size" );
+ }
+
+ set
+ {
+ objects.set_double( "default-font-size", value );
+ }
+ }
+
+
+ public Pango.Weight default_font_weight
+ {
+ get
+ {
+ return EnumUtil.string_to_weight( objects.get_string( "default-font-weight" ) );
+ }
+
+ set
+ {
+ objects.set_string( "default-font-weight", EnumUtil.weight_to_string( value ) );
+ }
+ }
+
+
+ public bool default_font_italic_flag
+ {
+ get
+ {
+ return objects.get_boolean( "default-font-italic-flag" );
+ }
+
+ set
+ {
+ objects.set_boolean( "default-font-italic-flag", value );
+ }
+ }
+
+
+ public Color default_text_color
+ {
+ get
+ {
+ return Color.from_legacy_color( objects.get_uint( "default-text-color" ) );
+ }
+
+ set
+ {
+ objects.set_uint( "default-text-color", value.to_legacy_color() );
+ }
+ }
+
+
+ public Pango.Alignment default_text_alignment
+ {
+ get
+ {
+ return EnumUtil.string_to_align( objects.get_string( "default-text-alignment" ) );
+ }
+
+ set
+ {
+ objects.set_string( "default-text-alignment", EnumUtil.align_to_string( value ) );
+ }
+ }
+
+
+ public double default_text_line_spacing
+ {
+ get
+ {
+ return objects.get_double( "default-text-line-spacing" );
+ }
+
+ set
+ {
+ objects.set_double( "default-text-line-spacing", value );
+ }
+ }
+
+
+ public double default_line_width
+ {
+ get
+ {
+ return objects.get_double( "default-line-width" );
+ }
+
+ set
+ {
+ objects.set_double( "default-line-width", value );
+ }
+ }
+
+
+ public Color default_line_color
+ {
+ get
+ {
+ return Color.from_legacy_color( objects.get_uint( "default-line-color" ) );
+ }
+
+ set
+ {
+ objects.set_uint( "default-line-color", value.to_legacy_color() );
+ }
+ }
+
+
+ public Color default_fill_color
+ {
+ get
+ {
+ return Color.from_legacy_color( objects.get_uint( "default-fill-color" ) );
+ }
+
+ set
+ {
+ objects.set_uint( "default-fill-color", value.to_legacy_color() );
+ }
+ }
+
+
+ public bool main_toolbar_visible
+ {
+ get
+ {
+ return ui.get_boolean( "main-toolbar-visible" );
+ }
+
+ set
+ {
+ ui.set_boolean( "main-toolbar-visible", value );
+ }
+ }
+
+
+ public bool drawing_toolbar_visible
+ {
+ get
+ {
+ return ui.get_boolean( "drawing-toolbar-visible" );
+ }
+
+ set
+ {
+ ui.set_boolean( "drawing-toolbar-visible", value );
+ }
+ }
+
+
+ public bool property_toolbar_visible
+ {
+ get
+ {
+ return ui.get_boolean( "property-toolbar-visible" );
+ }
+
+ set
+ {
+ ui.set_boolean( "property-toolbar-visible", value );
+ }
+ }
+
+
+ public bool grid_visible
+ {
+ get
+ {
+ return ui.get_boolean( "grid-visible" );
+ }
+
+ set
+ {
+ ui.set_boolean( "grid-visible", value );
+ }
+ }
+
+
+ public bool markup_visible
+ {
+ get
+ {
+ return ui.get_boolean( "markup-visible" );
+ }
+
+ set
+ {
+ ui.set_boolean( "markup-visible", value );
+ }
+ }
+
+
+ public int max_recents
+ {
+ get
+ {
+ return ui.get_int( "max-recents" );
+ }
+
+ set
+ {
+ ui.set_int( "max-recents", value );
+ }
+ }
+
+
+ }
+
+}
diff --git a/glabels/print.vala b/glabels/print.vala
new file mode 100644
index 0000000..0fdd7f3
--- /dev/null
+++ b/glabels/print.vala
@@ -0,0 +1,217 @@
+/* print.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ public class Print
+ {
+ private const double OUTLINE_WIDTH = 0.25;
+ private const double TICK_OFFSET = 2.25;
+ private const double TICK_LENGTH = 18.0;
+
+ public Label label { get; set; }
+ public bool outline_flag { get; set; }
+ public bool reverse_flag { get; set; }
+ public bool crop_marks_flag { get; set; }
+
+
+ public void print_simple_sheet( Cairo.Context cr )
+ {
+ if ( crop_marks_flag )
+ {
+ print_crop_marks( cr );
+ }
+
+ TemplateFrame frame = label.template.frames.first().data;
+ Gee.ArrayList<TemplateCoord?> origins = frame.get_origins();
+
+ foreach ( TemplateCoord origin in origins )
+ {
+ print_label( cr, origin.x, origin.y, null );
+ }
+ }
+
+
+ private void print_crop_marks( Cairo.Context cr )
+ {
+ TemplateFrame frame = label.template.frames.first().data;
+
+ double w, h;
+ frame.get_size( out w, out h );
+
+ cr.save();
+
+ cr.set_source_rgb( 0, 0, 0 );
+ cr.set_line_width( OUTLINE_WIDTH );
+
+ foreach ( TemplateLayout layout in frame.layouts )
+ {
+
+ double xmin = layout.x0;
+ double ymin = layout.y0;
+ double xmax = layout.x0 + layout.dx*(layout.nx - 1) + w;
+ double ymax = layout.y0 + layout.dy*(layout.ny - 1) + h;
+
+ for ( int ix=0; ix < layout.nx; ix++ )
+ {
+ double x1 = xmin + ix*layout.dx;
+ double x2 = x1 + w;
+
+ double y1 = double.max((ymin - TICK_OFFSET), 0.0);
+ double y2 = double.max((y1 - TICK_LENGTH), 0.0);
+
+ double y3 = double.min((ymax + TICK_OFFSET), label.template.page_height);
+ double y4 = double.min((y3 + TICK_LENGTH), label.template.page_height);
+
+ cr.move_to( x1, y1 );
+ cr.line_to( x1, y2 );
+ cr.stroke();
+
+ cr.move_to( x2, y1 );
+ cr.line_to( x2, y2 );
+ cr.stroke();
+
+ cr.move_to( x1, y3 );
+ cr.line_to( x1, y4 );
+ cr.stroke();
+
+ cr.move_to( x2, y3 );
+ cr.line_to( x2, y4 );
+ cr.stroke();
+ }
+
+ for (int iy=0; iy < layout.ny; iy++ )
+ {
+ double y1 = ymin + iy*layout.dy;
+ double y2 = y1 + h;
+
+ double x1 = double.max((xmin - TICK_OFFSET), 0.0);
+ double x2 = double.max((x1 - TICK_LENGTH), 0.0);
+
+ double x3 = double.min((xmax + TICK_OFFSET), label.template.page_width);
+ double x4 = double.min((x3 + TICK_LENGTH), label.template.page_width);
+
+ cr.move_to( x1, y1 );
+ cr.line_to( x2, y1 );
+ cr.stroke();
+
+ cr.move_to( x1, y2 );
+ cr.line_to( x2, y2 );
+ cr.stroke();
+
+ cr.move_to( x3, y1 );
+ cr.line_to( x4, y1 );
+ cr.stroke();
+
+ cr.move_to( x3, y2 );
+ cr.line_to( x4, y2 );
+ cr.stroke();
+ }
+
+ }
+
+ cr.restore();
+ }
+
+
+ private void print_label( Cairo.Context cr,
+ double x,
+ double y,
+ MergeRecord? record )
+ {
+
+ double w, h;
+ label.get_size( out w, out h );
+
+ cr.save();
+
+ /* Transform coordinate system to be relative to upper corner */
+ /* of the current label */
+ cr.translate( x, y );
+
+ cr.save();
+
+ clip_to_outline( cr );
+
+ cr.save();
+
+ /* Special transformations. */
+ if ( label.rotate )
+ {
+ cr.rotate( Math.PI/2 );
+ cr.translate( 0, -h );
+ }
+ if ( reverse_flag )
+ {
+ cr.translate( w, 0 );
+ cr.scale( -1, 1 );
+ }
+
+ label.draw( cr, false, record );
+
+ cr.restore(); /* From special transformations. */
+
+ cr.restore(); /* From clip to outline. */
+
+ if ( outline_flag )
+ {
+ draw_outline( cr );
+ }
+
+ cr.restore(); /* From translation. */
+ }
+
+
+ private void draw_outline( Cairo.Context cr )
+ {
+ cr.save();
+
+ cr.set_source_rgb( 0, 0, 0 );
+ cr.set_line_width( OUTLINE_WIDTH );
+
+ TemplateFrame frame = label.template.frames.first().data;
+ frame.cairo_path( cr, false );
+
+ cr.stroke();
+
+ cr.restore();
+ }
+
+
+ private void clip_to_outline( Cairo.Context cr )
+ {
+ TemplateFrame frame = label.template.frames.first().data;
+ frame.cairo_path( cr, true );
+
+ cr.set_fill_rule( Cairo.FillRule.EVEN_ODD );
+ cr.clip();
+ }
+
+
+
+ }
+
+}
+
diff --git a/glabels/template_history.vala b/glabels/template_history.vala
new file mode 100644
index 0000000..11d9c91
--- /dev/null
+++ b/glabels/template_history.vala
@@ -0,0 +1,94 @@
+/* template_history.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class TemplateHistory : Object
+ {
+
+ public signal void changed();
+
+ private static GLib.Settings history;
+ private int max_n;
+
+ static construct
+ {
+ history = new GLib.Settings( "org.gnome.glabels-3.history" );
+ }
+
+ public TemplateHistory( int n )
+ {
+ max_n = n;
+
+ history.changed["recent-templates"].connect( on_history_changed );
+ }
+
+ public void add_name( string template_name )
+ {
+ string[] old_templates;
+ string[] new_templates;
+ int i, j;
+
+ old_templates = history.get_strv( "recent-templates" );
+
+ new_templates = new string[1];
+ new_templates += template_name;
+
+ for ( i = 0, j = 1; (j < (max_n-1)) && (i < old_templates.length); i++ )
+ {
+ if ( template_name != old_templates[i] )
+ {
+ new_templates += old_templates[i];
+ }
+ }
+
+ history.set_strv( "recent-templates", new_templates );
+ }
+
+ public List<string> get_template_list()
+ {
+ string[] templates;
+ List<string> template_list = new List<string>();
+ int i;
+
+ templates = history.get_strv( "recent-templates" );
+
+ for ( i = 0; i < templates.length; i++ )
+ {
+ template_list.append( templates[i] );
+ }
+
+ return template_list;
+ }
+
+ private void on_history_changed()
+ {
+ changed();
+ }
+
+ }
+
+}
+
+
diff --git a/glabels/test.vala b/glabels/test.vala
new file mode 100644
index 0000000..60485a7
--- /dev/null
+++ b/glabels/test.vala
@@ -0,0 +1,221 @@
+
+using glabels;
+
+int main (string[] args) {
+
+ Gtk.init( ref args );
+
+ libglabels.Db.init();
+ libglabels.XmlUtil.init();
+
+ var win = new Gtk.Window();
+ win.set_size_request( 200, 200 );
+ win.border_width = 5;
+ win.title = "Widget test";
+ win.destroy.connect( Gtk.main_quit );
+
+ var frame = new Gtk.Frame( "Example Vala Widget" );
+ win.add( frame );
+
+ var vbox = new Gtk.VBox( false, 3 );
+ frame.add( vbox );
+
+
+ switch (args[1])
+ {
+
+ case "0":
+ stdout.printf( "TEST %s.\n", args[1] );
+ libglabels.Db.print_known_papers();
+ libglabels.Db.print_known_categories();
+ libglabels.Db.print_known_vendors();
+ libglabels.Db.print_known_templates();
+ break;
+
+ case "1":
+ stdout.printf( "TEST %s.\n", args[1] );
+ var color = Color.from_rgb( 0.75, 0.75, 1.0 );
+ var button1 = new glabels.ColorButton( "Xyzzy", color, color );
+ vbox.pack_start( button1 );
+
+ var button2 = new glabels.FontButton( "Sans" );
+ vbox.pack_start( button2 );
+ break;
+
+ case "2":
+ stdout.printf( "TEST %s.\n", args[1] );
+ var mini_preview = new glabels.MiniPreview( 200, 200 );
+ mini_preview.set_template_by_name( "Avery 3612" );
+ vbox.pack_start( mini_preview );
+ break;
+
+ case "3":
+ stdout.printf( "TEST %s.\n", args[1] );
+ Gtk.ListStore list_store = new Gtk.ListStore( 2, typeof(string), typeof(Gdk.Pixbuf) );
+
+ var icon_view = new Gtk.IconView.with_model( list_store );
+ icon_view.set_text_column( 0 );
+ icon_view.set_pixbuf_column( 1 );
+
+ var sw = new Gtk.ScrolledWindow( null, null );
+ sw.add( icon_view );
+ vbox.pack_start( sw );
+
+ foreach ( libglabels.Template template in libglabels.Db.templates )
+ {
+ Gtk.TreeIter iter;
+ list_store.append( out iter );
+
+ list_store.set( iter, 0, template.name, 1, template.preview_pixbuf, -1);
+ }
+ break;
+
+ case "4":
+ stdout.printf( "TEST %s.\n", args[1] );
+ var label = new glabels.Label();
+ label.template = libglabels.Db.lookup_template_from_name( "Avery 3612" );
+
+ var box = new LabelObjectBox();
+ box.x0 = 36;
+ box.y0 = 36;
+ box.w = 72;
+ box.h = 36;
+ box.line_width = 4;
+ box.line_color_node = ColorNode.from_color( Color.black() );
+ box.fill_color_node = ColorNode.from_color( Color.from_rgb( 0, 1, 0 ) );
+
+ label.add_object( box );
+
+ var mini_preview = new glabels.MiniPreview( 200, 200 );
+ mini_preview.set_label( label );
+ vbox.pack_start( mini_preview );
+ break;
+
+ case "4.1":
+ stdout.printf( "TEST %s.\n", args[1] );
+ var label = new glabels.Label();
+ label.template = libglabels.Db.lookup_template_from_name( "Avery 3612" );
+ label.rotate = true;
+
+ var box = new LabelObjectBox();
+ box.x0 = 36;
+ box.y0 = 36;
+ box.w = 72;
+ box.h = 36;
+ box.line_width = 4;
+ box.line_color_node = ColorNode.from_color( Color.black() );
+ box.fill_color_node = ColorNode.from_color( Color.from_rgb( 0, 1, 0 ) );
+
+ label.add_object( box );
+
+ var mini_preview = new glabels.MiniPreview( 200, 200 );
+ mini_preview.set_label( label );
+ vbox.pack_start( mini_preview );
+ break;
+
+ case "5":
+ stdout.printf( "TEST %s.\n", args[1] );
+ var label = new glabels.Label();
+ label.template = libglabels.Db.lookup_template_from_name( "Avery 5523" );
+
+ var box = new LabelObjectBox();
+ box.x0 = 36;
+ box.y0 = 36;
+ box.w = 72;
+ box.h = 36;
+ box.line_width = 2;
+ box.line_color_node = ColorNode.from_color( Color.black() );
+ box.fill_color_node = ColorNode.from_color( Color.from_rgb( 0, 1, 0 ) );
+
+ label.add_object( box );
+
+ var view = new glabels.View( label );
+ vbox.pack_start( view );
+ break;
+
+ case "5.1":
+ stdout.printf( "TEST %s.\n", args[1] );
+ var label = new glabels.Label();
+ label.template = libglabels.Db.lookup_template_from_name( "Avery 5523" );
+ label.rotate = true;
+
+ var box = new LabelObjectBox();
+ box.x0 = 36;
+ box.y0 = 36;
+ box.w = 72;
+ box.h = 36;
+ box.line_width = 2;
+ box.line_color_node = ColorNode.from_color( Color.black() );
+ box.fill_color_node = ColorNode.from_color( Color.from_rgb( 0, 1, 0 ) );
+
+ label.add_object( box );
+
+ var view = new glabels.View( label );
+ vbox.pack_start( view );
+ break;
+
+ case "6":
+ stdout.printf( "TEST %s.\n", args[1] );
+ Merge merge = MergeFactory.create_merge( "Text/Comma" );
+ merge.src = "./UH-UTF8.csv";
+ foreach ( MergeRecord record in merge.record_list )
+ {
+ stdout.printf( "RECORD:\n" );
+ foreach ( MergeField field in record.field_list )
+ {
+ stdout.printf( "[ %s ] = %s\n", field.key, field.value );
+ }
+ stdout.printf( "\n" );
+ }
+ break;
+
+ case "6.1":
+ stdout.printf( "TEST %s.\n", args[1] );
+ Merge merge = MergeFactory.create_merge( "Text/Comma/Line1Keys" );
+ merge.src = "./UH-UTF8.csv";
+ foreach ( MergeRecord record in merge.record_list )
+ {
+ stdout.printf( "RECORD:\n" );
+ foreach ( MergeField field in record.field_list )
+ {
+ stdout.printf( "[ %s ] = %s\n", field.key, field.value );
+ }
+ stdout.printf( "\n" );
+ }
+ break;
+
+ case "7":
+ stdout.printf( "TEST %s.\n", args[1] );
+ glabels.Label label = XmlLabel.open_file( "test1.glabels" );
+ var view = new glabels.View( label );
+ vbox.pack_start( view );
+ break;
+
+ case "8":
+ stdout.printf( "TEST %s.\n", args[1] );
+ libglabels.XmlUtil.default_units = libglabels.Units.inch();
+ glabels.Label label = XmlLabel.open_file( "test1.glabels" );
+ XmlLabel.save_file( label, "test1-out.glabels" );
+ break;
+
+ case "9":
+ stdout.printf( "TEST %s.\n", args[1] );
+ Gtk.IconTheme.get_default().append_search_path( Path.build_filename( Config.DATADIR, "glabels-3.0", "icons", null ) );
+ libglabels.XmlUtil.default_units = libglabels.Units.inch();
+ glabels.Label label = XmlLabel.open_file( "test1.glabels" );
+ var window = new Window.from_label( label );
+ window.show_all();
+ break;
+
+ default:
+ stdout.printf( "NO TEST SELECTED.\n" );
+ break;
+ }
+
+
+ win.show_all();
+ Gtk.main();
+
+ return 0;
+}
+
diff --git a/glabels/ui.vala b/glabels/ui.vala
new file mode 100644
index 0000000..7cfefa3
--- /dev/null
+++ b/glabels/ui.vala
@@ -0,0 +1,1391 @@
+/* ui.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+ public class Ui : Gtk.UIManager
+ {
+ private weak Window window;
+ private Prefs prefs;
+
+ private const Gtk.ActionEntry[] entries = {
+
+ /* Menu entries. */
+ { "FileMenu", null, N_("_File") },
+ { "FileRecentsMenu", null, N_("Open Recent _Files") },
+ { "EditMenu", null, N_("_Edit") },
+ { "ViewMenu", null, N_("_View") },
+ { "ViewMainToolBarMenu", null, N_("Customize Main Toolbar") },
+ { "ViewDrawingToolBarMenu", null, N_("Customize Drawing Toolbar") },
+ { "ViewPropertyToolBarMenu", null, N_("Customize Properties Toolbar") },
+ { "ObjectsMenu", null, N_("_Objects") },
+ { "ObjectsCreateMenu", null, N_("_Create") },
+ { "ObjectsOrderMenu", null, N_("_Order") },
+ { "ObjectsRotateFlipMenu", null, N_("_Rotate/Flip") },
+ { "ObjectsAlignMenu", null, N_("_Alignment") },
+ { "ObjectsCenterMenu", null, N_("C_enter") },
+ { "HelpMenu", null, N_("_Help") },
+
+ /* Popup entries. */
+ { "ContextMenu", null, N_("Context Menu") },
+ { "EmptySelectionContextMenu", null, N_("Context Menu") },
+
+ /* File action entries. */
+ { "FileNew",
+ "gtk-new",
+ N_("_New"),
+ "<control>N",
+ N_("Create a new file"),
+ on_file_new },
+
+ { "FileOpen",
+ "gtk-open",
+ N_("_Open..."),
+ "<control>O",
+ N_("Open a file"),
+ on_file_open },
+
+ { "FileSave",
+ "gtk-save",
+ N_("_Save"),
+ "<control>S",
+ N_("Save current file"),
+ on_file_save },
+
+ { "FileSaveAs",
+ "gtk-save",
+ N_("Save _As..."),
+ "<shift><control>S",
+ N_("Save the current file to a different name"),
+ on_file_save_as },
+
+ { "FilePrint",
+ "gtk-print",
+ N_("_Print..."),
+ "<control>P",
+ N_("Print the current file"),
+ on_file_print },
+
+ { "FileProperties",
+ "gtk-properties",
+ N_("Properties..."),
+ null,
+ N_("Modify document properties"),
+ on_file_properties },
+
+ { "FileTemplateDesigner",
+ null,
+ N_("Template _Designer..."),
+ null,
+ N_("Create a custom template"),
+ on_file_template_designer },
+
+ { "FileClose",
+ "gtk-close",
+ N_("_Close"),
+ "<alt>F4",
+ N_("Close the current file"),
+ on_file_close },
+
+ { "FileQuit",
+ "gtk-quit",
+ N_("_Quit"),
+ "<control>Q",
+ N_("Quit the program"),
+ on_file_quit },
+
+
+ /* Edit action entries. */
+ { "EditUndo",
+ "gtk-undo",
+ N_("Undo"),
+ "<control>Z",
+ N_("Undo"),
+ on_edit_undo },
+
+ { "EditRedo",
+ "gtk-redo",
+ N_("Redo"),
+ "<shift><control>Z",
+ N_("Redo"),
+ on_edit_redo },
+
+ { "EditCut",
+ "gtk-cut",
+ N_("Cut"),
+ "<control>X",
+ N_("Cut the selection"),
+ on_edit_cut },
+
+ { "EditCopy",
+ "gtk-copy",
+ N_("Copy"),
+ "<control>C",
+ N_("Copy the selection"),
+ on_edit_copy },
+
+ { "EditPaste",
+ "gtk-paste",
+ N_("Paste"),
+ "<control>V",
+ N_("Paste the clipboard"),
+ on_edit_paste },
+
+ { "EditDelete",
+ null,
+ N_("Delete"),
+ null,
+ N_("Delete the selected objects"),
+ on_edit_delete },
+
+ { "EditSelectAll",
+ null,
+ N_("Select All"),
+ "<control>A",
+ N_("Select all objects"),
+ on_edit_select_all },
+
+ { "EditUnSelectAll",
+ null,
+ N_("Un-select All"),
+ null,
+ N_("Remove all selections"),
+ on_edit_unselect_all },
+
+ { "EditPreferences",
+ "gtk-preferences",
+ N_("Preferences"),
+ null,
+ N_("Configure the application"),
+ on_edit_preferences },
+
+
+ /* View action entries. */
+ { "ViewZoomIn",
+ "gtk-zoom-in",
+ N_("Zoom in"),
+ null,
+ N_("Increase magnification"),
+ on_view_zoomin },
+
+ { "ViewZoomOut",
+ "gtk-zoom-out",
+ N_("Zoom out"),
+ null,
+ N_("Decrease magnification"),
+ on_view_zoomout },
+
+ { "ViewZoom1to1",
+ "gtk-zoom-100",
+ N_("Zoom 1 to 1"),
+ null,
+ N_("Restore scale to 100%"),
+ on_view_zoom1to1 },
+
+ { "ViewZoomToFit",
+ "gtk-zoom-fit",
+ N_("Zoom to fit"),
+ null,
+ N_("Set scale to fit window"),
+ on_view_zoom_to_fit },
+
+
+ /* Objects action entries. */
+ { "ObjectsArrowMode",
+ "glabels-arrow",
+ N_("Select Mode"),
+ null,
+ N_("Select, move and modify objects"),
+ on_objects_arrow_mode },
+
+ { "ObjectsCreateText",
+ "glabels-text",
+ N_("Text"),
+ null,
+ N_("Create text object"),
+ on_objects_create_text },
+
+ { "ObjectsCreateBox",
+ "glabels-box",
+ N_("Box"),
+ null,
+ N_("Create box/rectangle object"),
+ on_objects_create_box },
+
+ { "ObjectsCreateLine",
+ "glabels-line",
+ N_("Line"),
+ null,
+ N_("Create line object"),
+ on_objects_create_line },
+
+ { "ObjectsCreateEllipse",
+ "glabels-ellipse",
+ N_("Ellipse"),
+ null,
+ N_("Create ellipse/circle object"),
+ on_objects_create_ellipse },
+
+ { "ObjectsCreateImage",
+ "glabels-image",
+ N_("Image"),
+ null,
+ N_("Create image object"),
+ on_objects_create_image },
+
+ { "ObjectsCreateBarcode",
+ "glabels-barcode",
+ N_("Barcode"),
+ null,
+ N_("Create barcode object"),
+ on_objects_create_barcode },
+
+ { "ObjectsRaise",
+ "glabels-order-top",
+ N_("Bring to front"),
+ null,
+ N_("Raise object to top"),
+ on_objects_raise },
+
+ { "ObjectsLower",
+ "glabels-order-bottom",
+ N_("Send to back"),
+ null,
+ N_("Lower object to bottom"),
+ on_objects_lower },
+
+ { "ObjectsRotateLeft",
+ "glabels-rotate-left",
+ N_("Rotate left"),
+ null,
+ N_("Rotate object 90 degrees counter-clockwise"),
+ on_objects_rotate_left },
+
+ { "ObjectsRotateRight",
+ "glabels-rotate-right",
+ N_("Rotate right"),
+ null,
+ N_("Rotate object 90 degrees clockwise"),
+ on_objects_rotate_right },
+
+ { "ObjectsFlipHorizontal",
+ "glabels-flip-horiz",
+ N_("Flip horizontally"),
+ null,
+ N_("Flip object horizontally"),
+ on_objects_flip_horiz },
+
+ { "ObjectsFlipVertical",
+ "glabels-flip-vert",
+ N_("Flip vertically"),
+ null,
+ N_("Flip object vertically"),
+ on_objects_flip_vert },
+
+ { "ObjectsAlignLeft",
+ "glabels-align-left",
+ N_("Align left"),
+ null,
+ N_("Align objects to left edges"),
+ on_objects_align_left },
+
+ { "ObjectsAlignHCenter",
+ "glabels-align-hcenter",
+ N_("Align center"),
+ null,
+ N_("Align objects to horizontal centers"),
+ on_objects_align_hcenter },
+
+ { "ObjectsAlignRight",
+ "glabels-align-right",
+ N_("Align right"),
+ null,
+ N_("Align objects to right edges"),
+ on_objects_align_right },
+
+ { "ObjectsAlignTop",
+ "glabels-align-top",
+ N_("Align top"),
+ null,
+ N_("Align objects to top edges"),
+ on_objects_align_top },
+
+ { "ObjectsAlignVCenter",
+ "glabels-align-vcenter",
+ N_("Align middle"),
+ null,
+ N_("Align objects to vertical centers"),
+ on_objects_align_vcenter },
+
+ { "ObjectsAlignBottom",
+ "glabels-align-bottom",
+ N_("Align bottom"),
+ null,
+ N_("Align objects to bottom edges"),
+ on_objects_align_bottom },
+
+ { "ObjectsCenterHorizontal",
+ "glabels-center-horiz",
+ N_("Center horizontally"),
+ null,
+ N_("Center objects to horizontal label center"),
+ on_objects_center_horiz },
+
+ { "ObjectsCenterVertical",
+ "glabels-center-vert",
+ N_("Center vertically"),
+ null,
+ N_("Center objects to vertical label center"),
+ on_objects_center_vert },
+
+ { "ObjectsMergeProperties",
+ "glabels-merge",
+ N_("Merge properties"),
+ null,
+ N_("Edit merge properties"),
+ on_objects_merge_properties },
+
+
+ /* Help actions entries. */
+ { "HelpContents",
+ "gtk-help",
+ N_("Contents"),
+ "F1",
+ N_("Open glabels manual"),
+ on_help_contents },
+
+ { "HelpAbout",
+ "gtk-about",
+ N_("About..."),
+ null,
+ N_("About glabels"),
+ on_help_about }
+
+ };
+
+
+ private const Gtk.ToggleActionEntry[] toggle_entries = {
+
+ { "ViewMainToolBar",
+ null,
+ N_("Main toolbar"),
+ null,
+ N_("Change the visibility of the main toolbar in the current window"),
+ on_view_main_toolbar_toggled,
+ true },
+
+ { "ViewDrawingToolBar",
+ null,
+ N_("Drawing toolbar"),
+ null,
+ N_("Change the visibility of the drawing toolbar in the current window"),
+ on_view_drawing_toolbar_toggled,
+ true },
+
+ { "ViewPropertyToolBar",
+ null,
+ N_("Property toolbar"),
+ null,
+ N_("Change the visibility of the property toolbar in the current window"),
+ on_view_property_bar_toggled,
+ true },
+
+ { "ViewGrid",
+ null,
+ N_("Grid"),
+ null,
+ N_("Change the visibility of the grid in the current window"),
+ on_view_grid_toggled,
+ true },
+
+ { "ViewMarkup",
+ null,
+ N_("Markup"),
+ null,
+ N_("Change the visibility of markup lines in the current window"),
+ on_view_markup_toggled,
+ true }
+
+ };
+
+
+ private static const string ui_info = """
+ <ui>
+
+ <menubar name='MenuBar'>
+ <menu action='FileMenu'>
+ <menuitem action='FileNew' />
+ <menuitem action='FileOpen' />
+ <menuitem action='FileRecentsMenu' />
+ <separator />
+ <menuitem action='FileSave' />
+ <menuitem action='FileSaveAs' />
+ <separator />
+ <menuitem action='FilePrint' />
+ <separator />
+ <menuitem action='FileProperties' />
+ <menuitem action='FileTemplateDesigner' />
+ <separator />
+ <menuitem action='FileClose' />
+ <menuitem action='FileQuit' />
+ </menu>
+ <menu action='EditMenu'>
+ <menuitem action='EditUndo' />
+ <menuitem action='EditRedo' />
+ <separator />
+ <menuitem action='EditCut' />
+ <menuitem action='EditCopy' />
+ <menuitem action='EditPaste' />
+ <menuitem action='EditDelete' />
+ <separator />
+ <menuitem action='EditSelectAll' />
+ <menuitem action='EditUnSelectAll' />
+ <separator />
+ <menuitem action='EditPreferences' />
+ </menu>
+ <menu action='ViewMenu'>
+ <menuitem action='ViewMainToolBar' />
+ <menuitem action='ViewDrawingToolBar' />
+ <menuitem action='ViewPropertyToolBar' />
+ <separator />
+ <menuitem action='ViewGrid' />
+ <menuitem action='ViewMarkup' />
+ <separator />
+ <menuitem action='ViewZoomIn' />
+ <menuitem action='ViewZoomOut' />
+ <menuitem action='ViewZoom1to1' />
+ <menuitem action='ViewZoomToFit' />
+ </menu>
+ <menu action='ObjectsMenu'>
+ <menuitem action='ObjectsArrowMode' always-show-image='true' />
+ <menu action='ObjectsCreateMenu'>
+ <menuitem action='ObjectsCreateText' always-show-image='true' />
+ <menuitem action='ObjectsCreateBox' always-show-image='true' />
+ <menuitem action='ObjectsCreateLine' always-show-image='true' />
+ <menuitem action='ObjectsCreateEllipse' always-show-image='true' />
+ <menuitem action='ObjectsCreateImage' always-show-image='true' />
+ <menuitem action='ObjectsCreateBarcode' always-show-image='true' />
+ </menu>
+ <separator />
+ <menu action='ObjectsOrderMenu'>
+ <menuitem action='ObjectsRaise' always-show-image='true' />
+ <menuitem action='ObjectsLower' always-show-image='true' />
+ </menu>
+ <menu action='ObjectsRotateFlipMenu'>
+ <menuitem action='ObjectsRotateLeft' always-show-image='true' />
+ <menuitem action='ObjectsRotateRight' always-show-image='true' />
+ <menuitem action='ObjectsFlipHorizontal' always-show-image='true' />
+ <menuitem action='ObjectsFlipVertical' always-show-image='true' />
+ </menu>
+ <menu action='ObjectsAlignMenu'>
+ <menuitem action='ObjectsAlignLeft' always-show-image='true' />
+ <menuitem action='ObjectsAlignHCenter' always-show-image='true' />
+ <menuitem action='ObjectsAlignRight' always-show-image='true' />
+ <separator />
+ <menuitem action='ObjectsAlignTop' always-show-image='true' />
+ <menuitem action='ObjectsAlignVCenter' always-show-image='true' />
+ <menuitem action='ObjectsAlignBottom' always-show-image='true' />
+ </menu>
+ <menu action='ObjectsCenterMenu'>
+ <menuitem action='ObjectsCenterHorizontal' always-show-image='true' />
+ <menuitem action='ObjectsCenterVertical' always-show-image='true' />
+ </menu>
+ <separator />
+ <menuitem action='ObjectsMergeProperties' />
+ </menu>
+ <menu action='HelpMenu'>
+ <menuitem action='HelpContents' />
+ <menuitem action='HelpAbout' />
+ </menu>
+ </menubar>
+
+ <toolbar name='MainToolBar'>
+ <toolitem action='FileNew' />
+ <toolitem action='FileOpen' />
+ <toolitem action='FileSave' />
+ <separator />
+ <toolitem action='FilePrint' />
+ <separator />
+ <toolitem action='EditCut' />
+ <toolitem action='EditCopy' />
+ <toolitem action='EditPaste' />
+ </toolbar>
+
+ <toolbar name='DrawingToolBar'>
+ <toolitem action='ObjectsArrowMode' />
+ <separator />
+ <toolitem action='ObjectsCreateText' />
+ <toolitem action='ObjectsCreateBox' />
+ <toolitem action='ObjectsCreateLine' />
+ <toolitem action='ObjectsCreateEllipse' />
+ <toolitem action='ObjectsCreateImage' />
+ <toolitem action='ObjectsCreateBarcode' />
+ <separator />
+ <toolitem action='ViewZoomIn' />
+ <toolitem action='ViewZoomOut' />
+ <toolitem action='ViewZoom1to1' />
+ <toolitem action='ViewZoomToFit' />
+ <separator />
+ <toolitem action='ObjectsMergeProperties' />
+ </toolbar>
+
+ <popup action='ContextMenu'>
+ <menu action='ObjectsOrderMenu'>
+ <menuitem action='ObjectsRaise' always-show-image='true' />
+ <menuitem action='ObjectsLower' always-show-image='true' />
+ </menu>
+ <menu action='ObjectsRotateFlipMenu'>
+ <menuitem action='ObjectsRotateLeft' always-show-image='true' />
+ <menuitem action='ObjectsRotateRight' always-show-image='true' />
+ <menuitem action='ObjectsFlipHorizontal' always-show-image='true' />
+ <menuitem action='ObjectsFlipVertical' always-show-image='true' />
+ </menu>
+ <menu action='ObjectsAlignMenu'>
+ <menuitem action='ObjectsAlignLeft' always-show-image='true' />
+ <menuitem action='ObjectsAlignHCenter' always-show-image='true' />
+ <menuitem action='ObjectsAlignRight' always-show-image='true' />
+ <separator />
+ <menuitem action='ObjectsAlignTop' always-show-image='true' />
+ <menuitem action='ObjectsAlignVCenter' always-show-image='true' />
+ <menuitem action='ObjectsAlignBottom' always-show-image='true' />
+ </menu>
+ <menu action='ObjectsCenterMenu'>
+ <menuitem action='ObjectsCenterHorizontal' always-show-image='true' />
+ <menuitem action='ObjectsCenterVertical' always-show-image='true' />
+ </menu>
+ <separator />
+ <menuitem action='EditCut' />
+ <menuitem action='EditCopy' />
+ <menuitem action='EditPaste' />
+ <menuitem action='EditDelete' />
+ </popup>
+
+ <popup action='EmptySelectionContextMenu'>
+ <menuitem action='EditPaste' />
+ </popup>
+
+ </ui>
+ """;
+
+
+ static string[] doc_verbs = {
+ "/ui/MenuBar/FileMenu/FileProperties",
+ "/ui/MenuBar/FileMenu/FileSave",
+ "/ui/MenuBar/FileMenu/FileSaveAs",
+ "/ui/MenuBar/FileMenu/FilePrint",
+ "/ui/MenuBar/FileMenu/FileClose",
+ "/ui/MenuBar/EditMenu/EditUndo",
+ "/ui/MenuBar/EditMenu/EditRedo",
+ "/ui/MenuBar/EditMenu/EditCut",
+ "/ui/MenuBar/EditMenu/EditCopy",
+ "/ui/MenuBar/EditMenu/EditDelete",
+ "/ui/MenuBar/EditMenu/EditSelectAll",
+ "/ui/MenuBar/EditMenu/EditUnSelectAll",
+ "/ui/MenuBar/ViewMenu/ViewZoomIn",
+ "/ui/MenuBar/ViewMenu/ViewZoomOut",
+ "/ui/MenuBar/ViewMenu/ViewZoom1to1",
+ "/ui/MenuBar/ViewMenu/ViewZoomToFit",
+ "/ui/MenuBar/ViewMenu/ViewGrid",
+ "/ui/MenuBar/ViewMenu/ViewMarkup",
+ "/ui/MenuBar/ObjectsMenu/ObjectsArrowMode",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCreateMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCreateMenu/ObjectsCreateText",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCreateMenu/ObjectsCreateLine",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCreateMenu/ObjectsCreateBox",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCreateMenu/ObjectsCreateEllipse",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCreateMenu/ObjectsCreateImage",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCreateMenu/ObjectsCreateBarcode",
+ "/ui/MenuBar/ObjectsMenu/ObjectsOrderMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsOrderMenu/ObjectsRaise",
+ "/ui/MenuBar/ObjectsMenu/ObjectsOrderMenu/ObjectsLower",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu/ObjectsRotateLeft",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu/ObjectsRotateRight",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu/ObjectsFlipHorizontal",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu/ObjectsFlipVertical",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignLeft",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignRight",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignHCenter",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignTop",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignBottom",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignVCenter",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCenterMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCenterMenu/ObjectsCenterHorizontal",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCenterMenu/ObjectsCenterVertical",
+ "/ui/MenuBar/ObjectsMenu/ObjectsMergeProperties"
+ };
+
+ static string[] doc_modified_verbs = {
+ "/ui/MenuBar/FileMenu/FileSave"
+ };
+
+ static string[] paste_verbs = {
+ "/ui/MenuBar/EditMenu/EditPaste"
+ };
+
+ static string[] selection_verbs = {
+ "/ui/MenuBar/EditMenu/EditCut",
+ "/ui/MenuBar/EditMenu/EditCopy",
+ "/ui/MenuBar/EditMenu/EditDelete",
+ "/ui/MenuBar/EditMenu/EditUnSelectAll",
+ "/ui/MenuBar/ObjectsMenu/ObjectsOrderMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsOrderMenu/ObjectsRaise",
+ "/ui/MenuBar/ObjectsMenu/ObjectsOrderMenu/ObjectsLower",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu/ObjectsRotateLeft",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu/ObjectsRotateRight",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu/ObjectsFlipHorizontal",
+ "/ui/MenuBar/ObjectsMenu/ObjectsRotateFlipMenu/ObjectsFlipVertical",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCenterMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCenterMenu/ObjectsCenterHorizontal",
+ "/ui/MenuBar/ObjectsMenu/ObjectsCenterMenu/ObjectsCenterVertical"
+ };
+
+ static string[] atomic_selection_verbs = {
+ };
+
+ static string[] multi_selection_verbs = {
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignLeft",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignRight",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignHCenter",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignTop",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignBottom",
+ "/ui/MenuBar/ObjectsMenu/ObjectsAlignMenu/ObjectsAlignVCenter"
+ };
+
+
+ public Ui( Window window )
+ {
+ this.window = window;
+ this.prefs = new Prefs();
+
+ connect_proxy.connect( on_connect_proxy );
+ disconnect_proxy.connect( on_disconnect_proxy );
+
+ Gtk.ActionGroup actions = new Gtk.ActionGroup( "Actions" );
+ actions.set_translation_domain( "" );
+ actions.add_actions( entries, this );
+ actions.add_toggle_actions( toggle_entries, this );
+
+ insert_action_group( actions, 0 );
+ window.add_accel_group( get_accel_group() );
+
+ try
+ {
+ add_ui_from_string( ui_info, ui_info.length );
+ }
+ catch ( Error e )
+ {
+ message( "building menus failed: %s", e.message );
+ }
+
+ /* Set the toolbar styles according to prefs */
+ set_app_main_toolbar_style();
+ set_app_drawing_toolbar_style();
+
+ /* Set view grid and markup visibility according to prefs */
+ set_view_style();
+
+ /* TODO: add an Open Recents Submenu */
+
+ set_verb_list_sensitive( doc_verbs, false );
+ set_verb_list_sensitive( paste_verbs, false );
+ }
+
+
+ public void update_all( View view )
+ {
+ Label label = view.label;
+
+ set_verb_list_sensitive( doc_verbs, true );
+
+ set_verb_sensitive( "/ui/MenuBar/EditMenu/EditUndo", label.can_undo() );
+ set_verb_sensitive( "/ui/MenuBar/EditMenu/EditRedo", label.can_redo() );
+
+ set_verb_list_sensitive( doc_modified_verbs, label.modified );
+
+ set_verb_sensitive( "/ui/MenuBar/ViewMenu/ViewZoomIn", !view.is_zoom_max() );
+ set_verb_sensitive( "/ui/MenuBar/ViewMenu/ViewZoomOut", !view.is_zoom_min() );
+
+ set_verb_list_sensitive( selection_verbs, !label.is_selection_empty() );
+
+ set_verb_list_sensitive( atomic_selection_verbs, label.is_selection_atomic() );
+
+ set_verb_list_sensitive( multi_selection_verbs,
+ !label.is_selection_empty() && !label.is_selection_atomic() );
+ }
+
+
+ public void update_modified_verbs( Label label )
+ {
+ set_verb_list_sensitive( doc_modified_verbs, label.modified );
+ }
+
+
+ public void update_selection_verbs( View view,
+ bool has_focus )
+ {
+ if ( has_focus )
+ {
+ set_verb_list_sensitive( selection_verbs, !view.label.is_selection_empty() );
+
+ set_verb_list_sensitive( atomic_selection_verbs,
+ view.label.is_selection_atomic() );
+
+ set_verb_list_sensitive( multi_selection_verbs,
+ !view.label.is_selection_empty() &&
+ !view.label.is_selection_atomic() );
+ }
+ else
+ {
+ set_verb_list_sensitive( selection_verbs, false );
+ set_verb_list_sensitive( atomic_selection_verbs, false );
+ set_verb_list_sensitive( multi_selection_verbs, false );
+ }
+ }
+
+
+ public void update_zoom_verbs( View view )
+ {
+ set_verb_sensitive( "/ui/MenuBar/ViewMenu/ViewZoomIn", !view.is_zoom_max() );
+ set_verb_sensitive( "/ui/MenuBar/ViewMenu/ViewZoomOut", !view.is_zoom_min() );
+ }
+
+
+ public void update_paste_verbs( bool can_paste )
+ {
+ set_verb_list_sensitive( paste_verbs, can_paste );
+ }
+
+
+ public void update_undo_redo_verbs( Label label )
+ {
+ Gtk.MenuItem menu_item;
+ string description;
+ string menu_label;
+
+ menu_item = (Gtk.MenuItem)get_widget( "/MenuBar/EditMenu/EditUndo" );
+ description = label.get_undo_description();
+ menu_label = "%s: %s".printf( _("Undo"), description );
+ menu_item.set_label( menu_label );
+
+ menu_item = (Gtk.MenuItem)get_widget( "/MenuBar/EditMenu/EditRedo" );
+ description = label.get_redo_description();
+ menu_label = "%s: %s".printf( _("Redo"), description );
+ menu_item.set_label( menu_label );
+
+ set_verb_sensitive( "/ui/MenuBar/EditMenu/EditUndo", label.can_undo() );
+ set_verb_sensitive( "/ui/MenuBar/EditMenu/EditRedo", label.can_redo() );
+ }
+
+
+ private void set_app_main_toolbar_style()
+ {
+ /* Updated view menu */
+ set_verb_state( "/ui/ViewMenu/ViewMainToolBar", prefs.main_toolbar_visible );
+
+ Gtk.Toolbar toolbar = (Gtk.Toolbar)get_widget( "/MainToolBar" );
+
+ if ( prefs.main_toolbar_visible )
+ {
+ toolbar.show_all();
+ }
+ else
+ {
+ toolbar.hide();
+ }
+ }
+
+
+ private void set_app_drawing_toolbar_style()
+ {
+ /* Updated view menu */
+ set_verb_state( "/ui/MenuBar/ViewMenu/ViewDrawingToolBar",
+ prefs.drawing_toolbar_visible );
+
+ Gtk.Toolbar toolbar = (Gtk.Toolbar)get_widget( "/DrawingToolBar" );
+
+ toolbar.set_style( Gtk.ToolbarStyle.ICONS );
+
+ if ( prefs.drawing_toolbar_visible )
+ {
+ toolbar.show_all();
+ }
+ else
+ {
+ toolbar.hide();
+ }
+ }
+
+
+ private void set_view_style()
+ {
+ set_verb_state( "/ui/MenuBar/ViewMenu/ViewGrid", prefs.grid_visible );
+
+ set_verb_state( "/ui/MenuBar/ViewMenu/ViewMarkup", prefs.markup_visible );
+ }
+
+
+ private void on_connect_proxy( Gtk.Action action,
+ Gtk.Widget proxy )
+ {
+ if ( proxy is Gtk.MenuItem )
+ {
+ ((Gtk.MenuItem)proxy).select.connect( on_menu_item_select );
+ ((Gtk.MenuItem)proxy).deselect.connect( on_menu_item_deselect );
+ }
+ }
+
+
+ private void on_disconnect_proxy( Gtk.Action action,
+ Gtk.Widget proxy )
+ {
+ if ( proxy is Gtk.MenuItem )
+ {
+ ((Gtk.MenuItem)proxy).select.disconnect( on_menu_item_select );
+ ((Gtk.MenuItem)proxy).deselect.disconnect( on_menu_item_deselect );
+ }
+ }
+
+
+ private void on_menu_item_select( Gtk.MenuItem proxy )
+ {
+ Gtk.Action? action = proxy.get_data( "gtk-action" );
+ return_if_fail( action != null );
+
+ string? message = action.get_data( "tooltip" );
+ if ( message != null )
+ {
+ window.statusbar.push( window.menu_tips_context_id, message );
+ }
+ }
+
+
+ private void on_menu_item_deselect( Gtk.MenuItem proxy )
+ {
+ window.statusbar.pop( window.menu_tips_context_id );
+ }
+
+
+ private void set_verb_sensitive( string cname,
+ bool sensitive )
+ {
+ Gtk.Action action = get_action( cname );
+
+ if ( action != null )
+ {
+ action.set_sensitive( sensitive );
+ }
+ }
+
+
+ private void set_verb_list_sensitive( string[] vlist,
+ bool sensitive )
+ {
+ foreach ( string verb in vlist )
+ {
+ Gtk.Action action = get_action( verb );
+
+ if ( action != null )
+ {
+ action.set_sensitive( sensitive );
+ }
+ }
+ }
+
+
+ private void set_verb_state( string cname,
+ bool state )
+ {
+ Gtk.ToggleAction action = (Gtk.ToggleAction)get_action( cname );
+
+ if ( action != null )
+ {
+ action.set_active( state );
+ }
+ }
+
+
+
+ /*****************************
+ * Actions
+ ****************************/
+
+ private void on_file_new( Gtk.Action action )
+ {
+ File.new_label( window );
+ }
+
+
+ private void on_file_properties( Gtk.Action action )
+ {
+ stdout.printf( "gl_file_properties (GL_VIEW(window->view)->label, window)\n" );
+ }
+
+
+ private void on_file_template_designer( Gtk.Action action )
+ {
+ stdout.printf( """dialog = gl_template_designer_new (GTK_WINDOW(window));
+ gtk_widget_show (dialog);\n""" );
+ }
+
+
+ private void on_file_open( Gtk.Action action )
+ {
+ File.open( window );
+ }
+
+
+ private void on_file_open_recent (Gtk.RecentChooser chooser )
+ {
+ /*
+ GtkRecentInfo *item;
+ gchar *utf8_filename;
+
+ item = gtk_recent_chooser_get_current_item (chooser);
+ if (!item)
+ return;
+
+ utf8_filename = gl_recent_get_utf8_filename (item);
+
+ gl_debug (DEBUG_COMMANDS, "Selected %s\n", utf8_filename);
+ gl_file_open_recent (utf8_filename, window );
+
+ gtk_recent_info_unref (item);
+ */
+ }
+
+
+ private void on_file_save( Gtk.Action action )
+ {
+ File.save( window.view.label, window );
+ }
+
+
+ private void on_file_save_as( Gtk.Action action )
+ {
+ File.save_as( window.view.label, window );
+ }
+
+
+ private void on_file_print( Gtk.Action action )
+ {
+ /*
+ glPrintOpDialog *op;
+ GtkPrintOperationResult result;
+
+ op = gl_print_op_dialog_new (GL_VIEW(window->view)->label);
+
+ if (window->print_settings)
+ {
+ gl_print_op_set_settings (GL_PRINT_OP (op), window->print_settings);
+ }
+
+ result = gtk_print_operation_run (GTK_PRINT_OPERATION (op),
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ GTK_WINDOW (window ),
+ NULL);
+
+ if ( result == GTK_PRINT_OPERATION_RESULT_APPLY )
+ {
+ gl_print_op_free_settings (window->print_settings);
+ window->print_settings = gl_print_op_get_settings (GL_PRINT_OP (op));
+ }
+ */
+ }
+
+
+ private void on_file_close( Gtk.Action action )
+ {
+ File.close( window );
+ }
+
+
+ private void on_file_quit( Gtk.Action action )
+ {
+ File.exit();
+ }
+
+
+ private void on_edit_undo( Gtk.Action action )
+ {
+ stdout.printf( "gl_label_undo (GL_LABEL (GL_VIEW (window->view)->label));\n" );
+ }
+
+
+ private void on_edit_redo( Gtk.Action action )
+ {
+ stdout.printf( "gl_label_redo (GL_LABEL (GL_VIEW (window->view)->label));\n" );
+ }
+
+
+ private void on_edit_cut( Gtk.Action action )
+ {
+ stdout.printf( "gl_label_cut_selection (window->label);\n" );
+ }
+
+
+ private void on_edit_copy( Gtk.Action action )
+ {
+ stdout.printf( "gl_label_copy_selection (window->label);\n" );
+ }
+
+
+ private void on_edit_paste( Gtk.Action action )
+ {
+ stdout.printf( "gl_label_paste (window->label);\n" );
+ }
+
+
+ private void on_edit_delete( Gtk.Action action )
+ {
+ stdout.printf( "gl_label_delete_selection (GL_VIEW(window->view)->label);\n" );
+ }
+
+
+ private void on_edit_select_all( Gtk.Action action )
+ {
+ window.view.label.select_all();
+ }
+
+
+ private void on_edit_unselect_all( Gtk.Action action )
+ {
+ window.view.label.unselect_all();
+ }
+
+
+ private void on_edit_preferences( Gtk.Action action )
+ {
+ /*
+ static GtkWidget *dialog = NULL;
+
+ if (dialog != NULL)
+ {
+ gtk_window_present (GTK_WINDOW (dialog));
+ gtk_window_set_transient_for (GTK_WINDOW (dialog),
+ GTK_WINDOW(window ));
+
+ } else {
+
+ dialog = gl_prefs_dialog_new (GTK_WINDOW(window ));
+
+ g_signal_connect (G_OBJECT (dialog), "destroy",
+ G_CALLBACK (gtk_widget_destroyed), &dialog);
+
+ gtk_widget_show (dialog);
+
+ }
+ */
+ }
+
+
+ private void on_view_main_toolbar_toggled( Gtk.Action action )
+ {
+ Gtk.ToggleAction toggle_action = (Gtk.ToggleAction)action;
+
+ prefs.main_toolbar_visible = toggle_action.active;
+ set_app_main_toolbar_style();
+ }
+
+
+ private void on_view_drawing_toolbar_toggled( Gtk.Action action )
+ {
+ Gtk.ToggleAction toggle_action = (Gtk.ToggleAction)action;
+
+ prefs.drawing_toolbar_visible = toggle_action.active;
+ set_app_drawing_toolbar_style();
+ }
+
+
+ private void on_view_property_bar_toggled( Gtk.Action action )
+ {
+ /*
+ gboolean state;
+
+ state = gtk_toggle_action_get_active (action);
+
+ gl_prefs_model_set_property_toolbar_visible (gl_prefs, state);
+ if (state) {
+ gtk_widget_show (GTK_WIDGET (window->property_bar));
+ } else {
+ gtk_widget_hide (GTK_WIDGET (window->property_bar));
+ }
+ */
+ }
+
+
+ private void on_view_grid_toggled (Gtk.Action action )
+ {
+ Gtk.ToggleAction toggle_action = (Gtk.ToggleAction)action;
+
+ bool state = toggle_action.get_active();
+
+ if ( window.view != null )
+ {
+ window.view.grid_visible = state;
+ }
+ prefs.grid_visible = state;
+ }
+
+
+ private void on_view_markup_toggled (Gtk.Action action )
+ {
+ Gtk.ToggleAction toggle_action = (Gtk.ToggleAction)action;
+
+ bool state = toggle_action.get_active();
+
+ if ( window.view != null )
+ {
+ window.view.markup_visible = state;
+ }
+ prefs.markup_visible = state;
+ }
+
+
+ private void on_view_zoomin( Gtk.Action action )
+ {
+ window.view.zoom_in();
+ }
+
+
+ private void on_view_zoomout( Gtk.Action action )
+ {
+ window.view.zoom_out();
+ }
+
+
+ private void on_view_zoom1to1( Gtk.Action action )
+ {
+ window.view.zoom_1to1();
+ }
+
+
+ private void on_view_zoom_to_fit( Gtk.Action action )
+ {
+ window.view.zoom_to_fit();
+ }
+
+
+ private void on_objects_arrow_mode( Gtk.Action action )
+ {
+ window.view.arrow_mode();
+ }
+
+
+ private void on_objects_create_text( Gtk.Action action )
+ {
+ /*
+ if (window->view != NULL) {
+ gl_view_object_create_mode (GL_VIEW(window->view),
+ GL_LABEL_OBJECT_TEXT);
+ }
+ */
+ }
+
+
+ private void on_objects_create_box( Gtk.Action action )
+ {
+ /*
+ if (window->view != NULL) {
+ gl_view_object_create_mode (GL_VIEW(window->view),
+ GL_LABEL_OBJECT_BOX);
+ }
+ */
+ }
+
+
+ private void on_objects_create_line( Gtk.Action action )
+ {
+ /*
+ if (window->view != NULL) {
+ gl_view_object_create_mode (GL_VIEW(window->view),
+ GL_LABEL_OBJECT_LINE);
+ }
+ */
+ }
+
+
+ private void on_objects_create_ellipse( Gtk.Action action )
+ {
+ /*
+ if (window->view != NULL) {
+ gl_view_object_create_mode (GL_VIEW(window->view),
+ GL_LABEL_OBJECT_ELLIPSE);
+ }
+ */
+ }
+
+
+ private void on_objects_create_image( Gtk.Action action )
+ {
+ /*
+ if (window->view != NULL) {
+ gl_view_object_create_mode (GL_VIEW(window->view),
+ GL_LABEL_OBJECT_IMAGE);
+ }
+ */
+ }
+
+
+ private void on_objects_create_barcode( Gtk.Action action )
+ {
+ /*
+ if (window->view != NULL) {
+ gl_view_object_create_mode (GL_VIEW(window->view),
+ GL_LABEL_OBJECT_BARCODE);
+ }
+ */
+ }
+
+
+ private void on_objects_raise( Gtk.Action action )
+ {
+ window.view.label.raise_selection_to_top();
+ }
+
+
+ private void on_objects_lower( Gtk.Action action )
+ {
+ window.view.label.lower_selection_to_bottom();
+ }
+
+
+ private void on_objects_rotate_left( Gtk.Action action )
+ {
+ window.view.label.rotate_selection_left();
+ }
+
+
+ private void on_objects_rotate_right( Gtk.Action action )
+ {
+ window.view.label.rotate_selection_right();
+ }
+
+
+ private void on_objects_flip_horiz( Gtk.Action action )
+ {
+ window.view.label.flip_selection_horiz();
+ }
+
+
+ private void on_objects_flip_vert( Gtk.Action action )
+ {
+ window.view.label.flip_selection_vert();
+ }
+
+
+ private void on_objects_align_left( Gtk.Action action )
+ {
+ window.view.label.align_selection_left();
+ }
+
+
+ private void on_objects_align_right( Gtk.Action action )
+ {
+ window.view.label.align_selection_right();
+ }
+
+
+ private void on_objects_align_hcenter( Gtk.Action action )
+ {
+ window.view.label.align_selection_hcenter();
+ }
+
+
+ private void on_objects_align_top( Gtk.Action action )
+ {
+ window.view.label.align_selection_top();
+ }
+
+
+ private void on_objects_align_bottom( Gtk.Action action )
+ {
+ window.view.label.align_selection_bottom();
+ }
+
+
+ private void on_objects_align_vcenter( Gtk.Action action )
+ {
+ window.view.label.align_selection_vcenter();
+ }
+
+
+ private void on_objects_center_horiz( Gtk.Action action )
+ {
+ window.view.label.center_selection_horiz();
+ }
+
+
+ private void on_objects_center_vert( Gtk.Action action )
+ {
+ window.view.label.center_selection_vert();
+ }
+
+
+ private void on_objects_merge_properties( Gtk.Action action )
+ {
+ /*
+ if (window->merge_dialog) {
+
+ gtk_window_present (GTK_WINDOW(window->merge_dialog));
+ gtk_window_set_transient_for (GTK_WINDOW (window->merge_dialog),
+ GTK_WINDOW (window ));
+
+ } else {
+
+ window->merge_dialog =
+ g_object_ref (
+ gl_merge_properties_dialog_new (GL_VIEW(window->view)->label,
+ GTK_WINDOW(window )) );
+
+ g_signal_connect (G_OBJECT(window->merge_dialog), "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &window->merge_dialog);
+
+ gtk_widget_show (GTK_WIDGET (window->merge_dialog));
+
+ }
+ */
+ }
+
+
+ private void on_help_contents( Gtk.Action action )
+ {
+ Help.display_contents( window );
+ }
+
+
+ private void on_help_about( Gtk.Action action )
+ {
+ Help.display_about_dialog( window );
+ }
+
+ }
+
+}
+
+
diff --git a/glabels/units_util.vala b/glabels/units_util.vala
new file mode 100644
index 0000000..36edb13
--- /dev/null
+++ b/glabels/units_util.vala
@@ -0,0 +1,96 @@
+/* units_util.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ namespace UnitsUtil
+ {
+
+ /**
+ * Get step size for desired units.
+ */
+ public double get_step_size( Units units )
+ {
+
+ switch (units.id)
+ {
+ case "pt":
+ return 0.1; /* points */
+ case "in":
+ return 0.001; /* inches */
+ case "mm":
+ return 0.1; /* mm */
+ default:
+ warning( "Illegal units" ); /* Should not happen */
+ return 1.0;
+ }
+ }
+
+
+ /**
+ * Get precision for desired units.
+ */
+ public int get_precision( Units units )
+ {
+
+ switch (units.id)
+ {
+ case "pt":
+ return 1; /* points */
+ case "in":
+ return 3; /* inches */
+ case "mm":
+ return 1; /* mm */
+ default:
+ warning( "Illegal units" ); /* Should not happen */
+ return 1;
+ }
+ }
+
+
+ /**
+ * Get grid size for desired units.
+ */
+ public double get_grid_size( Units units )
+ {
+
+ switch (units.id)
+ {
+ case "pt":
+ return 10.0;
+ case "in":
+ return 0.125 * units.points_per_unit;
+ case "mm":
+ return 5 * units.points_per_unit;
+ default:
+ warning( "Illegal units" ); /* Should not happen */
+ return 10;
+ }
+ }
+
+
+ }
+
+}
diff --git a/glabels/view.vala b/glabels/view.vala
new file mode 100644
index 0000000..2bc3ac9
--- /dev/null
+++ b/glabels/view.vala
@@ -0,0 +1,1232 @@
+/* view.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ public class View : Gtk.ScrolledWindow
+ {
+ private const Color BG_COLOR = { 0.8, 0.8, 0.8, 1.0 };
+ private const Color SHADOW_COLOR = { 0.2, 0.2, 0.2, 1.0 };
+ private const Color PAPER_COLOR = { 1.0, 1.0, 1.0, 1.0 };
+ private const Color GRID_COLOR = { 0.753, 0.753, 0.753, 1.0 };
+ private const Color MARKUP_COLOR = { 0.94, 0.39, 0.39, 1.0 };
+ private const Color OUTLINE_COLOR = { 0, 0, 0, 1.0 };
+ private const Color SELECT_LINE_COLOR = { 0, 0, 1, 0.5 };
+ private const Color SELECT_FILL_COLOR = { 0.75, 0.75, 1, 0.5 };
+
+ private const double POINTS_PER_MM = 2.8346456692;
+
+ private const double GRID_LINE_WIDTH_PIXELS = 1;
+ private const double MARKUP_LINE_WIDTH_PIXELS = 1;
+ private const double OUTLINE_WIDTH_PIXELS = 1;
+ private const double SELECT_LINE_WIDTH_PIXELS = 3;
+
+ private const int ZOOMTOFIT_PAD = 16;
+ private const int SHADOW_OFFSET_PIXELS = ZOOMTOFIT_PAD/4;
+
+ private const double zooms[] = { 8, 6, 4, 3, 2, 1.5, 1, 0.75, 0.67, 0.50, 0.33, 0.25, 0.15, 0.10 };
+
+
+ private enum State { IDLE, ARROW_SELECT_REGION, ARROW_MOVE, ARROW_RESIZE, CREATE_DRAG }
+
+ private enum CreateType { BOX, ELLIPSE, LINE, IMAGE, TEXT, BARCODE }
+
+
+ public signal void context_menu_activate( uint event_button, uint event_time );
+ public signal void zoom_changed( double zoom );
+ public signal void pointer_moved( double x, double y );
+ public signal void pointer_exit();
+ public signal void mode_changed();
+
+
+ private Prefs prefs;
+
+ private Gtk.Layout canvas;
+
+ private double grid_spacing;
+ private bool zoom_to_fit_flag;
+ private double home_scale;
+
+ private bool update_scheduled_flag;
+ private double x0;
+ private double y0;
+ private double scale;
+
+ private bool in_object_create_mode;
+
+ private State state;
+
+ /* ARROW_SELECT_REGION state */
+ private bool select_region_visible;
+ private LabelRegion select_region;
+
+ /* ARROW_MOVE state */
+ private double move_last_x;
+ private double move_last_y;
+
+ /* ARROW_RESIZE state */
+ private LabelObject? resize_object;
+ private Handle? resize_handle;
+ private bool resize_honor_aspect;
+
+ /* CREATE_DRAG state */
+ private CreateType create_object_type;
+ private LabelObject? create_object;
+
+
+
+ public Label label { get; private set; }
+
+ public double zoom { get; private set; }
+
+ private bool _markup_visible;
+ public bool markup_visible
+ {
+ get
+ {
+ return _markup_visible;
+ }
+
+ set
+ {
+ _markup_visible = value;
+ update();
+ }
+ }
+
+ private bool _grid_visible;
+ public bool grid_visible
+ {
+ get
+ {
+ return _grid_visible;
+ }
+
+ set
+ {
+ _grid_visible = value;
+ update();
+ }
+ }
+
+
+ public View( Label label )
+ {
+ this.label = label;
+
+ prefs = new Prefs();
+
+ canvas = new Gtk.Layout( null, null );
+ this.set_policy( Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC );
+ this.add( canvas );
+
+ canvas.modify_bg( Gtk.StateType.NORMAL, BG_COLOR.to_gdk_color() );
+ canvas.set_can_focus( true );
+
+ canvas.add_events( Gdk.EventMask.FOCUS_CHANGE_MASK |
+ Gdk.EventMask.ENTER_NOTIFY_MASK |
+ Gdk.EventMask.LEAVE_NOTIFY_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK |
+ Gdk.EventMask.BUTTON_PRESS_MASK |
+ Gdk.EventMask.BUTTON_RELEASE_MASK |
+ Gdk.EventMask.KEY_PRESS_MASK );
+
+ grid_visible = true;
+ grid_spacing = UnitsUtil.get_grid_size( prefs.units );
+ markup_visible = true;
+ in_object_create_mode = false;
+ zoom = 1.0;
+ zoom_to_fit_flag = false;
+ home_scale = determine_home_scale();
+
+ prefs.changed.connect( on_prefs_changed );
+ canvas.draw.connect( on_draw );
+ canvas.realize.connect( on_realize );
+ canvas.size_allocate.connect( on_size_allocate );
+ canvas.screen_changed.connect( on_screen_changed );
+ canvas.focus_in_event.connect( on_focus_in_event );
+ canvas.focus_out_event.connect( on_focus_out_event );
+ canvas.enter_notify_event.connect( on_enter_notify_event );
+ canvas.leave_notify_event.connect( on_leave_notify_event );
+ canvas.motion_notify_event.connect( on_motion_notify_event );
+ canvas.button_press_event.connect( on_button_press_event );
+ canvas.button_release_event.connect( on_button_release_event );
+ canvas.key_press_event.connect( on_key_press_event );
+ label.changed.connect( on_label_changed );
+ label.selection_changed.connect( on_label_changed );
+ label.size_changed.connect( on_label_size_changed );
+ }
+
+
+ private double determine_home_scale()
+ {
+ if ( !canvas.has_screen() )
+ {
+ return 1.0;
+ }
+
+ Gdk.Screen screen = canvas.get_screen();
+
+ double screen_width_pixels = screen.get_width();
+ double screen_width_mm = screen.get_width_mm();
+ double screen_height_pixels = screen.get_height();
+ double screen_height_mm = screen.get_width_mm();
+
+ double x_pixels_per_mm = screen_width_pixels / screen_width_mm;
+ double y_pixels_per_mm = screen_height_pixels / screen_height_mm;
+
+ double scale = ( x_pixels_per_mm + y_pixels_per_mm ) / (2 * POINTS_PER_MM);
+
+ /* Make sure scale is somewhat sane. */
+ if ( (scale < 0.25) || (scale > 4.0) )
+ {
+ scale = 1.0;
+ }
+
+ return scale;
+ }
+
+
+ /**
+ * Zoom in one "notch"
+ */
+ public void zoom_in()
+ {
+ /* find closest standard zoom. */
+
+ /* start with 2nd largest scale */
+ int i_min = 1;
+ double dist_min = Math.fabs( zooms[1] - zoom );
+
+ for ( int i = 2; i < zooms.length; i++ )
+ {
+ double dist = Math.fabs( zooms[i] - zoom );
+ if ( dist < dist_min )
+ {
+ i_min = i;
+ dist_min = dist;
+ }
+ }
+
+ /* zoom in one notch */
+ set_zoom_real( zooms[i_min-1], false );
+ }
+
+
+ /**
+ * Zoom out one "notch"
+ */
+ public void zoom_out()
+ {
+ /* find closest standard zoom. */
+
+ /* start with largest scale, end on 2nd smallest */
+ int i_min = 0;
+ double dist_min = Math.fabs( zooms[0] - zoom );
+
+ for ( int i = 1; i < (zooms.length-1); i++ )
+ {
+ double dist = Math.fabs( zooms[i] - zoom );
+ if ( dist < dist_min )
+ {
+ i_min = i;
+ dist_min = dist;
+ }
+ }
+
+ /* zoom in one notch */
+ set_zoom_real( zooms[i_min+1], false );
+ }
+
+
+ /**
+ * Set zoom to 1 to 1
+ */
+ public void zoom_1to1()
+ {
+ set_zoom_real( 1.0, false );
+ }
+
+
+ /**
+ * Zoom to fit
+ */
+ public void zoom_to_fit()
+ {
+ if ( !has_screen() )
+ {
+ /* Delay until realized. */
+ zoom_to_fit_flag = true;
+ return;
+ }
+
+ int w_view = get_allocated_width();
+ int h_view = get_allocated_height();
+
+ double w_label, h_label;
+ label.get_size( out w_label, out h_label );
+
+ /* Calculate best scale */
+ double x_scale = (double)( w_view - ZOOMTOFIT_PAD ) / w_label;
+ double y_scale = (double)( h_view - ZOOMTOFIT_PAD ) / h_label;
+ double scale = double.min( x_scale, y_scale );
+
+ /* Limit */
+ scale = double.min( scale, zooms[0]*home_scale );
+ scale = double.max( scale, zooms[zooms.length - 1]*home_scale );
+
+ set_zoom_real( scale/home_scale, true );
+ }
+
+
+ private void set_zoom_real( double zoom,
+ bool zoom_to_fit_flag )
+ {
+ /* Limit, if needed. */
+ zoom = double.min( zoom, zooms[0] );
+ zoom = double.max( zoom, zooms[ zooms.length-1 ] );
+
+ if ( this.zoom != zoom )
+ {
+ this.zoom = zoom;
+ this.zoom_to_fit_flag = zoom_to_fit_flag;
+
+ update();
+
+ zoom_changed( zoom );
+ }
+ }
+
+
+ public bool is_zoom_max()
+ {
+ return ( zoom >= zooms[0] );
+ }
+
+
+ public bool is_zoom_min()
+ {
+ return ( zoom <= zooms[zooms.length-1] );
+ }
+
+
+ public void arrow_mode()
+ {
+ Gdk.Window window = canvas.get_window();
+
+ Gdk.Cursor cursor = new Gdk.Cursor( Gdk.CursorType.LEFT_PTR );
+ window.set_cursor( cursor );
+
+ in_object_create_mode = false;
+ state = State.IDLE;
+ }
+
+
+ private void on_prefs_changed()
+ {
+ grid_spacing = UnitsUtil.get_grid_size( prefs.units );
+ update();
+ }
+
+
+ private void update()
+ {
+ var window = canvas.get_window();
+ if (null == window)
+ {
+ return;
+ }
+
+ if ( !update_scheduled_flag )
+ {
+ update_scheduled_flag = true;
+
+ Gdk.Rectangle rect = Gdk.Rectangle();
+
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = canvas.get_allocated_width();
+ rect.height = canvas.get_allocated_height();
+
+ window.invalidate_rect( rect, true );
+ }
+ }
+
+
+ private bool on_draw( Cairo.Context cr )
+ {
+ update_scheduled_flag = false;
+
+ Gdk.Window bin_window = canvas.get_bin_window();
+ Cairo.Context bin_cr = Gdk.cairo_create( bin_window );
+
+ draw_layers( bin_window, bin_cr );
+
+ return false;
+ }
+
+
+ private void draw_layers( Gdk.Window window,
+ Cairo.Context cr )
+ {
+ this.scale = zoom * home_scale;
+
+ double w, h;
+ label.get_size( out w, out h );
+
+ canvas.set_size( (int)(w*scale + 8), (int)(h*scale + 8) );
+
+ int canvas_w = window.get_width();
+ int canvas_h = window.get_height();
+
+ this.x0 = (canvas_w/scale - w) / 2;
+ this.y0 = (canvas_h/scale - h) / 2;
+
+ cr.save();
+
+ cr.scale( scale, scale );
+ cr.translate( x0, y0 );
+
+ draw_bg_layer( cr );
+ draw_grid_layer( cr );
+ draw_markup_layer( cr );
+ draw_objects_layer( cr );
+ draw_fg_layer( cr );
+ draw_highlight_layer( cr );
+ draw_select_region_layer( cr );
+
+ cr.restore();
+ }
+
+
+ private void set_frame_path( Cairo.Context cr,
+ TemplateFrame frame )
+ {
+ cr.save();
+
+ if (label.rotate)
+ {
+ double w, h;
+ frame.get_size( out w, out h );
+
+ /* Canvas coordinates are relative to content, so we must rotate the frame. */
+ cr.rotate( -Math.PI / 2 );
+ cr.translate( -w, 0 );
+ }
+
+ frame.cairo_path( cr, false );
+
+ cr.restore();
+ }
+
+ private void draw_bg_layer( Cairo.Context cr )
+ {
+ TemplateFrame frame = label.template.frames.first().data;
+
+ double w, h;
+ frame.get_size( out w, out h );
+
+ cr.save();
+ cr.translate( SHADOW_OFFSET_PIXELS/scale, SHADOW_OFFSET_PIXELS/scale );
+
+ set_frame_path( cr, frame );
+ cr.set_source_rgb( SHADOW_COLOR.r, SHADOW_COLOR.g, SHADOW_COLOR.b );
+ cr.set_fill_rule( Cairo.FillRule.EVEN_ODD );
+ cr.fill();
+ cr.restore();
+
+ cr.save();
+ set_frame_path( cr, frame );
+ cr.set_source_rgb( PAPER_COLOR.r, PAPER_COLOR.g, PAPER_COLOR.b );
+ cr.set_fill_rule( Cairo.FillRule.EVEN_ODD );
+ cr.fill();
+ cr.restore();
+ }
+
+
+ private void draw_grid_layer( Cairo.Context cr )
+ {
+ if ( grid_visible )
+ {
+ TemplateFrame frame = label.template.frames.first().data;
+
+ double w, h;
+ label.get_size( out w, out h );
+
+ double x0, y0;
+ if ( frame is TemplateFrameRect )
+ {
+ x0 = grid_spacing;
+ y0 = grid_spacing;
+ }
+ else
+ {
+ /* round labels, adjust grid to line up with center of label. */
+ x0 = Math.fmod( w/2, grid_spacing );
+ y0 = Math.fmod( h/2, grid_spacing );
+ }
+
+ cr.save();
+
+ set_frame_path( cr, frame );
+ cr.clip();
+
+ cr.set_antialias( Cairo.Antialias.NONE );
+ cr.set_line_width( GRID_LINE_WIDTH_PIXELS/scale );
+ cr.set_source_rgb( GRID_COLOR.r, GRID_COLOR.g, GRID_COLOR.b );
+
+ for ( double x = x0; x < w; x += grid_spacing )
+ {
+ cr.move_to( x, 0 );
+ cr.line_to( x, h );
+ cr.stroke();
+ }
+
+ for ( double y = y0; y < h; y += grid_spacing )
+ {
+ cr.move_to( 0, y );
+ cr.line_to( w, y );
+ cr.stroke();
+ }
+
+ cr.restore();
+ }
+ }
+
+
+ private void draw_markup_layer( Cairo.Context cr )
+ {
+ if ( markup_visible )
+ {
+ TemplateFrame frame = label.template.frames.first().data;
+
+ cr.save();
+
+ if (label.rotate)
+ {
+ double w, h;
+ frame.get_size( out w, out h );
+
+ /* Markup is relative to the normal orientation of label. */
+ cr.rotate( -Math.PI / 2 );
+ cr.translate( -w, 0 );
+ }
+
+ cr.set_line_width( MARKUP_LINE_WIDTH_PIXELS / scale );
+ cr.set_source_rgb( MARKUP_COLOR.r, MARKUP_COLOR.g, MARKUP_COLOR.b );
+
+ foreach ( TemplateMarkup markup in frame.markups )
+ {
+ markup.cairo_path( cr, frame );
+ cr.stroke();
+ }
+
+ cr.restore();
+ }
+ }
+
+
+ private void draw_objects_layer( Cairo.Context cr )
+ {
+ label.draw( cr, true, null );
+ }
+
+
+ private void draw_fg_layer( Cairo.Context cr )
+ {
+ TemplateFrame frame = label.template.frames.first().data;
+
+ set_frame_path( cr, frame );
+
+ cr.set_line_width( OUTLINE_WIDTH_PIXELS/scale );
+ cr.set_source_rgb( OUTLINE_COLOR.r, OUTLINE_COLOR.g, OUTLINE_COLOR.b );
+ cr.stroke();
+ }
+
+
+ private void draw_highlight_layer( Cairo.Context cr )
+ {
+ cr.save();
+ cr.set_antialias( Cairo.Antialias.NONE );
+
+ foreach ( LabelObject object in label.object_list )
+ {
+ if ( object.is_selected() )
+ {
+ object.draw_selection_highlight( cr );
+ }
+ }
+
+ cr.restore();
+ }
+
+
+ private void draw_select_region_layer( Cairo.Context cr )
+ {
+ if ( select_region_visible )
+ {
+ double x1 = double.min( select_region.x1, select_region.x2 );
+ double y1 = double.min( select_region.y1, select_region.y2 );
+ double w = Math.fabs( select_region.x2 - select_region.x1 );
+ double h = Math.fabs( select_region.y2 - select_region.y1 );
+
+ cr.save();
+
+ cr.set_antialias( Cairo.Antialias.NONE );
+
+ cr.rectangle( x1, y1, w, h );
+
+ cr.set_source_rgba( SELECT_FILL_COLOR.r, SELECT_FILL_COLOR.g, SELECT_FILL_COLOR.b,
+ SELECT_FILL_COLOR.a );
+ cr.fill_preserve();
+
+ cr.set_line_width( SELECT_LINE_WIDTH_PIXELS/scale );
+ cr.set_source_rgba( SELECT_LINE_COLOR.r, SELECT_LINE_COLOR.g, SELECT_LINE_COLOR.b,
+ SELECT_LINE_COLOR.a );
+ cr.stroke();
+
+ cr.restore();
+ }
+ }
+
+
+ private void on_realize()
+ {
+ if ( zoom_to_fit_flag )
+ {
+ /* Maintain best fit zoom */
+ zoom_to_fit();
+ }
+ }
+
+
+ private void on_size_allocate( Gtk.Allocation allocation )
+ {
+ Gtk.Adjustment hadjustment = canvas.get_hadjustment();
+ Gtk.Adjustment vadjustment = canvas.get_vadjustment();
+
+ hadjustment.set_page_size( allocation.width );
+ hadjustment.set_page_increment( allocation.width / 2 );
+
+ vadjustment.set_page_size( allocation.height );
+ vadjustment.set_page_increment( allocation.height / 2 );
+
+ hadjustment.changed();
+ vadjustment.changed();
+
+ if ( zoom_to_fit_flag )
+ {
+ /* Maintain best fit zoom */
+ zoom_to_fit();
+ }
+ }
+
+
+ private void on_screen_changed()
+ {
+ if ( canvas.has_screen() )
+ {
+
+ home_scale = determine_home_scale();
+
+ if ( zoom_to_fit_flag )
+ {
+ /* Maintain best fit zoom */
+ zoom_to_fit();
+ }
+ }
+ }
+
+
+ private void on_label_changed()
+ {
+ update();
+ }
+
+
+ private void on_label_size_changed()
+ {
+ Gtk.Adjustment hadjustment = canvas.get_hadjustment();
+ Gtk.Adjustment vadjustment = canvas.get_vadjustment();
+
+ hadjustment.changed();
+ vadjustment.changed();
+
+ update();
+ }
+
+
+ private bool on_focus_in_event( Gdk.EventFocus event )
+ {
+ return false;
+ }
+
+
+ private bool on_focus_out_event( Gdk.EventFocus event )
+ {
+ return false;
+ }
+
+
+ private bool on_enter_notify_event( Gdk.EventCrossing event )
+ {
+ return false;
+ }
+
+
+ private bool on_leave_notify_event( Gdk.EventCrossing event )
+ {
+ pointer_exit();
+ return false;
+ }
+
+
+ private bool on_motion_notify_event( Gdk.EventMotion event )
+ {
+ bool return_value = false;
+
+ Gdk.Window bin_window = canvas.get_bin_window();
+ Gdk.Window window = canvas.get_window();
+
+ Cairo.Context cr = Gdk.cairo_create( bin_window );
+
+ /*
+ * Translate to label coordinates
+ */
+ cr.scale( scale, scale );
+ cr.translate( x0, y0 );
+
+ double x = event.x;
+ double y = event.y;
+ cr.device_to_user( ref x, ref y );
+
+ /*
+ * Emit signal regardless of mode
+ */
+ pointer_moved( x, y );
+
+ /*
+ * Handle event as appropriate for mode
+ */
+ if ( !in_object_create_mode )
+ {
+ switch (state)
+ {
+
+ case State.IDLE:
+ Gdk.Cursor cursor;
+ Handle? handle;
+ if ( label.is_selection_atomic() &&
+ (handle = label.handle_at( cr, event.x, event.y )) != null )
+ {
+ cursor = new Gdk.Cursor( Gdk.CursorType.CROSSHAIR );
+ }
+ else if ( label.object_at( cr, event.x, event.y) != null )
+ {
+ cursor = new Gdk.Cursor( Gdk.CursorType.FLEUR );
+ }
+ else
+ {
+ cursor = new Gdk.Cursor( Gdk.CursorType.LEFT_PTR );
+ }
+ window.set_cursor( cursor );
+ break;
+
+ case State.ARROW_SELECT_REGION:
+ select_region.x2 = x;
+ select_region.y2 = y;
+ update();
+ break;
+
+ case State.ARROW_MOVE:
+ label.move_selection( (x - move_last_x), (y - move_last_y) );
+ move_last_x = x;
+ move_last_y = y;
+ break;
+
+ case State.ARROW_RESIZE:
+ handle_resize_motion( cr, event.x, event.y );
+ break;
+
+ default:
+ warning( "Invalid arrow state." ); /*Should not happen!*/
+ break;
+
+ }
+ return_value = true;
+
+ }
+ else
+ {
+
+ if ( state != State.IDLE )
+ {
+ switch ( create_object_type )
+ {
+ case CreateType.BOX:
+ /* TODO */
+ break;
+ case CreateType.ELLIPSE:
+ /* TODO */
+ break;
+ case CreateType.LINE:
+ /* TODO */
+ break;
+ case CreateType.IMAGE:
+ /* TODO */
+ break;
+ case CreateType.TEXT:
+ /* TODO */
+ break;
+ case CreateType.BARCODE:
+ /* TODO */
+ break;
+ default:
+ warning( "Invalid create type." ); /* Should not happen! */
+ break;
+ }
+ }
+
+ }
+
+ return return_value;
+ }
+
+
+ private bool on_button_press_event( Gdk.EventButton event )
+ {
+ bool return_value = false;
+
+ canvas.grab_focus();
+
+ Gdk.Window bin_window = canvas.get_bin_window();
+
+ Cairo.Context cr = Gdk.cairo_create( bin_window );
+
+ /*
+ * Translate to label coordinates
+ */
+ cr.scale( scale, scale );
+ cr.translate( x0, y0 );
+
+ double x = event.x;
+ double y = event.y;
+ cr.device_to_user( ref x, ref y );
+
+ switch (event.button)
+ {
+
+ case 1:
+ /*
+ * Handle event as appropriate for mode
+ */
+ if ( !in_object_create_mode )
+ {
+ LabelObject object;
+ Handle? handle = null;
+ if ( label.is_selection_atomic() &&
+ (handle = label.handle_at( cr, event.x, event.y )) != null )
+ {
+ resize_object = handle.owner;
+ resize_handle = handle;
+ resize_honor_aspect = (event.state & Gdk.ModifierType.CONTROL_MASK) != 0;
+
+ state = State.ARROW_RESIZE;
+ }
+ else if ( (object = label.object_at( cr, event.x, event.y)) != null )
+ {
+ if ( (event.state & Gdk.ModifierType.CONTROL_MASK) != 0 )
+ {
+ if ( object.is_selected() )
+ {
+ /* Un-selecting a selected item */
+ label.unselect_object( object );
+ }
+ else
+ {
+ /* Add to current selection */
+ label.select_object( object );
+ }
+ }
+ else
+ {
+ if ( !object.is_selected() )
+ {
+ /* remove any selections before adding */
+ label.unselect_all();
+ /* Add to current selection */
+ label.select_object( object );
+ }
+ }
+
+ move_last_x = x;
+ move_last_y = y;
+
+ state = State.ARROW_MOVE;
+ }
+ else
+ {
+ if ( (event.state & Gdk.ModifierType.CONTROL_MASK) == 0 )
+ {
+ label.unselect_all();
+ }
+
+ select_region_visible = true;
+ select_region.x1 = x;
+ select_region.y1 = y;
+ select_region.x2 = x;
+ select_region.y2 = y;
+
+ state = State.ARROW_SELECT_REGION;
+ }
+
+ return_value = true;
+ }
+ else
+ {
+
+ if ( state != State.IDLE )
+ {
+ switch ( create_object_type )
+ {
+ case CreateType.BOX:
+ /* TODO */
+ break;
+ case CreateType.ELLIPSE:
+ /* TODO */
+ break;
+ case CreateType.LINE:
+ /* TODO */
+ break;
+ case CreateType.IMAGE:
+ /* TODO */
+ break;
+ case CreateType.TEXT:
+ /* TODO */
+ break;
+ case CreateType.BARCODE:
+ /* TODO */
+ break;
+ default:
+ warning( "Invalid create type." ); /* Should not happen! */
+ break;
+ }
+
+ state = State.CREATE_DRAG;
+
+ return_value = true;
+ }
+
+ }
+ event.device.grab( bin_window, Gdk.GrabOwnership.APPLICATION, false,
+ (Gdk.EventMask.BUTTON1_MOTION_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK),
+ null, event.time );
+ break;
+
+ case 3:
+ context_menu_activate( event.button, event.time );
+ return_value = true;
+ break;
+
+ default:
+ break;
+
+ }
+
+ return return_value;
+ }
+
+
+ private bool on_button_release_event( Gdk.EventButton event )
+ {
+ bool return_value = false;
+
+ canvas.grab_focus();
+
+ Gdk.Window bin_window = canvas.get_bin_window();
+ Gdk.Window window = canvas.get_window();
+
+ Cairo.Context cr = Gdk.cairo_create( bin_window );
+
+ /*
+ * Translate to label coordinates
+ */
+ cr.scale( scale, scale );
+ cr.translate( x0, y0 );
+
+ double x = event.x;
+ double y = event.y;
+ cr.device_to_user( ref x, ref y );
+
+ switch (event.button)
+ {
+
+ case 1:
+ event.device.ungrab( event.time );
+ /*
+ * Handle event as appropriate for mode
+ */
+ if ( !in_object_create_mode )
+ {
+
+ switch (state)
+ {
+ case State.ARROW_RESIZE:
+ resize_object = null;
+ state = State.IDLE;
+ break;
+
+ case State.ARROW_SELECT_REGION:
+ select_region_visible = false;
+ select_region.x2 = x;
+ select_region.y2 = y;
+ label.select_region( select_region );
+ update();
+ state = State.IDLE;
+ break;
+
+ default:
+ state = State.IDLE;
+ break;
+ }
+
+ return_value = true;
+
+ }
+ else
+ {
+
+ switch ( create_object_type )
+ {
+ case CreateType.BOX:
+ /* TODO */
+ break;
+ case CreateType.ELLIPSE:
+ /* TODO */
+ break;
+ case CreateType.LINE:
+ /* TODO */
+ break;
+ case CreateType.IMAGE:
+ /* TODO */
+ break;
+ case CreateType.TEXT:
+ /* TODO */
+ break;
+ case CreateType.BARCODE:
+ /* TODO */
+ break;
+ default:
+ warning( "Invalid create type." ); /* Should not happen! */
+ break;
+ }
+
+ Gdk.Cursor cursor = new Gdk.Cursor( Gdk.CursorType.LEFT_PTR );
+ window.set_cursor( cursor );
+
+ label.select_object( create_object );
+
+ in_object_create_mode = false;
+ state = State.IDLE;
+
+ }
+ break;
+
+ default:
+ break;
+
+ }
+
+ return return_value;
+ }
+
+
+ private bool on_key_press_event( Gdk.EventKey event )
+ {
+ if ( !in_object_create_mode && (state == State.IDLE) )
+ {
+ switch (event.keyval)
+ {
+
+ case Gdk.Key.Left:
+ case Gdk.Key.KP_Left:
+ label.move_selection( (-1 / zoom), 0 );
+ break;
+
+ case Gdk.Key.Up:
+ case Gdk.Key.KP_Up:
+ label.move_selection( 0, (-1 / zoom) );
+ break;
+
+ case Gdk.Key.Right:
+ case Gdk.Key.KP_Right:
+ label.move_selection( (1 / zoom), 0 );
+ break;
+
+ case Gdk.Key.Down:
+ case Gdk.Key.KP_Down:
+ label.move_selection( 0, (1 / zoom) );
+ break;
+
+ case Gdk.Key.Delete:
+ case Gdk.Key.KP_Delete:
+ label.delete_selection();
+ Gdk.Window window = canvas.get_window();
+ Gdk.Cursor cursor = new Gdk.Cursor( Gdk.CursorType.LEFT_PTR );
+ window.set_cursor( cursor );
+ break;
+
+ default:
+ return false;
+
+ }
+ }
+
+ return true;
+ }
+
+
+ private void handle_resize_motion( Cairo.Context cr,
+ double x_pixels,
+ double y_pixels )
+ {
+ cr.save();
+
+ /*
+ * Change to item relative coordinates
+ */
+ cr.translate( resize_object.x0, resize_object.y0 );
+ cr.transform( resize_object.matrix );
+
+ /*
+ * Initialize origin and 2 corners in object relative coordinates.
+ */
+ double x0 = 0.0;
+ double y0 = 0.0;
+
+ double x1 = 0.0;
+ double y1 = 0.0;
+
+ double x2 = resize_object.w;
+ double y2 = resize_object.h;
+
+ /*
+ * Translate x,y into object relative coordinates.
+ */
+ double x = x_pixels;
+ double y = y_pixels;
+ cr.device_to_user( ref x, ref y );
+
+ /*
+ * Calculate new size
+ */
+ double w, h;
+ if ( resize_handle is HandleNorthWest )
+ {
+ w = double.max( x2 - x, 0 );
+ h = double.max( y2 - y, 0 );
+ }
+ else if ( resize_handle is HandleNorth )
+ {
+ w = x2 - x1;
+ h = double.max( y2 - y, 0 );
+ }
+ else if ( resize_handle is HandleNorthEast )
+ {
+ w = double.max( x - x1, 0 );
+ h = double.max( y2 - y, 0 );
+ }
+ else if ( resize_handle is HandleEast )
+ {
+ w = double.max( x - x1, 0 );
+ h = y2 - y1;
+ }
+ else if ( resize_handle is HandleSouthEast )
+ {
+ w = double.max( x - x1, 0 );
+ h = double.max( y - y1, 0 );
+ }
+ else if ( resize_handle is HandleSouth )
+ {
+ w = x2 - x1;
+ h = double.max( y - y1, 0 );
+ }
+ else if ( resize_handle is HandleSouthWest )
+ {
+ w = double.max( x2 - x, 0 );
+ h = double.max( y - y1, 0 );
+ }
+ else if ( resize_handle is HandleWest )
+ {
+ w = double.max( x2 - x, 0 );
+ h = y2 - y1;
+ }
+ else
+ {
+ assert_not_reached();
+ }
+
+ /*
+ * Set size
+ */
+ if ( resize_honor_aspect )
+ {
+ resize_object.set_size_honor_aspect( w, h );
+ }
+ else
+ {
+ resize_object.set_size( w, h );
+ }
+
+ /*
+ * Adjust origin, if needed.
+ */
+ if ( resize_handle is HandleNorthWest )
+ {
+ x0 += x2 - resize_object.w;
+ y0 += y2 - resize_object.h;
+ }
+ else if ( (resize_handle is HandleNorth) || (resize_handle is HandleNorthEast) )
+ {
+ y0 += y2 - resize_object.h;
+ }
+ else if ( (resize_handle is HandleWest) || (resize_handle is HandleSouthWest) )
+ {
+ x0 += x2 - resize_object.w;
+ }
+
+ /*
+ * Put new origin back into world coordinates and set.
+ */
+ cr.user_to_device( ref x0, ref y0 );
+ cr.restore();
+ cr.device_to_user( ref x0, ref y0 );
+ resize_object.set_position( x0, y0 );
+ }
+
+
+ }
+
+}
diff --git a/glabels/window.vala b/glabels/window.vala
new file mode 100644
index 0000000..d73313e
--- /dev/null
+++ b/glabels/window.vala
@@ -0,0 +1,279 @@
+/* window.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ public class Window : Gtk.Window
+ {
+ private const int DEFAULT_WINDOW_WIDTH = 788;
+ private const int DEFAULT_WINDOW_HEIGHT = 600;
+
+ private const int ZOOM_INFO_WIDTH = 75;
+ private const int CURSOR_INFO_WIDTH = 150;
+
+
+ public static unowned List<weak Window> window_list { get; private set; }
+
+
+ private Gtk.HBox content_hbox;
+
+ public View? view { get; private set; }
+
+ public Gtk.Statusbar statusbar { get; private set; }
+ private Gtk.Label zoom_info_label;
+ private Gtk.Label cursor_info_label;
+ public uint menu_tips_context_id { get; private set; }
+
+ private Gtk.Menu context_menu;
+ private Gtk.Menu empty_selection_context_menu;
+
+ private Prefs prefs;
+ private Ui ui;
+
+
+ public Window()
+ {
+ prefs = new Prefs();
+
+ Gtk.VBox vbox1 = new Gtk.VBox( false, 0 );
+ add( vbox1 );
+
+ ui = new Ui( this );
+ vbox1.pack_start( ui.get_widget( "/MenuBar" ), false, false, 0 );
+ vbox1.pack_start( ui.get_widget( "/MainToolBar" ), false, false, 0 );
+ vbox1.pack_start( ui.get_widget( "/DrawingToolBar" ), false, false, 0 );
+
+ content_hbox = new Gtk.HBox( false, 0 );
+ vbox1.pack_start( content_hbox, true, true, 0 );
+
+ Gtk.HBox status_hbox = new Gtk.HBox( false, 0 );
+ vbox1.pack_start( status_hbox, false, false, 0 );
+
+ statusbar = new Gtk.Statusbar();
+ status_hbox.pack_start( statusbar, true, true, 0 );
+
+ zoom_info_label = new Gtk.Label( null );
+ zoom_info_label.set_size_request( ZOOM_INFO_WIDTH, -1 );
+
+ Gtk.Frame zoom_info_frame = new Gtk.Frame( null );
+ zoom_info_frame.set_shadow_type( Gtk.ShadowType.IN );
+ zoom_info_frame.add( zoom_info_label );
+ zoom_info_frame.show_all();
+ status_hbox.pack_end( zoom_info_frame, false, false, 0 );
+
+ cursor_info_label = new Gtk.Label( null );
+ cursor_info_label.set_size_request( CURSOR_INFO_WIDTH, -1 );
+
+ Gtk.Frame cursor_info_frame = new Gtk.Frame( null );
+ cursor_info_frame.set_shadow_type( Gtk.ShadowType.IN );
+ cursor_info_frame.add( cursor_info_label );
+ cursor_info_frame.show_all();
+ status_hbox.pack_end( cursor_info_frame, false, false, 0 );
+
+ this.set_default_size( DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT );
+
+ this.delete_event.connect( on_delete_event );
+
+ menu_tips_context_id = statusbar.get_context_id( "menu_tips" );
+
+ context_menu = (Gtk.Menu)ui.get_widget( "/ContextMenu" );
+ empty_selection_context_menu = (Gtk.Menu)ui.get_widget( "/EmptySelectionContextMenu" );
+
+ window_list.append( this );
+ }
+
+
+ public Window.from_label( Label label )
+ {
+ this();
+ set_label( label );
+ }
+
+
+ public Window.from_file( string filename )
+ {
+ this();
+
+ string abs_filename = FileUtil.make_absolute( filename );
+ Label label = XmlLabel.open_file( abs_filename );
+
+ set_label( label );
+ }
+
+
+ ~Window()
+ {
+ window_list.remove( this );
+
+ if ( window_list.length() == 0 )
+ {
+ Gtk.main_quit();
+ }
+ }
+
+
+ public bool is_empty()
+ {
+ return view == null;
+ }
+
+
+ public void set_label( Label label )
+ {
+ label.modified = false;
+ set_window_title( label );
+
+/*
+ MiniPreview mini_preview = new MiniPreview( 200, 200 );
+ mini_preview.set_label( label );
+ content_hbox.pack_start( mini_preview, true, true, 0 );
+*/
+
+ view = new View( label );
+ content_hbox.pack_start( view, true, true, 0 );
+ view.show_all();
+
+ view.zoom_to_fit();
+
+ view.grid_visible = prefs.grid_visible;
+ view.markup_visible = prefs.markup_visible;
+
+ ui.update_all( view );
+
+ string zoom_string = "%3.0f%%".printf( 100*view.zoom );
+ zoom_info_label.set_text( zoom_string );
+
+ label.name_changed.connect( on_name_changed );
+ label.modified_changed.connect( on_modified_changed );
+ label.selection_changed.connect( on_selection_changed );
+ label.changed.connect( on_label_changed );
+ view.context_menu_activate.connect( on_context_menu_activate );
+ view.zoom_changed.connect( on_zoom_changed );
+ view.pointer_moved.connect( on_pointer_moved );
+ view.pointer_exit.connect( on_pointer_exit );
+ this.set_focus.connect( on_set_focus );
+
+ /* TODO: clipboard changed. */
+ /* TODO: set copy/paste sensitivity. */
+ }
+
+
+ private void set_window_title( Label label )
+ {
+ string name = label.get_short_name();
+
+ if ( label.modified )
+ {
+ set_title( "%s %s - gLabels".printf( name, _("(modified)") ) );
+ }
+ else
+ {
+ set_title( "%s - gLabels".printf( name ) );
+ }
+ }
+
+
+ private bool on_delete_event()
+ {
+ File.close( this );
+
+ return true;
+ }
+
+
+ private void on_selection_changed()
+ {
+ ui.update_selection_verbs( view, true );
+ }
+
+
+ private void on_context_menu_activate( uint button, uint activate_time )
+ {
+ if ( view.label.is_selection_empty() )
+ {
+ empty_selection_context_menu.popup( null, null, null, button, activate_time );
+ }
+ else
+ {
+ context_menu.popup( null, null, null, button, activate_time );
+ }
+ }
+
+
+ private void on_zoom_changed()
+ {
+ zoom_info_label.set_text( "%3.0f%%".printf( 100*view.zoom ) );
+
+ ui.update_zoom_verbs( view );
+ }
+
+
+ private void on_pointer_moved( double x,
+ double y )
+ {
+ Units units = prefs.units;
+ int precision = UnitsUtil.get_precision( units );
+
+ cursor_info_label.set_text( "%.*f, %.*f".printf( precision, x*units.units_per_point,
+ precision, y*units.units_per_point ) );
+ }
+
+
+ private void on_pointer_exit()
+ {
+ cursor_info_label.set_text( "" );
+ }
+
+
+ private void on_name_changed()
+ {
+ set_window_title( view.label );
+ }
+
+
+ private void on_modified_changed()
+ {
+ set_window_title( view.label );
+
+ /* TODO: update modified verbs. */
+ }
+
+
+ private void on_label_changed()
+ {
+ /* TODO: update undo/redo verbs. */
+ }
+
+
+ private void on_set_focus()
+ {
+ /* TODO: set copy paste sensitivity. */
+ }
+
+
+ }
+
+}
+
diff --git a/glabels/xml_label.vala b/glabels/xml_label.vala
new file mode 100644
index 0000000..19d7dac
--- /dev/null
+++ b/glabels/xml_label.vala
@@ -0,0 +1,504 @@
+/* xml_label.vala
+ *
+ * Copyright (C) 2012 Jim Evins <evins snaught com>
+ *
+ * This file is part of gLabels.
+ *
+ * gLabels 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.
+ *
+ * gLabels 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 gLabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using libglabels;
+
+namespace glabels
+{
+
+ namespace XmlLabel
+ {
+
+ public errordomain XmlError
+ {
+ OPEN_ERROR,
+ UNKNOWN_MEDIA,
+ PARSE_ERROR,
+ SAVE_ERROR
+ }
+
+ /* TODO: pull HUGE from libxml vapi file when available. */
+ const int MY_XML_PARSE_HUGE = 1 << 19;
+
+
+ public Label open_file( string utf8_filename ) throws XmlError
+ {
+
+ string filename;
+ try
+ {
+ filename = Filename.from_utf8( utf8_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ throw new XmlError.OPEN_ERROR( "Utf8 filename conversion error." );
+ }
+
+ unowned Xml.Doc* doc = Xml.Parser.read_file( filename, null, MY_XML_PARSE_HUGE );
+ if ( doc == null )
+ {
+ throw new XmlError.PARSE_ERROR( "xmlReadFile error." );
+ }
+
+ /* TODO:
+ xmlXIncludeProcess (doc);
+ xmlReconciliateNs (doc, xmlDocGetRootElement (doc));
+ */
+
+ Label label = parse_doc( doc );
+ label.filename = filename;
+
+ return label;
+ }
+
+
+ public Label open_buffer( string buffer ) throws XmlError
+ {
+
+ unowned Xml.Doc* doc = Xml.Parser.read_doc( buffer, null, null, MY_XML_PARSE_HUGE );
+ if ( doc == null )
+ {
+ throw new XmlError.PARSE_ERROR( "xmlReadDoc error." );
+ }
+
+ Label? label = parse_doc( doc );
+
+ return label;
+ }
+
+
+ private Label parse_doc( Xml.Doc doc ) throws XmlError
+ {
+ unowned Xml.Node* root = doc.get_root_element();
+ if ( (root == null) || (root->name == null) ) {
+ throw new XmlError.PARSE_ERROR( "No document root." );
+ }
+
+#if TODO
+ /* Try compatability mode 0.4 */
+ if (xmlSearchNsByHref (doc, root, (xmlChar *)COMPAT04_NAME_SPACE))
+ {
+ message( "Importing from glabels 0.4 format" );
+ return gl_xml_label_04_parse( root, status );
+ }
+
+ /* Test for current namespaces. */
+ if ( !xmlSearchNsByHref (doc, root, (xmlChar *)COMPAT20_NAME_SPACE) &&
+ !xmlSearchNsByHref (doc, root, (xmlChar *)COMPAT22_NAME_SPACE) &&
+ !xmlSearchNsByHref (doc, root, (xmlChar *)LGL_XML_NAME_SPACE) )
+ {
+ message( "Unknown glabels Namespace -- Using %s", XML_NAME_SPACE );
+ }
+#endif
+
+ if ( root->name != "Glabels-document" )
+ {
+ throw new XmlError.PARSE_ERROR( "Root node != \"Glabels-document\"." );
+ }
+
+ Label label = parse_glabels_document_node( root );
+ label.compression = doc.get_compress_mode();
+
+ return label;
+ }
+
+
+ private Label parse_glabels_document_node( Xml.Node node ) throws XmlError
+ {
+ Label label = new Label();
+
+ /* Pass 1, extract data nodes to pre-load cache. */
+ for ( unowned Xml.Node* child = node.children; child != null; child = child->next )
+ {
+ if ( child->name == "Data" )
+ {
+ /* TODO: xml_parse_data (child_node, label); */
+ }
+ }
+
+ /* Pass 2, now extract everything else. */
+ for ( unowned Xml.Node* child = node.children; child != null; child = child->next )
+ {
+ switch (child->name)
+ {
+
+ case "Template":
+ Template? template = XmlTemplate.parse_template_node( child );
+ if ( template == null )
+ {
+ throw new XmlError.PARSE_ERROR( "Bad template." );
+ }
+ label.template = template;
+ break;
+
+ case "Objects":
+ parse_objects_node( child, label );
+ break;
+
+ case "Merge":
+ parse_merge_node( child, label );
+ break;
+
+ case "Data":
+ /* Handled in pass 1. */
+ break;
+
+ default:
+ if ( child->is_text() == 0 )
+ {
+ message( "Unexpected %s child: \"%s\"", node.name, child->name );
+ }
+ break;
+ }
+ }
+
+ return label;
+ }
+
+
+ private void parse_objects_node( Xml.Node node,
+ Label label )
+ {
+ label.rotate = XmlUtil.get_prop_bool( node, "rotate", false );
+
+ for ( unowned Xml.Node* child = node.children; child != null; child = child->next )
+ {
+ switch (child->name)
+ {
+
+ case "Object-text":
+ /* TODO. */
+ break;
+
+ case "Object-box":
+ parse_object_box_node( child, label );
+ break;
+
+ case "Object-ellipse":
+ /* TODO. */
+ break;
+
+ case "Object-line":
+ /* TODO. */
+ break;
+
+ case "Object-image":
+ /* TODO. */
+ break;
+
+ case "Object-barcode":
+ /* TODO. */
+ break;
+
+ default:
+ if ( child->is_text() == 0 )
+ {
+ message( "Unexpected %s child: \"%s\"", node.name, child->name );
+ }
+ break;
+ }
+ }
+
+ }
+
+
+ private void parse_object_box_node( Xml.Node node,
+ Label label )
+ {
+ LabelObjectBox object = new LabelObjectBox();
+
+
+ /* position attrs */
+ object.x0 = XmlUtil.get_prop_length( node, "x", 0.0 );
+ object.y0 = XmlUtil.get_prop_length( node, "y", 0.0 );
+
+ /* size attrs */
+ object.w = XmlUtil.get_prop_length( node, "w", 0 );
+ object.h = XmlUtil.get_prop_length( node, "h", 0 );
+
+ /* line attrs */
+ object.line_width = XmlUtil.get_prop_length( node, "line_width", 1.0 );
+
+ {
+ string key = XmlUtil.get_prop_string( node, "line_color_field", null );
+ bool field_flag = key != null;
+ Color color = Color.from_legacy_color( XmlUtil.get_prop_uint( node, "line_color", 0 ) );
+ object.line_color_node = ColorNode( field_flag, color, key );
+ }
+
+ /* fill attrs */
+ {
+ string key = XmlUtil.get_prop_string( node, "fill_color_field", null );
+ bool field_flag = key != null;
+ Color color = Color.from_legacy_color( XmlUtil.get_prop_uint( node, "fill_color", 0 ) );
+ object.fill_color_node = ColorNode( field_flag, color, key );
+ }
+
+ /* affine attrs */
+ parse_affine_attrs( node, object );
+
+ /* shadow attrs */
+ parse_shadow_attrs( node, object );
+
+ label.add_object( object );
+ }
+
+
+ private void parse_affine_attrs( Xml.Node node,
+ LabelObject object )
+ {
+ double a[6];
+
+ a[0] = XmlUtil.get_prop_double( node, "a0", 0.0 );
+ a[1] = XmlUtil.get_prop_double( node, "a1", 0.0 );
+ a[2] = XmlUtil.get_prop_double( node, "a2", 0.0 );
+ a[3] = XmlUtil.get_prop_double( node, "a3", 0.0 );
+ a[4] = XmlUtil.get_prop_double( node, "a4", 0.0 );
+ a[5] = XmlUtil.get_prop_double( node, "a5", 0.0 );
+
+ object.matrix = Cairo.Matrix( a[0], a[1], a[2], a[3], a[4], a[5] );
+ }
+
+
+ private void parse_shadow_attrs( Xml.Node node,
+ LabelObject object )
+ {
+ object.shadow_state = XmlUtil.get_prop_bool( node, "shadow", false );
+
+ if (object.shadow_state)
+ {
+ object.shadow_x = XmlUtil.get_prop_length( node, "shadow_x", 0.0 );
+ object.shadow_y = XmlUtil.get_prop_length( node, "shadow_y", 0.0 );
+
+ string key = XmlUtil.get_prop_string( node, "shadow_color_field", null );
+ bool field_flag = key != null;
+ Color color = Color.from_legacy_color( XmlUtil.get_prop_uint( node, "shadow_color", 0 ) );
+ object.shadow_color_node = ColorNode( field_flag, color, key );
+
+ object.shadow_opacity = XmlUtil.get_prop_double( node, "shadow_opacity", 1.0 );
+ }
+ }
+
+
+ private void parse_merge_node( Xml.Node node,
+ Label label )
+ {
+ Merge merge = MergeFactory.create_merge( XmlUtil.get_prop_string( node, "type", null ) );
+
+ merge.src = XmlUtil.get_prop_string( node, "src", null );
+
+ label.merge = merge;
+ }
+
+
+ public void save_file( Label label,
+ string utf8_filename ) throws XmlError
+ {
+ Xml.Doc doc = create_doc( label );
+
+ string filename;
+ try
+ {
+ filename = Filename.from_utf8( utf8_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ throw new XmlError.OPEN_ERROR( "Utf8 filename conversion error." );
+ }
+
+ if ( doc.save_format_file( filename, 1 ) < 0 )
+ {
+ throw new XmlError.SAVE_ERROR( "Problem saving xml file." );
+ }
+
+ label.filename = utf8_filename;
+ label.modified = false;
+ }
+
+
+ public void save_buffer( Label label,
+ out string buffer ) throws XmlError
+ {
+ Xml.Doc doc = create_doc( label );
+
+ int length;
+ doc.dump_memory( out buffer, out length );
+ if ( length <= 0 )
+ {
+ throw new XmlError.SAVE_ERROR( "Problem saving xml buffer." );
+ }
+
+ label.modified = false;
+ }
+
+
+ private Xml.Doc create_doc( Label label )
+ {
+ Xml.Doc doc = new Xml.Doc( "1.0" );
+ unowned Xml.Node* root_node = new Xml.Node( null, "Glabels-document" );
+ doc.set_root_element( root_node );
+ unowned Xml.Ns *ns = new Xml.Ns( root_node, NAME_SPACE, null );
+ root_node->ns = ns;
+
+ XmlTemplate.create_template_node( label.template, root_node, ns );
+
+ create_objects_node( root_node, ns, label );
+
+ if ( !(label.merge is MergeNone) )
+ {
+ create_merge_node( root_node, ns, label );
+ }
+
+ create_data_node( doc, root_node, ns, label );
+
+ return doc;
+ }
+
+
+ private void create_objects_node( Xml.Node root,
+ Xml.Ns ns,
+ Label label )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Objects" );
+
+ XmlUtil.set_prop_string( node, "id", "0" );
+ XmlUtil.set_prop_bool( node, "rotate", label.rotate );
+
+ foreach ( LabelObject object in label.object_list )
+ {
+ if ( object is LabelObjectBox )
+ {
+ create_object_box_node( node, ns, (LabelObjectBox)object );
+ }
+ else /* TODO: other object types. */
+ {
+ message( "Unknown label object." );
+ }
+ }
+ }
+
+
+ private void create_object_box_node( Xml.Node parent,
+ Xml.Ns ns,
+ LabelObjectBox object )
+ {
+ unowned Xml.Node *node = parent.new_child( ns, "Object-box" );
+
+ /* position attrs */
+ XmlUtil.set_prop_length( node, "x", object.x0 );
+ XmlUtil.set_prop_length( node, "y", object.y0 );
+
+ /* size attrs */
+ XmlUtil.set_prop_length( node, "w", object.w );
+ XmlUtil.set_prop_length( node, "h", object.h );
+
+ /* line attrs */
+ XmlUtil.set_prop_length( node, "line_width", object.line_width );
+ if ( object.line_color_node.field_flag )
+ {
+ XmlUtil.set_prop_string( node, "line_color_field", object.line_color_node.key );
+ }
+ else
+ {
+ XmlUtil.set_prop_uint_hex( node, "line_color", object.line_color_node.color.to_legacy_color() );
+ }
+
+ /* fill attrs */
+ if ( object.fill_color_node.field_flag )
+ {
+ XmlUtil.set_prop_string( node, "fill_color_field", object.fill_color_node.key );
+ }
+ else
+ {
+ XmlUtil.set_prop_uint_hex( node, "fill_color", object.fill_color_node.color.to_legacy_color() );
+ }
+
+ /* affine attrs */
+ create_affine_attrs( node, object );
+
+ /* shadow attrs */
+ create_shadow_attrs( node, object );
+ }
+
+
+ private void create_affine_attrs( Xml.Node node,
+ LabelObject object )
+ {
+ XmlUtil.set_prop_double( node, "a0", object.matrix.xx );
+ XmlUtil.set_prop_double( node, "a1", object.matrix.yx );
+ XmlUtil.set_prop_double( node, "a2", object.matrix.xy );
+ XmlUtil.set_prop_double( node, "a3", object.matrix.yy );
+ XmlUtil.set_prop_double( node, "a4", object.matrix.x0 );
+ XmlUtil.set_prop_double( node, "a5", object.matrix.y0 );
+ }
+
+
+ private void create_shadow_attrs( Xml.Node node,
+ LabelObject object )
+ {
+ if ( object.shadow_state )
+ {
+ XmlUtil.set_prop_bool( node, "shadow", object.shadow_state );
+
+ XmlUtil.set_prop_length( node, "shadow_x", object.shadow_x );
+ XmlUtil.set_prop_length( node, "shadow_y", object.shadow_y );
+
+ if ( object.shadow_color_node.field_flag )
+ {
+ XmlUtil.set_prop_string( node, "shadow_color_field", object.shadow_color_node.key );
+ }
+ else
+ {
+ XmlUtil.set_prop_uint_hex( node, "shadow_color", object.shadow_color_node.color.to_legacy_color() );
+ }
+
+ XmlUtil.set_prop_double( node, "shadow_opacity", object.shadow_opacity );
+
+ }
+ }
+
+
+ private void create_merge_node( Xml.Node root,
+ Xml.Ns ns,
+ Label label )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Merge" );
+
+ XmlUtil.set_prop_string( node, "type", label.merge.name );
+ XmlUtil.set_prop_string( node, "src", label.merge.src );
+ }
+
+
+ private void create_data_node( Xml.Doc doc,
+ Xml.Node root,
+ Xml.Ns ns,
+ Label label )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Data" );
+
+ /* TODO */
+ }
+
+ }
+
+}
+
diff --git a/libglabels/Makefile.am b/libglabels/Makefile.am
index f901346..29b5930 100644
--- a/libglabels/Makefile.am
+++ b/libglabels/Makefile.am
@@ -1,66 +1,62 @@
-configdir = $(datadir)/$(LIBGLABELS_BRANCH)
+NULL =
+
+lib_LTLIBRARIES = libglabels-4.0.la
+
+libglabels_4_0_la_SOURCES = \
+ category.vala \
+ db.vala \
+ mini_preview_pixbuf.vala \
+ paper.vala \
+ str_util.vala \
+ template_coord.vala \
+ template_frame_cd.vala \
+ template_frame_ellipse.vala \
+ template_frame_rect.vala \
+ template_frame_round.vala \
+ template_frame.vala \
+ template_layout.vala \
+ template_markup_circle.vala \
+ template_markup_ellipse.vala \
+ template_markup_line.vala \
+ template_markup_margin.vala \
+ template_markup_rect.vala \
+ template_markup.vala \
+ template.vala \
+ units.vala \
+ vendor.vala \
+ xml_category.vala \
+ xml_paper.vala \
+ xml_template.vala \
+ xml_util.vala \
+ xml_vendor.vala \
+ $(NULL)
INCLUDES = \
- $(LIBGLABELS_CFLAGS) \
- -DLIBGLABELS_CONFIG_DIR=\""$(configdir)"\" \
- $(DISABLE_DEPRECATED_CFLAGS)
-
-libglabels_3_0_la_LDFLAGS=\
- -version-info $(LIBGLABELS_API_VERSION) \
- $(LIBGLABELS_LIBS) \
- -no-undefined
-
-lib_LTLIBRARIES = libglabels-3.0.la
-
-libglabels_3_0_la_SOURCES = \
- libglabels-private.h \
- lgl-db.h \
- lgl-db.c \
- lgl-units.h \
- lgl-units.c \
- lgl-paper.h \
- lgl-paper.c \
- lgl-category.h \
- lgl-category.c \
- lgl-vendor.h \
- lgl-vendor.c \
- lgl-template.h \
- lgl-template.c \
- lgl-xml-paper.h \
- lgl-xml-paper.c \
- lgl-xml-category.h \
- lgl-xml-category.c \
- lgl-xml-vendor.h \
- lgl-xml-vendor.c \
- lgl-xml-template.h \
- lgl-xml-template.c \
- lgl-xml.h \
- lgl-xml.c \
- lgl-str.h \
- lgl-str.c
-
-libglabels_3_0includedir=$(includedir)/$(LIBGLABELS_BRANCH)
-
-libglabels_3_0include_HEADERS = \
- libglabels.h
-
-libglabels_3_0subincludedir=$(includedir)/$(LIBGLABELS_BRANCH)/libglabels
-
-libglabels_3_0subinclude_HEADERS = \
- lgl-db.h \
- lgl-units.h \
- lgl-paper.h \
- lgl-category.h \
- lgl-template.h \
- lgl-xml-paper.h \
- lgl-xml-category.h \
- lgl-xml-template.h \
- lgl-xml.h \
- lgl-str.h
-
-EXTRA_DIST = \
- $(LIBGLABELS_BRANCH).pc.in
-
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = $(LIBGLABELS_BRANCH).pc
+ -include config.h \
+ $(LIBGLABELS_CFLAGS) \
+ -DLOCALEDIR=\""$(localedir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DLIBGLABELS_BRANCH=\""$(LIBGLABELS_BRANCH)"\" \
+ $(NULL)
+
+VALAFLAGS = \
+ --vapidir=$(srcdir)/../vapi \
+ --pkg config \
+ --pkg posix \
+ --pkg gobject-2.0 \
+ --pkg libxml-2.0 \
+ --pkg cairo \
+ --pkg gdk-pixbuf-2.0 \
+ --pkg gee-1.0 \
+ --header=libglabels-4.h \
+ --vapi=libglabels-4.vapi \
+ $(NULL)
+
+EXTRA_DIST = \
+ libglabels-4.h \
+ libglabels-4.vapi \
+ $(NULL)
+
+DISTCLEANFILES = \
+ $(NULL)
diff --git a/libglabels/lgl-str.h b/libglabels/category.vala
similarity index 52%
rename from libglabels/lgl-str.h
rename to libglabels/category.vala
index 3c096b0..ad5ee8d 100644
--- a/libglabels/lgl-str.h
+++ b/libglabels/category.vala
@@ -1,6 +1,6 @@
-/*
- * lgl-str.h
- * Copyright (C) 2007-2010 Jim Evins <evins snaught com>.
+/* category.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
*
* This file is part of libglabels.
*
@@ -18,33 +18,25 @@
* along with libglabels. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef __LGL_STR_H__
-#define __LGL_STR_H__
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-gint lgl_str_utf8_casecmp (const gchar *s1,
- const gchar *s2);
+using GLib;
-gint lgl_str_part_name_cmp (const gchar *s1,
- const gchar *s2);
+namespace libglabels
+{
-gchar *lgl_str_format_fraction (gdouble x);
+ public struct Category
+ {
+ public string id { get; private set; } /* Unique ID of category */
+ public string name { get; private set; } /* Localized name of category */
-G_END_DECLS
+ public Category( string id,
+ string name )
+ {
+ this.id = id;
+ this.name = name;
+ }
+ }
-#endif /* __LGL_STR_H__ */
+}
-
-
-/*
- * Local Variables: -- emacs
- * mode: C -- emacs
- * c-basic-offset: 8 -- emacs
- * tab-width: 8 -- emacs
- * indent-tabs-mode: nil -- emacs
- * End: -- emacs
- */
diff --git a/libglabels/db.vala b/libglabels/db.vala
new file mode 100644
index 0000000..b75c7bf
--- /dev/null
+++ b/libglabels/db.vala
@@ -0,0 +1,788 @@
+/* db.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public class Db : Object
+ {
+
+ public signal void changed();
+
+ private class StaticChangedProxy
+ {
+ internal signal void changed();
+
+ internal void emit_changed()
+ {
+ changed();
+ }
+ }
+
+ private static StaticChangedProxy static_changed_proxy;
+
+ public static unowned List<Paper?> papers { get; private set; }
+ public static unowned List<string> paper_ids { get; private set; }
+ public static unowned List<string> paper_names { get; private set; }
+
+ public static unowned List<Category?> categories { get; private set; }
+ public static unowned List<string> category_ids { get; private set; }
+ public static unowned List<string> category_names { get; private set; }
+
+ public static unowned List<Vendor?> vendors { get; private set; }
+ public static unowned List<string> vendor_names { get; private set; }
+
+ public static unowned List<Template> templates { get; private set; }
+
+
+ public static void init()
+ {
+ /* Force construction of static fields. */
+ new Db();
+ }
+
+
+ static construct
+ {
+ static_changed_proxy = new StaticChangedProxy();
+
+ papers = null;
+ paper_ids = null;
+ paper_names = null;
+
+ categories = null;
+ category_ids = null;
+ category_names = null;
+
+ vendors = null;
+ vendor_names = null;
+
+ templates = null;
+
+ /*
+ * Paper definitions
+ */
+ read_papers();
+
+ /* Create and append an "Other" entry. */
+ /* Translators: "Other" here means other page size. Meaning a page size
+ * other than the standard ones that libglabels knows about such as
+ * "letter", "A4", etc. */
+ Paper paper_other = Paper( "Other", _("Other"), 0.0, 0.0, null );
+ register_paper( paper_other );
+
+ /*
+ * Category definitions
+ */
+ read_categories();
+
+ /* Create and append a "User defined" entry. */
+ Category category_user = Category( "user-defined", _("User defined") );
+ register_category( category_user );
+
+ /*
+ * Vendor definitions
+ */
+ read_vendors();
+
+ /*
+ * Templates
+ */
+ read_templates();
+
+ /* Create and append generic full page templates. */
+ foreach ( string paper_id in paper_ids )
+ {
+ if ( !is_paper_id_other( paper_id ) )
+ {
+ Template template = new Template.full_page( paper_id );
+ register_template( template );
+ }
+ }
+ }
+
+
+ construct
+ {
+ static_changed_proxy.changed.connect( on_proxy_changed );
+ }
+
+
+ private void on_proxy_changed()
+ {
+ changed();
+ }
+
+
+ internal static void register_paper( Paper paper )
+ {
+ if ( lookup_paper_from_id( paper.id ) == null )
+ {
+ papers.append( paper );
+ paper_ids.append( paper.id );
+ paper_names.append( paper.name );
+ }
+ else
+ {
+ message( "Duplicate paper id: %s.", paper.id );
+ }
+ }
+
+
+ public static Paper? lookup_paper_from_name( string? name )
+ {
+ if ( name == null )
+ {
+ return papers.first().data;
+ }
+
+ foreach ( Paper paper in papers )
+ {
+ if ( paper.name == name )
+ {
+ return paper;
+ }
+ }
+
+ return null;
+ }
+
+
+ public static Paper? lookup_paper_from_id( string? id )
+ {
+ if ( id == null )
+ {
+ return papers.first().data;
+ }
+
+ foreach ( Paper paper in papers )
+ {
+ if ( paper.id == id )
+ {
+ return paper;
+ }
+ }
+
+ return null;
+ }
+
+
+ public static string? lookup_paper_id_from_name( string? name )
+ {
+ if ( name != null )
+ {
+ Paper paper = lookup_paper_from_name( name );
+ return paper.id;
+ }
+
+ return null;
+ }
+
+
+ public static string? lookup_paper_name_from_id( string? id )
+ {
+ if ( id != null )
+ {
+ Paper paper = lookup_paper_from_id( id );
+ return paper.name;
+ }
+
+ return null;
+ }
+
+
+ public static bool is_paper_id_known( string? id )
+ {
+ if ( id == null )
+ {
+ return false;
+ }
+
+ foreach (Paper paper in papers)
+ {
+ if ( paper.id == id )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ public static bool is_paper_id_other( string? id )
+ {
+ return ( id == "Other" );
+ }
+
+
+ internal static void register_category( Category category )
+ {
+ if ( lookup_category_from_id( category.id ) == null )
+ {
+ categories.append( category );
+ category_ids.append( category.id );
+ category_names.append( category.name );
+ }
+ else
+ {
+ message( "Duplicate category id: %s.", category.id );
+ }
+ }
+
+
+ public static Category? lookup_category_from_name( string? name )
+ {
+ if ( name == null )
+ {
+ return categories.first().data;
+ }
+
+ foreach ( Category category in categories )
+ {
+ if ( category.name == name )
+ {
+ return category;
+ }
+ }
+
+ return null;
+ }
+
+
+ public static Category? lookup_category_from_id( string? id )
+ {
+ if ( id == null )
+ {
+ return categories.first().data;
+ }
+
+ foreach ( Category category in categories )
+ {
+ if ( category.id == id )
+ {
+ return category;
+ }
+ }
+
+ return null;
+ }
+
+
+ public static string? lookup_category_id_from_name( string? name )
+ {
+ if ( name != null )
+ {
+ Category category = lookup_category_from_name( name );
+ return category.id;
+ }
+
+ return null;
+ }
+
+
+ public static string? lookup_category_name_from_id( string? id )
+ {
+ if ( id != null )
+ {
+ Category category = lookup_category_from_id( id );
+ return category.name;
+ }
+
+ return null;
+ }
+
+
+ public static bool is_category_id_known( string? id )
+ {
+ if ( id == null )
+ {
+ return false;
+ }
+
+ foreach (Category category in categories)
+ {
+ if ( category.id == id )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ internal static void register_vendor( Vendor vendor )
+ {
+ if ( lookup_vendor_from_name( vendor.name ) == null )
+ {
+ vendors.append( vendor );
+ vendor_names.append( vendor.name );
+ }
+ else
+ {
+ message( "Duplicate vendor name: %s.", vendor.name );
+ }
+ }
+
+
+ public static Vendor? lookup_vendor_from_name( string? name )
+ {
+ if ( name == null )
+ {
+ return vendors.first().data;
+ }
+
+ foreach ( Vendor vendor in vendors )
+ {
+ if ( vendor.name == name )
+ {
+ return vendor;
+ }
+ }
+
+ return null;
+ }
+
+
+ public static string? lookup_url_from_name( string? name )
+ {
+ if ( name != null )
+ {
+ Vendor vendor = lookup_vendor_from_name( name );
+ return vendor.url;
+ }
+
+ return null;
+ }
+
+
+ public static bool is_vendor_name_known( string? name )
+ {
+ if ( name == null )
+ {
+ return false;
+ }
+
+ foreach (Vendor vendor in vendors)
+ {
+ if ( vendor.name == name )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ internal static void register_template( Template template )
+ {
+ if ( !does_template_exist( template.brand, template.part ) )
+ {
+ templates.insert_sorted( template, compare_template_names );
+ }
+ else
+ {
+ message( "Duplicate template: %s %s.",
+ template.brand, template.part );
+ }
+ }
+
+
+ public static void register_user_template( Template template )
+ {
+ return_if_fail( !does_template_exist( template.brand, template.part ) );
+ return_if_fail( is_paper_id_known( template.paper_id ) );
+
+ string dir = Path.build_filename( Environment.get_user_config_dir(),
+ "libglabels", "templates",
+ null );
+ DirUtils.create_with_parents( dir, 0775 ); /* Make sure dir exists. */
+
+ string filename = "%s_%s.template".printf( template.brand, template.part );
+ string abs_filename = Path.build_filename( dir, filename, null );
+
+ int bytes_written = XmlTemplate.write_template_to_file( template, abs_filename );
+
+ return_if_fail( bytes_written > 0 );
+
+ Template template_copy = template.dup();
+ template_copy.add_category( "user-defined" );
+ register_template( template_copy );
+
+ static_changed_proxy.emit_changed();
+ }
+
+
+ public static void delete_user_template_by_name( string name )
+ {
+ Template template = lookup_template_from_name( name );
+
+ return_if_fail( template != null );
+ return_if_fail( template.does_category_match( "user-defined" ) );
+
+ string dir = Path.build_filename( Environment.get_user_config_dir(),
+ "libglabels", "templates",
+ null );
+
+ string filename = "%s_%s.template".printf( template.brand, template.part );
+ string abs_filename = Path.build_filename( dir, filename, null );
+ return_if_fail( FileUtils.test( abs_filename, FileTest.EXISTS ) );
+
+ FileUtils.unlink( abs_filename );
+
+ templates.remove( template );
+ }
+
+
+ public static void delete_user_template_by_brand_part( string brand,
+ string part )
+ {
+ string name = "%s %s".printf( brand, part );
+
+ delete_user_template_by_name( name );
+ }
+
+
+ public static Template? lookup_template_from_name( string? name )
+ {
+ if ( name == null )
+ {
+ return templates.first().data;
+ }
+
+ foreach ( Template template in templates )
+ {
+ if ( template.name == name )
+ {
+ return template;
+ }
+ }
+
+ return null;
+ }
+
+
+ public static Template? lookup_template_from_brand_part( string? brand,
+ string? part )
+ {
+ if ( (brand == null) || (part == null) )
+ {
+ return templates.first().data;
+ }
+
+ foreach ( Template template in templates )
+ {
+ if ( (template.brand == brand) && (template.part == part) )
+ {
+ return template;
+ }
+ }
+
+ return null;
+ }
+
+
+ public static bool does_template_exist( string? brand,
+ string? part )
+ {
+ return ( lookup_template_from_brand_part( brand, part ) != null );
+ }
+
+
+ /************************************/
+ /* Debug methods. */
+ /************************************/
+
+ public static void print_known_papers()
+ {
+ stdout.printf( "KNOWN PAPERS:\n" );
+
+ foreach (Paper paper in papers)
+ {
+ stdout.printf( "paper id=\"%s\", name=\"%s\" width=%gpts, height=%gpts\n",
+ paper.id,
+ paper.name,
+ paper.width,
+ paper.height );
+ }
+
+ stdout.printf( "\n" );
+ }
+
+
+ public static void print_known_categories()
+ {
+ stdout.printf( "KNOWN CATEGORIES:\n" );
+
+ foreach (Category category in categories)
+ {
+ stdout.printf( "category id=\"%s\", name=\"%s\"\n",
+ category.id,
+ category.name );
+ }
+
+ stdout.printf( "\n" );
+ }
+
+
+ public static void print_known_vendors()
+ {
+ stdout.printf( "KNOWN VENDORS:\n" );
+
+ foreach (Vendor vendor in vendors)
+ {
+ stdout.printf( "vendor name=\"%s\", url=\"%s\"\n",
+ vendor.name,
+ vendor.url );
+ }
+
+ stdout.printf( "\n" );
+ }
+
+
+ public static void print_known_templates()
+ {
+ stdout.printf( "KNOWN TEMPLATES:\n" );
+
+ foreach (Template template in templates)
+ {
+ stdout.printf( "template brand=\"%s\", part=\"%s\", description=\"%s\"\n",
+ template.brand,
+ template.part,
+ template.description );
+ }
+
+ stdout.printf( "\n" );
+ }
+
+
+ /************************************/
+ /* Methods to initialize db. */
+ /************************************/
+
+ private static void read_papers()
+ {
+ string data_dir;
+
+ data_dir = Path.build_filename( Config.DATADIR, Config.LIBGLABELS_BRANCH, "templates", null );
+ read_paper_files_from_dir( data_dir );
+
+ data_dir = Path.build_filename( Environment.get_user_config_dir(), "libglabels", "templates", null );
+ read_paper_files_from_dir( data_dir );
+
+ data_dir = Path.build_filename( Environment.get_home_dir(), ".glabels", null );
+ read_paper_files_from_dir( data_dir );
+
+ if ( papers == null )
+ {
+ critical( "Unable to locate paper size definitions. Libglabels may not be installed correctly!" );
+ }
+ }
+
+
+ private static void read_paper_files_from_dir( string dirname )
+ {
+ if ( FileUtils.test( dirname, FileTest.IS_DIR ) )
+ {
+ Dir dir;
+
+ try {
+ dir = Dir.open( dirname, 0 );
+ }
+ catch( Error e )
+ {
+ message( "cannot open data directory: %s", e.message );
+ return;
+ }
+
+ string? filename;
+
+ while ( (filename = dir.read_name()) != null )
+ {
+ if ( filename == "paper-sizes.xml" )
+ {
+ string full_filename = Path.build_filename( dirname, filename, null );
+ XmlPaper.read_papers_from_file( full_filename );
+ }
+ }
+
+ }
+ }
+
+
+ private static void read_categories()
+ {
+ string data_dir;
+
+ data_dir = Path.build_filename( Config.DATADIR, Config.LIBGLABELS_BRANCH, "templates", null );
+ read_category_files_from_dir( data_dir );
+
+ data_dir = Path.build_filename( Environment.get_user_config_dir(), "libglabels", "templates", null );
+ read_category_files_from_dir( data_dir );
+
+ data_dir = Path.build_filename( Environment.get_home_dir(), ".glabels", null );
+ read_category_files_from_dir( data_dir );
+
+ if ( categories == null )
+ {
+ critical( "Unable to locate any category definitions. Libglabels may not be installed correctly!" );
+ }
+ }
+
+
+ private static void read_category_files_from_dir( string dirname )
+ {
+ if ( FileUtils.test( dirname, FileTest.IS_DIR ) )
+ {
+ Dir dir;
+
+ try {
+ dir = Dir.open( dirname, 0 );
+ }
+ catch( Error e )
+ {
+ message( "cannot open data directory: %s", e.message );
+ return;
+ }
+
+ string? filename;
+
+ while ( (filename = dir.read_name()) != null )
+ {
+ if ( filename == "categories.xml" )
+ {
+ string full_filename = Path.build_filename( dirname, filename, null );
+ XmlCategory.read_categories_from_file( full_filename );
+ }
+ }
+
+ }
+ }
+
+
+ private static void read_vendors()
+ {
+ string data_dir;
+
+ data_dir = Path.build_filename( Config.DATADIR, Config.LIBGLABELS_BRANCH, "templates", null );
+ read_vendor_files_from_dir( data_dir );
+
+ data_dir = Path.build_filename( Environment.get_user_config_dir(), "libglabels", "templates", null );
+ read_vendor_files_from_dir( data_dir );
+
+ data_dir = Path.build_filename( Environment.get_home_dir(), ".glabels", null );
+ read_vendor_files_from_dir( data_dir );
+
+ if ( vendors == null )
+ {
+ critical( "Unable to locate any vendor definitions. Libglabels may not be installed correctly!" );
+ }
+ }
+
+
+ private static void read_vendor_files_from_dir( string dirname )
+ {
+ if ( FileUtils.test( dirname, FileTest.IS_DIR ) )
+ {
+ Dir dir;
+
+ try {
+ dir = Dir.open( dirname, 0 );
+ }
+ catch( Error e )
+ {
+ message( "cannot open data directory: %s", e.message );
+ return;
+ }
+
+ string? filename;
+
+ while ( (filename = dir.read_name()) != null )
+ {
+ if ( filename == "vendors.xml" )
+ {
+ string full_filename = Path.build_filename( dirname, filename, null );
+ XmlVendor.read_vendors_from_file( full_filename );
+ }
+ }
+
+ }
+ }
+
+
+ private static void read_templates()
+ {
+ string data_dir;
+
+ data_dir = Path.build_filename( Config.DATADIR, Config.LIBGLABELS_BRANCH, "templates", null );
+ read_template_files_from_dir( data_dir );
+
+ data_dir = Path.build_filename( Environment.get_user_config_dir(), "libglabels", "templates", null );
+ read_template_files_from_dir( data_dir );
+
+ data_dir = Path.build_filename( Environment.get_home_dir(), ".glabels", null );
+ read_template_files_from_dir( data_dir );
+
+ if ( templates == null )
+ {
+ critical( "Unable to locate any template definitions. Libglabels may not be installed correctly!" );
+ }
+ }
+
+
+ private static void read_template_files_from_dir( string dirname )
+ {
+ if ( FileUtils.test( dirname, FileTest.IS_DIR ) )
+ {
+ Dir dir;
+
+ try {
+ dir = Dir.open( dirname, 0 );
+ }
+ catch( Error e )
+ {
+ message( "cannot open data directory: %s", e.message );
+ return;
+ }
+
+ string? filename;
+
+ while ( (filename = dir.read_name()) != null )
+ {
+ if ( filename.has_suffix( "-templates.xml" ) ||
+ filename.has_suffix( ".template" ) )
+ {
+ string full_filename = Path.build_filename( dirname, filename, null );
+ XmlTemplate.read_templates_from_file( full_filename );
+ }
+ }
+
+ }
+ }
+
+
+ }
+
+}
diff --git a/libglabels/mini_preview_pixbuf.vala b/libglabels/mini_preview_pixbuf.vala
new file mode 100644
index 0000000..cad9a0b
--- /dev/null
+++ b/libglabels/mini_preview_pixbuf.vala
@@ -0,0 +1,156 @@
+/* mini_preview_pixbuf.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ private struct ColorStruct
+ {
+ double r;
+ double g;
+ double b;
+ }
+
+ private const ColorStruct paper = { 0.85, 0.85, 0.85 };
+ private const ColorStruct paper_outline = { 0.0, 0.0, 0.0 };
+ private const ColorStruct label = { 0.95, 0.95, 0.95 };
+ private const ColorStruct label_outline = { 0.25, 0.25, 0.25 };
+
+ private const double paper_outline_width_pixels = 1.0;
+ private const double label_outline_width_pixels = 1.0;
+
+
+ public class MiniPreviewPixbuf
+ {
+ public Gdk.Pixbuf pixbuf { get; private set; }
+
+ public MiniPreviewPixbuf( Template template,
+ int width,
+ int height )
+ {
+ pixbuf = new Gdk.Pixbuf( Gdk.Colorspace.RGB, true, 8, width, height );
+
+ Cairo.ImageSurface surface = new Cairo.ImageSurface.for_data( pixbuf.get_pixels(),
+ Cairo.Format.RGB24,
+ pixbuf.get_width(),
+ pixbuf.get_height(),
+ pixbuf.get_rowstride() );
+ Cairo.Context cr = new Cairo.Context( surface );
+
+ /* Clear pixbuf */
+ cr.save();
+ cr.set_operator( Cairo.Operator.CLEAR );
+ cr.paint();
+ cr.restore();
+
+ cr.set_antialias( Cairo.Antialias.GRAY );
+
+ /* Set scale and offset */
+ double w = width - 1;
+ double h = height - 1;
+ double scale;
+ if ( (w/template.page_width) > (h/template.page_height) )
+ {
+ scale = h / template.page_height;
+ }
+ else
+ {
+ scale = w / template.page_width;
+ }
+ double offset_x = (width/scale - template.page_width) / 2.0;
+ double offset_y = (height/scale - template.page_height) / 2.0;
+ cr.identity_matrix();
+ cr.scale( scale, scale );
+ cr.translate( offset_x, offset_y );
+
+ /* Draw paper and label outlines */
+ draw_paper( cr, template, scale );
+ draw_label_outlines( cr, template, scale );
+ }
+
+
+ private void draw_paper( Cairo.Context cr,
+ Template template,
+ double scale )
+ {
+ cr.save();
+
+ cr.rectangle( 0, 0, template.page_width, template.page_height );
+
+ cr.set_source_rgb( paper.r, paper.g, paper.b );
+ cr.fill_preserve();
+
+ cr.set_line_width( paper_outline_width_pixels/scale );
+ cr.set_source_rgb( paper_outline.r, paper_outline.g, paper_outline.b );
+ cr.stroke();
+
+ cr.restore();
+ }
+
+
+ private void draw_label_outlines( Cairo.Context cr,
+ Template template,
+ double scale )
+ {
+ cr.save();
+
+ cr.set_line_width( label_outline_width_pixels/scale );
+
+ TemplateFrame frame = template.frames.first().data;
+
+ Gee.ArrayList<TemplateCoord?> origins = frame.get_origins();
+
+ foreach ( TemplateCoord origin in origins )
+ {
+ draw_label_outline( cr, frame, origin.x, origin.y );
+ }
+
+ cr.restore();
+ }
+
+
+ private void draw_label_outline( Cairo.Context cr,
+ TemplateFrame frame,
+ double x0,
+ double y0 )
+ {
+ cr.save();
+
+ cr.translate( x0, y0 );
+
+ frame.cairo_path( cr, false );
+
+ cr.set_source_rgb( label.r, label.g, label.b );
+ cr.set_fill_rule( Cairo.FillRule.EVEN_ODD );
+ cr.fill_preserve();
+
+ cr.set_source_rgb( label_outline.r, label_outline.g, label_outline.b );
+ cr.stroke();
+
+ cr.restore();
+ }
+
+
+ }
+
+}
diff --git a/libglabels/paper.vala b/libglabels/paper.vala
new file mode 100644
index 0000000..bf52777
--- /dev/null
+++ b/libglabels/paper.vala
@@ -0,0 +1,51 @@
+/* paper.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public struct Paper
+ {
+ public string id { get; private set; } /* Unique ID of category */
+ public string name { get; private set; } /* Localized name of category */
+ public double width { get; private set; } /* Width (in points) */
+ public double height { get; private set; } /* Width (in points) */
+ public string pwg_size { get; private set; } /* PWG 5101.1-2002 size name */
+
+ public Paper( string id,
+ string name,
+ double width,
+ double height,
+ string? pwg_size )
+ {
+ this.id = id;
+ this.name = name;
+ this.width = width;
+ this.height = height;
+ this.pwg_size = pwg_size;
+ }
+
+ }
+
+}
+
diff --git a/libglabels/str_util.vala b/libglabels/str_util.vala
new file mode 100644
index 0000000..5a4c8bb
--- /dev/null
+++ b/libglabels/str_util.vala
@@ -0,0 +1,213 @@
+/* str_util.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using Math;
+
+namespace libglabels
+{
+
+ namespace StrUtil
+ {
+
+ public string format_fraction( double x )
+ {
+ const double FRAC_EPSILON = 0.00005;
+ const double[] denom = { 1.0, 2.0, 3.0, 4.0, 8.0, 16.0, 32.0, 0.0 };
+ string[] denom_string = { "1", "â", "â", "â", "â", "ââ", "ââ", null };
+ string[] num_string = { "â", "Â", "Â", "Â", "â", "â", "â", "â", "â", "â",
+ "Ââ", "ÂÂ", "ÂÂ", "ÂÂ", "Ââ", "Ââ", "Ââ", "Ââ", "Ââ", "Ââ",
+ "Ââ", "ÂÂ", "ÂÂ", "ÂÂ", "Ââ", "Ââ", "Ââ", "Ââ", "Ââ", "Ââ",
+ "Ââ", "ÂÂ" };
+ int i;
+ double product, remainder;
+ int n, d;
+
+ for ( i=0; denom[i] != 0.0; i++ )
+ {
+ product = x * denom[i];
+ remainder = fabs(product - ((int)(product+0.5)));
+ if ( remainder < FRAC_EPSILON ) break;
+ }
+
+ if ( denom[i] == 0.0 )
+ {
+ /* None of our denominators work. */
+ return "%.5g".printf( x );
+ }
+ if ( denom[i] == 1.0 )
+ {
+ /* Simple integer. */
+ return "%.0f".printf( x );
+ }
+ n = (int)( x * denom[i] + 0.5 );
+ d = (int)denom[i];
+ if ( n > d )
+ {
+ return "%d%s/%s".printf( (n/d), num_string[n%d], denom_string[i] );
+ }
+ else
+ {
+ return "%s/%s".printf( num_string[n%d], denom_string[i] );
+ }
+ }
+
+
+ /**
+ * Compare part names
+ * @s1: string to compare with s2.
+ * @s2: string to compare with s1.
+ *
+ * Compare two UTF-8 strings representing part names or numbers. This function
+ * uses a natural sort order:
+ *
+ * - Ignores case.
+ *
+ * - Strings are divided into chunks (numeric and non-numeric)
+ *
+ * - Non-numeric chunks are compared character by character
+ *
+ * - Numerical chunks are compared numerically, so that "20" precedes "100".
+ *
+ * - Comparison of chunks is performed left to right until the first difference
+ * is encountered or all chunks evaluate as equal.
+ *
+ * This function should be used only on strings that are known to be encoded
+ * in UTF-8 or a compatible UTF-8 subset.
+ *
+ * Numeric chunks are converted to 64 bit unsigned integers for comparison,
+ * so the behaviour may be unpredictable for numeric chunks that exceed
+ * 18446744073709551615.
+ *
+ * Returns: 0 if the strings match, a negative value if s1 < s2,
+ * or a positive value if s1 > s2.
+ *
+ */
+ public int compare_part_names( string s1,
+ string s2 )
+ {
+ if ( s1 == s2 ) return 0;
+ if ( s1 == "" ) return -1;
+ if ( s2 == "" ) return 1;
+
+ string folded_s1 = s1.casefold( -1 );
+ string folded_s2 = s2.casefold( -1 );
+
+ int i1 = 0;
+ int i2 = 0;
+ int result = 0;
+ bool done = false;
+
+ while ( (result == 0) && !done )
+ {
+ string chunk1, chunk2;
+ bool isnum1, isnum2;
+
+ if ( folded_s1.get_char( i1 ).isdigit() )
+ {
+ chunk1 = span_digits( folded_s1, ref i1 );
+ isnum1 = true;
+ }
+ else
+ {
+ chunk1 = span_non_digits( folded_s1, ref i1 );
+ isnum1 = false;
+ }
+
+ if ( folded_s2.get_char( i2 ).isdigit() )
+ {
+ chunk2 = span_digits( folded_s2, ref i2 );
+ isnum2 = true;
+ }
+ else
+ {
+ chunk2 = span_non_digits( folded_s2, ref i2 );
+ isnum2 = false;
+ }
+
+ if ( ( chunk1 == "" ) && ( chunk2 == "" ) )
+ {
+ /* Case 1: Both are empty. */
+ done = true;
+ }
+ else if ( isnum1 && isnum2 )
+ {
+ /* Case 2: They both contain numbers */
+ uint64 n1 = uint64.parse( chunk1 );
+ uint64 n2 = uint64.parse( chunk2 );
+
+ if ( n1 < n2 ) result = -1;
+ else if ( n1 > n2 ) result = 1;
+ }
+ else
+ {
+ /* Case 3: One or both do not contain numbers */
+ if ( chunk1 < chunk2 ) result = -1;
+ else if( chunk1 > chunk2 ) result = 1;
+ }
+
+ }
+
+ return result;
+ }
+
+
+ private string span_digits( string s, ref int i )
+ {
+ StringBuilder chunk = new StringBuilder();
+
+ bool not_end;
+ unichar c;
+ int j;
+ for ( j = i, not_end = s.get_next_char( ref j, out c );
+ not_end && c.isdigit();
+ not_end = s.get_next_char( ref j, out c ) )
+ {
+ chunk.append_unichar( c );
+ i = j; /* only advance i, if character is used. */
+ }
+
+ return chunk.str;
+ }
+
+
+ private string span_non_digits( string s, ref int i )
+ {
+ StringBuilder chunk = new StringBuilder();
+
+ bool not_end;
+ unichar c;
+ int j;
+ for ( j = i, not_end = s.get_next_char( ref j, out c );
+ not_end && !c.isdigit();
+ not_end = s.get_next_char( ref j, out c ) )
+ {
+ chunk.append_unichar( c );
+ i = j; /* only advance i, if character is used. */
+ }
+
+ return chunk.str;
+ }
+
+
+ }
+
+}
diff --git a/libglabels/template.vala b/libglabels/template.vala
new file mode 100644
index 0000000..d563449
--- /dev/null
+++ b/libglabels/template.vala
@@ -0,0 +1,252 @@
+/* template.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using Math;
+
+namespace libglabels
+{
+
+ private const double EPSILON = 0.5; /* Allowed error when comparing dimensions. (0.5pts ~= .007in ~= .2mm) */
+
+ private const int PREVIEW_PIXBUF_SIZE = 72;
+
+
+ public int compare_template_names( Template a, Template b )
+ {
+ return ( StrUtil.compare_part_names( a.name, b.name ) );
+ }
+
+
+ public class Template
+ {
+ public string brand { get; private set; }
+ public string part { get; private set; }
+ public string equiv_part { get; set; }
+
+ public string name { get; private set; }
+
+ public string description { get; private set; }
+
+ public string paper_id { get; private set; }
+ public double page_width { get; private set; }
+ public double page_height { get; private set; }
+
+ public Gdk.Pixbuf preview_pixbuf { get; private set; }
+
+
+ /* Meta information */
+ public string product_url { get; set; }
+ public unowned List<string> category_ids { get; private set; }
+
+ /* List of label frames. Currently glabels only supports a single label frame per template. */
+ public unowned List<TemplateFrame> frames { get; private set; }
+
+
+ public Template( string brand,
+ string part,
+ string description,
+ string paper_id,
+ double page_width,
+ double page_height )
+ {
+ this.brand = brand;
+ this.part = part;
+ this.description = description;
+ this.paper_id = paper_id;
+ this.page_width = page_width;
+ this.page_height = page_height;
+
+ this.name = "%s %s".printf( brand, part );
+ }
+
+
+ public Template.full_page( string paper_id )
+ {
+ Paper? paper = Db.lookup_paper_from_id( paper_id );
+ if ( paper != null )
+ {
+ string part = "%s-Full-Page".printf( paper.id );
+ string desc = _("%s full page").printf( paper.name );
+
+ this( "Generic", part, desc, paper.id, paper.width, paper.height );
+
+ TemplateFrame frame = new TemplateFrameRect( "0",
+ paper.width,
+ paper.height,
+ 0, 0, 0 );
+ frame.add_layout( new TemplateLayout( 1, 1, 0, 0, 0, 0 ) );
+ frame.add_markup( new TemplateMarkupMargin( 9 ) );
+
+ this.add_frame( frame );
+
+ this.init_preview_pixbuf();
+
+ }
+ else
+ {
+ error( "Cannot create full page template for \"%s\"", paper_id );
+ }
+ }
+
+
+ public Template.from_equiv( string brand,
+ string part,
+ string equiv_part )
+ {
+ Template? template = Db.lookup_template_from_brand_part( brand, equiv_part );
+ if ( template != null )
+ {
+ this( brand, part, template.description,
+ template.paper_id, template.page_width, template.page_height );
+
+ this.equiv_part = equiv_part;
+ this.product_url = template.product_url;
+ this.preview_pixbuf = template.preview_pixbuf;
+
+ foreach (string category_id in template.category_ids)
+ {
+ add_category( category_id );
+ }
+
+ foreach (TemplateFrame frame in template.frames)
+ {
+ add_frame( frame.dup() );
+ }
+ }
+ else
+ {
+ error( "Cannot create equivalent template for \"%s\", \"%s\". Forward references not supported.",
+ brand, equiv_part );
+ }
+ }
+
+
+ public Template dup()
+ {
+ Template copy = new Template( brand, part, description,
+ paper_id, page_width, page_height );
+
+ copy.equiv_part = equiv_part;
+ copy.product_url = product_url;
+ copy.preview_pixbuf = preview_pixbuf;
+
+ foreach (string category_id in category_ids)
+ {
+ copy.add_category( category_id );
+ }
+
+ foreach (TemplateFrame frame in frames)
+ {
+ copy.add_frame( frame.dup() );
+ }
+
+ return copy;
+ }
+
+
+ public bool is_match( Template template2 )
+ {
+ return ( (template2.brand == brand) && (template2.part == part) );
+ }
+
+
+ public bool does_category_match( string? category_id )
+ {
+ if ( category_id == null )
+ {
+ return true;
+ }
+
+ foreach ( string my_category_id in category_ids )
+ {
+ if ( my_category_id == category_id )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ public bool is_similar( Template template2 )
+ {
+ if ( (template2.paper_id != paper_id) ||
+ (template2.page_width != page_width) ||
+ (template2.page_height != page_height) )
+ {
+ return false;
+ }
+
+ TemplateFrame frame1 = frames.first().data;
+ TemplateFrame frame2 = template2.frames.first().data;
+
+ if ( !frame1.is_similar( frame2 ) )
+ {
+ return false;
+ }
+
+ foreach ( TemplateLayout layout1 in frame1.layouts )
+ {
+ bool match_found = false;
+ foreach ( TemplateLayout layout2 in frame2.layouts )
+ {
+ if ( layout1.is_similar( layout2 ) )
+ {
+ match_found = true;
+ break;
+ }
+ }
+ if ( !match_found )
+ {
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+
+ public void add_frame( TemplateFrame frame )
+ {
+ frames.append( frame );
+ }
+
+
+ public void add_category( string category_id )
+ {
+ category_ids.append( category_id );
+ }
+
+
+ public void init_preview_pixbuf()
+ {
+ MiniPreviewPixbuf pb = new MiniPreviewPixbuf( this, PREVIEW_PIXBUF_SIZE, PREVIEW_PIXBUF_SIZE );
+
+ preview_pixbuf = pb.pixbuf;
+ }
+
+
+ }
+
+}
diff --git a/libglabels/template_coord.vala b/libglabels/template_coord.vala
new file mode 100644
index 0000000..902167c
--- /dev/null
+++ b/libglabels/template_coord.vala
@@ -0,0 +1,70 @@
+/* template_coord.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public struct TemplateCoord
+ {
+ public double x { get; set; } /* x coordinate of label left edge relative to left edge of paper */
+ public double y { get; set; } /* y coordinate of label top edge relative to top edge of paper */
+
+ public TemplateCoord( double x,
+ double y )
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ public int compare( TemplateCoord b )
+ {
+ if ( this.y < b.y )
+ {
+ return -1;
+ }
+ else if ( this.y > b.y )
+ {
+ return 1;
+ }
+ else
+ {
+ if ( this.x < b.x )
+ {
+ return -1;
+ }
+ else if ( this.x > b.x )
+ {
+ return 1;
+ }
+ else
+ {
+ return 0; /* hopefull 2 label frames won't have the same origin. */
+ }
+ }
+ }
+
+
+ }
+
+}
+
diff --git a/libglabels/template_frame.vala b/libglabels/template_frame.vala
new file mode 100644
index 0000000..c87274b
--- /dev/null
+++ b/libglabels/template_frame.vala
@@ -0,0 +1,123 @@
+/* template_frame.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public abstract class TemplateFrame
+ {
+ public string id { get; protected set; default = "0"; }
+
+ public unowned List<TemplateLayout> layouts { get; protected set; }
+ public unowned List<TemplateMarkup> markups { get; protected set; }
+
+
+ public abstract TemplateFrame dup();
+
+ public abstract void get_size( out double w, out double h );
+
+ public abstract bool is_similar( TemplateFrame frame2 );
+
+ public abstract string get_size_description( Units units );
+
+ public abstract void cairo_path( Cairo.Context cr,
+ bool waste_flag );
+
+
+ public int get_n_labels()
+ {
+ int n_labels = 0;
+
+ foreach ( TemplateLayout layout in layouts )
+ {
+ n_labels += layout.nx * layout.ny;
+ }
+
+ return n_labels;
+ }
+
+
+ public string get_layout_description()
+ {
+ string description;
+ int n_labels = get_n_labels();
+
+ if ( layouts.length() == 1 )
+ {
+ TemplateLayout layout = layouts.first().data;
+
+ /*
+ * Translators: 1st %d = number of labels across a page,
+ * 2nd %d = number of labels down a page,
+ * 3rd %d = total number of labels on a page (sheet).
+ */
+ description = _("%d à %d (%d per sheet)").printf( layout.nx, layout.ny, n_labels);
+ }
+ else
+ {
+ /* Translators: %d is the total number of labels on a page (sheet). */
+ description = _("%d per sheet").printf( n_labels );
+ }
+
+ return description;
+ }
+
+
+ public Gee.ArrayList<TemplateCoord?> get_origins()
+ {
+ Gee.ArrayList<TemplateCoord?> origins = new Gee.ArrayList<TemplateCoord?>();
+
+ foreach (TemplateLayout layout in layouts)
+ {
+ for ( int iy = 0; iy < layout.ny; iy++ )
+ {
+ for ( int ix = 0; ix < layout.nx; ix++ )
+ {
+ origins.add( TemplateCoord( ix*layout.dx + layout.x0,
+ iy*layout.dy + layout.y0 ) );
+ }
+ }
+ }
+
+ origins.sort();
+
+ return origins;
+ }
+
+
+ public void add_layout( TemplateLayout layout )
+ {
+ layouts.append( layout );
+ }
+
+
+ public void add_markup( TemplateMarkup markup )
+ {
+ markups.append( markup );
+ }
+
+
+ }
+
+
+}
diff --git a/libglabels/template_frame_cd.vala b/libglabels/template_frame_cd.vala
new file mode 100644
index 0000000..679d540
--- /dev/null
+++ b/libglabels/template_frame_cd.vala
@@ -0,0 +1,182 @@
+/* template_frame_cd.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using Math;
+
+namespace libglabels
+{
+
+ public class TemplateFrameCD : TemplateFrame
+ {
+ public double r1 { get; protected set; }
+ public double r2 { get; protected set; }
+
+ public double w { get; protected set; }
+ public double h { get; protected set; }
+
+ public double waste { get; protected set; }
+
+
+ public TemplateFrameCD( string id,
+ double r1,
+ double r2,
+ double w,
+ double h,
+ double waste )
+ {
+ this.id = id;
+
+ this.r1 = r1;
+ this.r2 = r2;
+
+ this.w = w;
+ this.h = h;
+
+ this.waste = waste;
+ }
+
+
+ public override TemplateFrame dup()
+ {
+ TemplateFrameCD copy = new TemplateFrameCD( id, r1, r2, w, h, waste );
+
+ foreach (TemplateLayout layout in layouts)
+ {
+ copy.add_layout( layout.dup() );
+ }
+
+ foreach (TemplateMarkup markup in markups)
+ {
+ copy.add_markup( markup.dup() );
+ }
+
+ return (TemplateFrame)copy;
+ }
+
+
+ public override void get_size( out double w,
+ out double h )
+ {
+ if ( this.w == 0 )
+ {
+ w = 2.0 * r1;
+ }
+ else
+ {
+ w = this.w;
+ }
+
+ if ( this.h == 0 )
+ {
+ h = 2.0 *r1;
+ }
+ else
+ {
+ h = this.h;
+ }
+ }
+
+
+ public override bool is_similar( TemplateFrame frame2 )
+ {
+ if ( frame2 is TemplateFrameCD )
+ {
+ TemplateFrameCD cd2 = (TemplateFrameCD) frame2;
+
+ if ( (fabs( w - cd2.w ) <= EPSILON) &&
+ (fabs( h - cd2.h ) <= EPSILON) &&
+ (fabs( r1 - cd2.r1 ) <= EPSILON) &&
+ (fabs( r2 - cd2.r2 ) <= EPSILON) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public override string get_size_description( Units units )
+ {
+ string description;
+
+ string units_string = units.name;
+ double units_per_point = units.units_per_point;
+
+ if ( units.id == "in" )
+ {
+ string dstr = StrUtil.format_fraction( 2 * r1 * units_per_point );
+
+ description = "%s %s %s".printf( dstr, units_string, _("diameter") );
+ }
+ else
+ {
+ description = "%.5g %s %s".printf( 2 * r1 * units_per_point,
+ units_string,
+ _("diameter") );
+ }
+
+ return description;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ bool waste_flag )
+ {
+ double w, h;
+
+ get_size( out w, out h );
+
+ double xc = w/2.0;
+ double yc = h/2.0;
+
+ double waste = 0.0;
+ if (waste_flag)
+ {
+ waste = this.waste;
+ }
+
+ /*
+ * Outer path (may be clipped in the case of a business card type CD)
+ */
+ double theta1 = Math.acos( w / (2.0*r1) );
+ double theta2 = Math.asin( h / (2.0*r1) );
+
+ cr.new_path();
+ cr.arc( xc, yc, r1+waste, theta1, theta2 );
+ cr.arc( xc, yc, r1+waste, Math.PI-theta2, Math.PI-theta1 );
+ cr.arc( xc, yc, r1+waste, Math.PI+theta1, Math.PI+theta2 );
+ cr.arc( xc, yc, r1+waste, 2*Math.PI-theta2, 2*Math.PI-theta1 );
+ cr.close_path();
+
+
+ /*
+ * Inner path (hole)
+ */
+ cr.new_sub_path();
+ cr.arc( xc, yc, r2-waste, 0.0, 2*Math.PI );
+ cr.close_path();
+ }
+
+
+ }
+
+}
diff --git a/libglabels/template_frame_ellipse.vala b/libglabels/template_frame_ellipse.vala
new file mode 100644
index 0000000..8e9d366
--- /dev/null
+++ b/libglabels/template_frame_ellipse.vala
@@ -0,0 +1,157 @@
+/* template_frame_ellipse.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using Math;
+
+namespace libglabels
+{
+
+ public class TemplateFrameEllipse : TemplateFrame
+ {
+
+ private const int ARC_FINE = 2;
+
+
+ public double w { get; protected set; }
+ public double h { get; protected set; }
+
+ public double waste { get; protected set; }
+
+
+ public TemplateFrameEllipse( string id,
+ double w,
+ double h,
+ double waste )
+ {
+ this.id = id;
+
+ this.w = w;
+ this.h = h;
+
+ this.waste = waste;
+ }
+
+
+ public override TemplateFrame dup()
+ {
+ TemplateFrameEllipse copy = new TemplateFrameEllipse( id, w, h, waste );
+
+ foreach (TemplateLayout layout in layouts)
+ {
+ copy.add_layout( layout.dup() );
+ }
+
+ foreach (TemplateMarkup markup in markups)
+ {
+ copy.add_markup( markup.dup() );
+ }
+
+ return (TemplateFrame)copy;
+ }
+
+
+ public override void get_size( out double w,
+ out double h )
+ {
+ w = this.w;
+ h = this.h;
+ }
+
+
+ public override bool is_similar( TemplateFrame frame2 )
+ {
+ if ( frame2 is TemplateFrameEllipse )
+ {
+ TemplateFrameEllipse ellipse2 = (TemplateFrameEllipse) frame2;
+
+ if ( (fabs( w - ellipse2.w) <= EPSILON) &&
+ (fabs( h - ellipse2.h) <= EPSILON) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public override string get_size_description( Units units )
+ {
+ string description;
+
+ string units_string = units.name;
+ double units_per_point = units.units_per_point;
+
+ if ( units.id == "in" )
+ {
+ string xstr = StrUtil.format_fraction( w * units_per_point );
+ string ystr = StrUtil.format_fraction( h * units_per_point );
+
+ description = "%s à %s %s".printf( xstr, ystr, units_string );
+ }
+ else
+ {
+ description = "%.5g à %.5g %s".printf( w * units_per_point,
+ h * units_per_point,
+ units_string );
+ }
+
+ return description;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ bool waste_flag )
+ {
+ double w, h;
+
+ w = this.w;
+ h = this.h;
+
+ double waste = 0.0;
+ if (waste_flag)
+ {
+ waste = this.waste;
+ }
+
+ cr.save();
+
+ cr.translate( -waste, -waste );
+ double rx = (w+waste)/2.0;
+ double ry = (h+waste)/2.0;
+
+ cr.new_path();
+ cr.move_to( 2*rx, ry );
+ for ( int i_theta = ARC_FINE; i_theta <= 360; i_theta += ARC_FINE )
+ {
+ double x = rx + rx*Math.cos( i_theta * Math.PI / 180.0 );
+ double y = ry + ry*Math.sin( i_theta * Math.PI / 180.0 );
+ cr.line_to( x, y );
+ }
+ cr.close_path();
+
+ cr.restore();
+ }
+
+
+ }
+
+}
diff --git a/libglabels/template_frame_rect.vala b/libglabels/template_frame_rect.vala
new file mode 100644
index 0000000..818ae27
--- /dev/null
+++ b/libglabels/template_frame_rect.vala
@@ -0,0 +1,160 @@
+/* template_frame_rect.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using Math;
+
+namespace libglabels
+{
+
+ public class TemplateFrameRect : TemplateFrame
+ {
+ public double w { get; protected set; }
+ public double h { get; protected set; }
+
+ public double r { get; protected set; }
+
+ public double x_waste { get; protected set; }
+ public double y_waste { get; protected set; }
+
+
+ public TemplateFrameRect( string id,
+ double w,
+ double h,
+ double r,
+ double x_waste,
+ double y_waste )
+ {
+ this.id = id;
+
+ this.w = w;
+ this.h = h;
+
+ this.r = r;
+
+ this.x_waste = x_waste;
+ this.y_waste = y_waste;
+ }
+
+
+ public override TemplateFrame dup()
+ {
+ TemplateFrameRect copy = new TemplateFrameRect( id, w, h, r, x_waste, y_waste );
+
+ foreach (TemplateLayout layout in layouts)
+ {
+ copy.add_layout( layout.dup() );
+ }
+
+ foreach (TemplateMarkup markup in markups)
+ {
+ copy.add_markup( markup.dup() );
+ }
+
+ return (TemplateFrame)copy;
+ }
+
+
+ public override void get_size( out double w,
+ out double h )
+ {
+ w = this.w;
+ h = this.h;
+ }
+
+
+ public override bool is_similar( TemplateFrame frame2 )
+ {
+ if ( frame2 is TemplateFrameRect )
+ {
+ TemplateFrameRect rect2 = (TemplateFrameRect) frame2;
+
+ if ( (fabs( w - rect2.w) <= EPSILON) &&
+ (fabs( h - rect2.h) <= EPSILON) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public override string get_size_description( Units units )
+ {
+ string description;
+
+ string units_string = units.name;
+ double units_per_point = units.units_per_point;
+
+ if ( units.id == "in" )
+ {
+ string xstr = StrUtil.format_fraction( w * units_per_point );
+ string ystr = StrUtil.format_fraction( h * units_per_point );
+
+ description = "%s à %s %s".printf( xstr, ystr, units_string );
+ }
+ else
+ {
+ description = "%.5g à %.5g %s".printf( w * units_per_point,
+ h * units_per_point,
+ units_string );
+ }
+
+ return description;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ bool waste_flag )
+ {
+ double x_waste = 0.0;
+ double y_waste = 0.0;
+
+ double w, h;
+
+ w = this.w;
+ h = this.h;
+
+ if (waste_flag)
+ {
+ x_waste = this.x_waste;
+ y_waste = this.y_waste;
+ }
+
+ if ( r == 0.0 )
+ {
+ cr.rectangle( -x_waste, -y_waste, w+2*x_waste, h+2*y_waste );
+ }
+ else
+ {
+ cr.new_path();
+ cr.arc_negative( r-x_waste, r-y_waste, r, 3*Math.PI/2, Math.PI );
+ cr.arc_negative( r-x_waste, h-r+y_waste, r, Math.PI, Math.PI/2 );
+ cr.arc_negative( w-r+x_waste, h-r+y_waste, r, Math.PI/2, 0.0 );
+ cr.arc_negative( w-r+x_waste, r-y_waste, r, 2*Math.PI, 3*Math.PI/2 );
+ cr.close_path();
+ }
+ }
+
+
+ }
+
+}
diff --git a/libglabels/template_frame_round.vala b/libglabels/template_frame_round.vala
new file mode 100644
index 0000000..be47c76
--- /dev/null
+++ b/libglabels/template_frame_round.vala
@@ -0,0 +1,128 @@
+/* template_frame_round.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using Math;
+
+namespace libglabels
+{
+
+ public class TemplateFrameRound : TemplateFrame
+ {
+ public double r { get; protected set; }
+
+ public double waste { get; protected set; }
+
+
+ public TemplateFrameRound( string id,
+ double r,
+ double waste )
+ {
+ this.id = id;
+
+ this.r = r;
+
+ this.waste = waste;
+ }
+
+
+ public override TemplateFrame dup()
+ {
+ TemplateFrameRound copy = new TemplateFrameRound( id, r, waste );
+
+ foreach (TemplateLayout layout in layouts)
+ {
+ copy.add_layout( layout.dup() );
+ }
+
+ foreach (TemplateMarkup markup in markups)
+ {
+ copy.add_markup( markup.dup() );
+ }
+
+ return (TemplateFrame)copy;
+ }
+
+
+ public override void get_size( out double w,
+ out double h )
+ {
+ w = h = 2.0 * r;
+ }
+
+
+ public override bool is_similar( TemplateFrame frame2 )
+ {
+ if ( frame2 is TemplateFrameRound )
+ {
+ TemplateFrameRound round2 = (TemplateFrameRound) frame2;
+
+ if ( fabs( r - round2.r) <= EPSILON )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public override string get_size_description( Units units )
+ {
+ string description;
+
+ string units_string = units.name;
+ double units_per_point = units.units_per_point;
+
+ if ( units.id == "in" )
+ {
+ string dstr = StrUtil.format_fraction( 2 * r * units_per_point );
+
+ description = "%s %s %s".printf( dstr, units_string, _("diameter") );
+ }
+ else
+ {
+ description = "%.5g %s %s".printf( 2 * r * units_per_point,
+ units_string,
+ _("diameter") );
+ }
+
+ return description;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ bool waste_flag )
+ {
+ double waste = 0.0;
+ if (waste_flag)
+ {
+ waste = this.waste;
+ }
+
+ cr.new_path();
+ cr.arc( r, r, r+waste, 0.0, 2*Math.PI );
+ cr.close_path();
+ }
+
+
+ }
+
+}
diff --git a/libglabels/template_layout.vala b/libglabels/template_layout.vala
new file mode 100644
index 0000000..a27d855
--- /dev/null
+++ b/libglabels/template_layout.vala
@@ -0,0 +1,86 @@
+/* template_layout.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+using Math;
+
+namespace libglabels
+{
+
+ public class TemplateLayout
+ {
+ public int nx { get; protected set; }
+ public int ny { get; protected set; }
+
+ public double x0 { get; protected set; }
+ public double y0 { get; protected set; }
+
+ public double dx { get; protected set; }
+ public double dy { get; protected set; }
+
+
+ public TemplateLayout( int nx,
+ int ny,
+ double x0,
+ double y0,
+ double dx,
+ double dy )
+ {
+ this.nx = nx;
+ this.ny = ny;
+
+ this.x0 = x0;
+ this.y0 = y0;
+
+ this.dx = dx;
+ this.dy = dy;
+ }
+
+
+ public TemplateLayout dup()
+ {
+ TemplateLayout copy = new TemplateLayout( nx, ny, x0, y0, dx, dy );
+
+ return copy;
+ }
+
+
+ public bool is_similar( TemplateLayout layout2 )
+ {
+ if ( (nx == layout2.nx) &&
+ (ny == layout2.ny) &&
+ (fabs(x0 - layout2.x0) < EPSILON) &&
+ (fabs(y0 - layout2.y0) < EPSILON) &&
+ (fabs(dx - layout2.dx) < EPSILON) &&
+ (fabs(dy - layout2.dy) < EPSILON) )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+ }
+
+}
diff --git a/libglabels/libglabels-private.h b/libglabels/template_markup.vala
similarity index 50%
rename from libglabels/libglabels-private.h
rename to libglabels/template_markup.vala
index 43ea4cd..88dbb63 100644
--- a/libglabels/libglabels-private.h
+++ b/libglabels/template_markup.vala
@@ -1,6 +1,6 @@
-/*
- * libglabels-private.h
- * Copyright (C) 2004-2009 Jim Evins <evins snaught com>.
+/* template_markup.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
*
* This file is part of libglabels.
*
@@ -18,32 +18,18 @@
* along with libglabels. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef __LIBGLABELS_PRIVATE_H__
-#define __LIBGLABELS_PRIVATE_H__
-
-#include <glib.h>
-
-#include "lgl-str.h"
-#include "lgl-template.h"
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "LibGlabels"
-#define UTF8_EQUAL(s1,s2) (!lgl_str_utf8_casecmp (s1, s2))
-#define ASCII_EQUAL(s1,s2) (!g_ascii_strcasecmp (s1, s2))
+using GLib;
-void _lgl_db_register_template_internal (const lglTemplate *template);
+namespace libglabels
+{
+ public abstract class TemplateMarkup
+ {
+ public abstract TemplateMarkup dup();
-#endif /* __LIBGLABELS_PRIVATE_H__ */
+ public abstract void cairo_path( Cairo.Context cr,
+ TemplateFrame frame );
+ }
-
-
-/*
- * Local Variables: -- emacs
- * mode: C -- emacs
- * c-basic-offset: 8 -- emacs
- * tab-width: 8 -- emacs
- * indent-tabs-mode: nil -- emacs
- * End: -- emacs
- */
+}
diff --git a/libglabels/template_markup_circle.vala b/libglabels/template_markup_circle.vala
new file mode 100644
index 0000000..ece0477
--- /dev/null
+++ b/libglabels/template_markup_circle.vala
@@ -0,0 +1,63 @@
+/* template_markup_circle.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public class TemplateMarkupCircle : TemplateMarkup
+ {
+ public double x0 { get; private set; }
+ public double y0 { get; private set; }
+
+ public double r { get; private set; }
+
+
+ public TemplateMarkupCircle( double x0,
+ double y0,
+ double r )
+ {
+ this.x0 = x0;
+ this.y0 = y0;
+
+ this.r = r;
+ }
+
+
+ public override TemplateMarkup dup()
+ {
+ TemplateMarkupCircle copy = new TemplateMarkupCircle( x0, y0, r );
+
+ return (TemplateMarkup)copy;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ TemplateFrame frame )
+ {
+ cr.arc( x0, y0, r, 0, 2*Math.PI );
+ cr.close_path();
+ }
+
+ }
+
+}
diff --git a/libglabels/template_markup_ellipse.vala b/libglabels/template_markup_ellipse.vala
new file mode 100644
index 0000000..988980a
--- /dev/null
+++ b/libglabels/template_markup_ellipse.vala
@@ -0,0 +1,83 @@
+/* template_markup_ellipse.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public class TemplateMarkupEllipse : TemplateMarkup
+ {
+ private const int ARC_FINE = 2;
+
+
+ public double x1 { get; private set; }
+ public double y1 { get; private set; }
+
+ public double w { get; private set; }
+ public double h { get; private set; }
+
+
+ public TemplateMarkupEllipse( double x1,
+ double y1,
+ double w,
+ double h )
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+
+ this.w = w;
+ this.h = h;
+ }
+
+
+ public override TemplateMarkup dup()
+ {
+ TemplateMarkupEllipse copy = new TemplateMarkupEllipse( x1, y1, w, h );
+
+ return (TemplateMarkup)copy;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ TemplateFrame frame )
+ {
+ cr.save();
+
+ cr.translate( x1, y1 );
+
+ cr.new_path();
+ cr.move_to( w, h/2 );
+ for ( int i_theta = ARC_FINE; i_theta <= 360; i_theta += ARC_FINE )
+ {
+ double x = (w/2) + (w/2) * Math.cos( i_theta * Math.PI / 180 );
+ double y = (h/2) + (h/2) * Math.sin( i_theta * Math.PI / 180 );
+
+ cr.line_to( x, y );
+ }
+ cr.close_path();
+
+ cr.restore();
+ }
+
+ }
+
+}
diff --git a/libglabels/template_markup_line.vala b/libglabels/template_markup_line.vala
new file mode 100644
index 0000000..d2ef427
--- /dev/null
+++ b/libglabels/template_markup_line.vala
@@ -0,0 +1,66 @@
+/* template_markup_line.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public class TemplateMarkupLine : TemplateMarkup
+ {
+ public double x1 { get; private set; }
+ public double y1 { get; private set; }
+
+ public double x2 { get; private set; }
+ public double y2 { get; private set; }
+
+
+ public TemplateMarkupLine( double x1,
+ double y1,
+ double x2,
+ double y2 )
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+
+ public override TemplateMarkup dup()
+ {
+ TemplateMarkupLine copy = new TemplateMarkupLine( x1, y1, x2, y2 );
+
+ return (TemplateMarkup)copy;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ TemplateFrame frame )
+ {
+ cr.move_to( x1, y1 );
+ cr.line_to( x2, y2 );
+ }
+
+ }
+
+}
diff --git a/libglabels/template_markup_margin.vala b/libglabels/template_markup_margin.vala
new file mode 100644
index 0000000..66475c7
--- /dev/null
+++ b/libglabels/template_markup_margin.vala
@@ -0,0 +1,171 @@
+/* template_markup_margin.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public class TemplateMarkupMargin : TemplateMarkup
+ {
+ private const int ARC_FINE = 2;
+
+
+ public double size { get; private set; } /* Margin size */
+
+
+ public TemplateMarkupMargin( double size )
+ {
+ this.size = size;
+ }
+
+
+ public override TemplateMarkup dup()
+ {
+ TemplateMarkupMargin copy = new TemplateMarkupMargin( size );
+
+ return (TemplateMarkup)copy;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ TemplateFrame frame )
+ {
+ if ( frame is TemplateFrameRect )
+ {
+ rect_cairo_path( cr, (TemplateFrameRect)frame );
+ }
+ else if ( frame is TemplateFrameEllipse )
+ {
+ ellipse_cairo_path( cr, (TemplateFrameEllipse)frame );
+ }
+ else if ( frame is TemplateFrameRound )
+ {
+ round_cairo_path( cr, (TemplateFrameRound)frame );
+ }
+ else if ( frame is TemplateFrameCD )
+ {
+ cd_cairo_path( cr, (TemplateFrameCD)frame );
+ }
+ else
+ {
+ assert_not_reached();
+ }
+ }
+
+
+ private void rect_cairo_path( Cairo.Context cr,
+ TemplateFrameRect frame )
+ {
+ double w, h;
+ frame.get_size( out w, out h );
+
+ w = w - 2*size;
+ h = h - 2*size;
+ double r = double.max( frame.r - size, 0.0);
+
+ if ( r == 0 )
+ {
+ cr.rectangle( size, size, w, h );
+ }
+ else
+ {
+ cr.new_path();
+ cr.arc_negative( size+r, size+r, r, 3*Math.PI/2, Math.PI );
+ cr.arc_negative( size+r, size+h-r, r, Math.PI, Math.PI/2 );
+ cr.arc_negative( size+w-r, size+h-r, r, Math.PI/2, 0 );
+ cr.arc_negative( size+w-r, size+r, r, 2*Math.PI, 3*Math.PI/2 );
+ cr.close_path();
+ }
+ }
+
+
+ private void ellipse_cairo_path( Cairo.Context cr,
+ TemplateFrameEllipse frame )
+ {
+ double w, h;
+ frame.get_size( out w, out h );
+
+ w = w - 2*size;
+ h = h - 2*size;
+
+ cr.save();
+ cr.translate(size, size);
+
+ cr.new_path();
+ cr.move_to( w, h/2 );
+ for ( int i_theta = ARC_FINE; i_theta <= 360; i_theta += ARC_FINE )
+ {
+ double x = (w/2) + (w/2) * Math.cos( i_theta * Math.PI / 180 );
+ double y = (h/2) + (h/2) * Math.sin( i_theta * Math.PI / 180 );
+
+ cr.line_to( x, y );
+ }
+ cr.close_path();
+
+ cr.restore();
+ }
+
+
+ private void round_cairo_path( Cairo.Context cr,
+ TemplateFrameRound frame )
+ {
+ cr.arc( frame.r, frame.r, frame.r-size, 0, 2*Math.PI );
+ cr.close_path();
+ }
+
+
+ private void cd_cairo_path( Cairo.Context cr,
+ TemplateFrameCD frame )
+ {
+ double w, h;
+ frame.get_size( out w, out h );
+
+ double xc = w/2;
+ double yc = h/2;
+
+ double r1 = frame.r1 - size;
+ double r2 = frame.r2 + size;
+
+ /*
+ * Outer path (may be clipped)
+ */
+ double theta1 = Math.acos( (w-2*size) / (2*r1) );
+ double theta2 = Math.asin( (h-2*size) / (2*r1) );
+
+ cr.new_path();
+ cr.arc( xc, yc, r1, theta1, theta2 );
+ cr.arc( xc, yc, r1, Math.PI-theta2, Math.PI-theta1 );
+ cr.arc( xc, yc, r1, Math.PI+theta1, Math.PI+theta2 );
+ cr.arc( xc, yc, r1, 2*Math.PI-theta2, 2*Math.PI-theta1 );
+ cr.close_path();
+
+
+ /* Inner path (hole) */
+ cr.new_sub_path();
+ cr.arc( xc, yc, r2, 0.0, 2*Math.PI );
+ cr.close_path();
+ }
+
+
+ }
+
+}
diff --git a/libglabels/template_markup_rect.vala b/libglabels/template_markup_rect.vala
new file mode 100644
index 0000000..c14f72b
--- /dev/null
+++ b/libglabels/template_markup_rect.vala
@@ -0,0 +1,82 @@
+/* template_markup_rect.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public class TemplateMarkupRect : TemplateMarkup
+ {
+ public double x1 { get; private set; }
+ public double y1 { get; private set; }
+
+ public double w { get; private set; }
+ public double h { get; private set; }
+
+ public double r { get; private set; }
+
+
+ public TemplateMarkupRect( double x1,
+ double y1,
+ double w,
+ double h,
+ double r )
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+
+ this.w = w;
+ this.h = h;
+
+ this.r = r;
+ }
+
+
+ public override TemplateMarkup dup()
+ {
+ TemplateMarkupRect copy = new TemplateMarkupRect( x1, y1, w, h, r );
+
+ return (TemplateMarkup)copy;
+ }
+
+
+ public override void cairo_path( Cairo.Context cr,
+ TemplateFrame frame )
+ {
+ if ( r == 0 )
+ {
+ cr.rectangle( x1, y1, w, h );
+ }
+ else
+ {
+ cr.new_path();
+ cr.arc_negative( x1+r, y1+r, r, 3*Math.PI/2, Math.PI );
+ cr.arc_negative( x1+r, y1+h-r, r, Math.PI, Math.PI/2 );
+ cr.arc_negative( x1+w-r, y1+h-r, r, Math.PI/2, 0 );
+ cr.arc_negative( x1+w-r, y1+r, r, 2*Math.PI, 3*Math.PI/2 );
+ cr.close_path();
+ }
+ }
+
+ }
+
+}
diff --git a/libglabels/units.vala b/libglabels/units.vala
new file mode 100644
index 0000000..70b1b2d
--- /dev/null
+++ b/libglabels/units.vala
@@ -0,0 +1,137 @@
+/* units.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ private const double POINTS_PER_POINT = 1.0; /* internal units are points. */
+ private const double POINTS_PER_INCH = 72.0;
+ private const double POINTS_PER_MM = 2.83464566929;
+ private const double POINTS_PER_CM = (10.0*POINTS_PER_MM);
+ private const double POINTS_PER_PICA = (1.0/12.0);
+
+
+ public struct Units
+ {
+ public string id { get; protected set; }
+ public string name { get; protected set; }
+ public double points_per_unit { get; protected set; }
+
+ public double units_per_point { get{ return 1.0 / points_per_unit; } }
+
+
+ private Units( string id,
+ string name,
+ double points_per_unit )
+ {
+ this.id = id;
+ this.name = name;
+ this.points_per_unit = points_per_unit;
+ }
+
+
+ public Units.from_id( string id )
+ {
+ string name;
+ double points_per_unit;
+
+ switch (id)
+ {
+
+ case "pt":
+ name = _("points");
+ points_per_unit = POINTS_PER_POINT;
+ break;
+
+ case "in":
+ name = _("inches");
+ points_per_unit = POINTS_PER_INCH;
+ break;
+
+ case "mm":
+ name = _("mm");
+ points_per_unit = POINTS_PER_MM;
+ break;
+
+ case "cm":
+ name = _("cm");
+ points_per_unit = POINTS_PER_CM;
+ break;
+
+ case "pc":
+ name = _("picas");
+ points_per_unit = POINTS_PER_PICA;
+ break;
+
+ case "":
+ /* Missing or empty units id defaults to points. */
+ id = "pt";
+ name = _("points");
+ points_per_unit = POINTS_PER_POINT;
+ break;
+
+ default:
+ warning( "Unknown Units.id \"%s\"", id );
+ id = "pt";
+ name = _("points");
+ points_per_unit = POINTS_PER_POINT;
+ break;
+
+ }
+
+ this( id, name, points_per_unit );
+ }
+
+ public Units.point()
+ {
+ this( "pt", _("points"), POINTS_PER_POINT );
+ }
+
+
+ public Units.inch()
+ {
+ this( "in", _("inches"), POINTS_PER_INCH );
+ }
+
+
+ public Units.mm()
+ {
+ this( "mm", _("mm"), POINTS_PER_MM );
+ }
+
+
+ public Units.cm()
+ {
+ this( "cm", _("cm"), POINTS_PER_CM );
+ }
+
+
+ public Units.pica()
+ {
+ this( "pc", _("picas"), POINTS_PER_PICA );
+ }
+
+
+ }
+
+}
diff --git a/libglabels/lgl-xml-paper.h b/libglabels/vendor.vala
similarity index 50%
rename from libglabels/lgl-xml-paper.h
rename to libglabels/vendor.vala
index e4704c6..269072a 100644
--- a/libglabels/lgl-xml-paper.h
+++ b/libglabels/vendor.vala
@@ -1,6 +1,6 @@
-/*
- * lgl-xml-paper.h
- * Copyright (C) 2003-2010 Jim Evins <evins snaught com>.
+/* vendor.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
*
* This file is part of libglabels.
*
@@ -18,34 +18,25 @@
* along with libglabels. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef __LGL_XML_PAPER_H__
-#define __LGL_XML_PAPER_H__
-
-#include <glib.h>
-#include <libxml/tree.h>
-
-#include "lgl-paper.h"
-
-G_BEGIN_DECLS
-GList *lgl_xml_paper_read_papers_from_file (gchar *utf8_filename);
+using GLib;
-GList *lgl_xml_paper_parse_papers_doc (xmlDocPtr papers_doc);
+namespace libglabels
+{
-lglPaper *lgl_xml_paper_parse_paper_node (xmlNodePtr paper_node);
+ public struct Vendor
+ {
+ public string name { get; private set; } /* Vendor name */
+ public string url { get; private set; } /* Vendor URL */
+ public Vendor( string name,
+ string url )
+ {
+ this.name = name;
+ this.url = url;
+ }
-G_END_DECLS
+ }
-#endif /* __LGL_XML_PAPER_H__ */
+}
-
-
-/*
- * Local Variables: -- emacs
- * mode: C -- emacs
- * c-basic-offset: 8 -- emacs
- * tab-width: 8 -- emacs
- * indent-tabs-mode: nil -- emacs
- * End: -- emacs
- */
diff --git a/libglabels/xml_category.vala b/libglabels/xml_category.vala
new file mode 100644
index 0000000..634426a
--- /dev/null
+++ b/libglabels/xml_category.vala
@@ -0,0 +1,104 @@
+/* xml_category.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ namespace XmlCategory
+ {
+
+ internal void read_categories_from_file( string utf8_filename )
+ {
+ string filename;
+ try
+ {
+ filename = Filename.from_utf8( utf8_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ message("Utf8 filename conversion: %s", e.message);
+ return;
+ }
+
+ unowned Xml.Doc* doc = Xml.Parser.parse_file( filename );
+ if ( doc == null )
+ {
+ message("\"%s\" is not a glabels category file (not XML)", filename);
+ return;
+ }
+
+ parse_categories_doc( doc );
+ }
+
+
+ internal void parse_categories_doc( Xml.Doc doc )
+ {
+ unowned Xml.Node* root = doc.get_root_element();
+ if ( (root == null) || (root->name == null) )
+ {
+ message("\"%s\" is not a glabels category file (no root node)", doc.name);
+ return;
+ }
+
+ if ( root->name != "Glabels-categories" )
+ {
+ message ("\"%s\" is not a glabels category file (wrong root node)", doc.name);
+ return;
+ }
+
+ for ( unowned Xml.Node* node = root->children; node != null; node = node->next )
+ {
+ if ( node->name == "Category" )
+ {
+ Category category = parse_category_node( node );
+ Db.register_category( category );
+ }
+ else
+ {
+ if ( node->is_text() == 0 )
+ {
+ if ( node->name != "comment" )
+ {
+ message("bad node = \"%s\"", node->name);
+ }
+ }
+ }
+ }
+
+ }
+
+
+ internal Category parse_category_node( Xml.Node node )
+ {
+ string? id = XmlUtil.get_prop_string( node, "id", null );
+ string? name = XmlUtil.get_prop_i18n_string( node, "name", null );
+
+ Category category = Category( id, name );
+
+ return category;
+ }
+
+
+ }
+
+}
diff --git a/libglabels/xml_paper.vala b/libglabels/xml_paper.vala
new file mode 100644
index 0000000..ac72d8a
--- /dev/null
+++ b/libglabels/xml_paper.vala
@@ -0,0 +1,109 @@
+/* xml_paper.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ namespace XmlPaper
+ {
+
+ internal void read_papers_from_file( string utf8_filename )
+ {
+ string filename;
+ try
+ {
+ filename = Filename.from_utf8( utf8_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ message("Utf8 filename conversion: %s", e.message);
+ return;
+ }
+
+ unowned Xml.Doc* doc = Xml.Parser.parse_file( filename );
+ if ( doc == null )
+ {
+ message("\"%s\" is not a glabels paper file (not XML)", filename);
+ return;
+ }
+
+ parse_papers_doc( doc );
+ }
+
+
+ internal void parse_papers_doc( Xml.Doc doc )
+ {
+ unowned Xml.Node* root = doc.get_root_element();
+ if ( (root == null) || (root->name == null) )
+ {
+ message("\"%s\" is not a glabels paper file (no root node)", doc.name);
+ return;
+ }
+
+ if ( root->name != "Glabels-paper-sizes" )
+ {
+ message ("\"%s\" is not a glabels paper file (wrong root node)", doc.name);
+ return;
+ }
+
+ for ( unowned Xml.Node* node = root->children; node != null; node = node->next )
+ {
+ if ( node->name == "Paper-size" )
+ {
+ Paper paper = parse_paper_node( node );
+ Db.register_paper( paper );
+ }
+ else
+ {
+ if ( node->is_text() == 0 )
+ {
+ if ( node->name != "comment" )
+ {
+ message("bad node = \"%s\"", node->name);
+ }
+ }
+ }
+ }
+
+ }
+
+
+ internal Paper parse_paper_node( Xml.Node node )
+ {
+ string? id = XmlUtil.get_prop_string( node, "id", null );
+ string? name = XmlUtil.get_prop_i18n_string( node, "name", null );
+
+ double width = XmlUtil.get_prop_length( node, "width", 0 );
+ double height = XmlUtil.get_prop_length( node, "height", 0 );
+
+ string? pwg_size = XmlUtil.get_prop_string( node, "pwg_size", null );
+
+ Paper paper = Paper( id, name, width, height, pwg_size );
+
+ return paper;
+ }
+
+
+ }
+
+}
diff --git a/libglabels/xml_template.vala b/libglabels/xml_template.vala
new file mode 100644
index 0000000..aff1719
--- /dev/null
+++ b/libglabels/xml_template.vala
@@ -0,0 +1,744 @@
+/* xml_template.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public const string NAME_SPACE = "http://glabels.org/xmlns/3.0/";
+
+ namespace XmlTemplate
+ {
+
+ internal void read_templates_from_file( string utf8_filename )
+ {
+ string filename;
+ try
+ {
+ filename = Filename.from_utf8( utf8_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ message("Utf8 filename conversion: %s", e.message);
+ return;
+ }
+
+ unowned Xml.Doc* doc = Xml.Parser.parse_file( filename );
+ if ( doc == null )
+ {
+ message("\"%s\" is not a glabels template file (not XML)", filename);
+ return;
+ }
+
+ parse_templates_doc( doc );
+ }
+
+
+ internal void parse_templates_doc( Xml.Doc doc )
+ {
+ unowned Xml.Node* root = doc.get_root_element();
+ if ( (root == null) || (root->name == null) )
+ {
+ message("\"%s\" is not a glabels template file (no root node)", doc.name);
+ return;
+ }
+
+ if ( root->name != "Glabels-templates" )
+ {
+ message ("\"%s\" is not a glabels template file (wrong root node)", doc.name);
+ return;
+ }
+
+ for ( unowned Xml.Node* node = root->children; node != null; node = node->next )
+ {
+ if ( node->name == "Template" )
+ {
+ Template template = parse_template_node( node );
+ Db.register_template( template );
+ }
+ else
+ {
+ if ( node->is_text() == 0 )
+ {
+ if ( node->name != "comment" )
+ {
+ message("bad node = \"%s\"", node->name);
+ }
+ }
+ }
+ }
+
+ }
+
+
+ public Template? parse_template_node( Xml.Node template_node )
+ {
+ Template? template = null;
+
+ string? brand = XmlUtil.get_prop_string( template_node, "brand", null );
+ string? part = XmlUtil.get_prop_string( template_node, "part", null );
+
+ if ( (brand == null) || (part == null) )
+ {
+ /* Try the deprecated "name" property. */
+ string? name = XmlUtil.get_prop_string( template_node, "name", null );
+ if ( name != null )
+ {
+ string[] fields = name.split( " ", 2 );
+ brand = fields[0];
+ part = fields[1];
+ }
+ else
+ {
+ message("Missing name or brand/part attributes.");
+ return null;
+ }
+ }
+
+
+ string? equiv_part = XmlUtil.get_prop_string( template_node, "equiv", null );
+
+ if ( equiv_part != null )
+ {
+
+ template = new Template.from_equiv( brand, part, equiv_part );
+
+ for ( unowned Xml.Node* node = template_node.children; node != null; node = node->next )
+ {
+ if ( node->name == "Meta" )
+ {
+ parse_meta_node( node, template );
+ }
+ else if ( node->is_text() == 0 )
+ {
+ if ( node->name != "comment" )
+ {
+ message("bad node = \"%s\"", node->name);
+ }
+ }
+ }
+
+ }
+ else
+ {
+
+ string? description = XmlUtil.get_prop_i18n_string( template_node, "description", null );
+ string? paper_id = XmlUtil.get_prop_string( template_node, "size", null );
+
+ if ( !Db.is_paper_id_other( paper_id ) )
+ {
+ Paper? paper = Db.lookup_paper_from_id( paper_id );
+ if ( paper == null )
+ {
+ message("Invalid paper ID \"%s\".", paper_id );
+ return null;
+ }
+
+ template = new Template( brand, part, description,
+ paper.id, paper.width, paper.height );
+ }
+ else
+ {
+ double width = XmlUtil.get_prop_length( template_node, "width", 0 );
+ double height = XmlUtil.get_prop_length( template_node, "height", 0 );
+ template = new Template( brand, part, description,
+ paper_id, width, height );
+ }
+
+ for ( unowned Xml.Node* node = template_node.children; node != null; node = node->next )
+ {
+ if ( node->name == "Meta" )
+ {
+ parse_meta_node( node, template );
+ }
+ else if ( node->name == "Label-rectangle" )
+ {
+ parse_label_rectangle_node( node, template );
+ }
+ else if ( node->name == "Label-ellipse" )
+ {
+ parse_label_ellipse_node( node, template );
+ }
+ else if ( node->name == "Label-round" )
+ {
+ parse_label_round_node( node, template );
+ }
+ else if ( node->name == "Label-cd" )
+ {
+ parse_label_cd_node( node, template );
+ }
+ else if ( node->is_text() == 0 )
+ {
+ if ( node->name != "comment" )
+ {
+ message("bad node = \"%s\"", node->name);
+ }
+ }
+ }
+
+ template.init_preview_pixbuf();
+
+ }
+
+ return template;
+ }
+
+
+ private void parse_meta_node( Xml.Node meta_node,
+ Template template )
+ {
+ string? product_url = XmlUtil.get_prop_string( meta_node, "product_url", null );
+ if ( product_url != null )
+ {
+ template.product_url = product_url;
+ }
+
+ string? category_id = XmlUtil.get_prop_string( meta_node, "category", null );
+ if ( category_id != null )
+ {
+ template.add_category( category_id );
+ }
+
+ parse_empty_node_common( meta_node );
+ }
+
+
+ private void parse_label_rectangle_node( Xml.Node label_node,
+ Template template )
+ {
+ double x_waste, y_waste;
+
+ string? id = XmlUtil.get_prop_string( label_node, "id", null );
+
+ double w = XmlUtil.get_prop_length( label_node, "width", 0 );
+ double h = XmlUtil.get_prop_length( label_node, "height", 0 );
+ double r = XmlUtil.get_prop_length( label_node, "round", 0 );
+
+ if ( XmlUtil.get_prop_string( label_node, "waste", null ) != null )
+ {
+ x_waste = y_waste = XmlUtil.get_prop_length( label_node, "waste", 0 );
+ }
+ else
+ {
+ x_waste = XmlUtil.get_prop_length( label_node, "x_waste", 0 );
+ y_waste = XmlUtil.get_prop_length( label_node, "y_waste", 0 );
+ }
+
+ TemplateFrame frame = new TemplateFrameRect( id, w, h, r, x_waste, y_waste );
+ template.add_frame( frame );
+
+ parse_label_node_common( label_node, frame );
+ }
+
+
+ private void parse_label_ellipse_node( Xml.Node label_node,
+ Template template )
+ {
+ string? id = XmlUtil.get_prop_string( label_node, "id", null );
+
+ double w = XmlUtil.get_prop_length( label_node, "width", 0 );
+ double h = XmlUtil.get_prop_length( label_node, "height", 0 );
+ double waste = XmlUtil.get_prop_length( label_node, "waste", 0 );
+
+ TemplateFrame frame = new TemplateFrameEllipse( id, w, h, waste );
+ template.add_frame( frame );
+
+ parse_label_node_common( label_node, frame );
+ }
+
+
+ private void parse_label_round_node( Xml.Node label_node,
+ Template template )
+ {
+ string? id = XmlUtil.get_prop_string( label_node, "id", null );
+
+ double r = XmlUtil.get_prop_length( label_node, "radius", 0 );
+ double waste = XmlUtil.get_prop_length( label_node, "waste", 0 );
+
+ TemplateFrame frame = new TemplateFrameRound( id, r, waste );
+ template.add_frame( frame );
+
+ parse_label_node_common( label_node, frame );
+ }
+
+
+ private void parse_label_cd_node( Xml.Node label_node,
+ Template template )
+ {
+ string? id = XmlUtil.get_prop_string( label_node, "id", null );
+
+ double r1 = XmlUtil.get_prop_length( label_node, "radius", 0 );
+ double r2 = XmlUtil.get_prop_length( label_node, "hole", 0 );
+ double w = XmlUtil.get_prop_length( label_node, "width", 0 );
+ double h = XmlUtil.get_prop_length( label_node, "height", 0 );
+ double waste = XmlUtil.get_prop_length( label_node, "waste", 0 );
+
+ TemplateFrame frame = new TemplateFrameCD( id, r1, r2, w, h, waste );
+ template.add_frame( frame );
+
+ parse_label_node_common( label_node, frame );
+ }
+
+
+ private void parse_label_node_common( Xml.Node label_node,
+ TemplateFrame frame )
+ {
+ for ( unowned Xml.Node* node = label_node.children; node != null; node = node->next )
+ {
+ if ( node->name == "Layout" )
+ {
+ parse_layout_node( node, frame );
+ }
+ else if ( node->name == "Markup-margin" )
+ {
+ parse_markup_margin_node( node, frame );
+ }
+ else if ( node->name == "Markup-line" )
+ {
+ parse_markup_line_node( node, frame );
+ }
+ else if ( node->name == "Markup-circle" )
+ {
+ parse_markup_circle_node( node, frame );
+ }
+ else if ( node->name == "Markup-rect" )
+ {
+ parse_markup_rect_node( node, frame );
+ }
+ else if ( node->name == "Markup-ellipse" )
+ {
+ parse_markup_ellipse_node( node, frame );
+ }
+ else if ( node->is_text() == 0 )
+ {
+ if ( node->name != "comment" )
+ {
+ message("bad node = \"%s\"", node->name);
+ }
+ }
+
+ }
+ }
+
+
+ private void parse_layout_node( Xml.Node label_node,
+ TemplateFrame frame )
+ {
+ int nx = XmlUtil.get_prop_int( label_node, "nx", 1 );
+ int ny = XmlUtil.get_prop_int( label_node, "ny", 1 );
+
+ double x0 = XmlUtil.get_prop_length( label_node, "x0", 0 );
+ double y0 = XmlUtil.get_prop_length( label_node, "y0", 0 );
+
+ double dx = XmlUtil.get_prop_length( label_node, "dx", 0 );
+ double dy = XmlUtil.get_prop_length( label_node, "dy", 0 );
+
+ frame.add_layout( new TemplateLayout( nx, ny, x0, y0, dx, dy ) );
+
+ parse_empty_node_common( label_node );
+ }
+
+
+ private void parse_markup_margin_node( Xml.Node label_node,
+ TemplateFrame frame )
+ {
+ double size = XmlUtil.get_prop_length( label_node, "size", 0 );
+
+ frame.add_markup( new TemplateMarkupMargin( size ) );
+
+ parse_empty_node_common( label_node );
+ }
+
+
+ private void parse_markup_line_node( Xml.Node label_node,
+ TemplateFrame frame )
+ {
+ double x1 = XmlUtil.get_prop_length( label_node, "x1", 0 );
+ double y1 = XmlUtil.get_prop_length( label_node, "y1", 0 );
+ double x2 = XmlUtil.get_prop_length( label_node, "x2", 0 );
+ double y2 = XmlUtil.get_prop_length( label_node, "y2", 0 );
+
+ frame.add_markup( new TemplateMarkupLine( x1, y1, x2, y2 ) );
+
+ parse_empty_node_common( label_node );
+ }
+
+
+ private void parse_markup_circle_node( Xml.Node label_node,
+ TemplateFrame frame )
+ {
+ double x0 = XmlUtil.get_prop_length( label_node, "x0", 0 );
+ double y0 = XmlUtil.get_prop_length( label_node, "y0", 0 );
+ double r = XmlUtil.get_prop_length( label_node, "radius", 0 );
+
+ frame.add_markup( new TemplateMarkupCircle( x0, y0, r ) );
+
+ parse_empty_node_common( label_node );
+ }
+
+
+ private void parse_markup_rect_node( Xml.Node label_node,
+ TemplateFrame frame )
+ {
+ double x1 = XmlUtil.get_prop_length( label_node, "x1", 0 );
+ double y1 = XmlUtil.get_prop_length( label_node, "y1", 0 );
+ double w = XmlUtil.get_prop_length( label_node, "w", 0 );
+ double h = XmlUtil.get_prop_length( label_node, "h", 0 );
+ double r = XmlUtil.get_prop_length( label_node, "r", 0 );
+
+ frame.add_markup( new TemplateMarkupRect( x1, y1, w, h, r ) );
+
+ parse_empty_node_common( label_node );
+ }
+
+
+ private void parse_markup_ellipse_node( Xml.Node label_node,
+ TemplateFrame frame )
+ {
+ double x1 = XmlUtil.get_prop_length( label_node, "x1", 0 );
+ double y1 = XmlUtil.get_prop_length( label_node, "y1", 0 );
+ double w = XmlUtil.get_prop_length( label_node, "w", 0 );
+ double h = XmlUtil.get_prop_length( label_node, "h", 0 );
+
+ frame.add_markup( new TemplateMarkupEllipse( x1, y1, w, h ) );
+
+ parse_empty_node_common( label_node );
+ }
+
+
+ private void parse_empty_node_common( Xml.Node empty_node )
+ {
+ for ( unowned Xml.Node* node = empty_node.children; node != null; node = node->next )
+ {
+ if ( node->is_text() == 0 )
+ {
+ if ( node->name != "comment" )
+ {
+ message("bad node = \"%s\"", node->name);
+ }
+ }
+
+ }
+ }
+
+
+ internal int write_templates_to_file( List<Template> templates,
+ string utf8_filename )
+ {
+ Xml.Doc doc = new Xml.Doc( "1.0" );
+ unowned Xml.Node *root_node = new Xml.Node(null, "Glabels-templates");
+ doc.set_root_element( root_node );
+ unowned Xml.Ns *ns = new Xml.Ns( root_node, NAME_SPACE, "glabels" );
+ root_node->ns = ns;
+
+ foreach ( Template template in templates )
+ {
+ create_template_node( template, root_node, ns );
+ }
+
+ string filename;
+ try
+ {
+ filename = Filename.from_utf8( utf8_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ message("Utf8 filename conversion: %s", e.message);
+ return 0;
+ }
+
+ doc.set_compress_mode( 0 );
+ return doc.save_format_file( filename, 1 );
+ }
+
+
+ internal int write_template_to_file( Template template,
+ string utf8_filename )
+ {
+ List<Template> templates = null;
+
+ templates.append( template );
+
+ return write_templates_to_file( templates, utf8_filename );
+ }
+
+
+ public void create_template_node( Template template,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Template" );
+
+ XmlUtil.set_prop_string( node, "brand", template.brand );
+ XmlUtil.set_prop_string( node, "part", template.part );
+
+ XmlUtil.set_prop_string( node, "size", template.paper_id );
+ if ( template.paper_id == "Other" )
+ {
+ XmlUtil.set_prop_length( node, "width", template.page_width );
+ XmlUtil.set_prop_length( node, "height", template.page_height );
+ }
+
+ XmlUtil.set_prop_string( node, "description", template.description );
+
+ create_meta_node( "product_url", template.product_url, node, ns );
+ foreach ( string category_id in template.category_ids )
+ {
+ create_meta_node( "category", category_id, node, ns );
+ }
+
+ foreach ( TemplateFrame frame in template.frames )
+ {
+ create_label_node( frame, node, ns );
+ }
+ }
+
+
+ private void create_meta_node( string attr,
+ string? value,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ if ( value != null )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Meta" );
+ XmlUtil.set_prop_string( node, attr, value );
+ }
+ }
+
+
+ private void create_label_node( TemplateFrame frame,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ if ( frame is TemplateFrameRect )
+ {
+ create_label_rectangle_node( (TemplateFrameRect)frame, root, ns );
+ }
+ else if ( frame is TemplateFrameEllipse )
+ {
+ create_label_ellipse_node( (TemplateFrameEllipse)frame, root, ns );
+ }
+ else if ( frame is TemplateFrameRound )
+ {
+ create_label_round_node( (TemplateFrameRound)frame, root, ns );
+ }
+ else if ( frame is TemplateFrameCD )
+ {
+ create_label_cd_node( (TemplateFrameCD)frame, root, ns );
+ }
+ else
+ {
+ error( "Unknown label style" );
+ }
+
+ }
+
+
+ private void create_label_rectangle_node( TemplateFrameRect frame,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Label-rectangle" );
+
+ XmlUtil.set_prop_string( node, "id", frame.id );
+ XmlUtil.set_prop_length( node, "width", frame.w );
+ XmlUtil.set_prop_length( node, "height", frame.h );
+ XmlUtil.set_prop_length( node, "round", frame.r );
+ XmlUtil.set_prop_length( node, "x_waste", frame.x_waste );
+ XmlUtil.set_prop_length( node, "y_waste", frame.y_waste );
+
+ create_label_node_common( frame, node, ns );
+ }
+
+
+ private void create_label_ellipse_node( TemplateFrameEllipse frame,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Label-ellipse" );
+
+ XmlUtil.set_prop_string( node, "id", frame.id );
+ XmlUtil.set_prop_length( node, "width", frame.w );
+ XmlUtil.set_prop_length( node, "height", frame.h );
+ XmlUtil.set_prop_length( node, "waste", frame.waste );
+
+ create_label_node_common( frame, node, ns );
+ }
+
+
+ private void create_label_round_node( TemplateFrameRound frame,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Label-round" );
+
+ XmlUtil.set_prop_string( node, "id", frame.id );
+ XmlUtil.set_prop_length( node, "radius", frame.r );
+ XmlUtil.set_prop_length( node, "waste", frame.waste );
+
+ create_label_node_common( frame, node, ns );
+ }
+
+
+ private void create_label_cd_node( TemplateFrameCD frame,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Label-cd" );
+
+ XmlUtil.set_prop_string( node, "id", frame.id );
+ XmlUtil.set_prop_length( node, "radius", frame.r1 );
+ XmlUtil.set_prop_length( node, "hole", frame.r2 );
+ XmlUtil.set_prop_length( node, "waste", frame.waste );
+ if ( frame.w != 0 )
+ {
+ XmlUtil.set_prop_length( node, "width", frame.w );
+ }
+ if ( frame.h != 0 )
+ {
+ XmlUtil.set_prop_length( node, "height", frame.h );
+ }
+
+ create_label_node_common( frame, node, ns );
+ }
+
+
+ private void create_label_node_common( TemplateFrame frame,
+ Xml.Node node,
+ Xml.Ns ns )
+ {
+ foreach ( TemplateMarkup markup in frame.markups )
+ {
+ if ( markup is TemplateMarkupMargin )
+ {
+ create_markup_margin_node( (TemplateMarkupMargin)markup, node, ns );
+ }
+ else if ( markup is TemplateMarkupLine )
+ {
+ create_markup_line_node( (TemplateMarkupLine)markup, node, ns );
+ }
+ else if ( markup is TemplateMarkupCircle )
+ {
+ create_markup_circle_node( (TemplateMarkupCircle)markup, node, ns );
+ }
+ else if ( markup is TemplateMarkupRect )
+ {
+ create_markup_rect_node( (TemplateMarkupRect)markup, node, ns );
+ }
+ else if ( markup is TemplateMarkupEllipse )
+ {
+ create_markup_ellipse_node( (TemplateMarkupEllipse)markup, node, ns );
+ }
+ else
+ {
+ error( "Unknown markup type" );
+ }
+ }
+
+ foreach ( TemplateLayout layout in frame.layouts )
+ {
+ create_layout_node( layout, node, ns );
+ }
+ }
+
+
+ private void create_markup_margin_node( TemplateMarkupMargin markup,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Markup-margin" );
+
+ XmlUtil.set_prop_length( node, "size", markup.size );
+ }
+
+
+ private void create_markup_line_node( TemplateMarkupLine markup,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Markup-line" );
+
+ XmlUtil.set_prop_length( node, "x1", markup.x1 );
+ XmlUtil.set_prop_length( node, "y1", markup.y1 );
+ XmlUtil.set_prop_length( node, "x2", markup.x2 );
+ XmlUtil.set_prop_length( node, "y2", markup.y2 );
+ }
+
+
+ private void create_markup_circle_node( TemplateMarkupCircle markup,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Markup-circle" );
+
+ XmlUtil.set_prop_length( node, "x0", markup.x0 );
+ XmlUtil.set_prop_length( node, "y0", markup.y0 );
+ XmlUtil.set_prop_length( node, "radius", markup.r );
+ }
+
+
+ private void create_markup_rect_node( TemplateMarkupRect markup,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Markup-rect" );
+
+ XmlUtil.set_prop_length( node, "x1", markup.x1 );
+ XmlUtil.set_prop_length( node, "y1", markup.y1 );
+ XmlUtil.set_prop_length( node, "w", markup.w );
+ XmlUtil.set_prop_length( node, "h", markup.h );
+ XmlUtil.set_prop_length( node, "r", markup.r );
+ }
+
+
+ private void create_markup_ellipse_node( TemplateMarkupEllipse markup,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Markup-ellipse" );
+
+ XmlUtil.set_prop_length( node, "x1", markup.x1 );
+ XmlUtil.set_prop_length( node, "y1", markup.y1 );
+ XmlUtil.set_prop_length( node, "w", markup.w );
+ XmlUtil.set_prop_length( node, "h", markup.h );
+ }
+
+
+ private void create_layout_node( TemplateLayout layout,
+ Xml.Node root,
+ Xml.Ns ns )
+ {
+ unowned Xml.Node *node = root.new_child( ns, "Layout" );
+
+ XmlUtil.set_prop_int( node, "nx", layout.nx );
+ XmlUtil.set_prop_int( node, "ny", layout.ny );
+ XmlUtil.set_prop_length( node, "x0", layout.x0 );
+ XmlUtil.set_prop_length( node, "y0", layout.y0 );
+ XmlUtil.set_prop_length( node, "dx", layout.dx );
+ XmlUtil.set_prop_length( node, "dy", layout.dy );
+ }
+
+
+ }
+
+}
diff --git a/libglabels/xml_util.vala b/libglabels/xml_util.vala
new file mode 100644
index 0000000..b36d4ee
--- /dev/null
+++ b/libglabels/xml_util.vala
@@ -0,0 +1,244 @@
+/* xml_util.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ public class XmlUtil
+ {
+ public static Units default_units { get; set; }
+
+
+ public static void init()
+ {
+ /* Force construction of static fields. */
+ new XmlUtil();
+ }
+
+
+ static construct {
+ default_units = Units.point();
+ }
+
+
+ public static string? get_prop_string( Xml.Node node,
+ string property,
+ string? default_val )
+ {
+ string? val;
+
+ val = node.get_prop( property );
+ if ( val != null )
+ {
+ return val;
+ }
+
+ return default_val;
+ }
+
+
+ public static string? get_prop_i18n_string( Xml.Node node,
+ string property,
+ string? default_val )
+ {
+ string i18n_property = "_%s".printf( property );
+ string? val;
+
+ val = node.get_prop( i18n_property );
+
+ if ( val != null )
+ {
+ return dgettext( null, val );
+ }
+
+ val = node.get_prop( property );
+ if ( val != null )
+ {
+ return val;
+ }
+
+ return default_val;
+ }
+
+
+ public static double get_prop_double( Xml.Node node,
+ string property,
+ double default_val )
+ {
+ string? val_string;
+
+ val_string = node.get_prop( property );
+ if ( val_string != null )
+ {
+ return double.parse( val_string );
+ }
+
+ return default_val;
+ }
+
+
+ public static bool get_prop_bool( Xml.Node node,
+ string property,
+ bool default_val )
+ {
+ string? val_string;
+
+ val_string = node.get_prop( property );
+ if ( val_string != null )
+ {
+ switch (val_string)
+ {
+ case "True":
+ case "TRUE":
+ case "true":
+ case "1":
+ return true;
+
+ default:
+ return bool.parse( val_string );
+ }
+ }
+
+ return default_val;
+ }
+
+
+ public static int get_prop_int( Xml.Node node,
+ string property,
+ int default_val )
+ {
+ string? val_string;
+
+ val_string = node.get_prop( property );
+ if ( val_string != null )
+ {
+ return int.parse( val_string );
+ }
+
+ return default_val;
+ }
+
+
+ public static uint get_prop_uint( Xml.Node node,
+ string property,
+ uint default_val )
+ {
+ string? val_string;
+
+ val_string = node.get_prop( property );
+ if ( val_string != null )
+ {
+ return (uint)uint64.parse( val_string );
+ }
+
+ return default_val;
+ }
+
+
+ public static double get_prop_length( Xml.Node node,
+ string property,
+ double default_val )
+ {
+ string? val_string;
+
+ val_string = node.get_prop( property );
+ if ( val_string != null )
+ {
+ double val;
+ string units_id = string.nfill( val_string.length, 0 );
+
+ val_string.scanf( "%lg%s", out val, units_id );
+
+ Units units = Units.from_id( units_id );
+ val *= units.points_per_unit;
+
+ return val;
+ }
+
+ return default_val;
+ }
+
+
+ public static void set_prop_string( Xml.Node node,
+ string property,
+ string? val )
+ {
+ if ( val != null )
+ {
+ node.set_prop( property, val );
+ }
+ }
+
+
+ public static void set_prop_double( Xml.Node node,
+ string property,
+ double val )
+ {
+ string val_string = "%g".printf( val );
+ node.set_prop( property, val_string );
+ }
+
+
+ public static void set_prop_bool( Xml.Node node,
+ string property,
+ bool val )
+ {
+ node.set_prop( property, val ? "true" : "false" );
+ }
+
+
+ public static void set_prop_int( Xml.Node node,
+ string property,
+ int val )
+ {
+ string val_string = "%d".printf( val );
+ node.set_prop( property, val_string );
+ }
+
+
+ public static void set_prop_uint_hex( Xml.Node node,
+ string property,
+ uint val )
+ {
+ string val_string = "0x%08x".printf( val );
+ node.set_prop( property, val_string );
+ }
+
+
+ public static void set_prop_length( Xml.Node node,
+ string property,
+ double val )
+ {
+ string units_id = default_units.id;
+ double units_per_point = default_units.units_per_point;
+
+ val *= units_per_point;
+
+ string val_string = "%g%s".printf( val, units_id );
+ node.set_prop( property, val_string );
+ }
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/libglabels/xml_vendor.vala b/libglabels/xml_vendor.vala
new file mode 100644
index 0000000..683470c
--- /dev/null
+++ b/libglabels/xml_vendor.vala
@@ -0,0 +1,104 @@
+/* xml_vendor.vala
+ *
+ * Copyright (C) 2011 Jim Evins <evins snaught com>
+ *
+ * This file is part of libglabels.
+ *
+ * libglabels is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libglabels is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libglabels. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace libglabels
+{
+
+ namespace XmlVendor
+ {
+
+ internal void read_vendors_from_file( string utf8_filename )
+ {
+ string filename;
+ try
+ {
+ filename = Filename.from_utf8( utf8_filename, -1, null, null );
+ }
+ catch ( ConvertError e )
+ {
+ message("Utf8 filename conversion: %s", e.message);
+ return;
+ }
+
+ unowned Xml.Doc* doc = Xml.Parser.parse_file( filename );
+ if ( doc == null )
+ {
+ message("\"%s\" is not a glabels vendor file (not XML)", filename);
+ return;
+ }
+
+ parse_vendors_doc( doc );
+ }
+
+
+ internal void parse_vendors_doc( Xml.Doc doc )
+ {
+ unowned Xml.Node* root = doc.get_root_element();
+ if ( (root == null) || (root->name == null) )
+ {
+ message("\"%s\" is not a glabels vendor file (no root node)", doc.name);
+ return;
+ }
+
+ if ( root->name != "Glabels-vendors" )
+ {
+ message ("\"%s\" is not a glabels vendor file (wrong root node)", doc.name);
+ return;
+ }
+
+ for ( unowned Xml.Node* node = root->children; node != null; node = node->next )
+ {
+ if ( node->name == "Vendor" )
+ {
+ Vendor vendor = parse_vendor_node( node );
+ Db.register_vendor( vendor );
+ }
+ else
+ {
+ if ( node->is_text() == 0 )
+ {
+ if ( node->name != "comment" )
+ {
+ message("bad node = \"%s\"", node->name);
+ }
+ }
+ }
+ }
+
+ }
+
+
+ internal Vendor parse_vendor_node( Xml.Node node )
+ {
+ string? name = XmlUtil.get_prop_string( node, "name", null );
+ string? url = XmlUtil.get_prop_string( node, "url", null );
+
+ Vendor vendor = Vendor( name, url );
+
+ return vendor;
+ }
+
+
+ }
+
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 24905c7..a849cb5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,252 +1,15 @@
-# List of source files containing translatable strings.
-
-src/bc-backends.c
-src/bc-backends.h
-src/bc-builtin.c
-src/bc-builtin.h
-src/bc-gnubarcode.c
-src/bc-gnubarcode.h
-src/bc-iec16022.c
-src/bc-iec16022.h
-src/bc-iec18004.c
-src/bc-iec18004.h
-src/bc-zint.c
-src/bc-zint.h
-src/builder-util.c
-src/builder-util.h
-src/cairo-ellipse-path.c
-src/cairo-ellipse-path.h
-src/cairo-label-path.c
-src/cairo-label-path.h
-src/cairo-markup-path.c
-src/cairo-markup-path.h
-src/color.c
-src/color.h
-src/color-combo-button.c
-src/color-combo-button.h
-src/color-combo.c
-src/color-combo-color-menu-item.c
-src/color-combo-color-menu-item.h
-src/color-combo.h
-src/color-combo-menu.c
-src/color-combo-menu.h
-src/color-history-model.c
-src/color-history-model.h
-src/color-swatch.c
-src/color-swatch.h
-src/combo-util.c
-src/combo-util.h
-src/critical-error-handler.c
-src/critical-error-handler.h
-src/debug.c
-src/debug.h
-src/field-button.c
-src/field-button.h
-src/field-button-menu.c
-src/field-button-menu.h
-src/file.c
-src/file.h
-src/file-util.c
-src/file-util.h
-src/font-combo.c
-src/font-combo.h
-src/font-combo-menu.c
-src/font-combo-menu.h
-src/font-combo-menu-item.c
-src/font-combo-menu-item.h
-src/font-history.c
-src/font-history.h
-src/font-history-model.c
-src/font-history-model.h
-src/font-sample.c
-src/font-sample.h
-src/font-util.c
-src/font-util.h
-src/glabels-batch.c
-src/glabels.c
-src/label-barcode.c
-src/label-barcode.h
-src/label-box.c
-src/label-box.h
-src/label.c
-src/label-ellipse.c
-src/label-ellipse.h
-src/label.h
-src/label-image.c
-src/label-image.h
-src/label-line.c
-src/label-line.h
-src/label-object.c
-src/label-object.h
-src/label-text.c
-src/label-text.h
-src/media-select.c
-src/media-select.h
-src/merge.c
-src/merge.h
-src/merge-evolution.c
-src/merge-evolution.h
-src/merge-init.c
-src/merge-init.h
-src/merge-properties-dialog.c
-src/merge-properties-dialog.h
-src/merge-text.c
-src/merge-text.h
-src/merge-vcard.c
-src/merge-vcard.h
-src/mini-label-preview.c
-src/mini-label-preview.h
-src/mini-preview.c
-src/mini-preview.h
-src/mini-preview-pixbuf.c
-src/mini-preview-pixbuf-cache.c
-src/mini-preview-pixbuf-cache.h
-src/mini-preview-pixbuf.h
-src/new-label-dialog.c
-src/new-label-dialog.h
-src/object-editor-bc-page.c
-src/object-editor.c
-src/object-editor-data-page.c
-src/object-editor-edit-page.c
-src/object-editor-fill-page.c
-src/object-editor.h
-src/object-editor-image-page.c
-src/object-editor-line-page.c
-src/object-editor-lsize-page.c
-src/object-editor-position-page.c
-src/object-editor-private.h
-src/object-editor-shadow-page.c
-src/object-editor-size-page.c
-src/object-editor-text-page.c
-src/pixbuf-cache.c
-src/pixbuf-cache.h
-src/prefs.c
-src/prefs.h
-src/prefs-dialog.c
-src/prefs-dialog.h
-src/prefs-model.c
-src/prefs-model.h
-src/print.c
-src/print.h
-src/print-op.c
-src/print-op-dialog.c
-src/print-op-dialog.h
-src/print-op.h
-src/message-bar.c
-src/message-bar.h
-src/recent.c
-src/recent.h
-src/str-util.c
-src/str-util.h
-src/svg-cache.c
-src/svg-cache.h
-src/template-designer.c
-src/template-designer.h
-src/template-history.c
-src/template-history.h
-src/template-history-model.c
-src/template-history-model.h
-src/text-node.c
-src/text-node.h
-src/ui.c
-src/ui.h
-src/ui-commands.c
-src/ui-commands.h
-src/ui-property-bar.c
-src/ui-property-bar.h
-src/ui-sidebar.c
-src/ui-sidebar.h
-src/ui-util.c
-src/ui-util.h
-src/units-util.c
-src/units-util.h
-src/view.c
-src/view.h
-src/view-barcode.c
-src/view-barcode.h
-src/view-box.c
-src/view-box.h
-src/view-ellipse.c
-src/view-ellipse.h
-src/view-image.c
-src/view-image.h
-src/view-line.c
-src/view-line.h
-src/view-text.c
-src/view-text.h
-src/warning-handler.c
-src/warning-handler.h
-src/wdgt-chain-button.c
-src/wdgt-chain-button.h
-src/window.c
-src/window.h
-src/xml-label-04.c
-src/xml-label-04.h
-src/xml-label.c
-src/xml-label.h
-
-libglabels/lgl-category.c
-libglabels/lgl-category.h
-libglabels/lgl-db.c
-libglabels/lgl-db.h
-libglabels/lgl-paper.c
-libglabels/lgl-paper.h
-libglabels/lgl-str.c
-libglabels/lgl-str.h
-libglabels/lgl-template.c
-libglabels/lgl-template.h
-libglabels/lgl-units.c
-libglabels/lgl-units.h
-libglabels/lgl-xml.c
-libglabels/lgl-xml.h
-libglabels/lgl-xml-category.c
-libglabels/lgl-xml-category.h
-libglabels/lgl-xml-paper.c
-libglabels/lgl-xml-paper.h
-libglabels/lgl-xml-template.c
-libglabels/lgl-xml-template.h
-libglabels/libglabels-private.h
-
-[type: gettext/glade]data/ui/merge-properties-dialog.ui
-[type: gettext/glade]data/ui/media-select.ui
-[type: gettext/glade]data/ui/new-label-dialog.ui
-[type: gettext/glade]data/ui/object-editor.ui
-[type: gettext/glade]data/ui/prefs-dialog.ui
-[type: gettext/glade]data/ui/property-bar.ui
-[type: gettext/glade]data/ui/template-designer.ui
-[type: gettext/glade]data/ui/print-op-dialog-custom-widget.ui
-
-data/schemas/org.gnome.glabels-3.gschema.xml.in.in
-
-[type: gettext/ini]data/desktop/glabels-3.0.desktop.in
-
-[type: gettext/xml]data/mime/glabels-3.0.xml.in
-
-[type: gettext/xml]templates/paper-sizes.xml
-[type: gettext/xml]templates/categories.xml
-[type: gettext/xml]templates/ascom-iso-templates.xml
-[type: gettext/xml]templates/avery-us-templates.xml
-[type: gettext/xml]templates/avery-iso-templates.xml
-[type: gettext/xml]templates/avery-other-templates.xml
-[type: gettext/xml]templates/brother-other-templates.xml
-[type: gettext/xml]templates/databecker-iso-templates.xml
-[type: gettext/xml]templates/dataline-iso-templates.xml
-[type: gettext/xml]templates/decadry-iso-templates.xml
-[type: gettext/xml]templates/desmat-templates.xml
-[type: gettext/xml]templates/dymo-other-templates.xml
-[type: gettext/xml]templates/endisch-templates.xml
-[type: gettext/xml]templates/geha-iso-templates.xml
-[type: gettext/xml]templates/hama-iso-templates.xml
-[type: gettext/xml]templates/herma-iso-templates.xml
-[type: gettext/xml]templates/jac-iso-templates.xml
-[type: gettext/xml]templates/maco-us-templates.xml
-[type: gettext/xml]templates/meritline-us-templates.xml
-[type: gettext/xml]templates/microapp-templates.xml
-[type: gettext/xml]templates/misc-us-templates.xml
-[type: gettext/xml]templates/misc-iso-templates.xml
-[type: gettext/xml]templates/misc-other-templates.xml
-[type: gettext/xml]templates/pearl-iso-templates.xml
-[type: gettext/xml]templates/sheetlabels-us-templates.xml
-[type: gettext/xml]templates/uline-us-templates.xml
-[type: gettext/xml]templates/worldlabel-us-templates.xml
-[type: gettext/xml]templates/zweckform-iso-templates.xml
+[encoding: UTF-8]
+# List of source files which contain translatable strings.
+glabels/color.vala
+glabels/color_button.vala
+glabels/color_history.vala
+glabels/color_menu.vala
+glabels/color_menu_item.vala
+glabels/color_swatch.vala
+glabels/font_button.vala
+glabels/font_families.vala
+glabels/font_history.vala
+glabels/font_menu.vala
+glabels/font_menu_item.vala
+glabels/font_sample.vala
+glabels/test.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index bdb5b80..e69de29 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -1,3 +0,0 @@
-# List of source files containing translatable strings that should be ignored.
-
-data/schemas/org.gnome.glabels-3.gschema.xml.in
diff --git a/vapi/Makefile.am b/vapi/Makefile.am
new file mode 100644
index 0000000..29cd822
--- /dev/null
+++ b/vapi/Makefile.am
@@ -0,0 +1,5 @@
+noinst_DATA = \
+ config.vapi
+
+EXTRA_DIST = \
+ $(noinst_DATA)
diff --git a/vapi/config.vapi b/vapi/config.vapi
new file mode 100644
index 0000000..97bb6dd
--- /dev/null
+++ b/vapi/config.vapi
@@ -0,0 +1,18 @@
+[CCode (prefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
+namespace Config
+{
+ /* Package information */
+ public const string PACKAGE_NAME;
+ public const string PACKAGE_STRING;
+ public const string PACKAGE_VERSION;
+
+ /* Gettext package */
+ public const string GETTEXT_PACKAGE;
+
+ /* Configured paths - these variables are not present in config.h, they are
+ * passed to underlying C code as cmd line macros. */
+ public const string LOCALEDIR; /* /usr/local/share/locale */
+ public const string DATADIR; /* /usr/local/share */
+ public const string GLABELS_BRANCH;
+ public const string LIBGLABELS_BRANCH;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]