language: c
os: linux
-dist: trusty
+dist: bionic
sudo: required
compiler:
- gcc
- clang
-addons:
- apt:
- sources:
- - trusty
- packages:
- - autoconf
- - automake
- - autotools-dev
- - build-essential
- - debhelper
- - dh-autoreconf
- - dpkg-dev
- - gnome-doc-utils
- - gnome-doc-utils
- - imagemagick
- - intltool
- - libexiv2-dev
- - libgtk2.0-dev
- - libjpeg-dev
- - liblcms2-dev
- - liblircclient-dev
- - liblua5.1-0-dev
- - libtiff-dev
- - libtool
-script: (CFLAGS= ./autogen.sh) && make
+before_install:
+ - sudo apt-get install -y autoconf automake autotools-dev build-essential debhelper dh-autoreconf dpkg-dev gnome-doc-utils gnome-doc-utils imagemagick intltool libexiv2-dev libgtk2.0-dev libjpeg-dev liblcms2-dev liblircclient-dev liblua5.1-0-dev libtiff-dev libtool
+script: (CFLAGS="-fPIC" ./autogen.sh) && make
prefix=$prefix
fi
-AM_PATH_GLIB_2_0(2.24.0,,AC_MSG_ERROR(GLIB >= 2.24.0 not installed.))
+AM_PATH_GLIB_2_0(2.52.0,,AC_MSG_ERROR(GLIB >= 2.52.0 not installed.))
AC_ARG_ENABLE([gtk3], AC_HELP_STRING([--disable-gtk3], [use gtk2 instead of gtk3]),[gtk3="${enableval}"], [gtk3=m4_ifdef([AM_PATH_GTK_3_0], [auto], [no])])
if test x$gtk3 = xyes; then
AM_CONDITIONAL(HAVE_MARKDOWN, [ "$(command -v markdown)" ])
+# _NL_TIME_FIRST_WEEKDAY support
+# note that it is an enum and not a define
+# ----------------------------------------------------------------------
+
+AC_MSG_CHECKING([for _NL_TIME_FIRST_WEEKDAY])
+AC_TRY_LINK([#include <langinfo.h>], [
+char c;
+c = *((unsigned char *) nl_langinfo(_NL_TIME_FIRST_WEEKDAY));
+], nl_ok=yes, nl_ok=no)
+AC_MSG_RESULT($nl_ok)
+if test "$nl_ok" = "yes"; then
+ AC_DEFINE([HAVE__NL_TIME_FIRST_WEEKDAY], [1],
+ [Define if _NL_TIME_FIRST_WEEKDAY is available])
+fi
+
# ----------------------------------------------------------------------
AH_TOP([
</section>\r
<section id="Gomenu">\r
<title>Go menu</title>\r
+ <para>The Page commands are for files which contain multiple images e.g. .tiff files</para>\r
<variablelist>\r
<varlistentry>\r
<term>\r
</menuchoice>\r
</term>\r
<listitem>\r
- <para>Goes tothe first image.</para>\r
+ <para>Goes to the first image.</para>\r
</listitem>\r
</varlistentry>\r
<varlistentry>\r
</para>\r
</listitem>\r
</varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <shortcut>\r
+ <keycombo>\r
+ <keycap>Ctrl</keycap>\r
+ <keycap>Home</keycap>\r
+ </keycombo>\r
+ </shortcut>\r
+ <guimenu>First Page</guimenu>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>Goes to the first page.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <shortcut>\r
+ <keycombo>\r
+ <keycap>Ctrl</keycap>\r
+ <keycap>End</keycap>\r
+ </keycombo>\r
+ </shortcut>\r
+ <guimenu>Last Page</guimenu>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>Goes to the last page.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <shortcut>\r
+ <keycombo>\r
+ <keycap>Ctrl</keycap>\r
+ <keycap>PageUp</keycap>\r
+ </keycombo>\r
+ </shortcut>\r
+ <guimenu>Previous Page</guimenu>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>Goes to the previous page.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <menuchoice>\r
+ <shortcut>\r
+ <keycombo>\r
+ <keycap>Ctrl</keycap>\r
+ <keycap>PageDown</keycap>\r
+ </keycombo>\r
+ </shortcut>\r
+ <guimenu>Next Page</guimenu>\r
+ </menuchoice>\r
+ </term>\r
+ <listitem>\r
+ <para>Goes to the next page.</para>\r
+ </listitem>\r
+ </varlistentry>\r
</variablelist>\r
<para />\r
</section>\r
<para>This section displays the dimensions (width x height) and file byte size of the image that is active in the image pane. When the format of the file in the image pane can not be determined the dimensions will show as “(0 x 0)”, in addition “(no read permision)” may appear if the file permissions do not allow reading the contents of the file.</para>\r
<para />\r
</section>\r
+ <section id="ZoomAndScroll">\r
+ <title>Zoom and Scroll</title>\r
+ <para>The button label displays the current zoom ratio. A ratio of 1:1 is the image's original size. When the left number is larger the image is displayed larger than original size, when the right number is larger the image is displayed smaller.</para>\r
+ <para>A tilde (~) appears within the ratio display when the zoom is set to fit the image within the display area. In this zoom mode the ratio is automatically adjusted, and the displayed ratio may not be the actual ratio because the status bar display rounds the actual value to the nearest tenth (0.1).</para>\r
+ <para />\r
+ <para>Clicking this button permits control of the behavior of the zoom and scroll settings used when changing the displayed image.</para>\r
+ <variablelist>\r
+ <varlistentry>\r
+ <term>\r
+ <guilabel>Zoom to original size</guilabel>\r
+ </term>\r
+ <listitem>\r
+ <para>The new image is set to its original size.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <guilabel>Fit image to window</guilabel>\r
+ </term>\r
+ <listitem>\r
+ <para>The new image's zoom is changed so that the image will fit within the current view area.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <guilabel>Leave zoom at previous setting</guilabel>\r
+ </term>\r
+ <listitem>\r
+ <para>The zoom setting is unchanged, the new image will be scaled the same as the previous image.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ </variablelist>\r
+ <variablelist>\r
+ <varlistentry>\r
+ <term>\r
+ <guilabel>Scroll to top left corner</guilabel>\r
+ </term>\r
+ <listitem>\r
+ <para>The new image is displayed from top left corner.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <guilabel>Scroll to image center</guilabel>\r
+ </term>\r
+ <listitem>\r
+ <para>The new image is centered</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ <varlistentry>\r
+ <term>\r
+ <guilabel>Keep the region from previous iamge</guilabel>\r
+ </term>\r
+ <listitem>\r
+ <para>The new image is positioned as the previous one, whenever possible.</para>\r
+ </listitem>\r
+ </varlistentry>\r
+ </variablelist>\r
+ </section>\r
<section id="Buttons">\r
<title>Buttons</title>\r
- <para>Statusbar buttons corresponds to selected menu action.</para>\r
+ <para>\r
+ The Status bar toolbar buttons, the defaults are shown below, correspond to selected menu action. The buttons displayed may be changed in the\r
+ <link linkend="GuideOptionsToolbar">Toolbar tab of Preferences.</link>\r
+ </para>\r
<para />\r
- <section id="ZoomAndScroll">\r
- <title>Zoom and Scroll</title>\r
- <para>The button label displays the current zoom ratio. A ratio of 1:1 is the image's original size. When the left number is larger the image is displayed larger than original size, when the right number is larger the image is displayed smaller.</para>\r
- <para>A tilde (~) appears within the ratio display when the zoom is set to fit the image within the display area. In this zoom mode the ratio is automatically adjusted, and the displayed ratio may not be the actual ratio because the status bar display rounds the actual value to the nearest tenth (0.1).</para>\r
- <para />\r
- <para>Clicking this button permits control of the behavior of the zoom and scroll settings used when changing the displayed image.</para>\r
- <variablelist>\r
- <varlistentry>\r
- <term>\r
- <guilabel>Zoom to original size</guilabel>\r
- </term>\r
- <listitem>\r
- <para>The new image is set to its original size.</para>\r
- </listitem>\r
- </varlistentry>\r
- <varlistentry>\r
- <term>\r
- <guilabel>Fit image to window</guilabel>\r
- </term>\r
- <listitem>\r
- <para>The new image's zoom is changed so that the image will fit within the current view area.</para>\r
- </listitem>\r
- </varlistentry>\r
- <varlistentry>\r
- <term>\r
- <guilabel>Leave zoom at previous setting</guilabel>\r
- </term>\r
- <listitem>\r
- <para>The zoom setting is unchanged, the new image will be scaled the same as the previous image.</para>\r
- </listitem>\r
- </varlistentry>\r
- </variablelist>\r
- <variablelist>\r
- <varlistentry>\r
- <term>\r
- <guilabel>Scroll to top left corner</guilabel>\r
- </term>\r
- <listitem>\r
- <para>The new image is displayed from top left corner.</para>\r
- </listitem>\r
- </varlistentry>\r
- <varlistentry>\r
- <term>\r
- <guilabel>Scroll to image center</guilabel>\r
- </term>\r
- <listitem>\r
- <para>The new image is centered</para>\r
- </listitem>\r
- </varlistentry>\r
- <varlistentry>\r
- <term>\r
- <guilabel>Keep the region from previous iamge</guilabel>\r
- </term>\r
- <listitem>\r
- <para>The new image is positioned as the previous one, whenever possible.</para>\r
- </listitem>\r
- </varlistentry>\r
- </variablelist>\r
- </section>\r
<section id="ExifRotate">\r
<title>Exif rotate</title>\r
<para>\r
<section id="GuideOptionsToolbar">\r
<title>Toolbar</title>\r
<para>\r
- This dialogue enables you to change the items displayed on the Toolbar.\r
+ This dialogue enables you to change the items displayed on either the Main Toolbar or the Status Toolbar.\r
<para />\r
The initial display shows the current setup. You may re-order these items by right-clicking on any item.\r
<para />\r
<entry colsep="0" />\r
<entry colsep="0" />\r
</row>\r
+ <row>\r
+ <entry colsep="0">\r
+ <emphasis role="strong">For files with multiple pages e.g. tiff files</emphasis>\r
+ </entry>\r
+ <entry colsep="0" />\r
+ <entry colsep="0" />\r
+ </row>\r
+ <row>\r
+ <entry>\r
+ <code>\r
+ Ctrl +\r
+ <keycap>Home</keycap>\r
+ </code>\r
+ </entry>\r
+ <entry />\r
+ <entry>Go to first page</entry>\r
+ </row>\r
+ <row>\r
+ <entry>\r
+ <code>\r
+ Ctrl +\r
+ <keycap>End</keycap>\r
+ </code>\r
+ </entry>\r
+ <entry />\r
+ <entry>Go to last page</entry>\r
+ </row>\r
+ <row>\r
+ <entry>\r
+ <code>\r
+ Ctrl +\r
+ <keycap>PageUp</keycap>\r
+ </code>\r
+ </entry>\r
+ <entry />\r
+ <entry>Go to previous page</entry>\r
+ </row>\r
+ <row>\r
+ <entry>\r
+ <code>\r
+ Ctrl +\r
+ <keycap>PageDown</keycap>\r
+ </code>\r
+ </entry>\r
+ <entry />\r
+ <entry>Go to next page</entry>\r
+ </row>\r
<row>\r
<entry colsep="0">\r
<emphasis role="strong">File Menu</emphasis>\r
</row>\r
<row>\r
<entry>\r
- <code>\r
- Shift + Delete\r
- </code>\r
+ <code>Shift + Delete</code>\r
</entry>\r
<entry />\r
<entry>Permanently delete selected images.</entry>\r
g_hash_table_foreach(file_data_pool, marks_clear, NULL);
}
+void file_data_set_page_num(FileData *fd, gint page_num)
+{
+ if (fd->page_total > 1 && page_num < 0)
+ {
+ fd->page_num = fd->page_total - 1;
+ }
+ else if (fd->page_total > 1 && page_num <= fd->page_total)
+ {
+ fd->page_num = page_num - 1;
+ }
+ else
+ {
+ fd->page_num = 0;
+ }
+ file_data_send_notification(fd, NOTIFY_REREAD);
+}
+
void file_data_inc_page_num(FileData *fd)
{
if (fd->page_total > 0 && fd->page_num < fd->page_total - 1)
void file_data_inc_page_num(FileData *fd);
void file_data_dec_page_num(FileData *fd);
void file_data_set_page_total(FileData *fd, gint page_total);
+void file_data_set_page_num(FileData *fd, gint page_num);
#endif
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
icon_trash.png \
icon_heic.png \
icon_grayscale.png \
- icon_exposure.png \
- icon_next_page.png \
- icon_previous_page.png
+ icon_exposure.png
ICONS_INLINE_PAIRS = \
folder_closed $(srcdir)/folder_closed.png \
icon_trash $(srcdir)/icon_trash.png \
icon_heic $(srcdir)/icon_heic.png \
icon_grayscale $(srcdir)/icon_grayscale.png \
- icon_exposure $(srcdir)/icon_exposure.png \
- icon_next_page $(srcdir)/icon_next_page.png \
- icon_previous_page $(srcdir)/icon_previous_page.png
+ icon_exposure $(srcdir)/icon_exposure.png
icons_inline.h: $(ICONS_INLINE) Makefile.in
@sh -ec "echo '/* Auto generated file, do not edit */'; echo; \
il->loader = il->backend.loader_new(image_loader_area_updated_cb, image_loader_size_cb, image_loader_area_prepared_cb, il);
+#ifdef HAVE_TIFF
+ format = il->backend.get_format_name(il->loader);
+ if (g_strcmp0(format, "tiff") == 0)
+ {
+ il->backend.set_page_num(il->loader, il->fd->page_num);
+ }
+ g_free(format);
+#endif
+
#ifdef HAVE_PDF
format = il->backend.get_format_name(il->loader);
if (g_strcmp0(format, "pdf") == 0)
}
g_free(format);
#endif
+#ifdef HAVE_TIFF
+ format = il->backend.get_format_name(il->loader);
+ if (g_strcmp0(format, "tiff") == 0)
+ {
+ gint i = il->backend.get_page_total(il->loader);
+ file_data_set_page_total(il->fd, i);
+ }
+ g_free(format);
+#endif
il->bytes_read += b;
gint stride;
gboolean alpha = FALSE;
cairo_surface_t *surface;
- gchar *pixels;
+ guchar *pixels;
ctx = ddjvu_context_create(NULL);
doc = ddjvu_document_create(ctx, NULL, FALSE);
- ddjvu_stream_write(doc, 0, buf, count );
+ ddjvu_stream_write(doc, 0, (char *)buf, count );
while (!ddjvu_document_decoding_done(doc));
ld->page_total = ddjvu_document_get_pagenum(doc);
height = ddjvu_page_get_height(page);
stride = width * 4;
- pixels = (gchar *)g_malloc(height * stride);
+ pixels = (guchar *)g_malloc(height * stride);
prect.x = 0;
prect.y = 0;
prect.h = height;
rrect = prect;
- surface = cairo_image_surface_create_for_data((guchar *)pixels, CAIRO_FORMAT_RGB24, width, height, stride);
+ surface = cairo_image_surface_create_for_data(pixels, CAIRO_FORMAT_RGB24, width, height, stride);
- ddjvu_page_render(page, DDJVU_RENDER_COLOR, &prect, &rrect, fmt, stride, pixels);
+ ddjvu_page_render(page, DDJVU_RENDER_COLOR, &prect, &rrect, fmt, stride, (char *)pixels);
/* FIXME implementation of rotation is not correct */
GdkPixbuf *tmp1;
static gchar* image_loader_gdk_get_format_name(gpointer loader)
{
- return gdk_pixbuf_format_get_name(gdk_pixbuf_loader_get_format(GDK_PIXBUF_LOADER(loader)));
+ GdkPixbufFormat *format;
+
+ format = gdk_pixbuf_loader_get_format(GDK_PIXBUF_LOADER(loader));
+ if (format)
+ {
+ return gdk_pixbuf_format_get_name(format);
+ }
+ else
+ {
+ return NULL;
+ }
}
static gchar** image_loader_gdk_get_format_mime_types(gpointer loader)
{
const guchar *buffer;
toff_t used;
toff_t pos;
+ gint page_num;
+ gint page_total;
};
static void free_buffer (guchar *pixels, gpointer data)
gint width, height, rowstride;
size_t bytes;
uint32 rowsperstrip;
+ gint dircount = 0;
lt->buffer = buf;
lt->used = count;
DEBUG_1("Failed to open TIFF image");
return FALSE;
}
+ else
+ {
+ do
+ {
+ dircount++;
+ } while (TIFFReadDirectory(tiff));
+ lt->page_total = dircount;
+ }
+ if (!TIFFSetDirectory(tiff, lt->page_num))
+ {
+ DEBUG_1("Failed to open TIFF image");
+ TIFFClose(tiff);
+ return FALSE;
+ }
if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width))
{
g_free(lt);
}
+static void image_loader_tiff_set_page_num(gpointer loader, gint page_num)
+{
+ ImageLoader *il = (ImageLoader *) loader;
+ ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
+
+ lt->page_num = page_num;
+}
+
+static gint image_loader_tiff_get_page_total(gpointer loader)
+{
+ ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
+
+ return lt->page_total;
+}
void image_loader_backend_set_tiff(ImageLoaderBackend *funcs)
{
funcs->get_format_name = image_loader_tiff_get_format_name;
funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types;
+
+ funcs->set_page_num = image_loader_tiff_set_page_num;
+ funcs->get_page_total = image_loader_tiff_get_page_total;
}
#define TOOLWINDOW_DEF_HEIGHT 450
#define PROGRESS_WIDTH 150
-#define ZOOM_LABEL_WIDTH 64
+#define ZOOM_LABEL_WIDTH 120
#define PANE_DIVIDER_SIZE 10
if (page_total > 0)
{
- text = g_strdup_printf(_("( %d x %d ) %s bytes%s%d%s%d%s"), width, height, b, "[", page_num, "/", page_total, "]");
+ text = g_strdup_printf(_("( %d x %d ) %s bytes %s%d%s%d%s"), width, height, b, "[", page_num, "/", page_total, "]");
}
else
{
}
lw->info_details = layout_status_label(NULL, hbox, TRUE, 0, TRUE);
DEBUG_NAME(lw->info_details);
- gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_details), _("(Image dimensions) Image size"));
+ gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_details), _("(Image dimensions) Image size [page n of m]"));
toolbar = layout_actions_toolbar(lw, TOOLBAR_STATUS);
toolbar_frame = gtk_frame_new(NULL);
WRITE_SEPARATOR();
layout_toolbar_write_config(lw, TOOLBAR_MAIN, outstr, indent + 1);
+ layout_toolbar_write_config(lw, TOOLBAR_STATUS, outstr, indent + 1);
WRITE_NL(); WRITE_STRING("</layout>");
}
if(!fd) return;
if(fd->iter) g_object_unref(fd->iter);
if(fd->gpa) g_object_unref(fd->gpa);
+ if(fd->cancellable) g_object_unref(fd->cancellable);
g_free(fd);
}
if(lw->animation)
{
lw->animation->valid = FALSE;
+ if (lw->animation->cancellable)
+ {
+ g_cancellable_cancel(lw->animation->cancellable);
+ }
lw->animation = NULL;
}
return FALSE;
}
}
+
+static void animation_async_ready_cb(GObject *source_object, GAsyncResult *res, gpointer data)
+{
+ GError *error = NULL;
+ AnimationData *animation = data;
+
+ if (animation)
+ {
+ if (g_cancellable_is_cancelled(animation->cancellable))
+ {
+ gdk_pixbuf_animation_new_from_stream_finish(res, NULL);
+ g_object_unref(animation->in_file);
+ g_object_unref(animation->gfstream);
+ image_animation_data_free(animation);
+ return;
+ }
+
+ animation->gpa = gdk_pixbuf_animation_new_from_stream_finish(res, &error);
+ if (animation->gpa)
+ {
+ if (!gdk_pixbuf_animation_is_static_image(animation->gpa))
+ {
+ if (animation->iter = gdk_pixbuf_animation_get_iter(animation->gpa,NULL))
+ {
+ animation->iter = gdk_pixbuf_animation_get_iter(animation->gpa, NULL);
+ // FIXME: the reference count is being incremented here. Reason unknown.
+ g_object_unref(animation->gpa);
+ animation->data_adr = animation->lw->image->image_fd;
+ animation->delay = gdk_pixbuf_animation_iter_get_delay_time(animation->iter);
+ animation->valid = TRUE;
+
+ layout_image_animate_update_image(animation->lw);
+
+ g_timeout_add(animation->delay, show_next_frame, animation);
+ }
+ }
+ }
+ else
+ {
+ log_printf("Error reading GIF file: %s\n", error->message);
+ }
+
+ g_object_unref(animation->in_file);
+ g_object_unref(animation->gfstream);
+ }
+}
+
static gboolean layout_image_animate_new_file(LayoutWindow *lw)
{
- GError *err=NULL;
+ GFileInputStream *gfstream;
+ GError *error = NULL;
+ AnimationData *animation;
+ GFile *in_file;
if(!layout_image_animate_check(lw)) return FALSE;
if(lw->animation) lw->animation->valid = FALSE;
- lw->animation = g_malloc0(sizeof(AnimationData));
-
- if(!(lw->animation->gpa = gdk_pixbuf_animation_new_from_file(lw->image->image_fd->path,&err)) || err ||
- gdk_pixbuf_animation_is_static_image(lw->animation->gpa) ||
- !(lw->animation->iter = gdk_pixbuf_animation_get_iter(lw->animation->gpa,NULL)))
+ if (lw->animation)
{
- image_animation_data_free(lw->animation);
- lw->animation = NULL;
- return FALSE;
+ g_cancellable_cancel(lw->animation->cancellable);
}
- lw->animation->data_adr = lw->image->image_fd;
- lw->animation->delay = gdk_pixbuf_animation_iter_get_delay_time(lw->animation->iter);
- lw->animation->valid = TRUE;
-
- layout_image_animate_update_image(lw);
+ animation = g_new0(AnimationData, 1);
+ lw->animation = animation;
+ animation->lw = lw;
+ animation->cancellable = g_cancellable_new();
- g_timeout_add(lw->animation->delay, show_next_frame, lw->animation);
+ in_file = g_file_new_for_path(lw->image->image_fd->path);
+ animation->in_file = in_file;
+ gfstream = g_file_read(in_file, NULL, &error);
+ if (gfstream)
+ {
+ animation->gfstream = gfstream;
+ gdk_pixbuf_animation_new_from_stream_async((GInputStream*)gfstream, animation->cancellable, animation_async_ready_cb, animation);
+ }
+ else
+ {
+ log_printf("Error reading GIF file: %s\nError: %s\n", lw->image->image_fd->path, error->message);
+ }
return TRUE;
}
if (info == TARGET_TEXT_PLAIN)
{
- url = g_strdup(gtk_selection_data_get_data(selection_data));
+ url = g_strdup((gchar *)gtk_selection_data_get_data(selection_data));
download_web_file(url, lw);
g_free(url);
}
layout_image_next(lw);
}
+static void layout_menu_page_first_cb(GtkAction *action, gpointer data)
+{
+ LayoutWindow *lw = data;
+ FileData *fd = layout_image_get_fd(lw);
+
+ file_data_set_page_num(fd, 1);
+}
+
+static void layout_menu_page_last_cb(GtkAction *action, gpointer data)
+{
+ LayoutWindow *lw = data;
+ FileData *fd = layout_image_get_fd(lw);
+
+ file_data_set_page_num(fd, -1);
+}
+
static void layout_menu_page_next_cb(GtkAction *action, gpointer data)
{
LayoutWindow *lw = data;
{ "NextImage", GTK_STOCK_GO_DOWN, N_("_Next Image"), "space", N_("Next Image"), CB(layout_menu_image_next_cb) },
{ "NextImageAlt1", GTK_STOCK_GO_DOWN, N_("_Next Image"), "Page_Down", N_("Next Image"), CB(layout_menu_image_next_cb) },
- { "NextPage", PIXBUF_INLINE_ICON_NEXT_PAGE, N_("_Next Page"), "<control>Page_Down", N_("Next Page"), CB(layout_menu_page_next_cb) },
- { "PrevPage", PIXBUF_INLINE_ICON_PREVIOUS_PAGE, N_("_Previous Page"), "<control>Page_Up", N_("Previous Page"), CB(layout_menu_page_previous_cb) },
+ { "FirstPage", GTK_STOCK_MEDIA_PREVIOUS, N_("_First Page"), "<control>Home", N_( "First Page"), CB(layout_menu_page_first_cb) },
+ { "LastPage", GTK_STOCK_MEDIA_NEXT, N_("_Last Page"), "<control>End", N_("Last Page"), CB(layout_menu_page_last_cb) },
+ { "NextPage", GTK_STOCK_MEDIA_FORWARD, N_("_Next Page"), "<control>Page_Down", N_("Next Page"), CB(layout_menu_page_next_cb) },
+ { "PrevPage", GTK_STOCK_MEDIA_REWIND, N_("_Previous Page"), "<control>Page_Up", N_("Previous Page"), CB(layout_menu_page_previous_cb) },
{ "NextImageAlt2", GTK_STOCK_GO_DOWN, N_("_Next Image"), "KP_Page_Down", N_("Next Image"), CB(layout_menu_image_next_cb) },
" <menuitem action='Up'/>"
" <menuitem action='Home'/>"
" <separator/>"
+" <menuitem action='FirstPage'/>"
+" <menuitem action='LastPage'/>"
" <menuitem action='NextPage'/>"
" <menuitem action='PrevPage'/>"
" </menu>"
" <toolbar name='ToolBar'>"
" </toolbar>"
" <toolbar name='StatusBar'>"
-" <toolitem action='ExifRotate'/>"
-" <toolitem action='ShowInfoPixel'/>"
-" <toolitem action='UseColorProfiles'/>"
-" <toolitem action='SaveMetadata'/>"
" </toolbar>"
"<accelerator action='PrevImageAlt1'/>"
"<accelerator action='PrevImageAlt2'/>"
void layout_actions_setup(LayoutWindow *lw)
{
GError *error;
+ gint i;
DEBUG_1("%s layout_actions_setup: start", get_exec_time());
if (lw->ui_manager) return;
exit(EXIT_FAILURE);
}
- layout_toolbar_clear(lw, TOOLBAR_MAIN);
- layout_toolbar_add_default(lw, TOOLBAR_MAIN);
+ DEBUG_1("%s layout_actions_setup: add toolbar", get_exec_time());
+ for (i = 0; i < TOOLBAR_COUNT; i++)
+ {
+ layout_toolbar_clear(lw, i);
+ layout_toolbar_add_default(lw, i);
+ }
DEBUG_1("%s layout_actions_setup: marks", get_exec_time());
layout_actions_setup_marks(lw);
case TOOLBAR_MAIN:
path = "/ToolBar";
break;
+ case TOOLBAR_STATUS:
+ path = "/StatusBar";
+ break;
default:
break;
}
layout_toolbar_add(lw, type, "FloatTools");
}
break;
+ case TOOLBAR_STATUS:
+ if (layout_window_list)
+ {
+ lw_first = layout_window_list->data;
+ if (lw_first->toolbar_actions[TOOLBAR_MAIN])
+ {
+ work_action = lw_first->toolbar_actions[type];
+ while (work_action)
+ {
+ gchar *action = work_action->data;
+ work_action = work_action->next;
+ layout_toolbar_add(lw, type, action);
+ }
+ }
+ else
+ {
+ layout_toolbar_add(lw, type, "ExifRotate");
+ layout_toolbar_add(lw, type, "ShowInfoPixel");
+ layout_toolbar_add(lw, type, "UseColorProfiles");
+ layout_toolbar_add(lw, type, "SaveMetadata");
+ }
+ }
+ else
+ {
+ layout_toolbar_add(lw, type, "ExifRotate");
+ layout_toolbar_add(lw, type, "ShowInfoPixel");
+ layout_toolbar_add(lw, type, "UseColorProfiles");
+ layout_toolbar_add(lw, type, "SaveMetadata");
+ }
+ break;
default:
break;
}
case TOOLBAR_MAIN:
name = "toolbar";
break;
+ case TOOLBAR_STATUS:
+ name = "statusbar";
+ break;
default:
break;
}
#include "ui_fileops.h"
#include <langinfo.h>
+#include <locale.h>
gdouble get_zoom_increment(void)
{
* @brief Returns integer representing first_day_of_week
* @returns Integer in range 1 to 7
*
- * Uses current locale to get first day of week
+ * Uses current locale to get first day of week.
+ * If _NL_TIME_FIRST_WEEKDAY is not available, ISO 8601
+ * states first day of week is Monday.
+ * USA, Mexico and Canada (and others) use Sunday as first day of week.
*
* Sunday == 1
*/
gint date_get_first_day_of_week()
{
+ gchar *dot;
+ gchar *current_locale;
+
+#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
return nl_langinfo(_NL_TIME_FIRST_WEEKDAY)[0];
+#else
+ current_locale = setlocale(LC_ALL, NULL);
+ dot = strstr(current_locale, ".");
+ if ((strncmp(dot - 2, "US", 2) == 0) || (strncmp(dot - 2, "MX", 2) == 0) || (strncmp(dot - 2, "CA", 2) == 0))
+ {
+ return 1;
+ }
+ else
+ {
+ return 2;
+ }
+#endif
}
/**
return -1;
}
+#if defined(__GLIBC_PREREQ)
+# if __GLIBC_PREREQ(2, 27)
+# define HAS_GLIBC_STRFTIME_EXTENSIONS
+# endif
+#endif
+
gchar *pan_date_value_string(time_t d, PanDateLengthType length)
{
struct tm td;
format = "%A %e";
break;
case PAN_DATE_LENGTH_MONTH:
-#if __GLIBC_PREREQ(2, 27)
+#if defined(HAS_GLIBC_STRFTIME_EXTENSIONS) || defined(__FreeBSD__)
format = "%OB %Y";
#else
format = "%B %Y";
{ PIXBUF_INLINE_ICON_HEIF, icon_heic },
{ PIXBUF_INLINE_ICON_GRAYSCALE, icon_grayscale },
{ PIXBUF_INLINE_ICON_EXPOSURE, icon_exposure },
- { PIXBUF_INLINE_ICON_NEXT_PAGE, icon_next_page },
- { PIXBUF_INLINE_ICON_PREVIOUS_PAGE, icon_previous_page },
{ NULL, NULL }
};
#define PIXBUF_INLINE_ICON_HEIF "icon_heic"
#define PIXBUF_INLINE_ICON_GRAYSCALE "icon_grayscale"
#define PIXBUF_INLINE_ICON_EXPOSURE "icon_exposure"
-#define PIXBUF_INLINE_ICON_NEXT_PAGE "icon_next_page"
-#define PIXBUF_INLINE_ICON_PREVIOUS_PAGE "icon_previous_page"
#define PIXBUF_INLINE_ICON_CW "icon_rotate_clockwise"
#define PIXBUF_INLINE_ICON_CCW "icon_rotate_counter_clockwise"
if (accel_store) gtk_tree_model_foreach(GTK_TREE_MODEL(accel_store), accel_apply_cb, NULL);
- toolbar_apply();
+ toolbar_apply(TOOLBAR_MAIN);
+ toolbar_apply(TOOLBAR_STATUS);
}
/*
{"Forward", N_("Forward"), GTK_STOCK_GO_FORWARD},
{"Home", N_("Home"), GTK_STOCK_HOME},
{"Up", N_("Up"), GTK_STOCK_GO_UP},
- {"NextPage", N_("Next Page"), PIXBUF_INLINE_ICON_NEXT_PAGE},
- {"PrevPage", N_("Previous Page"), PIXBUF_INLINE_ICON_PREVIOUS_PAGE},
+ {"FirstPage", N_("First page"), GTK_STOCK_MEDIA_PREVIOUS},
+ {"LastPage", N_("Last Page"), GTK_STOCK_MEDIA_NEXT},
+ {"NextPage", N_("Next page"), GTK_STOCK_MEDIA_FORWARD},
+ {"PrevPage", N_("Previous Page"), GTK_STOCK_MEDIA_REWIND},
{"NewWindow", N_("New _window"), GTK_STOCK_NEW},
{"NewCollection", N_("New collection"), GTK_STOCK_INDEX},
{"OpenCollection", N_("Open collection"), GTK_STOCK_OPEN},
gtk_widget_show(button);
}
-/* toolbar tab */
-static void config_tab_toolbar(GtkWidget *notebook)
+/* toolbar main tab */
+static void config_tab_toolbar_main(GtkWidget *notebook)
{
GtkWidget *vbox;
GtkWidget *toolbardata;
lw = layout_window_list->data;
- vbox = scrolled_notebook_page(notebook, _("Toolbar"));
+ vbox = scrolled_notebook_page(notebook, _("Toolbar Main"));
- toolbardata = toolbar_select_new(lw);
+ toolbardata = toolbar_select_new(lw, TOOLBAR_MAIN);
+ gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
+ gtk_widget_show(vbox);
+}
+
+/* toolbar status tab */
+static void config_tab_toolbar_status(GtkWidget *notebook)
+{
+ GtkWidget *vbox;
+ GtkWidget *toolbardata;
+ LayoutWindow *lw;
+
+ lw = layout_window_list->data;
+
+ vbox = scrolled_notebook_page(notebook, _("Toolbar Status"));
+
+ toolbardata = toolbar_select_new(lw, TOOLBAR_STATUS);
gtk_box_pack_start(GTK_BOX(vbox), toolbardata, TRUE, TRUE, 0);
gtk_widget_show(vbox);
}
config_tab_color(notebook);
config_tab_stereo(notebook);
config_tab_behavior(notebook);
- config_tab_toolbar(notebook);
+ config_tab_toolbar_main(notebook);
+ config_tab_toolbar_status(notebook);
hbox = gtk_hbutton_box_new();
gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
}
}
+
+static void options_parse_statusbar(GQParserData *parser_data, GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer data, GError **error)
+{
+ LayoutWindow *lw = data;
+ if (g_ascii_strcasecmp(element_name, "toolitem") == 0)
+ {
+ layout_toolbar_add_from_config(lw, TOOLBAR_STATUS, attribute_names, attribute_values);
+ options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+ }
+ else if (g_ascii_strcasecmp(element_name, "clear") == 0)
+ {
+ layout_toolbar_clear(lw, TOOLBAR_STATUS);
+ options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+ }
+ else
+ {
+ log_printf("unexpected in <statusbar>: <%s>\n", element_name);
+ options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+ }
+}
+
static void options_parse_dialogs(GQParserData *parser_data, GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer data, GError **error)
{
if (g_ascii_strcasecmp(element_name, "window") == 0)
}
else if (g_ascii_strcasecmp(element_name, "statusbar") == 0)
{
- options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+ options_parse_func_push(parser_data, options_parse_statusbar, NULL, lw);
}
else if (g_ascii_strcasecmp(element_name, "dialogs") == 0)
{
gchar *stock_id;
};
-static ToolbarData *toolbarlist;
+static ToolbarData *toolbarlist[2];
typedef struct _UseableToolbarItems UseableToolbarItems;
struct _UseableToolbarItems
{"Forward", N_("Forward"), GTK_STOCK_GO_FORWARD},
{"Home", N_("Home"), GTK_STOCK_HOME},
{"Up", N_("Up"), GTK_STOCK_GO_UP},
- {"NextPage", N_("Next page"), PIXBUF_INLINE_ICON_NEXT_PAGE},
- {"PrevPage", N_("Previous Page"), PIXBUF_INLINE_ICON_PREVIOUS_PAGE},
+ {"FirstPage", N_("First page"), GTK_STOCK_MEDIA_PREVIOUS},
+ {"LastPage", N_("Last Page"), GTK_STOCK_MEDIA_NEXT},
+ {"NextPage", N_("Next page"), GTK_STOCK_MEDIA_FORWARD},
+ {"PrevPage", N_("Previous Page"), GTK_STOCK_MEDIA_REWIND},
{"NewWindow", N_("New _window"), GTK_STOCK_NEW},
{"NewCollection", N_("New collection"), GTK_STOCK_INDEX},
{"OpenCollection", N_("Open collection"), GTK_STOCK_OPEN},
{"ConnectZoomIn", N_("Connected Zoom in"), GTK_STOCK_ZOOM_IN},
{"Grayscale", N_("Grayscale"), PIXBUF_INLINE_ICON_GRAYSCALE},
{"OverUnderExposed", N_("Over Under Exposed"), PIXBUF_INLINE_ICON_EXPOSURE},
+ {"ShowInfoPixel", N_("Pixel Info"), GTK_STOCK_COLOR_PICKER},
+ {"ExifRotate", N_("_Exif rotate"), GTK_STOCK_ORIENTATION_PORTRAIT},
+ {"UseColorProfiles", N_("Use color profiles"), GTK_STOCK_SELECT_COLOR},
+ {"SaveMetadata", N_("Save metadata"), GTK_STOCK_SAVE},
{"HideTools", N_("Hide file list"), PIXBUF_INLINE_ICON_HIDETOOLS},
{"SlideShowPause", N_("Pause slideshow"), GTK_STOCK_MEDIA_PAUSE},
{"SlideShowFaster", N_("Slideshow Faster"), GTK_STOCK_FILE},
/**
* @brief For each layoutwindow, clear toolbar and reload with current selection
+ * @param bar Main or Status toolbar
*
*/
-void toolbar_apply()
+void toolbar_apply(ToolbarType bar)
{
LayoutWindow *lw;
GList *work_windows;
{
lw = work_windows->data;
- layout_toolbar_clear(lw, TOOLBAR_MAIN);
+ layout_toolbar_clear(lw, bar);
- work_toolbar = gtk_container_get_children(GTK_CONTAINER(toolbarlist->vbox));
+ work_toolbar = gtk_container_get_children(GTK_CONTAINER(toolbarlist[bar]->vbox));
while (work_toolbar)
{
GtkButton *button = work_toolbar->data;
ToolbarButtonData *tbbd;
tbbd = g_object_get_data(G_OBJECT(button),"toolbarbuttondata");
- layout_toolbar_add(lw, TOOLBAR_MAIN, tbbd->name);
+ layout_toolbar_add(lw, bar, tbbd->name);
work_toolbar = work_toolbar->next;
}
* @brief Load the current toolbar items into the vbox
* @param lw
* @param box The vbox displayed in the preferences Toolbar tab
+ * @param bar Main or Status toolbar
*
* Get the current contents of the toolbar, both menu items
* and desktop items, and load them into the vbox
*/
-static void toolbarlist_populate(LayoutWindow *lw, GtkBox *box)
+static void toolbarlist_populate(LayoutWindow *lw, GtkBox *box, ToolbarType bar)
{
- GList *work = g_list_first(lw->toolbar_actions[TOOLBAR_MAIN]);
+ GList *work = g_list_first(lw->toolbar_actions[bar]);
while (work)
{
}
}
-GtkWidget *toolbar_select_new(LayoutWindow *lw)
+GtkWidget *toolbar_select_new(LayoutWindow *lw, ToolbarType bar)
{
GtkWidget *scrolled;
GtkWidget *tbar;
if (!lw) return NULL;
- if (!toolbarlist)
+ if (!toolbarlist[bar])
{
- toolbarlist = g_new0(ToolbarData, 1);
+ toolbarlist[bar] = g_new0(ToolbarData, 1);
}
- toolbarlist->lw = lw;
+ toolbarlist[bar]->lw = lw;
- toolbarlist->widget = gtk_vbox_new(FALSE, PREF_PAD_GAP);
- gtk_widget_show(toolbarlist->widget);
+ toolbarlist[bar]->widget = gtk_vbox_new(FALSE, PREF_PAD_GAP);
+ gtk_widget_show(toolbarlist[bar]->widget);
scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
- gtk_box_pack_start(GTK_BOX(toolbarlist->widget), scrolled, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(toolbarlist[bar]->widget), scrolled, TRUE, TRUE, 0);
gtk_widget_show(scrolled);
- toolbarlist->vbox = gtk_vbox_new(FALSE, 0);
- gtk_widget_show(toolbarlist->vbox);
- gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), toolbarlist->vbox);
+ toolbarlist[bar]->vbox = gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(toolbarlist[bar]->vbox);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), toolbarlist[bar]->vbox);
gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(scrolled))),
GTK_SHADOW_NONE);
add_box = gtk_vbox_new(FALSE, 0);
gtk_widget_show(add_box);
- gtk_box_pack_end(GTK_BOX(toolbarlist->widget), add_box, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(toolbarlist[bar]->widget), add_box, FALSE, FALSE, 0);
tbar = pref_toolbar_new(add_box, GTK_TOOLBAR_ICONS);
- toolbarlist->add_button = pref_toolbar_button(tbar, GTK_STOCK_ADD, "NULL", FALSE,
+ toolbarlist[bar]->add_button = pref_toolbar_button(tbar, GTK_STOCK_ADD, "NULL", FALSE,
_("Add Toolbar Item"),
- G_CALLBACK(toolbar_menu_add_cb), toolbarlist);
- gtk_widget_show(toolbarlist->add_button);
+ G_CALLBACK(toolbar_menu_add_cb), toolbarlist[bar]);
+ gtk_widget_show(toolbarlist[bar]->add_button);
- toolbarlist_populate(lw,GTK_BOX(toolbarlist->vbox));
+ toolbarlist_populate(lw,GTK_BOX(toolbarlist[bar]->vbox), bar);
- return toolbarlist->widget;
+ return toolbarlist[bar]->widget;
}
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
#ifndef TOOLBAR_H
#define TOOLBAR_H
-GtkWidget *toolbar_select_new(LayoutWindow *lw);
-void toolbar_apply();
+GtkWidget *toolbar_select_new(LayoutWindow *lw, ToolbarType bar);
+void toolbar_apply(ToolbarType bar);
#endif
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
struct _AnimationData
{
ImageWindow *iw;
+ LayoutWindow *lw;
GdkPixbufAnimation *gpa;
GdkPixbufAnimationIter *iter;
GdkPixbuf *gpb;
FileData *data_adr;
guint delay;
gboolean valid;
+ GCancellable *cancellable;
+ GFile *in_file;
+ GFileInputStream *gfstream;
};
struct _CollectInfo
window.open( this.options[ this.selectedIndex ].value );
};
</script>
- <p>MacOS X <a href="http://brewformulas.org/Geeqie">Brewformulas.org</a></p>
+ <p>MacOS X <a href="https://formulae.brew.sh/formula/geeqie">Homebrew Formulae</a></p>
<p>Solaris <a href="https://www.opencsw.org/package/geeqie/">Openscw.org Solaris 10 & 11</a></p>
- <p>The source tar for Geeqie v1.4 is available <a href="http://geeqie.org/geeqie-1.4.tar.xz">here.</a></p>
+ <p>The source tar for Geeqie v1.5 is available <a href="/geeqie-1.5.tar.xz">here.</a> (Signature also available under <a href="/geeqie-1.5.tar.xz.asc">geeqie-1.5.tar.xz.asc</a>)</p>
<p>However if you are compiling from sources it is recommended that you get the latest sources from the
- <a href="http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git">Geeqie repository.</a></p>Geeqie is stable, there
+ <a href="/cgi-bin/gitweb.cgi?p=geeqie.git">Geeqie repository.</a></p>Geeqie is stable, there
are frequent updates, and compiling is a quick and painless task. The instructions for <a href=
"installing.html">downloading and installing</a> are easy to follow.
<p>For Ubuntu and other Debian-based systems, <a href="http://www.geeqie.org/web/geeqie-install-debian.sh"> this script</a> will download Geeqie sources, all dependencies, and compile and install Geeqie. You may also select which optional libraries to install.</p>