# HG changeset patch # User Klaus Ethgen # Date 1549397898 -3600 # Node ID 7e769ff099977040461211fc7e05abf433dee723 # Parent ce889887354fd768e0d8c4c85f937633d50f3c73# Parent 5705ded05a19653b22beb6625ba457a92888924e Merge merge requests 641, 638, 630 and 627 Just pro forma as they are already in the history. * github/merge-requests/641: Add export-jpg to POTFILES.in * github/merge-requests/638: Avoid non-portable == for the test command * github/merge-requests/630: add headline field * github/merge-requests/627: Add awk shebang to geocode-parameters.awk diff -r 5705ded05a19 -r 7e769ff09997 README.md --- a/README.md Tue Feb 05 21:18:18 2019 +0100 +++ b/README.md Tue Feb 05 21:18:18 2019 +0100 @@ -231,6 +231,13 @@ for displaying pdf files disable with configure option: --disable-pdf + ZoneDetect + For displaying timezone and UTC/DST time offset + Download file: + https://github.com/BertoldVdb/ZoneDetect/raw/master/database/timezone21.bin + and copy to the location of the Geeqie ancilliary files, which is usually: + /usr/local/lib/geeqie/ + ### Code hackers: If you plan on making any major changes to the code that will be offered for diff -r 5705ded05a19 -r 7e769ff09997 configure.in --- a/configure.in Tue Feb 05 21:18:18 2019 +0100 +++ b/configure.in Tue Feb 05 21:18:18 2019 +0100 @@ -200,7 +200,7 @@ AM_PATH_GLIB_2_0(2.24.0,,AC_MSG_ERROR(GLIB >= 2.24.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 +if test x$gtk3 = xyes; then AC_MSG_WARN("Gtk3 enabled") m4_ifdef([AM_PATH_GTK_3_0], [AM_PATH_GTK_3_0(3.0.0,,AC_MSG_ERROR(GTK+ >= 3.0.0 not installed.))]) else @@ -210,7 +210,7 @@ fi fi -if test x$gtk3 == xno; then +if test x$gtk3 = xno; then m4_ifdef([AM_PATH_GTK_2_0], [AM_PATH_GTK_2_0(2.20.0,,AC_MSG_ERROR(GTK+ >= 2.20.0 not installed.))]) true fi @@ -479,7 +479,7 @@ # Libchamplain support - used for map facility # ---------------------------------------------------------------------- -if test "x${libgps}" != "xno" -a "x${HAVE_CLUTTER}" == "xyes"; then +if test "x${libgps}" != "xno" -a "x${HAVE_CLUTTER}" = "xyes"; then if test "x${gtk3}" != "xno"; then PKG_CHECK_MODULES(LIBCHAMPLAIN, [champlain-0.12 >= 0.12], [ @@ -497,7 +497,7 @@ HAVE_LIBCHAMPLAIN=disabled fi -if test "x${libgps}" != "xno" -a "x${HAVE_LIBCHAMPLAIN}" == "xyes" ; then +if test "x${libgps}" != "xno" -a "x${HAVE_LIBCHAMPLAIN}" = "xyes" ; then PKG_CHECK_MODULES(LIBCHAMPLAIN_GTK, [champlain-gtk-0.12 >= 0.12], [ HAVE_LIBCHAMPLAIN_GTK=yes @@ -619,7 +619,6 @@ plugins/ufraw/Makefile plugins/import/Makefile plugins/geocode-parameters/Makefile - plugins/ZoneDetect/Makefile geeqie.spec ]) @@ -685,4 +684,3 @@ (or you take blue pill and the story ends :) EOF - diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideMainWindowFilePane.xml --- a/doc/docbook/GuideMainWindowFilePane.xml Tue Feb 05 21:18:18 2019 +0100 +++ b/doc/docbook/GuideMainWindowFilePane.xml Tue Feb 05 21:18:18 2019 +0100 @@ -57,6 +57,32 @@ +
+ File Filter + + A filter box can be opened by selecting + Show File Filter + from the View menu. + + + Characters you type into this box will be used for pattern matching to select the files displayed. + Regular expressions + are used in this field. + + For basic pattern matching "JPG" will match any filename containing those characters. + + To make a case-insensitive search, use "(?i)JPG" + + If you type a newline, the text will be saved in the dropdown box list. The last 10 items are saved. + + To delete an item, select it from the drop-down list, click on the text box to select the text, and then press + Delete + followed by + Enter + . + + +
Multiple selection It is possible to select more than one image from the file pane. Multiple files can be selected with several methods: diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideOptionsGeneral.xml --- a/doc/docbook/GuideOptionsGeneral.xml Tue Feb 05 21:18:18 2019 +0100 +++ b/doc/docbook/GuideOptionsGeneral.xml Tue Feb 05 21:18:18 2019 +0100 @@ -226,6 +226,14 @@
+
+ Show predefined keyword tree + Deselecting this option will hide the list of predefined keywords on the right-hand side of the keywords pane of the info sidebar. + + Geeqie must be restarted for the change to take effect. + + +
On-line help search diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideOptionsKeywords.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/docbook/GuideOptionsKeywords.xml Tue Feb 05 21:18:18 2019 +0100 @@ -0,0 +1,20 @@ + +
+ Keywords + This section describes the keywords list used for autocompletion. + +
+ Keyword Search + + Pressing the Search button will open a dialog which permits a recursive search to be made for keywords already attached to images. The result of the search is automatically appended to the existing list. +
+
+ Keyword List + + + The list shows all keywords currently used for autocompletion. Text may be copy-pasted into the list or deleted from the list. + + When the list is saved, duplicates will autmatically be removed and the list sorted into alphabetical order. + +
+
diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideOptionsMain.xml --- a/doc/docbook/GuideOptionsMain.xml Tue Feb 05 21:18:18 2019 +0100 +++ b/doc/docbook/GuideOptionsMain.xml Tue Feb 05 21:18:18 2019 +0100 @@ -23,10 +23,12 @@
+ + diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideOptionsOSD.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/docbook/GuideOptionsOSD.xml Tue Feb 05 21:18:18 2019 +0100 @@ -0,0 +1,237 @@ + +
+ OverlayScreen Display + This section describes the options presented under the OSD Tab of the preferences dialog. +
+ Overlay Screen Display + The contents of the Overlay Screen Display is defined by the Image Overlay Template. This template is easily customised to display a wide range of data. + + The format of each entry is: + %tag[:max_length][:extra]% + + + + + + + Tag + + + Replaced by + + + + name + Filename of the picture + + + collection + Name of the collection + + + number + Current number of image in the list + + + total + Total number of images + + + date + File date + + + size + File size + + + width + Image width + + + height + Image height + + + res + Image resolution + + + keywords + Image keywords from metadata + + + comment + Image comment from XMP metadata + + + imagecomment + JPEG image comment + + + rating + Image rating + + + <meta_tag> + The Exif, XMP, or IPTC tag from metadata + + + lua/<lua_script>/ + The output of a Lua script file + + + lua//<lua_command> + The output of a Lua command + + + +
+ + As an aide, in addition to standard metadata tags, Geeqie provides a number of + pre-formatted tags + . + + Examples of usage are: + + + %keywords% + %Exif.Photo.DateTimeOriginal% + %lua/jpeg_comment.lua/:12% + %lua//return(os.date())% + + + + Refer to + Lua Extensions + for further information. + + + The length of displayed data can be limited by using the + :max_length + parameter. The following example will truncate the displayed data to 20 characters and will add 3 dots at the end to denote the truncation. + %formatted.Camera:20% + + + If two or more variables are connected with the + | + character, the variables are displayed with a separator. For example:: + %formatted.ShutterSpeed%|%formatted.ISOSpeedRating%|%formatted.FocalLength% + could show: + "1/20s - 400 - 80 mm" + Or, if there is no ISO information in the Exif data: + "1/200 - 80 mm" + + If there is no data for a requested tag, the line is not displayed. + + The + :extra + parameter may be used to format the output by prepending and appending a text string to the displayed item. + + + The special character + * + is used to mark the position of the Tag data item. If no + * + is present, the extra string is just appended to the standard data displayed. Any "\n" is replaced by a newline on display. + Pango mark up + is accepted in both left and right parts. If the data item is empty, nothing will be displayed. + + Examples: + + + + + + Template + + + Example display + + + + + %name: + <i>*</i>\n% + + + filename001.jpg + + + + %size:\n% + 123456 + + + %formatted.ISOSpeedRating:ISO *% + ISO 100 + + + + %collection:Collection: + <b>*</b> + \n% + + + Collection: + my_new_collection + + + + +
+ + + + + + + + + + Font + , + + + + + + Text + , + + + + + + Background + + The font used for the Overlay Screen Display, as well as the font colour and background colour, may be set via these buttons. Colour transparency can also be set. + + + + + + + + + + + Help + + + Show syntax help + + + + + + + Defaults + + + Restore default image overlay template + + + +
+
diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideOptionsWindow.xml --- a/doc/docbook/GuideOptionsWindow.xml Tue Feb 05 21:18:18 2019 +0100 +++ b/doc/docbook/GuideOptionsWindow.xml Tue Feb 05 21:18:18 2019 +0100 @@ -145,236 +145,4 @@
-
- Overlay Screen Display - The contents of the Overlay Screen Display is defined by the Image Overlay Template. This template is easily customised to display a wide range of data. - - The format of each entry is: - %tag[:max_length][:extra]% - - - - - - - Tag - - - Replaced by - - - - name - Filename of the picture - - - collection - Name of the collection - - - number - Current number of image in the list - - - total - Total number of images - - - date - File date - - - size - File size - - - width - Image width - - - height - Image height - - - res - Image resolution - - - keywords - Image keywords from metadata - - - comment - Image comment from XMP metadata - - - imagecomment - JPEG image comment - - - rating - Image rating - - - <meta_tag> - The Exif, XMP, or IPTC tag from metadata - - - lua/<lua_script>/ - The output of a Lua script file - - - lua//<lua_command> - The output of a Lua command - - - -
- - As an aide, in addition to standard metadata tags, Geeqie provides a number of - pre-formatted tags - . - - Examples of usage are: - - - %keywords% - %Exif.Photo.DateTimeOriginal% - %lua/jpeg_comment.lua/:12% - %lua//return(os.date())% - - - - Refer to - Lua Extensions - for further information. - - - The length of displayed data can be limited by using the - :max_length - parameter. The following example will truncate the displayed data to 20 characters and will add 3 dots at the end to denote the truncation. - %formatted.Camera:20% - - - If two or more variables are connected with the - | - character, the variables are displayed with a separator. For example:: - %formatted.ShutterSpeed%|%formatted.ISOSpeedRating%|%formatted.FocalLength% - could show: - "1/20s - 400 - 80 mm" - Or, if there is no ISO information in the Exif data: - "1/200 - 80 mm" - - If there is no data for a requested tag, the line is not displayed. - - The - :extra - parameter may be used to format the output by prepending and appending a text string to the displayed item. - - - The special character - * - is used to mark the position of the Tag data item. If no - * - is present, the extra string is just appended to the standard data displayed. Any "\n" is replaced by a newline on display. - Pango mark up - is accepted in both left and right parts. If the data item is empty, nothing will be displayed. - - Examples: - - - - - - Template - - - Example display - - - - - %name: - <i>*</i>\n% - - - filename001.jpg - - - - %size:\n% - 123456 - - - %formatted.ISOSpeedRating:ISO *% - ISO 100 - - - - %collection:Collection: - <b>*</b> - \n% - - - Collection: - my_new_collection - - - - -
- - - - - - - - - - Font - , - - - - - - Text - , - - - - - - Background - - The font used for the Overlay Screen Display, as well as the font colour and background colour, may be set via these buttons. Colour transparency can also be set. - - - - - - - - - - - Help - - - Show syntax help - - - - - - - Defaults - - - Restore default image overlay template - - - -
diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideReferenceConfig.xml --- a/doc/docbook/GuideReferenceConfig.xml Tue Feb 05 21:18:18 2019 +0100 +++ b/doc/docbook/GuideReferenceConfig.xml Tue Feb 05 21:18:18 2019 +0100 @@ -3,7 +3,7 @@ Configuration Files and Locations The following data lists the locations Geeqie uses for various actions. The uppercase symbols are environment variables. If they are not set on your system the fallback locations are listed in parentheses. - Geqqie will first attempt to load a configuration file from: + Geeqie will first attempt to load a configuration file from: /etc/geeqie/geeqierc.xml It will then continue with the following locations. diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideReferenceTags.xml --- a/doc/docbook/GuideReferenceTags.xml Tue Feb 05 21:18:18 2019 +0100 +++ b/doc/docbook/GuideReferenceTags.xml Tue Feb 05 21:18:18 2019 +0100 @@ -301,6 +301,44 @@ + formatted.countryname + + + + Exif.GPSInfo.GPSLatitude + + Exif.GPSInfo.GPSLatitudeRef + + Exif.GPSInfo.GPSLongitude + + Exif.GPSInfo.GPSLongitudeRef + + + + ISO 3166 country name indicated by lat/long + + + + + formatted.countrycode + + + + Exif.GPSInfo.GPSLatitude + + Exif.GPSInfo.GPSLatitudeRef + + Exif.GPSInfo.GPSLongitude + + Exif.GPSInfo.GPSLongitudeRef + + + + ISO 3166 two-letter abbreviated country name indicated by lat/long + + + + formatted.star_rating @@ -362,6 +400,14 @@ Rating + + + Xmp.photoshop.Headline + + + Headline + + diff -r 5705ded05a19 -r 7e769ff09997 doc/docbook/GuideSidebarsInfo.xml --- a/doc/docbook/GuideSidebarsInfo.xml Tue Feb 05 21:18:18 2019 +0100 +++ b/doc/docbook/GuideSidebarsInfo.xml Tue Feb 05 21:18:18 2019 +0100 @@ -114,7 +114,12 @@ This action cannot be undone. Irrespective of their position in a hierarchy in the right-hand box, all keywords are written as individual entries into metadata. When metadata for an image is read in, Geeqie will attempt to recreate any hierarchies within the data, and display it accordingly in the right-hand box. - Right-click on the left-hand box allows the keywords to be written to the currently selected files - the keywords may be either added to the existing contents, or entirely replace the existing content. + + Right-click on the left-hand box allows the selected keywords to be written to the currently selected files - the keywords may be either added to the existing contents, or entirely replace the existing content. + + Keyword completion is used when selecting keywords. If the selected area includes part of a keyword, the selection will autmatically be expanded to cover the whole keyword. If no text is selected, all keywords will be used. + + Right-click on the right-hand box allows a connection to be made between a keyword and a mark. The @@ -160,6 +165,25 @@ +
+ Keyword Autocompletion + + The text box at the bottom of the keywords pane is used for autocompletion. Any keywords typed into the standard keyword box or the autocompletion box will be remembered as candidates for future autocompletion. + + Frequently used sets of keywords can be entered as comma-delimited lists. + + The list of keywords used for autocompletion can be edited on the + Keywords + tab of the Preferences dialog. + + The menu action "Keyword autocomplete", set to + Alt + K + by default, will shift the keyboard focus to the autocomplete box. Pressing + Alt + K + a second time will shift the keyboard focus back to the previous object. + + +
List panes @@ -277,6 +301,14 @@ + formatted.countryname + ISO 3166 country name indicated by GPS lat/long values + + + formatted.countrycode + ISO 3166 two-letter abbreviated country name indicated by GPS lat/long values + + file.size file size in bytes diff -r 5705ded05a19 -r 7e769ff09997 geeqie.1 --- a/geeqie.1 Tue Feb 05 21:18:18 2019 +0100 +++ b/geeqie.1 Tue Feb 05 21:18:18 2019 +0100 @@ -644,6 +644,59 @@ close window .br +.SH FILES +The following data lists the locations Geeqie uses for various actions. The +uppercase symbols are environment variables. If they are not set on your system +the fallback locations are listed in parentheses. Geeqie will first attempt to +load a configuration file from: + +.B /etc/geeqie/geeqierc.xml + +It will then continue with the following locations. +Most of Geeqie's configuration files are contained in the folder, and sub-folders of: + +.B $XDG_CONFIG_HOME/geeqie/ +.B ($~/.config/geeqie/) + +Geeqie's standard configuration file is: + +.B .../geeqierc.xml + +An alternative configuration file may be used by executing: + +.B geeqie -r --config-load: + +Geeqie-created desktop files used by Plugins are in the folder: + +.B .../applications + +Lua script files for Lua Extensions are in the folder: + +.B .../lua + +Historic data such as last several folders visited, bookmarks, and recently used collections, as well as default print settings are contained in this text file: + +.B .../history + +Keyboard shortcut maps are contained in this text file: + +.B .../accels + +The location for Collections is in the folder: + +.B $XDG_DATA_HOME/geeqie/collections +.br +.B ($~/.local/share/geeqie/collections) + +The lirc Infra-red controller configuration file must be located at: + +.B $HOME/.lircrc + +Thumbnails are stored in a location specified in Thumbnail Preferences + +Metadata is stored either in the image file or in the location specified in Safe Delete + +The safe delete folder is specified in the Metadata tab of main Preferences .SH LICENSE Copyright (C) 1999-2004 by John Ellis. diff -r 5705ded05a19 -r 7e769ff09997 plugins/Makefile.am --- a/plugins/Makefile.am Tue Feb 05 21:18:18 2019 +0100 +++ b/plugins/Makefile.am Tue Feb 05 21:18:18 2019 +0100 @@ -1,6 +1,6 @@ #FIXME enable or disable individual plugins from configure -SUBDIRS = rotate symlink ufraw import geocode-parameters ZoneDetect +SUBDIRS = rotate symlink ufraw import geocode-parameters qq_desktoptemplatedir = $(appdir) qq_desktoptemplate_DATA = template.desktop diff -r 5705ded05a19 -r 7e769ff09997 plugins/ZoneDetect/Makefile.am --- a/plugins/ZoneDetect/Makefile.am Tue Feb 05 21:18:18 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -dist_gq_bin_SCRIPTS = timezone21.bin diff -r 5705ded05a19 -r 7e769ff09997 plugins/ZoneDetect/timezone21.bin Binary file plugins/ZoneDetect/timezone21.bin has changed diff -r 5705ded05a19 -r 7e769ff09997 src/bar_keywords.c --- a/src/bar_keywords.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/bar_keywords.c Tue Feb 05 21:18:18 2019 +0100 @@ -37,11 +37,17 @@ #include "rcfile.h" #include "layout.h" #include "dnd.h" +#include "secure_save.h" //static void bar_pane_keywords_keyword_update_all(void); static void bar_pane_keywords_changed(GtkTextBuffer *buffer, gpointer data); +static void autocomplete_keywords_list_load(const gchar *path); +static GtkListStore *keyword_store = NULL; +static gboolean autocomplete_keywords_list_save(gchar *path); +static gboolean autocomplete_activate_cb(GtkWidget *widget, gpointer data); + /* *------------------------------------------------------------------- * keyword / comment utils @@ -62,6 +68,19 @@ return list; } +static GList *keyword_list_pull_selected(GtkWidget *text_widget) +{ + GList *list; + gchar *text; + + text = text_widget_text_pull_selected(text_widget); + list = string_to_keywords_list(text); + + g_free(text); + + return list; +} + /* the "changed" signal should be blocked before calling this */ static void keyword_list_push(GtkWidget *textview, GList *list) { @@ -125,6 +144,8 @@ gint height; GList *expanded_rows; + + GtkWidget *autocomplete; }; typedef struct _ConfDialogData ConfDialogData; @@ -323,6 +344,10 @@ if (gtk_widget_has_focus(pkd->keyword_view)) return gtk_widget_event(pkd->keyword_view, event); + if (gtk_widget_has_focus(pkd->autocomplete)) + { + return gtk_widget_event(pkd->autocomplete, event); + } return FALSE; } @@ -416,7 +441,7 @@ GList *list = NULL; GList *work; - keywords = keyword_list_pull(pkd->keyword_view); + keywords = keyword_list_pull_selected(pkd->keyword_view); list = layout_selection_list(pkd->pane.lw); list = file_data_process_groups_in_selection(list, FALSE, NULL); @@ -460,8 +485,8 @@ PaneKeywordsData *pkd = data; menu_item_add_divider(GTK_WIDGET(menu)); - menu_item_add_stock(GTK_WIDGET(menu), _("Add keywords to selected files"), GTK_STOCK_ADD, G_CALLBACK(bar_pane_keywords_sel_add_cb), pkd); - menu_item_add_stock(GTK_WIDGET(menu), _("Replace existing keywords in selected files"), GTK_STOCK_CONVERT, G_CALLBACK(bar_pane_keywords_sel_replace_cb), pkd); + menu_item_add_stock(GTK_WIDGET(menu), _("Add selected keywords to selected files"), GTK_STOCK_ADD, G_CALLBACK(bar_pane_keywords_sel_add_cb), pkd); + menu_item_add_stock(GTK_WIDGET(menu), _("Replace existing keywords in selected files with selected keywords"), GTK_STOCK_CONVERT, G_CALLBACK(bar_pane_keywords_sel_replace_cb), pkd); } @@ -1417,6 +1442,10 @@ static void bar_pane_keywords_destroy(GtkWidget *widget, gpointer data) { PaneKeywordsData *pkd = data; + gchar *path; + + path = g_build_filename(get_rc_dir(), "keywords", NULL); + autocomplete_keywords_list_save(path); string_list_free(pkd->expanded_rows); if (pkd->click_tpath) gtk_tree_path_free(pkd->click_tpath); @@ -1433,13 +1462,15 @@ static GtkWidget *bar_pane_keywords_new(const gchar *id, const gchar *title, const gchar *key, gboolean expanded, gint height) { PaneKeywordsData *pkd; - GtkWidget *hbox; + GtkWidget *hbox, *vbox; GtkWidget *scrolled; GtkTextBuffer *buffer; GtkTreeModel *store; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeIter iter; + GtkEntryCompletion *completion; + gchar *path; pkd = g_new0(PaneKeywordsData, 1); @@ -1458,9 +1489,11 @@ pkd->expand_checked = TRUE; pkd->expanded_rows = NULL; + vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP); hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); - pkd->widget = hbox; + pkd->widget = vbox; g_object_set_data(G_OBJECT(pkd->widget), "pane_data", pkd); g_signal_connect(G_OBJECT(pkd->widget), "destroy", G_CALLBACK(bar_pane_keywords_destroy), pkd); @@ -1484,13 +1517,36 @@ g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(bar_pane_keywords_changed), pkd); - scrolled = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0); - gtk_widget_show(scrolled); + if (options->show_predefined_keyword_tree) + { + scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0); + gtk_widget_show(scrolled); + } + pkd->autocomplete = gtk_entry_new(); + gtk_box_pack_end(GTK_BOX(vbox), pkd->autocomplete, FALSE, FALSE, 0); + gtk_widget_show(pkd->autocomplete); + gtk_widget_show(vbox); + gtk_widget_set_tooltip_text(pkd->autocomplete, "Keyword autocomplete"); + + path = g_build_filename(get_rc_dir(), "keywords", NULL); + autocomplete_keywords_list_load(path); + + completion = gtk_entry_completion_new(); + gtk_entry_set_completion(GTK_ENTRY(pkd->autocomplete), completion); + gtk_entry_completion_set_inline_completion(completion, TRUE); + gtk_entry_completion_set_inline_selection(completion, TRUE); + g_object_unref(completion); + + gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(keyword_store)); + gtk_entry_completion_set_text_column(completion, 0); + + g_signal_connect(G_OBJECT(pkd->autocomplete), "activate", + G_CALLBACK(autocomplete_activate_cb), pkd); if (!keyword_tree || !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_tree), &iter)) { @@ -1573,8 +1629,11 @@ g_signal_connect(G_OBJECT(pkd->keyword_treeview), "button_release_event", G_CALLBACK(bar_pane_keywords_menu_cb), pkd); - gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_treeview); - gtk_widget_show(pkd->keyword_treeview); + if (options->show_predefined_keyword_tree) + { + gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_treeview); + gtk_widget_show(pkd->keyword_treeview); + } file_data_register_notify_func(bar_pane_keywords_notify_cb, pkd, NOTIFY_PRIORITY_LOW); @@ -1674,4 +1733,258 @@ log_printf("unknown attribute %s = %s\n", option, value); } } + +/* + *----------------------------------------------------------------------------- + * Autocomplete keywords + *----------------------------------------------------------------------------- + */ + +static gboolean autocomplete_activate_cb(GtkWidget *widget, gpointer data) +{ + PaneKeywordsData *pkd = data; + gchar *entry_text; + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkTreeIter iter_t; + gchar *kw_cr; + gchar *kw_split; + gboolean valid; + gboolean found = FALSE; + gchar *string; + + entry_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pkd->autocomplete))); + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(pkd->keyword_view)); + + kw_split = strtok(entry_text, ","); + while (kw_split != NULL) + { + kw_cr = g_strconcat(kw_split, "\n", NULL); + g_strchug(kw_cr); + gtk_text_buffer_get_end_iter(buffer, &iter); + gtk_text_buffer_insert(buffer, &iter, kw_cr, -1); + + kw_split = strtok(NULL, ","); + g_free(kw_cr); + } + + g_free(entry_text); + entry_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pkd->autocomplete))); + + gtk_entry_set_text(GTK_ENTRY(pkd->autocomplete), ""); + + valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_store), &iter_t); + while (valid) + { + gtk_tree_model_get (GTK_TREE_MODEL(keyword_store), &iter_t, 0, &string, -1); + if (g_strcmp0(entry_text, string) == 0) + { + found = TRUE; + break; + } + valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(keyword_store), &iter_t); + } + + if (!found) + { + gtk_list_store_append (keyword_store, &iter_t); + gtk_list_store_set(keyword_store, &iter_t, 0, entry_text, -1); + } + + g_free(entry_text); + return FALSE; +} + +gint autocomplete_sort_iter_compare_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer userdata) +{ + gint ret = 0; + gchar *name1, *name2; + + gtk_tree_model_get(model, a, 0, &name1, -1); + gtk_tree_model_get(model, b, 0, &name2, -1); + + if (name1 == NULL || name2 == NULL) + { + if (name1 == NULL && name2 == NULL) + { + ret = 0; + } + else + { + ret = (name1 == NULL) ? -1 : 1; + } + } + else + { + ret = g_utf8_collate(name1,name2); + } + + g_free(name1); + g_free(name2); + + return ret; +} + +static void autocomplete_keywords_list_load(const gchar *path) +{ + FILE *f; + gchar s_buf[1024]; + gchar *pathl; + gint len; + GtkTreeIter iter; + GtkTreeSortable *sortable; + + if (keyword_store) return; + keyword_store = gtk_list_store_new(1, G_TYPE_STRING); + + sortable = GTK_TREE_SORTABLE(keyword_store); + gtk_tree_sortable_set_sort_func(sortable, 0, autocomplete_sort_iter_compare_func, + GINT_TO_POINTER(0), NULL); + + gtk_tree_sortable_set_sort_column_id(sortable, 0, GTK_SORT_ASCENDING); + + pathl = path_from_utf8(path); + f = fopen(pathl, "r"); + g_free(pathl); + + if (!f) + { + log_printf("Warning: keywords file %s not loaded", pathl); + return; + } + + /* first line must start with Keywords comment */ + if (!fgets(s_buf, sizeof(s_buf), f) || + strncmp(s_buf, "#Keywords", 9) != 0) + { + fclose(f); + log_printf("Warning: keywords file %s not loaded", pathl); + return; + } + + while (fgets(s_buf, sizeof(s_buf), f)) + { + if (s_buf[0]=='#') continue; + + len = strlen(s_buf); + if( s_buf[len-1] == '\n' ) + { + s_buf[len-1] = 0; + } + gtk_list_store_append (keyword_store, &iter); + gtk_list_store_set(keyword_store, &iter, 0, g_strdup(s_buf), -1); + } + + fclose(f); +} + +static gboolean autocomplete_keywords_list_save(gchar *path) +{ + SecureSaveInfo *ssi; + gchar *pathl; + gchar *string; + gchar *string_nl; + GtkTreeIter iter; + gboolean valid; + + pathl = path_from_utf8(path); + ssi = secure_open(pathl); + g_free(pathl); + + if (!ssi) + { + log_printf(_("Error: Unable to write keywords list to: %s\n"), path); + return FALSE; + } + + secure_fprintf(ssi, "#Keywords list\n"); + + valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_store), &iter); + + while (valid) + { + gtk_tree_model_get (GTK_TREE_MODEL(keyword_store), &iter, 0, &string, -1); + string_nl = g_strconcat(string, "\n", NULL); + secure_fprintf(ssi, "%s", string_nl); + + valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(keyword_store), &iter); + + g_free(string_nl); + } + + secure_fprintf(ssi, "#end\n"); + return (secure_close(ssi) == 0); +} + +GList *keyword_list_get() +{ + GList *ret_list = NULL; + gchar *string; + gchar *string_nl; + GtkTreeIter iter; + gboolean valid; + + valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_store), &iter); + + while (valid) + { + gtk_tree_model_get (GTK_TREE_MODEL(keyword_store), &iter, 0, &string, -1); + string_nl = g_strconcat(string, "\n", NULL); + ret_list = g_list_append(ret_list, string); + valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(keyword_store), &iter); + + g_free(string_nl); + } + + return ret_list; +} + +void keyword_list_set(GList *keyword_list) +{ + GList *ret = NULL; + GtkTreeIter iter; + + if (!keyword_list) return; + + gtk_list_store_clear(keyword_store); + + while (keyword_list) + { + gtk_list_store_append (keyword_store, &iter); + gtk_list_store_set(keyword_store, &iter, 0, keyword_list->data, -1); + + keyword_list = keyword_list->next; + } +} + +gboolean bar_keywords_autocomplete_focus(LayoutWindow *lw) +{ + GtkWidget *pane; + GtkWidget *current_focus; + GList *children; + gboolean ret; + + current_focus = gtk_window_get_focus(GTK_WINDOW(lw->window)); + pane = bar_find_pane_by_id(lw->bar, PANE_KEYWORDS, "keywords"); + + children = gtk_container_get_children(GTK_CONTAINER(pane)); + children = g_list_last(children); + + if (current_focus == children->data) + { + ret = TRUE; + } + else + { + gtk_widget_grab_focus(children->data); + ret = FALSE; + } + + g_list_free(children); + + return ret; +} /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff -r 5705ded05a19 -r 7e769ff09997 src/bar_keywords.h --- a/src/bar_keywords.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/bar_keywords.h Tue Feb 05 21:18:18 2019 +0100 @@ -28,5 +28,8 @@ /* used in search.c */ GList *keyword_list_pull(GtkWidget *text_widget); +GList *keyword_list_get(); +void keyword_list_set(GList *keyword_list); +gboolean bar_keywords_autocomplete_focus(LayoutWindow *lw); #endif /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff -r 5705ded05a19 -r 7e769ff09997 src/collect-table.c --- a/src/collect-table.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/collect-table.c Tue Feb 05 21:18:18 2019 +0100 @@ -904,7 +904,7 @@ fd = (ct->click_info) ? ct->click_info->fd : NULL; - print_window_new(fd, collection_table_selection_get_list(ct), collection_table_get_list(ct), ct->listview); + print_window_new(fd, collection_table_selection_get_list(ct), collection_table_get_list(ct), gtk_widget_get_toplevel(ct->listview)); } static void collection_table_popup_show_names_cb(GtkWidget *widget, gpointer data) diff -r 5705ded05a19 -r 7e769ff09997 src/exif-common.c --- a/src/exif-common.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/exif-common.c Tue Feb 05 21:18:18 2019 +0100 @@ -612,25 +612,21 @@ } /** - * @brief Extracts timezone from a ZoneDetect search structure - * @param results ZoneDetect search structure - * @returns Timezone in the form "Europe/London" + * @brief Extracts timezone data from a ZoneDetect search structure + * @param[in] results ZoneDetect search structure + * @param[out] timezone in the form "Europe/London" + * @param[out] countryname in the form "United Kingdom" + * @param[out] countryalpha2 in the form "GB" * * Refer to https://github.com/BertoldVdb/ZoneDetect * for structure details */ -static gchar *zd_tz(ZoneDetectResult* results) +static void zd_tz(ZoneDetectResult *results, gchar **timezone, gchar **countryname, gchar **countryalpha2) { - gchar *timezone = NULL; gchar *timezone_pre = NULL; gchar *timezone_id = NULL; unsigned int index = 0; - if (!results) - { - return NULL; - } - while(results[index].lookupResult != ZD_LOOKUP_END) { if(results[index].data) @@ -645,29 +641,36 @@ { timezone_id = g_strdup(results[index].data[i]); } + if (g_strstr_len(results[index].fieldNames[i], -1, "CountryName")) + { + *countryname = g_strdup(results[index].data[i]); + } + if (g_strstr_len(results[index].fieldNames[i], -1, "CountryAlpha2")) + { + *countryalpha2 = g_strdup(results[index].data[i]); + } } } index++; } - timezone = g_strconcat(timezone_pre, timezone_id, NULL); + *timezone = g_strconcat(timezone_pre, timezone_id, NULL); g_free(timezone_pre); g_free(timezone_id); - return timezone; } /** - * @brief Creates local time from GPS lat/long - * @param exif - * @returns Localised time and date - * - * GPS lat/long is translated to timezone using ZoneDetect. - * GPS UTC is converted to Unix time stamp (seconds since 1970). - * The TZ environment variable is set to the relevant timezone - * and the Unix timestamp converted to local time using locale. - * If the conversion fails, unformatted UTC is returned. + * @brief Gets timezone data from an exif structure + * @param[in] exif + * @returns TRUE if timezone data found + * @param[out] exif_date_time exif date/time in the form 2018:11:30:17:05:04 + * @param[out] timezone in the form "Europe/London" + * @param[out] countryname in the form "United Kingdom" + * @param[out] countryalpha2 in the form "GB" + * + * */ -static gchar *exif_build_formatted_localtime(ExifData *exif) +static gboolean exif_build_tz_data(ExifData *exif, gchar **exif_date_time, gchar **timezone, gchar **countryname, gchar **countryalpha2) { gfloat latitude; gfloat longitude; @@ -677,24 +680,14 @@ gchar *text_longitude_ref; gchar *text_date; gchar *text_time; - gchar *text_date_time = NULL; - gchar buf[128]; - gchar *tmp; - gint buflen; - GError *error = NULL; gchar *lat_deg; gchar *lat_min; gchar *lon_deg; gchar *lon_min; - gchar *time_zone; - gchar *time_zone_org; - struct tm *tm_local; - struct tm tm_utc; - time_t stamp; gchar *zd_path; - gchar *zone_selected; ZoneDetect *cd; ZoneDetectResult *results; + gboolean ret = FALSE; text_latitude = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLatitude"); text_longitude = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLongitude"); @@ -706,7 +699,7 @@ if (text_latitude && text_longitude && text_latitude_ref && text_longitude_ref && text_date && text_time) { - text_date_time = g_strconcat(text_date, ":", text_time, NULL); + *exif_date_time = g_strconcat(text_date, ":", text_time, NULL); lat_deg = strtok(text_latitude, "deg'"); lat_min = strtok(NULL, "deg'"); @@ -724,142 +717,170 @@ } zd_path = g_build_filename(GQ_BIN_DIR, TIMEZONE_DATABASE, NULL); - cd = ZDOpenDatabase(zd_path); - if (cd) + if (g_file_test(zd_path, G_FILE_TEST_EXISTS)) { - results = ZDLookup(cd, latitude, longitude, NULL); - zone_selected = zd_tz(results); - time_zone = g_strconcat("TZ=", zone_selected, NULL); - time_zone_org = g_strconcat("TZ=", getenv("TZ"), NULL); - putenv("TZ=UTC"); - g_free(zone_selected); - - memset(&tm_utc, 0, sizeof(tm_utc)); - if (text_date_time && strptime(text_date_time, "%Y:%m:%d:%H:%M:%S", &tm_utc)) + cd = ZDOpenDatabase(zd_path); + if (cd) { - stamp = mktime(&tm_utc); // Convert the struct to a Unix timestamp - putenv(time_zone); // Switch to destination time zone - - tm_local = localtime(&stamp); - - /* Convert to localtime using locale */ - buflen = strftime(buf, sizeof(buf), "%x %X", tm_local); - if (buflen > 0) + results = ZDLookup(cd, latitude, longitude, NULL); + if (results) { - tmp = g_locale_to_utf8(buf, buflen, NULL, NULL, &error); - if (error) - { - log_printf("Error converting locale strftime to UTF-8: %s\n", error->message); - g_error_free(error); - } - else - { - g_free(text_date_time); - text_date_time = g_strdup(tmp); - } + zd_tz(results, timezone, countryname, countryalpha2); + ret = TRUE; } - g_free(tmp); } - putenv(time_zone_org); - - g_free(time_zone); - g_free(time_zone_org); + else + { + log_printf("Error: Init of timezone database %s failed\n", zd_path); + } + ZDCloseDatabase(cd); } - else - { - log_printf("Error: Init of timezone database %s failed\n", zd_path); - } - ZDCloseDatabase(cd); g_free(zd_path); } - g_free(text_latitude); - g_free(text_longitude); - g_free(text_latitude_ref); - g_free(text_longitude_ref); - g_free(text_date); - g_free(text_time); + return ret; +} + +/** + * @brief Creates local time from GPS lat/long + * @param[in] exif + * @returns Localised time and date + * + * GPS lat/long is translated to timezone using ZoneDetect. + * GPS UTC is converted to Unix time stamp (seconds since 1970). + * The TZ environment variable is set to the relevant timezone + * and the Unix timestamp converted to local time using locale. + * If the conversion fails, unformatted UTC is returned. + */ +static gchar *exif_build_formatted_localtime(ExifData *exif) +{ + gchar buf[128]; + gchar *tmp; + gint buflen; + GError *error = NULL; + gchar *time_zone_image; + gchar *time_zone_org; + struct tm *tm_local; + struct tm tm_utc; + time_t stamp; + gchar *exif_date_time = NULL; + gchar *timezone = NULL; + gchar *countryname = NULL; + gchar *countryalpha2 = NULL; + + if (exif_build_tz_data(exif, &exif_date_time, &timezone, &countryname, &countryalpha2)) + { + time_zone_image = g_strconcat("TZ=", timezone, NULL); + time_zone_org = g_strconcat("TZ=", getenv("TZ"), NULL); + putenv("TZ=UTC"); - return text_date_time; + memset(&tm_utc, 0, sizeof(tm_utc)); + if (exif_date_time && strptime(exif_date_time, "%Y:%m:%d:%H:%M:%S", &tm_utc)) + { + stamp = mktime(&tm_utc); // Convert the struct to a Unix timestamp + putenv(time_zone_image); // Switch to destination time zone + + tm_local = localtime(&stamp); + + /* Convert to localtime using locale */ + buflen = strftime(buf, sizeof(buf), "%x %X", tm_local); + if (buflen > 0) + { + tmp = g_locale_to_utf8(buf, buflen, NULL, NULL, &error); + if (error) + { + log_printf("Error converting locale strftime to UTF-8: %s\n", error->message); + g_error_free(error); + } + else + { + g_free(exif_date_time); + exif_date_time = g_strdup(tmp); + } + } + g_free(tmp); + } + putenv(time_zone_org); + + g_free(time_zone_image); + g_free(time_zone_org); + } + + g_free(timezone); + g_free(countryname); + g_free(countryalpha2); + + return exif_date_time; } /** * @brief Gets timezone from GPS lat/long - * @param exif + * @param[in] exif * @returns Timezone string in the form "Europe/London" - * - * + * + * */ static gchar *exif_build_formatted_timezone(ExifData *exif) { - gfloat latitude; - gfloat longitude; - gchar *text_latitude; - gchar *text_longitude; - gchar *text_latitude_ref; - gchar *text_longitude_ref; - gchar *lat_deg; - gchar *lat_min; - gchar *lon_deg; - gchar *lon_min; gchar *time_zone = NULL; - gchar *zd_path; - ZoneDetect *cd; - ZoneDetectResult *results; + gchar *exif_date_time = NULL; + gchar *timezone = NULL; + gchar *countryname = NULL; + gchar *countryalpha2 = NULL; + + exif_build_tz_data(exif, &exif_date_time, &timezone, &countryname, &countryalpha2); + + g_free(exif_date_time); + g_free(countryname); + g_free(countryalpha2); + + return timezone; +} - text_latitude = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLatitude"); - text_longitude = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLongitude"); - text_latitude_ref = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLatitudeRef"); - text_longitude_ref = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLongitudeRef"); +/** + * @brief Gets countryname from GPS lat/long + * @param[in] exif + * @returns Countryname string + * + * + */ +static gchar *exif_build_formatted_countryname(ExifData *exif) +{ + gchar *exif_date_time = NULL; + gchar *timezone = NULL; + gchar *countryname = NULL; + gchar *countryalpha2 = NULL; - if ((text_latitude && g_strrstr(text_latitude, "deg")) && - (text_longitude && g_strrstr(text_longitude, "deg")) && - ( - (text_latitude_ref && g_strrstr(text_latitude_ref, "N")) || - (text_latitude_ref && g_strrstr(text_latitude_ref, "S")) - ) && - ( - (text_longitude_ref && g_strrstr(text_longitude_ref, "E")) || - (text_longitude_ref && g_strrstr(text_longitude_ref, "W")) - ) - ) - { - lat_deg = strtok(text_latitude, "deg'"); - lat_min = strtok(NULL, "deg'"); - latitude = atof(lat_deg) + atof(lat_min) / 60; - if (g_strcmp0(text_latitude_ref, "South") == 0) - { - latitude = -latitude; - } - lon_deg = strtok(text_longitude, "deg'"); - lon_min = strtok(NULL, "deg'"); - longitude = atof(lon_deg) + atof(lon_min) / 60; - if (g_strcmp0(text_longitude_ref, "West") == 0) - { - longitude = -longitude; - } - zd_path = g_build_filename(GQ_BIN_DIR, TIMEZONE_DATABASE, NULL); - cd = ZDOpenDatabase(zd_path); - if (cd) - { - results = ZDLookup(cd, latitude, longitude, NULL); - time_zone = zd_tz(results); - ZDFreeResults(results); - } - else - { - log_printf("Error: Init of timezone database %s failed\n", zd_path); - } - ZDCloseDatabase(cd); - g_free(zd_path); - } + exif_build_tz_data(exif, &exif_date_time, &timezone, &countryname, &countryalpha2); + + g_free(exif_date_time); + g_free(timezone); + g_free(countryalpha2); + + return countryname; +} - g_free(text_latitude); - g_free(text_longitude); - g_free(text_latitude_ref); - g_free(text_longitude_ref); +/** + * @brief Gets two-letter country code from GPS lat/long + * @param[in] exif + * @returns Countryalpha2 string + * + * + */ +static gchar *exif_build_formatted_countrycode(ExifData *exif) +{ + gchar *exif_date_time = NULL; + gchar *timezone = NULL; + gchar *countryname = NULL; + gchar *countryalpha2 = NULL; - return time_zone; + exif_build_tz_data(exif, &exif_date_time, &timezone, &countryname, &countryalpha2); + + g_free(exif_date_time); + g_free(timezone); + g_free(countryname); + + return countryalpha2; } static gchar *exif_build_formatted_star_rating(ExifData *exif) @@ -892,6 +913,8 @@ EXIF_FORMATTED_TAG(GPSAltitude, N_("GPS altitude")), EXIF_FORMATTED_TAG(localtime, N_("Local time")), EXIF_FORMATTED_TAG(timezone, N_("Time zone")), + EXIF_FORMATTED_TAG(countryname, N_("Country name")), + EXIF_FORMATTED_TAG(countrycode, N_("Country code")), EXIF_FORMATTED_TAG(star_rating, N_("Star rating")), {"file.size", N_("File size"), NULL}, {"file.date", N_("File date"), NULL}, diff -r 5705ded05a19 -r 7e769ff09997 src/filedata.c --- a/src/filedata.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/filedata.c Tue Feb 05 21:18:18 2019 +0100 @@ -1817,6 +1817,33 @@ return list; } +gboolean file_data_filter_file_filter(FileData *fd, GRegex *filter) +{ + return g_regex_match(filter, fd->name, 0, NULL); +} + +GList *file_data_filter_file_filter_list(GList *list, GRegex *filter) +{ + GList *work; + + work = list; + while (work) + { + FileData *fd = work->data; + GList *link = work; + work = work->next; + + if (!file_data_filter_file_filter(fd, filter)) + { + list = g_list_remove_link(list, link); + file_data_unref(fd); + g_list_free(link); + } + } + + return list; +} + static void file_data_notify_mark_func(gpointer key, gpointer value, gpointer user_data) { FileData *fd = value; diff -r 5705ded05a19 -r 7e769ff09997 src/filedata.h --- a/src/filedata.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/filedata.h Tue Feb 05 21:18:18 2019 +0100 @@ -99,6 +99,9 @@ gboolean file_data_filter_marks(FileData *fd, guint filter); GList *file_data_filter_marks_list(GList *list, guint filter); +gboolean file_data_filter_file_filter(FileData *fd, GRegex *filter); +GList *file_data_filter_file_filter_list(GList *list, GRegex *filter); + gint file_data_get_user_orientation(FileData *fd); void file_data_set_user_orientation(FileData *fd, gint value); diff -r 5705ded05a19 -r 7e769ff09997 src/icons/Makefile.am --- a/src/icons/Makefile.am Tue Feb 05 21:18:18 2019 +0100 +++ b/src/icons/Makefile.am Tue Feb 05 21:18:18 2019 +0100 @@ -39,7 +39,8 @@ icon_select_all.png \ icon_select_none.png \ icon_select_invert.png \ - icon_select_rectangle.png + icon_select_rectangle.png \ + icon_file_filter.png ICONS_INLINE_PAIRS = \ @@ -78,7 +79,8 @@ icon_select_all $(srcdir)/icon_select_all.png \ icon_select_none $(srcdir)/icon_select_none.png \ icon_select_invert $(srcdir)/icon_select_invert.png \ - icon_select_rectangle $(srcdir)/icon_select_rectangle.png + icon_select_rectangle $(srcdir)/icon_select_rectangle.png \ + icon_file_filter $(srcdir)/icon_file_filter.png icons_inline.h: $(ICONS_INLINE) Makefile.in @sh -ec "echo '/* Auto generated file, do not edit */'; echo; \ diff -r 5705ded05a19 -r 7e769ff09997 src/icons/icon_file_filter.png Binary file src/icons/icon_file_filter.png has changed diff -r 5705ded05a19 -r 7e769ff09997 src/layout.c --- a/src/layout.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/layout.c Tue Feb 05 21:18:18 2019 +0100 @@ -862,6 +862,11 @@ if (lw->vf) vf_thumb_set(lw->vf, lw->options.show_thumbnails); } +static void layout_list_sync_file_filter(LayoutWindow *lw) +{ + if (lw->vf) vf_file_filter_set(lw->vf, lw->options.show_file_filter); +} + static GtkWidget *layout_list_new(LayoutWindow *lw) { lw->vf = vf_new(lw->options.file_view_type, NULL); @@ -873,6 +878,7 @@ vf_marks_set(lw->vf, lw->options.show_marks); layout_list_sync_thumb(lw); + layout_list_sync_file_filter(lw); return lw->vf->widget; } @@ -1178,6 +1184,18 @@ layout_list_sync_thumb(lw); } +void layout_file_filter_set(LayoutWindow *lw, gboolean enable) +{ + if (!layout_valid(&lw)) return; + + if (lw->options.show_file_filter == enable) return; + + lw->options.show_file_filter = enable; + + layout_util_sync_file_filter(lw); + layout_list_sync_file_filter(lw); +} + void layout_marks_set(LayoutWindow *lw, gboolean enable) { if (!layout_valid(&lw)) return; @@ -2476,6 +2494,7 @@ WRITE_NL(); WRITE_UINT(*layout, dir_view_type); WRITE_NL(); WRITE_UINT(*layout, file_view_type); WRITE_NL(); WRITE_BOOL(*layout, show_marks); + WRITE_NL(); WRITE_BOOL(*layout, show_file_filter); WRITE_NL(); WRITE_BOOL(*layout, show_thumbnails); WRITE_NL(); WRITE_BOOL(*layout, show_directory_date); WRITE_NL(); WRITE_CHAR(*layout, home_path); @@ -2565,6 +2584,7 @@ if (READ_UINT(*layout, dir_view_type)) continue; if (READ_UINT(*layout, file_view_type)) continue; if (READ_BOOL(*layout, show_marks)) continue; + if (READ_BOOL(*layout, show_file_filter)) continue; if (READ_BOOL(*layout, show_thumbnails)) continue; if (READ_BOOL(*layout, show_directory_date)) continue; if (READ_CHAR(*layout, home_path)) continue; diff -r 5705ded05a19 -r 7e769ff09997 src/layout.h --- a/src/layout.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/layout.h Tue Feb 05 21:18:18 2019 +0100 @@ -89,6 +89,8 @@ void layout_marks_set(LayoutWindow *lw, gboolean enable); gboolean layout_marks_get(LayoutWindow *lw); +void layout_file_filter_set(LayoutWindow *lw, gboolean enable); + void layout_sort_set(LayoutWindow *lw, SortType type, gboolean ascend); gboolean layout_sort_get(LayoutWindow *lw, SortType *type, gboolean *ascend); diff -r 5705ded05a19 -r 7e769ff09997 src/layout_util.c --- a/src/layout_util.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/layout_util.c Tue Feb 05 21:18:18 2019 +0100 @@ -25,6 +25,7 @@ #include "advanced_exif.h" #include "bar_sort.h" #include "bar.h" +#include "bar_keywords.h" #include "cache_maint.h" #include "collect.h" #include "collect-dlg.h" @@ -117,6 +118,15 @@ return TRUE; } } + + if (lw->vf->file_filter.combo && gtk_widget_has_focus(gtk_bin_get_child(GTK_BIN(lw->vf->file_filter.combo)))) + { + if (gtk_widget_event(gtk_bin_get_child(GTK_BIN(lw->vf->file_filter.combo)), (GdkEvent *)event)) + { + return TRUE; + } + } + if (lw->vd && lw->options.dir_view_type == DIRVIEW_TREE && gtk_widget_has_focus(lw->vd->view) && !layout_key_match(event->keyval) && gtk_widget_event(lw->vd->view, (GdkEvent *)event)) @@ -1387,6 +1397,13 @@ layout_select_invert(lw); } +static void layout_menu_file_filter_cb(GtkToggleAction *action, gpointer data) +{ + LayoutWindow *lw = data; + + layout_file_filter_set(lw, gtk_toggle_action_get_active(action)); +} + static void layout_menu_marks_cb(GtkToggleAction *action, gpointer data) { LayoutWindow *lw = data; @@ -1640,6 +1657,25 @@ metadata_write_queue_confirm(TRUE, NULL, NULL); } +static GtkWidget *last_focussed = NULL; +static void layout_menu_keyword_autocomplete_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + GtkWidget *tmp; + gboolean auto_has_focus; + + tmp = gtk_window_get_focus(GTK_WINDOW(lw->window)); + auto_has_focus = bar_keywords_autocomplete_focus(lw); + + if (auto_has_focus) + { + gtk_widget_grab_focus(last_focussed); + } + else + { + last_focussed = tmp; + } +} /* *----------------------------------------------------------------------------- @@ -1872,6 +1908,7 @@ { "Maintenance", PIXBUF_INLINE_ICON_MAINTENANCE, N_("_Cache maintenance..."), NULL, N_("Cache maintenance..."), CB(layout_menu_remove_thumb_cb) }, { "Wallpaper", NULL, N_("Set as _wallpaper"), NULL, N_("Set as wallpaper"), CB(layout_menu_wallpaper_cb) }, { "SaveMetadata", GTK_STOCK_SAVE, N_("_Save metadata"), "S", N_("Save metadata"), CB(layout_menu_metadata_write_cb) }, + { "KeywordAutocomplete", NULL, N_("Keyword autocomplete"), "K", N_("Keyword Autocomplete"), CB(layout_menu_keyword_autocomplete_cb) }, { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _in"), "equal", N_("Zoom in"), CB(layout_menu_zoom_in_cb) }, { "ZoomInAlt1", GTK_STOCK_ZOOM_IN, N_("Zoom _in"), "KP_Add", N_("Zoom in"), CB(layout_menu_zoom_in_cb) }, { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _out"), "minus", N_("Zoom out"), CB(layout_menu_zoom_out_cb) }, @@ -1940,6 +1977,7 @@ static GtkToggleActionEntry menu_toggle_entries[] = { { "Thumbnails", PIXBUF_INLINE_ICON_THUMB,N_("Show _Thumbnails"), "T", N_("Show Thumbnails"), CB(layout_menu_thumb_cb), FALSE }, { "ShowMarks", PIXBUF_INLINE_ICON_MARKS, N_("Show _Marks"), "M", N_("Show Marks"), CB(layout_menu_marks_cb), FALSE }, + { "ShowFileFilter", PIXBUF_INLINE_ICON_FILE_FILTER, N_("Show File Filter"), NULL, N_("Show File Filter"), CB(layout_menu_file_filter_cb), FALSE }, { "ShowInfoPixel", GTK_STOCK_COLOR_PICKER, N_("Pi_xel Info"), NULL, N_("Show Pixel Info"), CB(layout_menu_info_pixel_cb), FALSE }, { "FloatTools", PIXBUF_INLINE_ICON_FLOAT,N_("_Float file list"), "L", N_("Float file list"), CB(layout_menu_float_cb), FALSE }, { "HideToolbar", NULL, N_("Hide tool_bar"), NULL, N_("Hide toolbar"), CB(layout_menu_toolbar_cb), FALSE }, @@ -2053,6 +2091,7 @@ " " " " " " +" " " " " " " " @@ -2092,6 +2131,7 @@ " " " " " " +" " " " " " " " @@ -3004,6 +3044,16 @@ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), layout_image_get_desaturate(lw)); } +void layout_util_sync_file_filter(LayoutWindow *lw) +{ + GtkAction *action; + + if (!lw->action_group) return; + + action = gtk_action_group_get_action(lw->action_group, "ShowFileFilter"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), lw->options.show_file_filter); +} + void layout_util_sync_marks(LayoutWindow *lw) { GtkAction *action; @@ -3078,6 +3128,9 @@ action = gtk_action_group_get_action(lw->action_group, "RectangularSelection"); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), options->collections.rectangular_selection); + action = gtk_action_group_get_action(lw->action_group, "ShowFileFilter"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), lw->options.show_file_filter); + if (osd_flags & OSD_SHOW_HISTOGRAM) { action = gtk_action_group_get_action(lw->action_group, "HistogramChanR"); diff -r 5705ded05a19 -r 7e769ff09997 src/layout_util.h --- a/src/layout_util.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/layout_util.h Tue Feb 05 21:18:18 2019 +0100 @@ -29,6 +29,7 @@ void layout_util_sync_thumb(LayoutWindow *lw); void layout_util_sync_marks(LayoutWindow *lw); +void layout_util_sync_file_filter(LayoutWindow *lw); void layout_util_sync_color(LayoutWindow *lw); void layout_util_sync(LayoutWindow *lw); diff -r 5705ded05a19 -r 7e769ff09997 src/menu.c --- a/src/menu.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/menu.c Tue Feb 05 21:18:18 2019 +0100 @@ -116,7 +116,7 @@ GtkWidget *item; GtkWidget *submenu; - item = menu_item_add(menu, _("_Edit"), NULL, NULL); + item = menu_item_add(menu, _("Plugins"), NULL, NULL); submenu = gtk_menu_new(); g_object_set_data(G_OBJECT(submenu), "submenu_data", data); diff -r 5705ded05a19 -r 7e769ff09997 src/options.c --- a/src/options.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/options.c Tue Feb 05 21:18:18 2019 +0100 @@ -156,6 +156,7 @@ options->show_icon_names = TRUE; options->show_star_rating = FALSE; + options->show_predefined_keyword_tree = TRUE; options->slideshow.delay = 50; options->slideshow.random = FALSE; @@ -193,6 +194,14 @@ options->read_metadata_in_idle = FALSE; options->star_rating.star = STAR_RATING_STAR; options->star_rating.rejected = STAR_RATING_REJECTED; + + options->printer.image_font = g_strdup("Serif 10"); + options->printer.page_font = g_strdup("Serif 10"); + options->printer.page_text = NULL; + options->printer.text_fields = 1; + options->printer.image_text_position = 1; + options->printer.page_text_position = 3; + return options; } @@ -273,6 +282,7 @@ options->order = g_strdup("123"); options->show_directory_date = FALSE; options->show_marks = FALSE; + options->show_file_filter = FALSE; options->show_thumbnails = FALSE; options->style = 0; options->show_info_pixel = FALSE; diff -r 5705ded05a19 -r 7e769ff09997 src/options.h --- a/src/options.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/options.h Tue Feb 05 21:18:18 2019 +0100 @@ -37,6 +37,7 @@ gboolean show_star_rating; gboolean show_guidelines; gboolean draw_rectangle; + gboolean show_predefined_keyword_tree; /* various */ gboolean tree_descend_subdirs; @@ -300,6 +301,18 @@ gunichar rejected; } star_rating; + /* Printer */ + struct { + gchar *image_font; + gchar *page_font; + gint text_fields; + gboolean show_image_text; + gboolean show_page_text; + gchar *page_text; + gint image_text_position; + gint page_text_position; + } printer; + gboolean read_metadata_in_idle; }; diff -r 5705ded05a19 -r 7e769ff09997 src/pixbuf_util.c --- a/src/pixbuf_util.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/pixbuf_util.c Tue Feb 05 21:18:18 2019 +0100 @@ -138,6 +138,7 @@ { PIXBUF_INLINE_ICON_SELECT_NONE, icon_select_none }, { PIXBUF_INLINE_ICON_SELECT_INVERT, icon_select_invert }, { PIXBUF_INLINE_ICON_SELECT_RECTANGLE, icon_select_rectangle }, + { PIXBUF_INLINE_ICON_FILE_FILTER, icon_file_filter }, { NULL, NULL } }; diff -r 5705ded05a19 -r 7e769ff09997 src/pixbuf_util.h --- a/src/pixbuf_util.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/pixbuf_util.h Tue Feb 05 21:18:18 2019 +0100 @@ -72,6 +72,7 @@ #define PIXBUF_INLINE_ICON_SELECT_NONE "icon_select_none" #define PIXBUF_INLINE_ICON_SELECT_INVERT "icon_select_invert" #define PIXBUF_INLINE_ICON_SELECT_RECTANGLE "icon_select_rectangle" +#define PIXBUF_INLINE_ICON_FILE_FILTER "icon_file_filter" GdkPixbuf *pixbuf_copy_rotate_90(GdkPixbuf *src, gboolean counter_clockwise); GdkPixbuf *pixbuf_copy_mirror(GdkPixbuf *src, gboolean mirror, gboolean flip); diff -r 5705ded05a19 -r 7e769ff09997 src/preferences.c --- a/src/preferences.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/preferences.c Tue Feb 05 21:18:18 2019 +0100 @@ -23,8 +23,10 @@ #include "preferences.h" #include "bar_exif.h" +#include "bar_keywords.h" #include "cache.h" #include "cache_maint.h" +#include "dnd.h" #include "editors.h" #include "exif.h" #include "filedata.h" @@ -36,6 +38,7 @@ #include "img-view.h" #include "layout_config.h" #include "layout_util.h" +#include "metadata.h" #include "pixbuf_util.h" #include "slideshow.h" #include "toolbar.h" @@ -43,6 +46,7 @@ #include "utilops.h" #include "ui_fileops.h" #include "ui_misc.h" +#include "ui_spinner.h" #include "ui_tabcomp.h" #include "ui_utildlg.h" #include "window.h" @@ -63,6 +67,9 @@ static void image_overlay_set_text_colours(); +GtkWidget *keyword_text; +static void config_tab_keywords_save(); + typedef struct _ThumbSize ThumbSize; struct _ThumbSize { @@ -411,6 +418,8 @@ options->info_comment.height = c_options->info_comment.height; options->info_rating.height = c_options->info_rating.height; + options->show_predefined_keyword_tree = c_options->show_predefined_keyword_tree; + options->marks_save = c_options->marks_save; options->with_rename = c_options->with_rename; options->collections_on_top = c_options->collections_on_top; @@ -439,6 +448,8 @@ } #endif + config_tab_keywords_save(); + image_options_sync(); if (refresh) @@ -474,10 +485,12 @@ { "GuideOptionsGeneral.html", "GuideOptionsImage.html", + "GuideOptionsOSD.html", "GuideOptionsWindow.html", "GuideOptionsKeyboard.html", "GuideOptionsFiltering.html", "GuideOptionsMetadata.html", + "GuideOptionsKeywords.html", "GuideOptionsColor.html", "GuideOptionsStereo.html", "GuideOptionsBehavior.html", @@ -1220,7 +1233,7 @@ static void image_overlay_help_cb(GtkWidget *widget, gpointer data) { - help_window_show("GuideOptionsWindow.html#OverlayScreenDisplay"); + help_window_show("GuideOptionsOSD.html"); } static void image_overlay_set_font_cb(GtkWidget *widget, gpointer data) @@ -1878,6 +1891,11 @@ 1, 9999, 1, options->info_rating.height, &c_options->info_rating.height); + group = pref_group_new(vbox, FALSE, _("Show predefined keyword tree"), GTK_ORIENTATION_VERTICAL); + + pref_checkbox_new_int(group, _("Show predefined keyword tree (NOTE! Geeqie must be restarted for change to take effect)"), + options->show_predefined_keyword_tree, &c_options->show_predefined_keyword_tree); + group = pref_group_new(vbox, FALSE, _("On-line help search engine"), GTK_ORIENTATION_VERTICAL); help_search_engine_entry = gtk_entry_new(); @@ -1989,9 +2007,6 @@ GtkWidget *button; GtkWidget *ct_button; GtkWidget *spin; - GtkWidget *image_overlay_template_view; - GtkWidget *scrolled; - GtkTextBuffer *buffer; vbox = scrolled_notebook_page(notebook, _("Windows")); @@ -2038,35 +2053,183 @@ options->fullscreen.clean_flip, &c_options->fullscreen.clean_flip); pref_checkbox_new_int(group, _("Disable screen saver"), options->fullscreen.disable_saver, &c_options->fullscreen.disable_saver); - +} + +/* overlay screen display tab */ +static const gchar *predefined_tags[][2] = { + {"%name%", N_("Name")}, + {"%path:60%*", N_("Path")}, + {"%date%", N_("Date")}, + {"%size%", N_("Size")}, + {"%zoom%", N_("Zoom")}, + {"%dimensions%", N_("Dimensions")}, + {"%collection%", N_("Collection")}, + {"%number%", N_("Collection number")}, + {"%total%", N_("Collection total")}, + {"%file.ctime%", N_("File ctime")}, + {"%file.mode%", N_("File mode")}, + {"%file.owner%", N_("File owner")}, + {"%file.group%", N_("File group")}, + {"%file.link%", N_("File link")}, + {"%file.class%", N_("File class")}, + {"%formatted.DateTime%", N_("Image date")}, + {"%formatted.DateTimeDigitized%", N_("Date digitized")}, + {"%formatted.ShutterSpeed%", N_("ShutterSpeed")}, + {"%formatted.Aperture%", N_("Aperture")}, + {"%formatted.ExposureBias%", N_("Exposure bias")}, + {"%formatted.Resolution%", N_("Resolution")}, + {"%formatted.Camera%", N_("Camera")}, + {"%formatted.ShutterSpeed%", N_("Shutter speed")}, + {"%formatted.ISOSpeedRating%", N_("ISO")}, + {"%formatted.FocalLength%", N_("Focal length")}, + {"%formatted.FocalLength35mmFilm%", N_("Focal len. 35mm")}, + {"%formatted.SubjectDistance%", N_("Subject distance")}, + {"%formatted.Flash%", N_("Flash")}, + {"%formatted.ColorProfile%", N_("Color profile")}, + {"%formatted.GPSPosition%", N_("Lat, Long")}, + {"%formatted.GPSAltitude%", N_("Altitude")}, + {"%formatted.localtime%", N_("Local time")}, + {"%formatted.timezone%", N_("Timezone")}, + {"%formatted.countryname%", N_("Country name")}, + {"%formatted.countrycode%", N_("Country code")}, + {"%formatted.star_rating%", N_("Star rating")}, + {NULL, NULL}}; + +static GtkTargetEntry osd_drag_types[] = { + { "text/plain", GTK_TARGET_SAME_APP, TARGET_TEXT_PLAIN } +}; + +typedef struct _TagData TagData; +struct _TagData +{ + gchar *key; + gchar *title; +}; + +static void tag_button_cb(GtkWidget *widget, gpointer data) +{ + GtkTextView *image_overlay_template_view = data; + GtkTextBuffer *buffer; + TagData *td; + + buffer = gtk_text_view_get_buffer(image_overlay_template_view); + td = g_object_get_data(G_OBJECT(widget), "tag_data"); + gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(buffer), td->key, -1); + + gtk_widget_grab_focus(GTK_WIDGET(image_overlay_template_view)); +} + +static void osd_dnd_get_cb(GtkWidget *btn, GdkDragContext *context, + GtkSelectionData *selection_data, guint info, + guint time, gpointer data) +{ + TagData *td; + GtkTextView *image_overlay_template_view = data; + + td = g_object_get_data(G_OBJECT(btn), "tag_data"); + gtk_selection_data_set_text(selection_data, td->key, -1); + + gtk_widget_grab_focus(GTK_WIDGET(image_overlay_template_view)); +} + +static void osd_btn_destroy_cb(GtkWidget *btn, GdkDragContext *context, + GtkSelectionData *selection_data, guint info, + guint time, gpointer data) +{ + TagData *td; + + td = g_object_get_data(G_OBJECT(btn), "tag_data"); + g_free(td->key); + g_free(td->title); +} + +static void set_osd_button(GtkWidget *widget, const gchar *key, const gchar *title, + GtkWidget *image_overlay_template_view) +{ + GtkWidget *new_button; + TagData *td; + + new_button = pref_button_new(widget, NULL, _(title), TRUE, + G_CALLBACK(tag_button_cb), image_overlay_template_view); + + td = g_new0(TagData, 1); + td->key = g_strdup(key); + td->title = g_strdup(title); + + g_object_set_data(G_OBJECT(new_button), "tag_data", td); + + gtk_drag_source_set(new_button, GDK_BUTTON1_MASK, osd_drag_types, 1, GDK_ACTION_COPY); + g_signal_connect(G_OBJECT(new_button), "drag_data_get", + G_CALLBACK(osd_dnd_get_cb), image_overlay_template_view); + g_signal_connect(G_OBJECT(new_button), "destroy", + G_CALLBACK(osd_btn_destroy_cb), new_button); +} + +static void config_tab_osd(GtkWidget *notebook) +{ + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *vbox_buttons; + GtkWidget *group; + GtkWidget *button; + GtkWidget *image_overlay_template_view; + GtkWidget *scrolled; + GtkTextBuffer *buffer; + GtkWidget *label; + GtkWidget * subgroup; + gint i = 0; + gint rows = 0; + gint cols = 0; + + vbox = scrolled_notebook_page(notebook, _("OSD")); + + image_overlay_template_view = gtk_text_view_new(); group = pref_group_new(vbox, FALSE, _("Overlay Screen Display"), GTK_ORIENTATION_VERTICAL); + hbox = gtk_hbox_new(FALSE, 0); + + gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + + pref_label_new(hbox, _("To include predefined tags in the template, click a button or drag-and-drop")); + + subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(subgroup), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + + for (cols = 0; cols < 6; cols++) + { + vbox_buttons = gtk_vbox_new(FALSE, 0); + rows = 0; + + gtk_box_pack_start(GTK_BOX(hbox), vbox_buttons, FALSE, FALSE, 0); + + while (rows < 6 && predefined_tags[i][0]) + { + set_osd_button(vbox_buttons, predefined_tags[i][0], predefined_tags[i][1], image_overlay_template_view); + i = i + 1; + rows++; + } + gtk_widget_show(vbox_buttons); + } + + pref_line(group, PREF_PAD_GAP); + pref_label_new(group, _("Image overlay template")); scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_widget_set_size_request(scrolled, 200, 150); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 5); gtk_widget_show(scrolled); - image_overlay_template_view = gtk_text_view_new(); - gtk_widget_set_tooltip_markup(image_overlay_template_view, - _("%name% results in the filename of the picture.\n" - "Also available: %collection%, %number%, %total%, %date%,\n" - "%size% (filesize), %width%, %height%, %res% (resolution),\n" - "%rating%, %keywords%, %comment% (XMP), %imagecomment% (JPEG)\n" - "To access exif data use the exif name, e. g. %formatted.Camera% is the formatted camera name,\n" - "%Exif.Photo.DateTimeOriginal% the date of the original shot.\n" - "%formatted.Camera:20 notation will truncate the displayed data to 20 characters and will add 3 dots at the end to denote the truncation.\n" - "If two or more variables are connected with the |-sign, it prints available variables with a separator.\n" - "%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%|%formatted.FocalLength% could show \"1/20s - 400 - 80 mm\" or \"1/200 - 80 mm\",\n" - "if there's no ISO information in the Exif data.\n" - "If a line is empty, it is removed. This allows one to add lines that totally disappear when no data is available.\n" - )); + _("Extensive formatting options are shown in the Help file")); + gtk_container_add(GTK_CONTAINER(scrolled), image_overlay_template_view); gtk_widget_show(image_overlay_template_view); @@ -2111,7 +2274,51 @@ g_signal_connect(G_OBJECT(buffer), "changed", G_CALLBACK(image_overlay_template_view_changed_cb), image_overlay_template_view); - + pref_line(group, PREF_PAD_GAP); + + group = pref_group_new(vbox, FALSE, _("Exif, XMP or IPTC tags"), GTK_ORIENTATION_VERTICAL); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + label = gtk_label_new(_("%Exif.Image.Orientation%")); + gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0); + gtk_widget_show(label); + pref_spacer(group,TRUE); + + group = pref_group_new(vbox, FALSE, _("Field separators"), GTK_ORIENTATION_VERTICAL); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + label = gtk_label_new(_("Separator shown only if both fields are non-null:\n%formatted.ShutterSpeed%|%formatted.ISOSpeedRating%")); + gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0); + gtk_widget_show(label); + pref_spacer(group,TRUE); + + group = pref_group_new(vbox, FALSE, _("Field maximum length"), GTK_ORIENTATION_VERTICAL); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + label = gtk_label_new(_("%path:39%")); + gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0); + gtk_widget_show(label); + pref_spacer(group,TRUE); + + group = pref_group_new(vbox, FALSE, _("Pre- and post- text"), GTK_ORIENTATION_VERTICAL); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + label = gtk_label_new(_("Text shown only if the field is non-null:\n%formatted.Aperture:F no. * setting%\n %formatted.Aperture:10:F no. * setting%")); + gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0); + gtk_widget_show(label); + pref_spacer(group,TRUE); + + group = pref_group_new(vbox, FALSE, _("Pango markup"), GTK_ORIENTATION_VERTICAL); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(group), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + label = gtk_label_new(_("bold\nunderline\nitalic\nstrikethrough")); + gtk_box_pack_start(GTK_BOX(hbox),label, FALSE,FALSE,0); + gtk_widget_show(label); } static GtkTreeModel *create_class_model(void) @@ -2391,6 +2598,349 @@ gtk_widget_set_tooltip_text(ct_button,"On folder change, read DateTimeOriginal, DateTimeDigitized and Star Rating in the idle loop.\nIf this is not selected, initial loading of the folder will be faster but sorting on these items will be slower"); } +/* keywords tab */ + +typedef struct _KeywordFindData KeywordFindData; +struct _KeywordFindData +{ + GenericDialog *gd; + + GList *list; + GList *list_dir; + + GtkWidget *button_close; + GtkWidget *button_stop; + GtkWidget *button_start; + GtkWidget *progress; + GtkWidget *spinner; + + GtkWidget *group; + GtkWidget *entry; + + gboolean recurse; + + guint idle_id; /* event source id */ +}; + +#define KEYWORD_DIALOG_WIDTH 400 + +static void keywords_find_folder(KeywordFindData *kfd, FileData *dir_fd) +{ + GList *list_d = NULL; + GList *list_f = NULL; + + if (kfd->recurse) + { + filelist_read(dir_fd, &list_f, &list_d); + } + else + { + filelist_read(dir_fd, &list_f, NULL); + } + + list_f = filelist_filter(list_f, FALSE); + list_d = filelist_filter(list_d, TRUE); + + kfd->list = g_list_concat(list_f, kfd->list); + kfd->list_dir = g_list_concat(list_d, kfd->list_dir); +} + +static void keywords_find_reset(KeywordFindData *kfd) +{ + filelist_free(kfd->list); + kfd->list = NULL; + + filelist_free(kfd->list_dir); + kfd->list_dir = NULL; +} + +static void keywords_find_close_cb(GenericDialog *fd, gpointer data) +{ + KeywordFindData *kfd = data; + + if (!gtk_widget_get_sensitive(kfd->button_close)) return; + + keywords_find_reset(kfd); + generic_dialog_close(kfd->gd); + g_free(kfd); +} + +static void keywords_find_finish(KeywordFindData *kfd) +{ + keywords_find_reset(kfd); + + gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("done")); + spinner_set_interval(kfd->spinner, -1); + + gtk_widget_set_sensitive(kfd->group, TRUE); + gtk_widget_set_sensitive(kfd->button_start, TRUE); + gtk_widget_set_sensitive(kfd->button_stop, FALSE); + gtk_widget_set_sensitive(kfd->button_close, TRUE); +} + +static void keywords_find_stop_cb(GenericDialog *fd, gpointer data) +{ + KeywordFindData *kfd = data; + + g_idle_remove_by_data(kfd); + + keywords_find_finish(kfd); +} + +static gboolean keywords_find_file(gpointer data) +{ + KeywordFindData *kfd = data; + GtkTextIter iter; + GtkTextBuffer *buffer; + gchar *tmp; + GList *keywords; + + if (kfd->list) + { + FileData *fd; + + fd = kfd->list->data; + kfd->list = g_list_remove(kfd->list, fd); + + keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN); + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text)); + + while (keywords) + { + gtk_text_buffer_get_end_iter(buffer, &iter); + tmp = g_strconcat(keywords->data, "\n", NULL); + gtk_text_buffer_insert(buffer, &iter, tmp, -1); + g_free(tmp); + keywords = keywords->next; + } + + gtk_entry_set_text(GTK_ENTRY(kfd->progress), fd->path); + file_data_unref(fd); + string_list_free(keywords); + + return (TRUE); + } + else if (kfd->list_dir) + { + FileData *fd; + + fd = kfd->list_dir->data; + kfd->list_dir = g_list_remove(kfd->list_dir, fd); + + keywords_find_folder(kfd, fd); + + file_data_unref(fd); + + return TRUE; + } + + keywords_find_finish(kfd); + + return FALSE; +} + +static void keywords_find_start_cb(GenericDialog *fd, gpointer data) +{ + KeywordFindData *kfd = data; + gchar *path; + + if (kfd->list || !gtk_widget_get_sensitive(kfd->button_start)) return; + + path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(kfd->entry)))); + parse_out_relatives(path); + + if (!isdir(path)) + { + warning_dialog(_("Invalid folder"), + _("The specified folder can not be found."), + GTK_STOCK_DIALOG_WARNING, kfd->gd->dialog); + } + else + { + FileData *dir_fd; + + gtk_widget_set_sensitive(kfd->group, FALSE); + gtk_widget_set_sensitive(kfd->button_start, FALSE); + gtk_widget_set_sensitive(kfd->button_stop, TRUE); + gtk_widget_set_sensitive(kfd->button_close, FALSE); + spinner_set_interval(kfd->spinner, SPINNER_SPEED); + + dir_fd = file_data_new_dir(path); + keywords_find_folder(kfd, dir_fd); + file_data_unref(dir_fd); + kfd->idle_id = g_idle_add(keywords_find_file, kfd); + } + + g_free(path); +} + +static void keywords_find_dialog(GtkWidget *widget, const gchar *path) +{ + KeywordFindData *kfd; + GtkWidget *hbox; + GtkWidget *label; + + kfd = g_new0(KeywordFindData, 1); + + kfd->gd = generic_dialog_new(_("Search for keywords"), + "search_for_keywords", + widget, FALSE, + NULL, kfd); + gtk_window_set_default_size(GTK_WINDOW(kfd->gd->dialog), KEYWORD_DIALOG_WIDTH, -1); + kfd->gd->cancel_cb = keywords_find_close_cb; + kfd->button_close = generic_dialog_add_button(kfd->gd, GTK_STOCK_CLOSE, NULL, + keywords_find_close_cb, FALSE); + kfd->button_start = generic_dialog_add_button(kfd->gd, GTK_STOCK_OK, _("S_tart"), + keywords_find_start_cb, FALSE); + kfd->button_stop = generic_dialog_add_button(kfd->gd, GTK_STOCK_STOP, NULL, + keywords_find_stop_cb, FALSE); + gtk_widget_set_sensitive(kfd->button_stop, FALSE); + + generic_dialog_add_message(kfd->gd, NULL, _("Search for keywords"), NULL, FALSE); + + hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0); + pref_spacer(hbox, PREF_PAD_INDENT); + kfd->group = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP); + + hbox = pref_box_new(kfd->group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE); + pref_label_new(hbox, _("Folder:")); + + label = tab_completion_new(&kfd->entry, path, NULL, NULL, NULL, NULL); + tab_completion_add_select_button(kfd->entry,_("Select folder") , TRUE); + gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); + gtk_widget_show(label); + + pref_checkbox_new_int(kfd->group, _("Include subfolders"), FALSE, &kfd->recurse); + + pref_line(kfd->gd->vbox, PREF_PAD_SPACE); + hbox = pref_box_new(kfd->gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE); + + kfd->progress = gtk_entry_new(); + gtk_widget_set_can_focus(kfd->progress, FALSE); + gtk_editable_set_editable(GTK_EDITABLE(kfd->progress), FALSE); + gtk_entry_set_text(GTK_ENTRY(kfd->progress), _("click start to begin")); + gtk_box_pack_start(GTK_BOX(hbox), kfd->progress, TRUE, TRUE, 0); + gtk_widget_show(kfd->progress); + + kfd->spinner = spinner_new(NULL, -1); + gtk_box_pack_start(GTK_BOX(hbox), kfd->spinner, FALSE, FALSE, 0); + gtk_widget_show(kfd->spinner); + + kfd->list = NULL; + + gtk_widget_show(kfd->gd->dialog); +} + +static void keywords_find_cb(GtkWidget *widget, gpointer data) +{ + const gchar *path = layout_get_path(NULL); + + if (!path || !*path) path = homedir(); + keywords_find_dialog(widget, path); +} + +static void config_tab_keywords_save() +{ + GtkTextIter start, end; + GtkTextBuffer *buffer; + GList *kw_list = NULL; + GList *work; + gchar *buffer_text; + gchar *kw_split; + gboolean found; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + + buffer_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + + kw_split = strtok(buffer_text, "\n"); + while (kw_split != NULL) + { + work = kw_list; + found = FALSE; + while (work) + { + if (g_strcmp0(work->data, kw_split) == 0) + { + found = TRUE; + break; + } + work = work->next; + } + if (!found) + { + kw_list = g_list_append(kw_list, g_strdup(kw_split)); + } + kw_split = strtok(NULL, "\n"); + } + + keyword_list_set(kw_list); + + string_list_free(kw_list); + g_free(buffer_text); +} + +static void config_tab_keywords(GtkWidget *notebook) +{ + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *group; + GtkWidget *button; + GtkWidget *scrolled; + GtkTextIter iter; + GtkTextBuffer *buffer; + gchar *tmp; + + vbox = scrolled_notebook_page(notebook, _("Keywords")); + + group = pref_group_new(vbox, TRUE, _("Edit keywords autocompletion list"), GTK_ORIENTATION_VERTICAL); + + hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP); + + button = pref_button_new(hbox, GTK_STOCK_EXECUTE, _("Search"), FALSE, + G_CALLBACK(keywords_find_cb), keyword_text); + gtk_widget_set_tooltip_text(button, "Search for existing keywords"); + + + keyword_text = gtk_text_view_new(); + gtk_widget_set_size_request(keyword_text, 20, 20); + scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_box_pack_start(GTK_BOX(group), scrolled, TRUE, TRUE, 0); + gtk_widget_show(scrolled); + + gtk_container_add(GTK_CONTAINER(scrolled), keyword_text); + gtk_widget_show(keyword_text); + + gtk_text_view_set_editable(GTK_TEXT_VIEW(keyword_text), TRUE); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(keyword_text)); + gtk_text_buffer_create_tag(buffer, "monospace", + "family", "monospace", NULL); + + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(keyword_text), GTK_WRAP_WORD); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE); + gchar *path; + + path = g_build_filename(get_rc_dir(), "keywords", NULL); + + GList *kwl = keyword_list_get(); + kwl = g_list_first(kwl); + while (kwl) + { + gtk_text_buffer_get_end_iter (buffer, &iter); + tmp = g_strconcat(kwl->data, "\n", NULL); + gtk_text_buffer_insert(buffer, &iter, tmp, -1); + kwl = kwl->next; + g_free(tmp); + } + + gtk_text_buffer_set_modified(buffer, FALSE); + + g_free(path); +} + /* metadata tab */ #ifdef HAVE_LCMS static void intent_menu_cb(GtkWidget *combo, gpointer data) @@ -2870,14 +3420,17 @@ notebook = gtk_notebook_new(); gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE); gtk_box_pack_start(GTK_BOX(win_vbox), notebook, TRUE, TRUE, 0); config_tab_general(notebook); config_tab_image(notebook); + config_tab_osd(notebook); config_tab_windows(notebook); config_tab_accelerators(notebook); config_tab_files(notebook); config_tab_metadata(notebook); + config_tab_keywords(notebook); config_tab_color(notebook); config_tab_stereo(notebook); config_tab_behavior(notebook); diff -r 5705ded05a19 -r 7e769ff09997 src/print.c --- a/src/print.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/print.c Tue Feb 05 21:18:18 2019 +0100 @@ -1,8 +1,7 @@ /* - * Copyright (C) 2004 John Ellis - * Copyright (C) 2008 - 2016 The Geeqie Team + * Copyright (C) 2018 The Geeqie Team * - * Author: John Ellis + * Author: Colin Clark * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,170 +21,21 @@ #include "main.h" #include "print.h" +#include "exif.h" #include "filedata.h" -#include "filefilter.h" -#include "image.h" #include "image-load.h" #include "pixbuf_util.h" -#include "thumb.h" -#include "utilops.h" -#include "ui_bookmark.h" -#include "ui_menu.h" #include "ui_misc.h" -#include "ui_utildlg.h" #include "ui_fileops.h" -#include "ui_spinner.h" -#include "ui_tabcomp.h" - -#include -#include -#include - -#define PRINT_LPR_COMMAND "lpr" -#define PRINT_LPR_CUSTOM "lpr -P %s" -#define PRINT_LPR_QUERY "lpstat -p" - -#define PRINT_DLG_WIDTH 600 -#define PRINT_DLG_HEIGHT 400 - -#define PRINT_DLG_PREVIEW_WIDTH 270 -#define PRINT_DLG_PREVIEW_HEIGHT -1 -/* these are in point units */ -#define PRINT_MIN_WIDTH 100 -#define PRINT_MIN_HEIGHT 100 -#define PRINT_MAX_WIDTH 4000 -#define PRINT_MAX_HEIGHT 4000 - -#define PRINT_MARGIN_DEFAULT 36 - -#define PRINT_PROOF_MIN_SIZE 8 -#define PRINT_PROOF_MAX_SIZE 720 -#define PRINT_PROOF_DEFAULT_SIZE 144 -#define PRINT_PROOF_MARGIN 5 - -/* default page size */ -#define PAGE_LAYOUT_WIDTH 850 -#define PAGE_LAYOUT_HEIGHT 1100 - -/* preview uses 1 pixel = PRINT_PREVIEW_SCALE points */ -#define PRINT_PREVIEW_SCALE 4 - -/* default dpi to use for printing ps image data */ -#define PRINT_PS_DPI_DEFAULT 300.0 -#define PRINT_PS_DPI_MIN 150.0 -/* method to use when scaling down image data */ -#define PRINT_PS_MAX_INTERP GDK_INTERP_BILINEAR -/* color to use as mask when printing transparent images */ -#define PRINT_PS_MASK_R 255 -#define PRINT_PS_MASK_G 255 -#define PRINT_PS_MASK_B 255 +#define PRINT_SETTINGS "print_settings" // filename save printer settings +#define PAGE_SETUP "page_setup" // filename save page setup /* padding between objects */ #define PRINT_TEXT_PADDING 3.0 -/* locale for postscript portability */ -#define POSTSCRIPT_LOCALE "C" - - -/* group and keys for saving prefs */ -#define PRINT_PREF_GROUP "print_settings" - -#define PRINT_PREF_SAVE "save_settings" - -#define PRINT_PREF_OUTPUT "output" -#define PRINT_PREF_FORMAT "format" -#define PRINT_PREF_DPI "dpi" -#define PRINT_PREF_UNITS "units" -#define PRINT_PREF_SIZE "size" -#define PRINT_PREF_ORIENTATION "orientation" - -#define PRINT_PREF_CUSTOM_WIDTH "custom_width" -#define PRINT_PREF_CUSTOM_HEIGHT "custom_height" -#define PRINT_PREF_MARGIN_LEFT "margin_left" -#define PRINT_PREF_MARGIN_RIGHT "margin_right" -#define PRINT_PREF_MARGIN_TOP "margin_top" -#define PRINT_PREF_MARGIN_BOTTOM "margin_bottom" -#define PRINT_PREF_PROOF_WIDTH "proof_width" -#define PRINT_PREF_PROOF_HEIGHT "proof_height" - -#define PRINT_PREF_PRINTERC "custom_printer" - -#define PRINT_PREF_TEXT "text" -#define PRINT_PREF_TEXTSIZE "textsize" -#define PRINT_PREF_TEXTCOLOR_R "textcolor_r" -#define PRINT_PREF_TEXTCOLOR_G "textcolor_g" -#define PRINT_PREF_TEXTCOLOR_B "textcolor_b" - -#define PRINT_PREF_SOURCE "source" -#define PRINT_PREF_LAYOUT "layout" - -#define PRINT_PREF_IMAGE_SCALE "image_scale" - -typedef enum { - PRINT_SOURCE_IMAGE = 0, - PRINT_SOURCE_SELECTION, - PRINT_SOURCE_ALL, - PRINT_SOURCE_COUNT -} PrintSource; - -const gchar *print_source_text[] = { - N_("Image"), - N_("Selection"), - N_("All"), - NULL -}; - -typedef enum { - PRINT_LAYOUT_IMAGE = 0, - PRINT_LAYOUT_PROOF, - PRINT_LAYOUT_COUNT -} PrintLayout; - -const gchar *print_layout_text[] = { - N_("One image per page"), - N_("Proof sheet"), - NULL -}; - -typedef enum { - PRINT_OUTPUT_PS_LPR = 0, - PRINT_OUTPUT_PS_CUSTOM, - PRINT_OUTPUT_PS_FILE, - PRINT_OUTPUT_RGB_FILE, - PRINT_OUTPUT_COUNT -} PrintOutput; - -const gchar *print_output_text[] = { - N_("Default printer"), - N_("Custom printer"), - N_("PostScript file"), - N_("Image file"), - NULL, - NULL -}; - -typedef enum { - PRINT_FILE_JPG_LOW = 0, - PRINT_FILE_JPG_NORMAL, - PRINT_FILE_JPG_HIGH, - PRINT_FILE_PNG, - PRINT_FILE_COUNT -} PrintFileFormat; - -const gchar *print_file_format_text[] = { - N_("jpeg, low quality"), - N_("jpeg, normal quality"), - N_("jpeg, high quality"), - "png", - NULL -}; - -typedef enum { - RENDER_FORMAT_PREVIEW, - RENDER_FORMAT_RGB, - RENDER_FORMAT_PS -} RenderFormat; +/* method to use when scaling down image data */ +#define PRINT_MAX_INTERP GDK_INTERP_HYPER typedef enum { TEXT_INFO_FILENAME = 1 << 0, @@ -195,1960 +45,62 @@ TEXT_INFO_FILEPATH = 1 << 4 } TextInfo; +/* reverse order is important */ typedef enum { - PAPER_UNIT_POINTS = 0, - PAPER_UNIT_MM, - PAPER_UNIT_CM, - PAPER_UNIT_INCH, - PAPER_UNIT_PICAS, - PAPER_UNIT_COUNT -} PaperUnits; - -typedef enum { - PAPER_ORIENTATION_PORTRAIT = 0, - PAPER_ORIENTATION_LANDSCAPE, - PAPER_ORIENTATION_COUNT -} PaperOrientation; - + FOOTER_2, + FOOTER_1, + HEADER_2, + HEADER_1 +} TextPosition; typedef struct _PrintWindow PrintWindow; struct _PrintWindow { - GenericDialog *dialog; - - FileData *source_fd; + GtkWidget *vbox; GList *source_selection; - GList *source_list; - - PrintSource source; - PrintLayout layout; - PrintOutput output; - - gchar *output_path; - gchar *output_custom; - - PrintFileFormat output_format; - - gdouble max_dpi; - - GtkWidget *notebook; - - GtkWidget *path_entry; - GtkWidget *custom_entry; - GtkWidget *path_format_menu; - GtkWidget *max_dpi_menu; - - ImageWindow *layout_image; - gdouble layout_width; - gdouble layout_height; - - guint layout_idle_id; /* event source id */ - - gdouble image_scale; - - GtkWidget *image_scale_spin; - - gdouble proof_width; - gdouble proof_height; - gint proof_columns; - gint proof_rows; - GList *proof_point; - gint proof_position; - gint proof_page; - - GtkWidget *proof_group; - GtkWidget *proof_width_spin; - GtkWidget *proof_height_spin; - - GtkWidget *paper_menu; - GtkWidget *paper_width_spin; - GtkWidget *paper_height_spin; - GtkWidget *paper_units_menu; - GtkWidget *paper_orientation_menu; - - GtkWidget *margin_left_spin; - GtkWidget *margin_right_spin; - GtkWidget *margin_top_spin; - GtkWidget *margin_bottom_spin; - - PaperUnits paper_units; - gint paper_size; - gdouble paper_width; - gdouble paper_height; - PaperOrientation paper_orientation; - - gdouble margin_left; - gdouble margin_right; - gdouble margin_top; - gdouble margin_bottom; - - GtkWidget *button_back; - GtkWidget *button_next; - GtkWidget *page_label; - GtkWidget *print_button; - - gdouble single_scale; - gdouble single_x; - gdouble single_y; - - GtkWidget *single_scale_spin; TextInfo text_fields; - gint text_points; - guint8 text_r; - guint8 text_g; - guint8 text_b; - - gint save_settings; - - /* job printing */ - - GenericDialog *job_dialog; - GtkWidget *job_progress; - GtkWidget *job_progress_label; - - RenderFormat job_format; - PrintOutput job_output; - - FILE *job_file; - FILE *job_pipe; - gchar *job_path; - - GdkPixbuf *job_pixbuf; - gint job_page; + GtkTextBuffer *page_text; ImageLoader *job_loader; -}; - - -static void print_job_throw_error(PrintWindow *pw, const gchar *message); -static gint print_job_start(PrintWindow *pw, RenderFormat format, PrintOutput output); -static void print_job_close(PrintWindow *pw, gint error); -static void print_window_close(PrintWindow *pw); - - -/* misc utils */ - -static gboolean clip_region(gdouble x1, gdouble y1, gdouble w1, gdouble h1, - gdouble x2, gdouble y2, gdouble w2, gdouble h2, - gdouble *rx, gdouble *ry, gdouble *rw, gdouble *rh) -{ - if (x2 + w2 <= x1 || x2 >= x1 + w1 || - y2 + h2 <= y1 || y2 >= y1 + h1) - { - return FALSE; - } - - *rx = MAX(x1, x2); - *rw = MIN((x1 + w1), (x2 + w2)) - *rx; - - *ry = MAX(y1, y2); - *rh = MIN((y1 + h1), (y2 + h2)) - *ry; - - return TRUE; -} - -static const gchar *print_output_name(PrintOutput output) -{ - if (output >= PRINT_OUTPUT_COUNT) return ""; - - return _(print_output_text[output]); -} - - -/* - *----------------------------------------------------------------------------- - * data - *----------------------------------------------------------------------------- - */ - - -typedef struct _PaperSize PaperSize; -struct _PaperSize { - gchar *description; - gint width; - gint height; - PaperOrientation orientation; -}; - -const gchar *print_paper_units[] = { - N_("points"), - N_("millimeters"), - N_("centimeters"), - N_("inches"), - N_("picas"), - NULL -}; - -const gchar *print_paper_orientation[] = { - N_("Portrait"), - N_("Landscape"), - NULL -}; - -PaperSize print_paper_sizes[] = { - { N_("Custom"), 360, 720, PAPER_ORIENTATION_PORTRAIT }, - { N_("Letter"), 612, 792, PAPER_ORIENTATION_PORTRAIT }, /* in 8.5 x 11 */ - { N_("Legal"), 612, 1008, PAPER_ORIENTATION_PORTRAIT }, /* in 8.5 x 14 */ - { N_("Executive"), 522, 756, PAPER_ORIENTATION_PORTRAIT }, /* in 7.25x 10.5 */ - { "A0", 2384, 3370, PAPER_ORIENTATION_PORTRAIT }, /* mm 841 x 1189 */ - { "A1", 1684, 2384, PAPER_ORIENTATION_PORTRAIT }, /* mm 594 x 841 */ - { "A2", 1191, 1684, PAPER_ORIENTATION_PORTRAIT }, /* mm 420 x 594 */ - { "A3", 842, 1191, PAPER_ORIENTATION_PORTRAIT }, /* mm 297 x 420 */ - { "A4", 595, 842, PAPER_ORIENTATION_PORTRAIT }, /* mm 210 x 297 */ - { "A5", 420, 595, PAPER_ORIENTATION_PORTRAIT }, /* mm 148 x 210 */ - { "A6", 298, 420, PAPER_ORIENTATION_PORTRAIT }, /* mm 105 x 148 */ - { "B3", 1001, 1417, PAPER_ORIENTATION_PORTRAIT }, /* mm 353 x 500 */ - { "B4", 709, 1001, PAPER_ORIENTATION_PORTRAIT }, /* mm 250 x 353 */ - { "B5", 499, 709, PAPER_ORIENTATION_PORTRAIT }, /* mm 176 x 250 */ - { "B6", 354, 499, PAPER_ORIENTATION_PORTRAIT }, /* mm 125 x 176 */ - { N_("Envelope #10"), 297, 684, PAPER_ORIENTATION_LANDSCAPE }, /* in 4.125 x 9.5 */ - { N_("Envelope #9"), 279, 639, PAPER_ORIENTATION_LANDSCAPE }, /* in 3.875 x 8.875 */ - { N_("Envelope C4"), 649, 918, PAPER_ORIENTATION_LANDSCAPE }, /* mm 229 x 324 */ - { N_("Envelope C5"), 459, 649, PAPER_ORIENTATION_LANDSCAPE }, /* mm 162 x 229 */ - { N_("Envelope C6"), 323, 459, PAPER_ORIENTATION_LANDSCAPE }, /* mm 114 x 162 */ - { N_("Photo 6x4"), 432, 288, PAPER_ORIENTATION_PORTRAIT }, /* in 6 x 4 */ - { N_("Photo 8x10"), 576, 720, PAPER_ORIENTATION_PORTRAIT }, /* in 8 x 10 */ - { N_("Postcard"), 284, 419, PAPER_ORIENTATION_LANDSCAPE }, /* mm 100 x 148 */ - { N_("Tabloid"), 792, 1224, PAPER_ORIENTATION_PORTRAIT }, /* in 11 x 17 */ - { NULL, 0, 0, 0 } -}; - - -static PaperSize *print_paper_size_nth(gint n) -{ - PaperSize *ps = NULL; - gint i = 0; - - while (i <= n && print_paper_sizes[i].description) - { - ps = &print_paper_sizes[i]; - i++; - } - - return ps; -} - -static gint print_paper_size_lookup(gint n, gdouble *width, gdouble *height) -{ - PaperSize *ps; - gdouble w, h; - - ps = print_paper_size_nth(n); - if (!ps) return FALSE; - - if (ps->orientation == PAPER_ORIENTATION_PORTRAIT) - { - w = ps->width; - h = ps->height; - } - else - { - h = ps->width; - w = ps->height; - } - - if (width) *width = w; - if (height) *height = h; - - return TRUE; -} - -static gdouble print_paper_size_convert_units(gdouble value, PaperUnits src, PaperUnits dst) -{ - gdouble ret; - - if (src == dst) return value; - switch (src) - { - case PAPER_UNIT_MM: - ret = value / 25.4 * 72.0; - break; - case PAPER_UNIT_CM: - ret = value / 2.54 * 72.0; - break; - case PAPER_UNIT_INCH: - ret = value * 72.0; - break; - case PAPER_UNIT_PICAS: - ret = value * 12.0; - break; - case PAPER_UNIT_POINTS: - default: - ret = value; - break; - } - - switch (dst) - { - case PAPER_UNIT_MM: - ret = ret / 72.0 * 25.4; - break; - case PAPER_UNIT_CM: - ret = ret / 72.0 * 2.54; - break; - case PAPER_UNIT_INCH: - ret = ret / 72.0; - break; - case PAPER_UNIT_PICAS: - ret = ret / 12.0; - break; - case PAPER_UNIT_POINTS: - default: - break; - } - - return ret; -} - -static PaperUnits paper_unit_default(void) -{ - const gchar *result; -#if 0 - /* this is not used because it is not even slightly portable */ - #include - - result = nl_langinfo(_NL_MEASUREMENT_MEASUREMENT); - if (result[0] == '2') return PAPER_UNIT_INCH; -#endif - -#ifdef LC_MEASUREMENT - result = setlocale(LC_MEASUREMENT, NULL); -#else - result = setlocale(LC_ALL, NULL); -#endif - if (result && - (strstr(result, "_US") || strstr(result, "_PR")) ) - { - return PAPER_UNIT_INCH; - } - - return PAPER_UNIT_CM; -} - -/* - *----------------------------------------------------------------------------- - * the layout window - *----------------------------------------------------------------------------- - */ - -static gint print_layout_page_count(PrintWindow *pw); - - -static gint print_preview_unit(gdouble points) -{ - return (gint)(points / PRINT_PREVIEW_SCALE); -} - -static void print_proof_size(PrintWindow *pw, gdouble *width, gdouble *height) -{ - if (width) *width = pw->proof_width + PRINT_PROOF_MARGIN * 2; - if (height) - { - gdouble h; - - h = pw->proof_height + PRINT_PROOF_MARGIN * 2; - if (pw->text_fields != 0) h += PRINT_TEXT_PADDING; - if (pw->text_fields & TEXT_INFO_FILENAME) h+= (gdouble)pw->text_points * 1.25; - if (pw->text_fields & TEXT_INFO_DIMENSIONS) h+= (gdouble)pw->text_points * 1.25; - if (pw->text_fields & TEXT_INFO_FILEDATE) h+= (gdouble)pw->text_points * 1.25; - if (pw->text_fields & TEXT_INFO_FILESIZE) h+= (gdouble)pw->text_points * 1.25; - *height = h; - } -} - -static void print_window_layout_status(PrintWindow *pw) -{ - gint total; - gchar *buf; - - total = print_layout_page_count(pw); - pw->proof_page = CLAMP(pw->proof_page, 0, total - 1); - - buf = g_strdup_printf(_("page %d of %d"), pw->proof_page + 1, (total > 0) ? total : 1); - gtk_label_set_text(GTK_LABEL(pw->page_label), buf); - g_free(buf); - - gtk_widget_set_sensitive(pw->page_label, (total > 0)); - - gtk_widget_set_sensitive(pw->button_back, (pw->proof_page > 0)); - gtk_widget_set_sensitive(pw->button_next, (pw->proof_page < total - 1)); - - gtk_widget_set_sensitive(pw->print_button, total > 0); -} - -static void print_window_layout_render_stop(PrintWindow *pw) -{ - if (pw->layout_idle_id) - { - g_source_remove(pw->layout_idle_id); - pw->layout_idle_id = 0; - } -} - -static gboolean print_window_layout_render_idle(gpointer data) -{ - PrintWindow *pw = data; - - print_job_close(pw, FALSE); - print_job_start(pw, RENDER_FORMAT_PREVIEW, 0); - - pw->layout_idle_id = 0; - return FALSE; -} - -static void print_window_layout_render(PrintWindow *pw) -{ - gdouble proof_w, proof_h; - - print_proof_size(pw, &proof_w, &proof_h); - pw->proof_columns = (pw->layout_width - pw->margin_left - pw->margin_right) / proof_w; - pw->proof_rows = (pw->layout_height - pw->margin_top - pw->margin_bottom) / proof_h; - - print_window_layout_status(pw); - - if (!pw->layout_idle_id) - { - pw->layout_idle_id = g_idle_add(print_window_layout_render_idle, pw); - } -} - -static void print_window_layout_size(PrintWindow *pw) -{ - GdkPixbuf *pixbuf; - gdouble width; - gdouble height; - gint sw, sh; - - if (!pw->layout_image) return; - - if (pw->paper_orientation == PAPER_ORIENTATION_LANDSCAPE) - { - width = pw->paper_height; - height = pw->paper_width; - } - else - { - width = pw->paper_width; - height = pw->paper_height; - } - - pw->layout_width = width; - pw->layout_height = height; - - sw = print_preview_unit(width); - sh = print_preview_unit(height); - pixbuf = image_get_pixbuf(pw->layout_image); - if (!pixbuf || - gdk_pixbuf_get_width(pixbuf) != sw || - gdk_pixbuf_get_height(pixbuf) != sh) - { - pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, sw, sh); - image_change_pixbuf(pw->layout_image, pixbuf, 0.0, FALSE); - g_object_unref(pixbuf); - } - - print_window_layout_render(pw); - print_window_layout_status(pw); -} + GList *print_pixbuf_queue; + gboolean job_render_finished; + GSList *image_group; + GSList *page_group; +}; static gint print_layout_page_count(PrintWindow *pw) { gint images; - gint images_per_page; - gint pages; - if (pw->layout_width - pw->margin_left - pw->margin_right <= 0.0 || - pw->layout_height - pw->margin_top - pw->margin_bottom <= 0.0) - { - return 0; - } - - switch (pw->source) - { - case PRINT_SOURCE_ALL: - images = g_list_length(pw->source_list); - break; - case PRINT_SOURCE_SELECTION: - images = g_list_length(pw->source_selection); - break; - case PRINT_SOURCE_IMAGE: - default: - images = (pw->source_fd) ? 1 : 0; - break; - } - - switch (pw->layout) - { - case PRINT_LAYOUT_PROOF: - images_per_page = pw->proof_columns * pw->proof_rows; - break; - case PRINT_LAYOUT_IMAGE: - default: - images_per_page = 1; - break; - } - - if (images < 1 || images_per_page < 1) return 0; - - pages = images / images_per_page; - if (pages * images_per_page < images) pages++; - - return pages; -} - -static void print_layout_page_step(PrintWindow *pw, gint step) -{ - gint max; - gint page; - - max = print_layout_page_count(pw); - page = pw->proof_page + step; - - if (page >= max) page = max - 1; - if (page < 0) page = 0; - - if (page == pw->proof_page) return; - - pw->proof_page = page; - print_window_layout_size(pw); -} - -static void print_layout_page_back_cb(GtkWidget *widget, gpointer data) -{ - PrintWindow *pw = data; - - print_layout_page_step(pw, -1); -} - -static void print_layout_page_next_cb(GtkWidget *widget, gpointer data) -{ - PrintWindow *pw = data; - - print_layout_page_step(pw, 1); -} - -static void print_layout_zoom_in_cb(GtkWidget *widget, gpointer data) -{ - PrintWindow *pw = data; - image_zoom_adjust(pw->layout_image, 0.25); -} - -static void print_layout_zoom_out_cb(GtkWidget *widget, gpointer data) -{ - PrintWindow *pw = data; - image_zoom_adjust(pw->layout_image, -0.25); -} - -static void print_layout_zoom_original_cb(GtkWidget *widget, gpointer data) -{ - PrintWindow *pw = data; - gdouble zoom; - - zoom = image_zoom_get(pw->layout_image); - image_zoom_set(pw->layout_image, (zoom == 1.0) ? 0.0 : 1.0); -} - -static GtkWidget *print_window_layout_setup(PrintWindow *pw, GtkWidget *box) -{ - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *group; - GtkWidget *button; - - vbox = pref_box_new(box, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP); - group = pref_frame_new(vbox, TRUE, _("Preview"), GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP); - - pw->layout_idle_id = 0; - - pw->layout_image = image_new(FALSE); - gtk_widget_set_size_request(pw->layout_image->widget, PRINT_DLG_PREVIEW_WIDTH, PRINT_DLG_PREVIEW_HEIGHT); - - gtk_box_pack_start(GTK_BOX(group), pw->layout_image->widget, TRUE, TRUE, 0); - gtk_widget_show(pw->layout_image->widget); - - hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP); - pw->button_back = pref_button_new(hbox, GTK_STOCK_GO_BACK, NULL, TRUE, - G_CALLBACK(print_layout_page_back_cb), pw); - pw->button_next = pref_button_new(hbox, GTK_STOCK_GO_FORWARD, NULL, TRUE, - G_CALLBACK(print_layout_page_next_cb), pw); - pw->page_label = pref_label_new(hbox, ""); - - button = pref_button_new(NULL, GTK_STOCK_ZOOM_OUT, NULL, TRUE, - G_CALLBACK(print_layout_zoom_out_cb), pw); - gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - button = pref_button_new(NULL, GTK_STOCK_ZOOM_IN, NULL, TRUE, - G_CALLBACK(print_layout_zoom_in_cb), pw); - gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - button = pref_button_new(NULL, GTK_STOCK_ZOOM_100, NULL, TRUE, - G_CALLBACK(print_layout_zoom_original_cb), pw); - gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); - gtk_widget_show(button); - - print_window_layout_size(pw); - - return vbox; -} - -static void print_window_spin_set(GtkSpinButton *spin, gpointer block_data, - gdouble value, gdouble min, gdouble max, - gdouble step, gdouble page, gint digits) -{ - if (block_data) g_signal_handlers_block_matched(G_OBJECT(spin), G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, block_data); - gtk_spin_button_set_digits(spin, digits); - gtk_spin_button_set_increments(spin, step, page); - gtk_spin_button_set_range(spin, min, max); - gtk_spin_button_set_value(spin, value); - - if (block_data) g_signal_handlers_unblock_matched(G_OBJECT(spin), G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, block_data); -} - -static void print_window_layout_sync_layout(PrintWindow *pw) -{ - gtk_widget_set_sensitive(pw->image_scale_spin, (pw->layout == PRINT_LAYOUT_IMAGE)); - gtk_widget_set_sensitive(pw->proof_group, (pw->layout == PRINT_LAYOUT_PROOF)); -} - -static void print_window_layout_sync_paper(PrintWindow *pw) -{ - gdouble width, height; - gint digits; - gdouble step; - gdouble page; - - gtk_widget_set_sensitive(pw->paper_width_spin, (pw->paper_size == 0)); - gtk_widget_set_sensitive(pw->paper_height_spin, (pw->paper_size == 0)); - - width = print_paper_size_convert_units((gdouble)pw->paper_width, PAPER_UNIT_POINTS, pw->paper_units); - height = print_paper_size_convert_units((gdouble)pw->paper_height, PAPER_UNIT_POINTS, pw->paper_units); - - switch (pw->paper_units) - { - case PAPER_UNIT_MM: - digits = 1; - step = 1.0; - page = 10.0; - break; - case PAPER_UNIT_CM: - digits = 2; - step = 0.5; - page = 1.0; - break; - case PAPER_UNIT_INCH: - digits = 3; - step = 0.25; - page = 1.0; - break; - case PAPER_UNIT_PICAS: - digits = 2; - step = 1.0; - page = 6.0; - break; - case PAPER_UNIT_POINTS: - default: - digits = 1; - step = 1.0; - page = 10.0; - break; - } - - print_window_spin_set(GTK_SPIN_BUTTON(pw->paper_width_spin), pw, width, - print_paper_size_convert_units(PRINT_MIN_WIDTH, PAPER_UNIT_POINTS, pw->paper_units), - print_paper_size_convert_units(PRINT_MAX_WIDTH, PAPER_UNIT_POINTS, pw->paper_units), - step, page, digits); - - print_window_spin_set(GTK_SPIN_BUTTON(pw->paper_height_spin), pw, height, - print_paper_size_convert_units(PRINT_MIN_HEIGHT, PAPER_UNIT_POINTS, pw->paper_units), - print_paper_size_convert_units(PRINT_MAX_HEIGHT, PAPER_UNIT_POINTS, pw->paper_units), - step, page, digits); - - print_window_spin_set(GTK_SPIN_BUTTON(pw->margin_left_spin), pw, - print_paper_size_convert_units(pw->margin_left, PAPER_UNIT_POINTS, pw->paper_units), - 0.0, - print_paper_size_convert_units(PRINT_MAX_WIDTH, PAPER_UNIT_POINTS, pw->paper_units), - step, page, digits); - - print_window_spin_set(GTK_SPIN_BUTTON(pw->margin_right_spin), pw, - print_paper_size_convert_units(pw->margin_right, PAPER_UNIT_POINTS, pw->paper_units), - 0.0, - print_paper_size_convert_units(PRINT_MAX_WIDTH, PAPER_UNIT_POINTS, pw->paper_units), - step, page, digits); - - print_window_spin_set(GTK_SPIN_BUTTON(pw->margin_top_spin), pw, - print_paper_size_convert_units(pw->margin_top, PAPER_UNIT_POINTS, pw->paper_units), - 0.0, - print_paper_size_convert_units(PRINT_MAX_HEIGHT, PAPER_UNIT_POINTS, pw->paper_units), - step, page, digits); - - print_window_spin_set(GTK_SPIN_BUTTON(pw->margin_bottom_spin), pw, - print_paper_size_convert_units(pw->margin_bottom, PAPER_UNIT_POINTS, pw->paper_units), - 0.0, - print_paper_size_convert_units(PRINT_MAX_HEIGHT, PAPER_UNIT_POINTS, pw->paper_units), - step, page, digits); - - print_window_spin_set(GTK_SPIN_BUTTON(pw->proof_width_spin), pw, - print_paper_size_convert_units(pw->proof_width, PAPER_UNIT_POINTS, pw->paper_units), - print_paper_size_convert_units(PRINT_PROOF_MIN_SIZE, PAPER_UNIT_POINTS, pw->paper_units), - print_paper_size_convert_units(PRINT_PROOF_MAX_SIZE, PAPER_UNIT_POINTS, pw->paper_units), - step, page, digits); - - print_window_spin_set(GTK_SPIN_BUTTON(pw->proof_height_spin), pw, - print_paper_size_convert_units(pw->proof_height, PAPER_UNIT_POINTS, pw->paper_units), - print_paper_size_convert_units(PRINT_PROOF_MIN_SIZE, PAPER_UNIT_POINTS, pw->paper_units), - print_paper_size_convert_units(PRINT_PROOF_MAX_SIZE, PAPER_UNIT_POINTS, pw->paper_units), - step, page, digits); -} - -static void print_window_layout_set_size(PrintWindow *pw, gdouble width, gdouble height) -{ - pw->paper_width = width; - pw->paper_height = height; - - print_window_layout_sync_paper(pw); - - print_window_layout_size(pw); -} - -static void print_window_layout_set_orientation(PrintWindow *pw, PaperOrientation o) -{ - if (pw->paper_orientation == o) return; - - pw->paper_orientation = o; - - print_window_layout_size(pw); -} - -/* - *----------------------------------------------------------------------------- - * list printers - *----------------------------------------------------------------------------- - */ - -static GList *print_window_list_printers(void) -{ - FILE *p; - GList *list = NULL; - gchar buffer[2048]; - - p = popen(PRINT_LPR_QUERY, "r"); - if (!p) return NULL; - - while (fgets(buffer, sizeof(buffer), p) != NULL) - { - gchar *ptr; - gchar *end; - - ptr = buffer; - if (strncmp(ptr, "printer ", 8) != 0) continue; - if (strstr(ptr, "enabled") == NULL) continue; - ptr += 8; - end = ptr; - while (*end != '\0' && *end != '\n' && *end != ' ' && *end != '\t') end++; - *end = '\0'; - list = g_list_append(list, g_strdup(ptr)); - DEBUG_1("adding printer: %s", ptr); - } - - pclose(p); - - return list; -} - -/* - *----------------------------------------------------------------------------- - * print ps - *----------------------------------------------------------------------------- - */ - -typedef struct _PipeError PipeError; -struct _PipeError { - struct sigaction old_action; - sig_atomic_t *error; -}; - -static sig_atomic_t pipe_handler_error = FALSE; -static PipeError *pipe_handler_data = NULL; - -static void pipe_handler_sigpipe_cb(gint fd) -{ - pipe_handler_error = TRUE; -} - -static PipeError *pipe_handler_new(void) -{ - struct sigaction new_action; - PipeError *pe; - - if (pipe_handler_data) - { - log_printf("warning SIGPIPE handler already in use\n"); - return NULL; - } - - pe = g_new0(PipeError, 1); - - pipe_handler_error = FALSE; - pe->error = &pipe_handler_error; - - new_action.sa_handler = pipe_handler_sigpipe_cb; - sigemptyset(&new_action.sa_mask); - new_action.sa_flags = 0; + images = g_list_length(pw->source_selection); - /* setup our signal handler */ - sigaction(SIGPIPE, &new_action, &pe->old_action); - - pipe_handler_data = pe; - return pe; -} - -static void pipe_handler_free(PipeError *pe) -{ - if (!pe) return; - if (pe != pipe_handler_data) log_printf("warning SIGPIPE handler not closing same data\n"); - - /* restore the original signal handler */ - sigaction(SIGPIPE, &pe->old_action, NULL); - - pipe_handler_data = NULL; - g_free(pe); -} - -static gboolean pipe_handler_check(PipeError *pe) -{ - if (!pe) return FALSE; - return !!(*pe->error); -} - -static FILE *print_job_ps_fd(PrintWindow *pw) -{ - if (pw->job_file) return pw->job_file; - if (pw->job_pipe) return pw->job_pipe; - return NULL; -} - -static gboolean print_job_ps_init(PrintWindow *pw) -{ - FILE *f; - PipeError *pe; - const gchar *cmd = NULL; - const gchar *path = NULL; - gchar *lc_pointer; - gboolean ret; - - if (pw->job_file != NULL || pw->job_pipe != NULL) return FALSE; - - switch (pw->job_output) - { - case PRINT_OUTPUT_PS_LPR: - cmd = PRINT_LPR_COMMAND; - break; - case PRINT_OUTPUT_PS_CUSTOM: - cmd = pw->output_custom; - break; - case PRINT_OUTPUT_PS_FILE: - path = pw->output_path; - break; - default: - return FALSE; - break; - } - - if (cmd) - { - pw->job_pipe = popen(cmd, "w"); - - if (!pw->job_pipe) - { - gchar *buf; - - buf = g_strdup_printf(_("Unable to open pipe for writing.\n\"%s\""), cmd); - print_job_throw_error(pw, buf); - g_free(buf); - - return FALSE; - } - } - else if (path) - { - gchar *pathl; - - if (isfile(path)) - { - gchar *buf; - - buf = g_strdup_printf(_("A file with name %s already exists."), path); - print_job_throw_error(pw, buf); - g_free(buf); - - return FALSE; - } - - pathl = path_from_utf8(path); - pw->job_file = fopen(pathl, "w"); - g_free(pathl); - - if (!pw->job_file) - { - gchar *buf; - - buf = g_strdup_printf(_("Failure writing to file %s"), path); - print_job_throw_error(pw, buf); - g_free(buf); - - return FALSE; - } - - g_free(pw->job_path); - pw->job_path = g_strdup(path); - } - - f = print_job_ps_fd(pw); - if (!f) return FALSE; - - lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL)); - setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE); - - pe = pipe_handler_new(); - - /* comments, etc. */ - g_fprintf(f, "%%!PS-Adobe-3.0\n"); - g_fprintf(f, "%%%%Creator: %s Version %s\n", GQ_APPNAME, VERSION); - g_fprintf(f, "%%%%CreationDate: \n"); - g_fprintf(f, "%%%%LanguageLevel 2\n"); - g_fprintf(f, "%%%%DocumentMedia: \n"); - g_fprintf(f, "%%%%Orientation: %s\n", - (pw->paper_orientation == PAPER_ORIENTATION_PORTRAIT) ? "Portrait" : "Landscape"); - g_fprintf(f, "%%%%BoundingBox: %f %f %f %f\n", - 0.0, 0.0, pw->paper_width, pw->paper_height); - g_fprintf(f, "%%%%Pages: %d\n", print_layout_page_count(pw)); - g_fprintf(f, "%%%%PageOrder: Ascend\n"); - g_fprintf(f, "%%%%Title:\n"); - - /* setup page size, coordinates (do we really need this?) */ - /* enabled for 1.0beta2 https://bugzilla.redhat.com/222639 */ -#if 1 - g_fprintf(f, "<<\n"); - g_fprintf(f, "/PageSize [%f %f]\n", pw->layout_width, pw->layout_height); - g_fprintf(f, "/ImagingBBox [%f %f %f %f]\n", /* l b r t */ - pw->margin_left, pw->margin_bottom, - pw->layout_width - pw->margin_right, pw->layout_height - pw->margin_top); - g_fprintf(f, "/Orientation %d\n", - (pw->paper_orientation == PAPER_ORIENTATION_PORTRAIT) ? 0 : 1); - g_fprintf(f, ">> setpagedevice\n"); -#endif - - ret = !pipe_handler_check(pe); - pipe_handler_free(pe); - - if (lc_pointer) - { - setlocale(LC_NUMERIC, lc_pointer); - g_free(lc_pointer); - } - - if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer.")); - - return ret; -} - -static gboolean print_job_ps_page_new(PrintWindow *pw, gint page) -{ - FILE *f; - PipeError *pe; - gchar *lc_pointer; - gboolean ret; - - f= print_job_ps_fd(pw); - if (!f) return FALSE; - - lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL)); - setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE); - - pe = pipe_handler_new(); + if (images < 1 ) return 0; - g_fprintf(f, "%%%% page %d\n", page + 1); - - if (pw->paper_orientation == PAPER_ORIENTATION_LANDSCAPE) - { - g_fprintf(f, "/pagelevel save def\n"); - g_fprintf(f, "%d 0 translate 90 rotate\n", (gint)pw->layout_height); - } - - ret = !pipe_handler_check(pe); - pipe_handler_free(pe); - - if (lc_pointer) - { - setlocale(LC_NUMERIC, lc_pointer); - g_free(lc_pointer); - } - - if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer.")); - - return ret; -} - -static gboolean print_job_ps_page_done(PrintWindow *pw) -{ - FILE *f; - PipeError *pe; - gchar *lc_pointer; - gboolean ret; - - f = print_job_ps_fd(pw); - if (!f) return FALSE; - - lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL)); - setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE); - - pe = pipe_handler_new(); - - if (pw->paper_orientation == PAPER_ORIENTATION_LANDSCAPE) - { - g_fprintf(f, "pagelevel restore\n"); - } - - g_fprintf(f, "showpage\n"); - - ret = !pipe_handler_check(pe); - pipe_handler_free(pe); - - if (lc_pointer) - { - setlocale(LC_NUMERIC, lc_pointer); - g_free(lc_pointer); - } - - if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer.")); - - return ret; -} - -static void print_job_ps_page_image_pixel(FILE *f, guchar *pix) -{ - static gchar hex_digits[] = "0123456789abcdef"; - gchar text[8]; - gint i; - - for (i = 0; i < 3; i++) - { - text[i*2] = hex_digits[pix[i] >> 4]; - text[i*2+1] = hex_digits[pix[i] & 0xf]; - } - text[6] = '\0'; - - g_fprintf(f, "%s", text); -} -static gboolean print_job_ps_page_image(PrintWindow *pw, GdkPixbuf *pixbuf, - gdouble x, gdouble y, gdouble w, gdouble h, - gdouble offx, gdouble offy) -{ - FILE *f; - PipeError *pe; - gchar *lc_pointer; - gint sw, sh; - gint bps; - gint rowstride; - guchar *pix; - gint i, j; - gint c; - guchar *p; - guchar bps_buf[3]; - gboolean ret; - - if (!pixbuf) return TRUE; - - f = print_job_ps_fd(pw); - if (!f) return FALSE; - - sw = gdk_pixbuf_get_width(pixbuf); - sh = gdk_pixbuf_get_height(pixbuf); - - if (pw->max_dpi >= PRINT_PS_DPI_MIN && - sw / pw->max_dpi > w / 72.0) - { - pixbuf = gdk_pixbuf_scale_simple(pixbuf, - (gint)(w / 72.0 * pw->max_dpi), - (gint)(h / 72.0 * pw->max_dpi), - PRINT_PS_MAX_INTERP); - sw = gdk_pixbuf_get_width(pixbuf); - sh = gdk_pixbuf_get_height(pixbuf); - } - else - { - g_object_ref(G_OBJECT(pixbuf)); - } - - bps = (gdk_pixbuf_get_has_alpha(pixbuf)) ? 4 : 3; - rowstride = gdk_pixbuf_get_rowstride(pixbuf); - pix = gdk_pixbuf_get_pixels(pixbuf); - - lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL)); - setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE); - - pe = pipe_handler_new(); - - g_fprintf(f, "gsave\n"); - g_fprintf(f, "[%f 0 0 %f %f %f] concat\n", w, h, x, pw->layout_height - h - y); - g_fprintf(f, "/buf %d string def\n", sw * 3); - g_fprintf(f, "%d %d %d\n", sw, sh, 8); - g_fprintf(f, "[%d 0 0 -%d 0 %d]\n", sw, sh, sh); - g_fprintf(f, "{ currentfile buf readhexstring pop }\n"); - g_fprintf(f, "false %d colorimage\n", 3); - - c = 0; - for (j = 0; j < sh; j++) - { - p = pix + j * rowstride; - for (i = 0; i < sw; i++) - { - if (bps == 3) - { - print_job_ps_page_image_pixel(f, p); - } - else - { - bps_buf[0] = (p[0] * p[3] + PRINT_PS_MASK_R * (256 - p[3])) >> 8; - bps_buf[1] = (p[1] * p[3] + PRINT_PS_MASK_G * (256 - p[3])) >> 8; - bps_buf[2] = (p[2] * p[3] + PRINT_PS_MASK_B * (256 - p[3])) >> 8; - print_job_ps_page_image_pixel(f, bps_buf); - } - p+=bps; - c++; - if (c > 11) - { - g_fprintf(f, "\n"); - c = 0; - } - } - } - if (c > 0) g_fprintf(f, "\n"); - g_fprintf(f, "grestore\n"); - - ret = !pipe_handler_check(pe); - pipe_handler_free(pe); - - if (lc_pointer) - { - setlocale(LC_NUMERIC, lc_pointer); - g_free(lc_pointer); - } - - g_object_unref(G_OBJECT(pixbuf)); - - if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer.")); - - return ret; + return images; } -static gdouble convert_pango_dpi(gdouble points); - -static gboolean print_job_ps_page_text(PrintWindow *pw, const gchar *text, gdouble point_size, - gdouble x, gdouble y, gdouble width, - guint8 r, guint8 g, guint8 b) -{ - PangoLayout *layout; - PangoFontDescription *desc; - GdkPixbuf *pixbuf; - gint lw, lh; - gboolean ret; - gdouble scale_to_max_dpi = (pw->max_dpi >= PRINT_PS_DPI_MIN) ? pw->max_dpi / 72.0 : 1200.0 / 72.0; - - layout = gtk_widget_create_pango_layout(pw->dialog->dialog, NULL); - - desc = pango_font_description_new(); - pango_font_description_set_size(desc, convert_pango_dpi(point_size) * PANGO_SCALE * scale_to_max_dpi); - pango_layout_set_font_description(layout, desc); - pango_font_description_free(desc); - - pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); - pango_layout_set_text(layout, text, -1); - - pango_layout_get_pixel_size(layout, &lw, &lh); - x = x - (gdouble)lw / 2.0 / scale_to_max_dpi; - - pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, lw, lh); - gdk_pixbuf_fill(pixbuf, 0xffffffff); - pixbuf_draw_layout(pixbuf, layout, pw->dialog->dialog, 0, 0, r, g, b, 255); - g_object_unref(G_OBJECT(layout)); - - ret = print_job_ps_page_image(pw, pixbuf, x, y, - /* do not allow rescaling of the pixbuf due to rounding errors */ - ((gdouble)lw + 0.01) / scale_to_max_dpi, - ((gdouble)lh + 0.01) / scale_to_max_dpi, - 0, 0); - - g_object_unref(G_OBJECT(pixbuf)); - - return ret; -} - -static gboolean print_job_ps_end(PrintWindow *pw) -{ - FILE *f; - PipeError *pe; - gchar *lc_pointer; - gboolean ret; - - f = print_job_ps_fd(pw); - if (!f) return FALSE; - - lc_pointer = g_strdup(setlocale(LC_NUMERIC, NULL)); - setlocale(LC_NUMERIC, POSTSCRIPT_LOCALE); - - pe = pipe_handler_new(); - - g_fprintf(f, "%%%%EOF\n"); - - ret = !pipe_handler_check(pe); - pipe_handler_free(pe); - - if (lc_pointer) - { - setlocale(LC_NUMERIC, lc_pointer); - g_free(lc_pointer); - } - - if (!ret) print_job_throw_error(pw, _("SIGPIPE error writing to printer.")); - - return ret; -} - -/* - *----------------------------------------------------------------------------- - * print rgb - *----------------------------------------------------------------------------- - */ - -static gboolean print_job_rgb_page_new(PrintWindow *pw, gint page) -{ - gint total; - - if (pw->job_pixbuf) - { - pixbuf_set_rect_fill(pw->job_pixbuf, 0, 0, - gdk_pixbuf_get_width(pw->job_pixbuf), - gdk_pixbuf_get_height(pw->job_pixbuf), - 255, 255, 255, 255); - } - - g_free(pw->job_path); - pw->job_path = NULL; - - total = print_layout_page_count(pw); - - if (!pw->output_path || - page < 0 || page >= total) return FALSE; - - if (total > 1) - { - const gchar *ext; - gchar *base; - - ext = registered_extension_from_path(pw->output_path); - - if (ext) - { - base = g_strndup(pw->output_path, ext - pw->output_path); - } - else - { - base = g_strdup(pw->output_path); - ext = ""; - } - pw->job_path = g_strdup_printf("%s_%03d%s", base, page + 1, ext); - g_free(base); - } - else - { - pw->job_path = g_strdup(pw->output_path); - } - - if (isfile(pw->job_path)) - { - gchar *buf; - - buf = g_strdup_printf(_("A file with name %s already exists."), pw->job_path); - print_job_throw_error(pw, buf); - g_free(buf); - - g_free(pw->job_path); - pw->job_path = NULL; - } - - return (pw->job_path != NULL); -} - -static gboolean print_job_rgb_page_done(PrintWindow *pw) -{ - gchar *pathl; - gboolean ret = FALSE; - - if (!pw->job_pixbuf) return FALSE; - - pathl = path_from_utf8(pw->job_path); - - if (pw->output_format == PRINT_FILE_PNG) - { - ret = pixbuf_to_file_as_png(pw->job_pixbuf, pathl); - } - else - { - gint quality = 0; - - switch (pw->output_format) - { - case PRINT_FILE_JPG_LOW: - quality = 65; - break; - case PRINT_FILE_JPG_NORMAL: - quality = 80; - break; - case PRINT_FILE_JPG_HIGH: - quality = 95; - break; - default: - break; - } - - if (quality > 0) - { - ret = pixbuf_to_file_as_jpg(pw->job_pixbuf, pathl, quality); - } - } - - g_free(pathl); - - if (!ret) - { - gchar *buf; - - buf = g_strdup_printf(_("Failure writing to file %s"), pw->job_path); - print_job_throw_error(pw, buf); - g_free(buf); - } - - return ret; -} - -static gboolean print_job_rgb_page_image(PrintWindow *pw, GdkPixbuf *pixbuf, - gdouble x, gdouble y, gdouble w, gdouble h, - gdouble offx, gdouble offy) -{ - gdouble sw, sh; - gdouble dw, dh; - gdouble rx, ry, rw, rh; - - if (!pw->job_pixbuf) return FALSE; - if (!pixbuf) return TRUE; - - sw = (gdouble)gdk_pixbuf_get_width(pixbuf); - sh = (gdouble)gdk_pixbuf_get_height(pixbuf); - - dw = (gdouble)gdk_pixbuf_get_width(pw->job_pixbuf); - dh = (gdouble)gdk_pixbuf_get_height(pw->job_pixbuf); - - if (clip_region(x, y, w, h, - 0.0, 0.0, dw, dh, - &rx, &ry, &rw, &rh)) - { - gdk_pixbuf_composite(pixbuf, pw->job_pixbuf, rx, ry, rw, rh, - x + offx, y + offy, - w / sw, h / sh, - (w / sw < 0.01 || h / sh < 0.01) ? GDK_INTERP_NEAREST : GDK_INTERP_BILINEAR, 255); - } - - return TRUE; -} - -static gdouble convert_pango_dpi(gdouble points) -{ - static gdouble dpi = 0.0; - - if (dpi == 0.0) - { - GtkSettings *settings; - GObjectClass *klass; - - settings = gtk_settings_get_default(); - klass = G_OBJECT_CLASS(GTK_SETTINGS_GET_CLASS(settings)); - if (g_object_class_find_property(klass, "gtk-xft-dpi")) - { - gint int_dpi; - g_object_get(settings, "gtk-xft-dpi", &int_dpi, NULL); - dpi = (gdouble)int_dpi / PANGO_SCALE; - } - - if (dpi < 25.0) - { - static gboolean warned = FALSE; - gdouble fallback_dpi = 96.0; - - if (!warned) - { - if (dpi == 0.0) - { - log_printf("pango dpi unknown, assuming %.0f\n", fallback_dpi); - } - else - { - log_printf("pango dpi reported as %.0f ignored, assuming %.0f\n", dpi, fallback_dpi); - } - warned = TRUE; - } - - dpi = fallback_dpi; - } - } - - if (dpi == 0) return points; - return points * 72.0 / dpi; -} - -static gboolean print_job_rgb_page_text(PrintWindow *pw, const gchar *text, gdouble point_size, - gdouble x, gdouble y, gdouble width, - guint8 r, guint8 g, guint8 b) -{ - PangoLayout *layout; - PangoFontDescription *desc; - gint lw, lh; - - if (!pw->job_pixbuf) return FALSE; - - layout = gtk_widget_create_pango_layout(pw->dialog->dialog, NULL); - - desc = pango_font_description_new(); - pango_font_description_set_size(desc, convert_pango_dpi(point_size) * PANGO_SCALE); - pango_layout_set_font_description(layout, desc); - pango_font_description_free(desc); - - pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); - pango_layout_set_text(layout, text, -1); - - pango_layout_get_pixel_size(layout, &lw, &lh); - x = x - (gdouble)lw / 2.0; - - pixbuf_draw_layout(pw->job_pixbuf, layout, pw->dialog->dialog, x, y, r, g, b, 255); - g_object_unref(G_OBJECT(layout)); - - return TRUE; -} - -static gboolean print_job_rgb_init(PrintWindow *pw) -{ - if (pw->job_pixbuf) g_object_unref(pw->job_pixbuf); - pw->job_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, - (gint)pw->layout_width, (gint)pw->layout_height); - - return print_job_rgb_page_new(pw, pw->job_page); -} - -/* - *----------------------------------------------------------------------------- - * print preview - *----------------------------------------------------------------------------- - */ - -static gboolean print_job_preview_page_new(PrintWindow *pw, gint page) -{ - GdkPixbuf *pixbuf; - gint w, h; - gint l, r, t, b; - - pixbuf = pw->job_pixbuf; - if (!pixbuf) return FALSE; - - w = print_preview_unit(pw->layout_width); - h = print_preview_unit(pw->layout_height); - l = print_preview_unit(pw->margin_left); - r = print_preview_unit(pw->margin_right); - t = print_preview_unit(pw->margin_top); - b = print_preview_unit(pw->margin_bottom); - - /* fill background */ - pixbuf_set_rect_fill(pixbuf, 0, 0, w, h, - 255, 255, 255, 255); - - /* draw cm or inch grid */ - if (TRUE) - { - gdouble i; - gdouble grid; - PaperUnits units; - - units = (pw->paper_units == PAPER_UNIT_MM || - pw->paper_units == PAPER_UNIT_CM) ? PAPER_UNIT_CM : PAPER_UNIT_INCH; - - grid = print_paper_size_convert_units(1.0, units, PAPER_UNIT_POINTS); - for (i = grid ; i < pw->layout_width; i += grid) - { - pixbuf_draw_rect_fill(pixbuf, print_preview_unit(i), 0, 1, h, 0, 0, 0, 16); - } - for (i = grid; i < pw->layout_height; i += grid) - { - pixbuf_draw_rect_fill(pixbuf, 0, print_preview_unit(i), w, 1, 0, 0, 0, 16); - } - } - - /* proof sheet grid */ - if (pw->layout == PRINT_LAYOUT_PROOF) - { - gdouble i, j; - gdouble proof_w, proof_h; - gint uw, uh; - - print_proof_size(pw, &proof_w, &proof_h); - uw = print_preview_unit(proof_w + PRINT_PREVIEW_SCALE - 0.1); - uh = print_preview_unit(proof_h + PRINT_PREVIEW_SCALE - 0.1); - - for (i = 0; i < pw->proof_columns; i++) - for (j = 0; j < pw->proof_rows; j++) - { - gint x, y; - - x = pw->margin_left + (pw->layout_width - pw->margin_left - pw->margin_right - (pw->proof_columns * proof_w)) / 2 + i * proof_w; - y = pw->margin_top + j * proof_h; - - pixbuf_draw_rect(pixbuf, print_preview_unit(x), print_preview_unit(y), uw, uh, - 255, 0, 0, 64, 1, 1, 1, 1); - } - } - - /* non-printable region (margins) */ - pixbuf_draw_rect(pixbuf, 0, 0, w, h, - 0, 0, 0, 16, - l, r, t, b); - - /* margin lines */ - pixbuf_draw_rect(pixbuf, l, 0, w - l - r, h, - 0, 0, 255, 128, - 1, 1, 0, 0); - pixbuf_draw_rect(pixbuf, 0, t, w, h - t - b, - 0, 0, 255, 128, - 0, 0, 1, 1); - - /* border */ - pixbuf_draw_rect(pixbuf, 0, 0, w, h, - 0, 0, 0, 255, - 1, 1, 1, 1); - - image_area_changed(pw->layout_image, 0, 0, w, h); - - return TRUE; -} - -static gboolean print_job_preview_page_done(PrintWindow *pw) -{ - return TRUE; -} - -static gboolean print_job_preview_page_image(PrintWindow *pw, GdkPixbuf *pixbuf, - gdouble x, gdouble y, gdouble w, gdouble h, - gdouble offx, gdouble offy) -{ - gdouble sw, sh; - gdouble dw, dh; - gdouble rx, ry, rw, rh; - - if (!pw->job_pixbuf) return FALSE; - if (!pixbuf) return TRUE; - - sw = (gdouble)gdk_pixbuf_get_width(pixbuf); - sh = (gdouble)gdk_pixbuf_get_height(pixbuf); - - dw = (gdouble)gdk_pixbuf_get_width(pw->job_pixbuf); - dh = (gdouble)gdk_pixbuf_get_height(pw->job_pixbuf); - - x = print_preview_unit(x); - y = print_preview_unit(y); - w = print_preview_unit(w); - h = print_preview_unit(h); - offx = print_preview_unit(offx); - offy = print_preview_unit(offy); - - if (clip_region(x, y, w, h, - 0.0, 0.0, dw, dh, - &rx, &ry, &rw, &rh)) - { - gdk_pixbuf_composite(pixbuf, pw->job_pixbuf, rx, ry, rw, rh, - x + offx, y + offy, - w / sw, h / sh, - (w / sw < 0.01 || h / sh < 0.01) ? GDK_INTERP_NEAREST : GDK_INTERP_BILINEAR, 255); - - image_area_changed(pw->layout_image, rx, ry, rw, rh); - } - - return TRUE; -} - -static gboolean print_job_preview_page_text(PrintWindow *pw, const gchar *text, gdouble point_size, - gdouble x, gdouble y, gdouble width, - guint8 r, guint8 g, guint8 b) -{ - PangoLayout *layout; - PangoFontDescription *desc; - gint lw, lh; - GdkPixbuf *pixbuf; - - if (!pw->job_pixbuf) return FALSE; - - layout = gtk_widget_create_pango_layout(pw->dialog->dialog, NULL); - - desc = pango_font_description_new(); - pango_font_description_set_size(desc, convert_pango_dpi(point_size) * PANGO_SCALE); - pango_layout_set_font_description(layout, desc); - pango_font_description_free(desc); - - pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); - pango_layout_set_text(layout, text, -1); - - pango_layout_get_pixel_size(layout, &lw, &lh); - x = x - (gdouble)lw / 2.0; - - pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, lw, lh); - pixbuf_set_rect_fill(pixbuf, 0, 0, lw, lh, 0, 0, 0, 0); - pixbuf_draw_layout(pixbuf, layout, pw->dialog->dialog, 0, 0, r, g, b, 255); - g_object_unref(G_OBJECT(layout)); - - print_job_preview_page_image(pw, pixbuf, x, y, (gdouble)lw, (gdouble)lh, 0, 0); - g_object_unref(pixbuf); - - return TRUE; -} - -static gboolean print_job_preview_init(PrintWindow *pw) -{ - if (pw->job_pixbuf) g_object_unref(pw->job_pixbuf); - pw->job_pixbuf = image_get_pixbuf(pw->layout_image); - g_object_ref(pw->job_pixbuf); - - return print_job_preview_page_new(pw, pw->job_page); -} - - -/* - *----------------------------------------------------------------------------- - * wrappers - *----------------------------------------------------------------------------- - */ - -static gboolean print_job_page_new(PrintWindow *pw) -{ - switch (pw->job_format) - { - case RENDER_FORMAT_RGB: - return print_job_rgb_page_new(pw, pw->job_page); - case RENDER_FORMAT_PS: - return print_job_ps_page_new(pw, pw->job_page); - case RENDER_FORMAT_PREVIEW: - return print_job_preview_page_new(pw, pw->job_page); - } - - return FALSE; -} - -static gboolean print_job_page_done(PrintWindow *pw) -{ - switch (pw->job_format) - { - case RENDER_FORMAT_RGB: - return print_job_rgb_page_done(pw); - case RENDER_FORMAT_PS: - return print_job_ps_page_done(pw); - case RENDER_FORMAT_PREVIEW: - return print_job_preview_page_done(pw); - } - - return FALSE; -} - -static gboolean print_job_page_image(PrintWindow *pw, GdkPixbuf *pixbuf, - gdouble x, gdouble y, gdouble w, gdouble h, - gdouble offx, gdouble offy) -{ - gboolean success = FALSE; - - if (w <= 0.0 || h <= 0.0) return TRUE; - - switch (pw->job_format) - { - case RENDER_FORMAT_RGB: - success = print_job_rgb_page_image(pw, pixbuf, x, y, w, h, offx, offy); - break; - case RENDER_FORMAT_PS: - success = print_job_ps_page_image(pw, pixbuf, x, y, w, h, offx, offy); - break; - case RENDER_FORMAT_PREVIEW: - success = print_job_preview_page_image(pw, pixbuf, x, y, w, h, offx, offy); - break; - } - - return success; -} - -static gboolean print_job_page_text(PrintWindow *pw, const gchar *text, gdouble point_size, - gdouble x, gdouble y, gdouble width, - guint8 r, guint8 g, guint8 b) -{ - gboolean success = TRUE; - - if (!text) return TRUE; - - switch (pw->job_format) - { - case RENDER_FORMAT_RGB: - success = print_job_rgb_page_text(pw, text, point_size, x, y, width, r, g, b); - break; - case RENDER_FORMAT_PS: - success = print_job_ps_page_text(pw, text, point_size, x, y, width, r, g, b); - break; - case RENDER_FORMAT_PREVIEW: - success = print_job_preview_page_text(pw, text, point_size, x, y, width, r, g, b); - break; - } - - return success; -} - -/* - *----------------------------------------------------------------------------- - * print ? - *----------------------------------------------------------------------------- - */ - static gboolean print_job_render_image(PrintWindow *pw); -static gboolean print_job_render_proof(PrintWindow *pw); - - -static void print_job_status(PrintWindow *pw) -{ - gdouble value; - gint page; - gint total; - gchar *buf; - - if (!pw->job_progress) return; - - page = pw->job_page; - total = print_layout_page_count(pw); - - if (pw->layout == PRINT_LAYOUT_PROOF && pw->proof_point) - { - GList *start; - - start = g_list_first(pw->proof_point); - value = (gdouble)g_list_position(start, pw->proof_point) / g_list_length(start); - } - else - { - value = (total > 0) ? (gdouble)page / total : 0.0; - } - - buf = g_strdup_printf(_("Page %d"), page + 1); - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pw->job_progress), buf); - g_free(buf); - - if (pw->job_path && pw->job_progress_label) - { - gtk_label_set_text(GTK_LABEL(pw->job_progress_label), pw->job_path); - } - - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pw->job_progress), value); -} - -static void print_job_throw_error(PrintWindow *pw, const gchar *message) -{ - GenericDialog *gd; - GtkWidget *parent = NULL; - GtkWidget *group; - GtkWidget *label; - gchar *buf; - - if (gtk_widget_get_visible(pw->dialog->dialog)) parent = pw->dialog->dialog; - - gd = generic_dialog_new(_("Printing error"), "print_warning", - parent, TRUE, NULL, NULL); - generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, NULL, TRUE); - - buf = g_strdup_printf(_("An error occurred printing to %s."), print_output_name(pw->output)); - generic_dialog_add_message(gd, GTK_STOCK_DIALOG_ERROR, _("Printing error"), buf, TRUE); - g_free(buf); - - group = pref_group_new(gd->vbox, FALSE, _("Details"), GTK_ORIENTATION_VERTICAL); - label = pref_label_new(group, message); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); - - gtk_widget_show(gd->dialog); -} - -static void print_job_done(PrintWindow *pw) -{ - print_job_close(pw, FALSE); -} - -static gboolean print_job_text_image(PrintWindow *pw, const gchar *path, - gdouble x, gdouble y, gdouble width, - gint sw, gint sh, gint proof) -{ - GString *string; - gboolean space = FALSE; - gboolean newline = FALSE; - gboolean ret; - - if (pw->text_fields == 0) return TRUE; - - string = g_string_new(""); - path = image_loader_get_fd(pw->job_loader)->path; - - if (pw->text_fields & TEXT_INFO_FILENAME) - { - if (pw->text_fields & TEXT_INFO_FILEPATH) - g_string_append(string, path); - else - g_string_append(string, filename_from_path(path)); - newline = TRUE; - } - else if (pw->text_fields & TEXT_INFO_FILEPATH) - { - gchar *dirname = g_path_get_dirname(path); - - g_string_append_printf(string, "%s%s", dirname, G_DIR_SEPARATOR_S); - g_free(dirname); - newline = TRUE; - } - if (pw->text_fields & TEXT_INFO_DIMENSIONS) - { - if (newline) g_string_append(string, "\n"); - g_string_append_printf(string, "%d x %d", (gint)sw, (gint)sh); - newline = proof; - space = !proof; - } - if (pw->text_fields & TEXT_INFO_FILEDATE) - { - if (newline) g_string_append(string, "\n"); - if (space) g_string_append(string, " - "); - g_string_append(string, text_from_time(filetime(image_loader_get_fd(pw->job_loader)->path))); - newline = proof; - space = !proof; - } - if (pw->text_fields & TEXT_INFO_FILESIZE) - { - gchar *size; - - if (newline) g_string_append(string, "\n"); - if (space) g_string_append(string, " - "); - size = text_from_size_abrev(filesize(image_loader_get_fd(pw->job_loader)->path)); - g_string_append(string, size); - g_free(size); - } - - ret = print_job_page_text(pw, string->str, pw->text_points, x, y, width, - pw->text_r, pw->text_g, pw->text_b); - - g_string_free(string, TRUE); - - return ret; -} static void print_job_render_image_loader_done(ImageLoader *il, gpointer data) { PrintWindow *pw = data; GdkPixbuf *pixbuf; - gboolean success = TRUE; pixbuf = image_loader_get_pixbuf(il); - if (pixbuf) - { - gdouble sw, sh; - gdouble dw, dh; - gdouble x, y, w, h; - gdouble offx, offy; - sw = (gdouble)gdk_pixbuf_get_width(pixbuf); - sh = (gdouble)gdk_pixbuf_get_height(pixbuf); - - dw = pw->layout_width - pw->margin_left - pw->margin_right; - dh = pw->layout_height - pw->margin_top - pw->margin_bottom; - - if (dw / sw < dh / sh) - { - w = dw; - h = dw / sw * sh; - } - else - { - h = dh; - w = dh / sh *sw; - } - - if (pw->image_scale >= 5.0) - { - w = w * pw->image_scale / 100.0; - h = h * pw->image_scale / 100.0; - } - - x = pw->margin_left + (dw / 2) - (w / 2); - y = pw->margin_top + (dh / 2) - (h / 2); - - offx = offy = 0; - - if (x < 0) - { - w += x; - offx = x; - x = 0; - } - if (x + w >= pw->layout_width) w = pw->layout_width - x; - - if (y < 0) - { - h += y; - offy = y; - y = 0; - } - if (y + h >= pw->layout_height) h = pw->layout_height - y; - - success = (success && - print_job_page_image(pw, pixbuf, x, y, w, h, offx, offy)); - - x = x + w / 2; - y = y + h + PRINT_TEXT_PADDING; - - success = (success && - print_job_text_image(pw, image_loader_get_fd(pw->job_loader)->path, x, y, dw, sw, sh, FALSE)); - } + g_object_ref(pixbuf); + pw->print_pixbuf_queue = g_list_append(pw->print_pixbuf_queue, pixbuf); image_loader_free(pw->job_loader); pw->job_loader = NULL; - if (pw->job_format == RENDER_FORMAT_PREVIEW) - { - print_job_done(pw); - return; - } + pw->job_page++; - success = (success && print_job_page_done(pw)); - if (!success) + if (!print_job_render_image(pw)) { - print_job_close(pw, TRUE); - return; - } - - pw->job_page++; - print_job_status(pw); - - if (print_job_render_image(pw)) - { - if (!print_job_page_new(pw)) print_job_close(pw, TRUE); - } - else - { - print_job_done(pw); + pw->job_render_finished = TRUE; } } @@ -2156,27 +108,16 @@ { FileData *fd = NULL; - switch (pw->source) - { - case PRINT_SOURCE_SELECTION: - fd = g_list_nth_data(pw->source_selection, pw->job_page); - break; - case PRINT_SOURCE_ALL: - fd = g_list_nth_data(pw->source_list, pw->job_page); - break; - case PRINT_SOURCE_IMAGE: - default: - if (pw->job_page == 0) fd = pw->source_fd; - break; - } + fd = g_list_nth_data(pw->source_selection, pw->job_page); + if (!fd) return FALSE; image_loader_free(pw->job_loader); pw->job_loader = NULL; - if (!fd) return FALSE; + pw->job_loader = image_loader_new(fd); + g_signal_connect(G_OBJECT(pw->job_loader), "done", + (GCallback)print_job_render_image_loader_done, pw); - pw->job_loader = image_loader_new(fd); - g_signal_connect(G_OBJECT(pw->job_loader), "done", (GCallback)print_job_render_image_loader_done, pw); if (!image_loader_start(pw->job_loader)) { image_loader_free(pw->job_loader); @@ -2186,953 +127,6 @@ return TRUE; } -static void print_job_render_proof_loader_done(ImageLoader *il, gpointer data) -{ - PrintWindow *pw = data; - GdkPixbuf *pixbuf; - gdouble x, y; - gdouble w, h; - gdouble proof_w, proof_h; - gdouble icon_w, icon_h; - gboolean success = TRUE; - - if (pw->proof_columns < 1 || pw->proof_rows < 1) - { - image_loader_free(pw->job_loader); - pw->job_loader = NULL; - - print_job_done(pw); - - return; - } - - pixbuf = image_loader_get_pixbuf(il); - - if (options->image.exif_proof_rotate_enable == TRUE) { - pixbuf = pixbuf_apply_orientation(pixbuf, il->fd->exif_orientation); - } - - w = gdk_pixbuf_get_width(pixbuf); - h = gdk_pixbuf_get_height(pixbuf); - - if (pw->proof_width / w < pw->proof_height / h) - { - icon_w = pw->proof_width; - icon_h = pw->proof_width / w * h; - } - else - { - icon_h = pw->proof_height; - icon_w = pw->proof_height / h * w; - } - - y = pw->proof_position / pw->proof_columns; - x = pw->proof_position - (y * pw->proof_columns); - - print_proof_size(pw, &proof_w, &proof_h); - - x *= proof_w; - y *= proof_h; - x += pw->margin_left + (pw->layout_width - pw->margin_left - pw->margin_right - (pw->proof_columns * proof_w)) / 2 + (proof_w - icon_w) / 2; - y += pw->margin_top + PRINT_PROOF_MARGIN + (pw->proof_height - icon_h) / 2; - - success = (success && - print_job_page_image(pw, pixbuf, x, y, icon_w, icon_h, 0, 0)); - - x = x + icon_w / 2; - y = y + icon_h + (pw->proof_height - icon_h) / 2 + PRINT_TEXT_PADDING; - - success = (success && - print_job_text_image(pw, image_loader_get_fd(pw->job_loader)->path, x, y, icon_w + PRINT_PROOF_MARGIN * 2, w, h, TRUE)); - - if (!success) - { - print_job_close(pw, TRUE); - return; - } - - if (pw->proof_point) pw->proof_point = pw->proof_point->next; - - pw->proof_position++; - if (pw->proof_position >= pw->proof_columns * pw->proof_rows) - { - if (pw->job_format == RENDER_FORMAT_PREVIEW) - { - print_job_done(pw); - return; - } - - if (!print_job_page_done(pw)) - { - print_job_close(pw, TRUE); - return; - } - - pw->proof_position = 0; - pw->job_page++; - if (print_job_render_proof(pw)) - { - if (!print_job_page_new(pw)) - { - print_job_close(pw, TRUE); - return; - } - print_job_status(pw); - } - else - { - print_job_done(pw); - } - } - else - { - if (print_job_render_proof(pw)) - { - print_job_status(pw); - } - else - { - if (print_job_page_done(pw)) - { - print_job_done(pw); - } - else - { - print_job_close(pw, TRUE); - } - } - } -} - -static gboolean print_job_render_proof(PrintWindow *pw) -{ - FileData *fd = NULL; - - if (pw->proof_columns < 1 || pw->proof_rows < 1) return FALSE; - - if (!pw->proof_point && pw->proof_position == 0 && pw->source == PRINT_SOURCE_IMAGE) - { - fd = pw->source_fd; - } - else if (pw->proof_point && - pw->proof_position < pw->proof_columns * pw->proof_rows) - { - fd = pw->proof_point->data; - } - - if (!fd) return FALSE; - - image_loader_free(pw->job_loader); - pw->job_loader = image_loader_new(fd); - g_signal_connect(G_OBJECT(pw->job_loader), "done", (GCallback)print_job_render_proof_loader_done, pw); - if (!image_loader_start(pw->job_loader)) - { - image_loader_free(pw->job_loader); - pw->job_loader = NULL; - } - - return TRUE; -} - -static void print_job_render(PrintWindow *pw) -{ - gdouble proof_w, proof_h; - gboolean finished; - - pw->proof_position = 0; - - switch (pw->source) - { - case PRINT_SOURCE_SELECTION: - pw->proof_point = pw->source_selection; - break; - case PRINT_SOURCE_ALL: - pw->proof_point = pw->source_list; - break; - case PRINT_SOURCE_IMAGE: - default: - pw->proof_point = NULL; - break; - } - - print_proof_size(pw, &proof_w, &proof_h); - pw->proof_columns = (pw->layout_width - pw->margin_left - pw->margin_right) / proof_w; - pw->proof_rows = (pw->layout_height - pw->margin_top - pw->margin_bottom) / proof_h; - - if (pw->job_format == RENDER_FORMAT_PREVIEW) - { - gint total; - - total = print_layout_page_count(pw); - if (pw->job_page < 0 || pw->job_page >= total) - { - print_job_done(pw); - return; - } - - if (pw->proof_point && pw->job_page > 0) - { - pw->proof_point = g_list_nth(pw->proof_point, pw->job_page * pw->proof_columns * pw->proof_rows); - } - } - - if (!print_job_page_new(pw)) - { - print_job_close(pw, TRUE); - return; - } - - if (pw->layout == PRINT_LAYOUT_IMAGE) - { - finished = !print_job_render_image(pw); - } - else - { - finished = !print_job_render_proof(pw); - } - - if (finished) print_job_done(pw); -} - -static gboolean print_job_init(PrintWindow *pw) -{ - gboolean success = FALSE; - - pw->job_page = 0; - - switch (pw->job_format) - { - case RENDER_FORMAT_RGB: - success = print_job_rgb_init(pw); - break; - case RENDER_FORMAT_PS: - success = print_job_ps_init(pw); - break; - case RENDER_FORMAT_PREVIEW: - pw->job_page = pw->proof_page; - success = print_job_preview_init(pw); - break; - } - - return success; -} - -static gboolean print_job_finish(PrintWindow *pw) -{ - gboolean success = FALSE; - - switch (pw->job_format) - { - case RENDER_FORMAT_RGB: - success = TRUE; - break; - case RENDER_FORMAT_PS: - print_job_ps_end(pw); - break; - case RENDER_FORMAT_PREVIEW: - success = TRUE; - break; - } - - return success; -} - -static void print_job_close_file(PrintWindow *pw) -{ - if (pw->job_file) - { - fclose(pw->job_file); - pw->job_file = NULL; - } - - if (pw->job_pipe) - { - PipeError *pe; - - pe = pipe_handler_new(); - pclose(pw->job_pipe); - pipe_handler_free(pe); - - pw->job_pipe = NULL; - } -} - -static gboolean print_job_close_finish_cb(gpointer data) -{ - PrintWindow *pw = data; - - print_window_close(pw); - return FALSE; -} - -static void print_job_close(PrintWindow *pw, gint error) -{ - if (!error) print_job_finish(pw); - - print_job_close_file(pw); - g_free(pw->job_path); - pw->job_path = NULL; - - if (pw->job_dialog) - { - generic_dialog_close(pw->job_dialog); - pw->job_dialog = NULL; - pw->job_progress = NULL; - } - - image_loader_free(pw->job_loader); - pw->job_loader = NULL; - - if (pw->job_pixbuf) - { - g_object_unref(pw->job_pixbuf); - pw->job_pixbuf = NULL; - } - - if (pw->dialog && !gtk_widget_get_visible(pw->dialog->dialog)) - { - g_idle_add_full(G_PRIORITY_HIGH_IDLE, print_job_close_finish_cb, pw, NULL); - } -} - -static void print_job_cancel_cb(GenericDialog *gd, gpointer data) -{ - PrintWindow *pw = data; - - print_job_close(pw, FALSE); -} - -static void print_pref_store(PrintWindow *pw) -{ - - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_SAVE, pw->save_settings); - - if (!pw->save_settings) return; - - /* only store values that are actually used in this print job, hence the if()s */ - - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_OUTPUT, pw->output); - - if (pw->output == PRINT_OUTPUT_RGB_FILE) - { - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_FORMAT, pw->output_format); - } - - if (pw->job_format == RENDER_FORMAT_PS) - { - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_DPI, pw->max_dpi); - } - - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_UNITS, pw->paper_units); - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_SIZE, pw->paper_size); - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_ORIENTATION, pw->paper_orientation); - - if (pw->paper_size == 0) - { - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_CUSTOM_WIDTH, pw->paper_width); - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_CUSTOM_HEIGHT, pw->paper_height); - } - - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_MARGIN_LEFT, pw->margin_left); - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_MARGIN_RIGHT, pw->margin_right); - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_MARGIN_TOP, pw->margin_top); - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_MARGIN_BOTTOM, pw->margin_bottom); - - if (pw->layout == PRINT_LAYOUT_PROOF) - { - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_PROOF_WIDTH, pw->proof_width); - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_PROOF_HEIGHT, pw->proof_height); - } - - if (pw->output == PRINT_OUTPUT_PS_CUSTOM) - { - pref_list_string_set(PRINT_PREF_GROUP, PRINT_PREF_PRINTERC, pw->output_custom); - } - - if (pw->output == PRINT_OUTPUT_RGB_FILE || - pw->output == PRINT_OUTPUT_PS_FILE) - { - tab_completion_append_to_history(pw->path_entry, pw->output_path); - } - - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_TEXT, pw->text_fields); - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_TEXTSIZE, pw->text_points); - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_TEXTCOLOR_R, pw->text_r); - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_TEXTCOLOR_G, pw->text_g); - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_TEXTCOLOR_B, pw->text_b); - - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_SOURCE, pw->source); - pref_list_int_set(PRINT_PREF_GROUP, PRINT_PREF_LAYOUT, pw->layout); - - pref_list_double_set(PRINT_PREF_GROUP, PRINT_PREF_IMAGE_SCALE, pw->image_scale); -} - -static gboolean print_job_start(PrintWindow *pw, RenderFormat format, PrintOutput output) -{ - GtkWidget *hbox; - GtkWidget *spinner; - gchar *msg; - - if (pw->job_dialog) return FALSE; - - pw->job_format = format; - pw->job_output = output; - - if (!print_job_init(pw)) - { - print_job_close(pw, TRUE); - return FALSE; - } - - if (format == RENDER_FORMAT_PREVIEW) - { - print_job_render(pw); - return TRUE; - } - - print_pref_store(pw); - - gtk_widget_hide(pw->dialog->dialog); - - pw->job_dialog = file_util_gen_dlg(_("Print"), "print_job_dialog", - (GtkWidget *)gtk_window_get_transient_for(GTK_WINDOW(pw->dialog->dialog)), FALSE, - print_job_cancel_cb, pw); - - msg = g_strdup_printf(_("Printing %d pages to %s."), print_layout_page_count(pw), print_output_name(pw->output)); - generic_dialog_add_message(pw->job_dialog, NULL, msg, NULL, TRUE); - g_free(msg); - - if (pw->job_output == PRINT_OUTPUT_PS_FILE || - pw->job_output == PRINT_OUTPUT_RGB_FILE) - { - hbox = pref_box_new(pw->job_dialog->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE); - pref_label_new(hbox, _("Filename:")); - - pw->job_progress_label = pref_label_new(hbox, ""); - } - else - { - pw->job_progress_label = NULL; - } - - pref_spacer(pw->job_dialog->vbox, PREF_PAD_SPACE); - - hbox = pref_box_new(pw->job_dialog->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE); - - pw->job_progress = gtk_progress_bar_new(); - gtk_box_pack_start(GTK_BOX(hbox), pw->job_progress, TRUE, TRUE, 0); - gtk_widget_show(pw->job_progress); - - spinner = spinner_new(NULL, SPINNER_SPEED); - gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0); - gtk_widget_show(spinner); - - gtk_widget_show(pw->job_dialog->dialog); - - print_job_render(pw); - print_job_status(pw); - - return TRUE; -} - -static void print_window_print_start(PrintWindow *pw) -{ - RenderFormat format; - - switch (pw->output) - { - case PRINT_OUTPUT_RGB_FILE: - format = RENDER_FORMAT_RGB; - break; - case PRINT_OUTPUT_PS_FILE: - case PRINT_OUTPUT_PS_CUSTOM: - case PRINT_OUTPUT_PS_LPR: - default: - format = RENDER_FORMAT_PS; - break; - } - - print_job_start(pw, format, pw->output); -} - - -/* - *----------------------------------------------------------------------------- - * combo box util - *----------------------------------------------------------------------------- - */ - -static GtkWidget *print_combo_menu(const gchar *text[], gint count, gint preferred, - GCallback func, gpointer data) -{ - GtkWidget *combo; - gint i; - - combo = gtk_combo_box_text_new(); - - for (i = 0 ; i < count; i++) - { - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _(text[i])); - } - - if (preferred >= 0 && preferred < count) - { - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), preferred); - } - - if (func) g_signal_connect(G_OBJECT(combo), "changed", func, data); - - return combo; -} - - -/* - *----------------------------------------------------------------------------- - * paper selection - *----------------------------------------------------------------------------- - */ - -static GtkWidget *print_paper_menu(GtkWidget *table, gint column, gint row, - PaperOrientation preferred, GCallback func, gpointer data) -{ - GtkWidget *combo; - gint i; - - pref_table_label(table, column, row, (_("Format:")), 1.0); - - combo = gtk_combo_box_text_new(); - - i = 0; - while (print_paper_sizes[i].description) - { - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), _(print_paper_sizes[i].description)); - i++; - } - - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), preferred); - if (func) g_signal_connect(G_OBJECT(combo), "changed", func, data); - - gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_widget_show(combo); - - return combo; -} - -static void print_paper_select_cb(GtkWidget *combo, gpointer data) -{ - PrintWindow *pw = data; - PaperSize *ps; - gint n; - - n = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); - ps = print_paper_size_nth(n); - - if (!ps) return; - - pw->paper_size = n; - - if (pw->paper_size == 0) - { - print_window_layout_sync_paper(pw); - return; - } - - if (ps->orientation == PAPER_ORIENTATION_PORTRAIT) - { - print_window_layout_set_size(pw, ps->width, ps->height); - } - else - { - print_window_layout_set_size(pw, ps->height, ps->width); - } -} - -static void print_paper_size_cb(GtkWidget *spin, gpointer data) -{ - PrintWindow *pw = data; - gdouble value; - - value = print_paper_size_convert_units(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)), - pw->paper_units, PAPER_UNIT_POINTS); - - if (spin == pw->paper_width_spin) - { - pw->paper_width = value; - } - else - { - pw->paper_height = value; - } - - print_window_layout_set_size(pw, pw->paper_width, pw->paper_height); -} - -static GtkWidget *print_paper_units_menu(GtkWidget *table, gint column, gint row, - PaperUnits units, GCallback func, gpointer data) -{ - GtkWidget *combo; - - pref_table_label(table, column, row, (_("Units:")), 1.0); - - combo = print_combo_menu(print_paper_units, PAPER_UNIT_COUNT, units, func, data); - - gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_widget_show(combo); - - return combo; -} - -static void print_paper_units_set(PrintWindow *pw, PaperUnits units) -{ - PaperUnits old_units; - - if (units >= PAPER_UNIT_COUNT) return; - - old_units = pw->paper_units; - pw->paper_units = units; - print_window_layout_sync_paper(pw); - - if ((units == PAPER_UNIT_MM || units == PAPER_UNIT_CM) != - (old_units == PAPER_UNIT_MM || old_units == PAPER_UNIT_CM)) - { - print_window_layout_render(pw); - } -} - -static void print_paper_units_cb(GtkWidget *combo, gpointer data) -{ - PrintWindow *pw = data; - PaperUnits units; - - units = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); - - print_paper_units_set(pw, units); -} - -static GtkWidget *print_paper_orientation_menu(GtkWidget *table, gint column, gint row, - PaperOrientation preferred, - GCallback func, gpointer data) -{ - GtkWidget *combo; - - pref_table_label(table, column, row, (_("Orientation:")), 1.0); - - combo = print_combo_menu(print_paper_orientation, PAPER_ORIENTATION_COUNT, preferred, func, data); - - gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_widget_show(combo); - - return combo; -} - -static void print_paper_orientation_cb(GtkWidget *combo, gpointer data) -{ - PrintWindow *pw = data; - PaperOrientation o; - - o = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); - - print_window_layout_set_orientation(pw, o); -} - -static void print_paper_margin_cb(GtkWidget *spin, gpointer data) -{ - PrintWindow *pw = data; - gdouble value; - - value = print_paper_size_convert_units(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)), - pw->paper_units, PAPER_UNIT_POINTS); - - if (spin == pw->margin_left_spin) - { - pw->margin_left = CLAMP(value, 0.0, pw->paper_width); - } - else if (spin == pw->margin_right_spin) - { - pw->margin_right = CLAMP(value, 0.0, pw->paper_width); - } - else if (spin == pw->margin_top_spin) - { - pw->margin_top = CLAMP(value, 0.0, pw->paper_height); - } - else if (spin == pw->margin_bottom_spin) - { - pw->margin_bottom = CLAMP(value, 0.0, pw->paper_height); - } - - print_window_layout_set_size(pw, pw->paper_width, pw->paper_height); -} - -static GtkWidget *print_misc_menu(GtkWidget *parent_box, gint preferred, - const gchar *title, const gchar *key, - gint count, const gchar **text, - GCallback func, gpointer data) -{ - GtkWidget *box; - GtkWidget *button = NULL; - gint i; - - box = pref_group_new(parent_box, FALSE, title, GTK_ORIENTATION_VERTICAL); - - for (i = 0; i < count; i++) - { - button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button), _(text[i])); - if (i == preferred) - { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); - } - g_object_set_data(G_OBJECT(button), key, GINT_TO_POINTER(i)); - if (func) g_signal_connect(G_OBJECT(button), "clicked", func, data); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0); - gtk_widget_show(button); - } - - return box; -} - -static void print_source_select_cb(GtkWidget *widget, gpointer data) -{ - PrintWindow *pw = data; - - if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) return; - - pw->source = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "print_source")); - print_window_layout_size(pw); -} - -static void print_layout_select_cb(GtkWidget *widget, gpointer data) -{ - PrintWindow *pw = data; - - if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) return; - - pw->layout = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "print_layout")); - - print_window_layout_sync_layout(pw); - print_window_layout_size(pw); -} - -static void print_image_scale_cb(GtkWidget *spin, gpointer data) -{ - PrintWindow *pw = data; - - pw->image_scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)); - - print_window_layout_set_size(pw, pw->paper_width, pw->paper_height); -} - -static void print_proof_size_cb(GtkWidget *spin, gpointer data) -{ - PrintWindow *pw = data; - gdouble value; - - value = print_paper_size_convert_units(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)), - pw->paper_units, PAPER_UNIT_POINTS); - - if (spin == pw->proof_width_spin) - { - pw->proof_width = value; - } - else - { - pw->proof_height = value; - } - - print_window_layout_render(pw); -} - -static GtkWidget *print_output_menu(GtkWidget *table, gint column, gint row, - PrintOutput preferred, GCallback func, gpointer data) -{ - GtkWidget *combo; - - pref_table_label(table, column, row, (_("Destination:")), 1.0); - - combo = print_combo_menu(print_output_text, PRINT_OUTPUT_COUNT, preferred, func, data); - - gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_widget_show(combo); - - return combo; -} - -static void print_custom_entry_set(PrintWindow *pw, GtkWidget *combo) -{ - GtkListStore *store; - const gchar *text; - GList *list; - GList *work; - - store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); - gtk_list_store_clear(store); - - list = print_window_list_printers(); - work = list; - while (work) - { - gchar *name; - gchar *buf; - - name = work->data; - work = work->next; - - buf = g_strdup_printf(PRINT_LPR_CUSTOM, name); - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), buf); - g_free(buf); - } - string_list_free(list); - - if (pref_list_string_get(PRINT_PREF_GROUP, PRINT_PREF_PRINTERC, &text)) - { - gtk_entry_set_text(GTK_ENTRY(pw->custom_entry), text); - } - else - { - text = gtk_entry_get_text(GTK_ENTRY(pw->custom_entry)); - if (!text || strlen(text) == 0) - { - gchar *buf; - - buf = g_strdup_printf(PRINT_LPR_CUSTOM, _("")); - gtk_entry_set_text(GTK_ENTRY(pw->custom_entry), buf); - g_free(buf); - } - } -} - -static void print_output_set(PrintWindow *pw, PrintOutput output) -{ - gboolean use_file = FALSE; - gboolean use_custom = FALSE; - gboolean use_format = FALSE; - - pw->output = output; - - switch (pw->output) - { - case PRINT_OUTPUT_RGB_FILE: - use_file = TRUE; - use_format = TRUE; - break; - case PRINT_OUTPUT_PS_FILE: - use_file = TRUE; - break; - case PRINT_OUTPUT_PS_CUSTOM: - use_custom = TRUE; - break; - case PRINT_OUTPUT_PS_LPR: - default: - break; - } - - gtk_widget_set_sensitive(gtk_widget_get_parent(pw->path_entry), use_file); - gtk_widget_set_sensitive(gtk_widget_get_parent(pw->custom_entry), use_custom); - gtk_widget_set_sensitive(pw->path_format_menu, use_format); - gtk_widget_set_sensitive(pw->max_dpi_menu, !use_format); -} - -static void print_output_cb(GtkWidget *combo, gpointer data) -{ - PrintWindow *pw = data; - PrintOutput output; - - output = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); - - print_output_set(pw, output); -} - -static GtkWidget *print_output_format_menu(GtkWidget * table, gint column, gint row, - PrintFileFormat preferred, GCallback func, gpointer data) -{ - GtkWidget *combo; - - combo = print_combo_menu(print_file_format_text, PRINT_FILE_COUNT, preferred, func, data); - - gtk_table_attach(GTK_TABLE(table), combo, column, column + 1, row, row + 1, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_widget_show(combo); - - return combo; -} - -static void print_output_format_cb(GtkWidget *combo, gpointer data) -{ - PrintWindow *pw = data; - - pw->output_format = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); -} - -static GtkWidget *print_output_dpi_menu(GtkWidget * table, gint column, gint row, - gdouble dpi, GCallback func, gpointer data) -{ - static gint dpilist[] = { 150, 300, 600, 1200, 0, -1}; - GtkWidget *combo; - GtkListStore *store; - GtkCellRenderer *renderer; - gint current = 1; - gint i; - - store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); - - i = 0; - while (dpilist[i] != -1) - { - GtkTreeIter iter; - gchar *text; - - if (dpilist[i] == 0) - { - text = g_strdup(_("Unlimited")); - } - else - { - text = g_strdup_printf("%d", dpilist[i]); - } - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, text, 1, dpilist[i], -1); - g_free(text); - - if (dpi == (gdouble)dpilist[i]) current = i; - - i++; - } - - combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); - g_object_unref(store); - - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current); - if (func) g_signal_connect(G_OBJECT(combo), "changed", func, data); - - renderer = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "text", 0, NULL); - - gtk_table_attach(GTK_TABLE(table), combo, column, column + 1, row, row + 1, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_widget_show(combo); - - return combo; -} - -static void print_output_dpi_cb(GtkWidget *combo, gpointer data) -{ - PrintWindow *pw = data; - GtkTreeModel *store; - GtkTreeIter iter; - gint n = -1; - - store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); - if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return; - gtk_tree_model_get(store, &iter, 1, &n, -1); - - pw->max_dpi = (gdouble)n; -} - static void print_text_field_set(PrintWindow *pw, TextInfo field, gboolean active) { if (active) @@ -3143,8 +137,6 @@ { pw->text_fields &= ~field; } - - print_window_layout_render(pw); } static void print_text_cb_name(GtkWidget *widget, gpointer data) @@ -3192,317 +184,781 @@ print_text_field_set(pw, TEXT_INFO_DIMENSIONS, active); } -static void print_text_cb_points(GtkWidget *widget, gpointer data) +static void print_set_font_cb(GtkWidget *widget, gpointer data) +{ + gpointer option; + + if (g_strcmp0(data, "Image text font") == 0) + { + option = options->printer.image_font; + } + else + { + option = options->printer.page_font; + } + +#if GTK_CHECK_VERSION(3,4,0) + GtkWidget *dialog; + char *font; + PangoFontDescription *font_desc; + + dialog = gtk_font_chooser_dialog_new(data, GTK_WINDOW(gtk_widget_get_toplevel(widget))); + gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dialog), option); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_CANCEL) + { + font_desc = gtk_font_chooser_get_font_desc(GTK_FONT_CHOOSER(dialog)); + font = pango_font_description_to_string(font_desc); + g_free(option); + option = g_strdup(font); + g_free(font); + } + + gtk_widget_destroy(dialog); +#else + const char *font; + + font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(widget)); + option = g_strdup(font); +#endif +} + +static gint set_toggle(GSList *list, TextPosition pos) +{ + GtkToggleButton *current_sel; + GtkToggleButton *new_sel; + gint new_pos = - 1; + + current_sel = g_slist_nth(list, pos)->data; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(current_sel))) + { + new_pos = (pos - 1); + if (new_pos < 0) + { + new_pos = HEADER_1; + } + new_sel = g_slist_nth(list, new_pos)->data; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(new_sel), TRUE); + } + return new_pos; +} + +static void image_text_position_h1_cb(GtkWidget *widget, gpointer data) +{ + PrintWindow *pw = data; + gint new_set; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + { + new_set = set_toggle(pw->page_group, HEADER_1); + if (new_set >= 0) + { + options->printer.page_text_position = new_set; + } + options->printer.image_text_position = HEADER_1; + } +} + +static void image_text_position_h2_cb(GtkWidget *widget, gpointer data) { PrintWindow *pw = data; + gint new_set; - pw->text_points = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); - print_window_layout_render(pw); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + { + new_set = set_toggle(pw->page_group, HEADER_2); + if (new_set >= 0) + { + options->printer.page_text_position = new_set; + } + options->printer.image_text_position = HEADER_2; + } +} + +static void image_text_position_f1_cb(GtkWidget *widget, gpointer data) +{ + PrintWindow *pw = data; + gint new_set; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + { + new_set = set_toggle(pw->page_group, FOOTER_1); + if (new_set >= 0) + { + options->printer.page_text_position = new_set; + } + options->printer.image_text_position = FOOTER_1; + } +} + +static void image_text_position_f2_cb(GtkWidget *widget, gpointer data) +{ + PrintWindow *pw = data; + gint new_set; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + { + new_set = set_toggle(pw->page_group, FOOTER_2); + if (new_set >= 0) + { + options->printer.page_text_position = new_set; + } + options->printer.image_text_position = FOOTER_2; + } +} + +static void page_text_position_h1_cb(GtkWidget *widget, gpointer data) +{ + PrintWindow *pw = data; + gint new_set; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + { + new_set = set_toggle(pw->image_group, HEADER_1); + if (new_set >= 0) + { + options->printer.image_text_position = new_set; + } + options->printer.page_text_position = HEADER_1; + } +} + +static void page_text_position_h2_cb(GtkWidget *widget, gpointer data) +{ + PrintWindow *pw = data; + gint new_set; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + { + new_set = set_toggle(pw->image_group, HEADER_2); + if (new_set >= 0) + { + options->printer.image_text_position = new_set; + } + options->printer.page_text_position = HEADER_2; + } +} + +static void page_text_position_f1_cb(GtkWidget *widget, gpointer data) +{ + PrintWindow *pw = data; + gint new_set; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + { + new_set = set_toggle(pw->image_group, FOOTER_1); + if (new_set >= 0) + { + options->printer.image_text_position = new_set; + } + options->printer.page_text_position = FOOTER_1; + } +} + +static void page_text_position_f2_cb(GtkWidget *widget, gpointer data) +{ + PrintWindow *pw = data; + gint new_set; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) + { + new_set = set_toggle(pw->image_group, FOOTER_2); + if (new_set >= 0) + { + options->printer.image_text_position = new_set; + } + options->printer.page_text_position = FOOTER_2; + } } static void print_text_menu(GtkWidget *box, PrintWindow *pw) { GtkWidget *group; + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *button1; + GtkWidget *button2; + GtkWidget *image_text_button; + GtkWidget *page_text_button; + GtkWidget *subgroup; + GtkWidget *page_text_view; - group = pref_group_new(box, FALSE, _("Show"), GTK_ORIENTATION_VERTICAL); + group = pref_group_new(box, FALSE, _("Image text"), GTK_ORIENTATION_VERTICAL); + + image_text_button = pref_checkbox_new_int(group, _("Show image text"), + options->printer.show_image_text, &options->printer.show_image_text); + + subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP); + + pref_checkbox_link_sensitivity(image_text_button, subgroup); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(subgroup), hbox, FALSE, FALSE, 0); - pref_checkbox_new(group, _("Name"), (pw->text_fields & TEXT_INFO_FILENAME), + /* order is important */ + button1 = pref_radiobutton_new(hbox, NULL, "Header 1", + options->printer.image_text_position == HEADER_1, + G_CALLBACK(image_text_position_h1_cb), pw); + button1 = pref_radiobutton_new(hbox, button1, "Header 2", + options->printer.image_text_position == HEADER_2, + G_CALLBACK(image_text_position_h2_cb), pw); + button1 = pref_radiobutton_new(hbox, button1, "Footer 1", + options->printer.image_text_position == FOOTER_1, + G_CALLBACK(image_text_position_f1_cb), pw); + button1 = pref_radiobutton_new(hbox, button1, "Footer 2", + options->printer.image_text_position == FOOTER_2, + G_CALLBACK(image_text_position_f2_cb), pw); + gtk_widget_show(hbox); + pw->image_group = (gtk_radio_button_get_group(GTK_RADIO_BUTTON(button1))); + + pref_checkbox_new(subgroup, _("Name"), (pw->text_fields & TEXT_INFO_FILENAME), G_CALLBACK(print_text_cb_name), pw); - pref_checkbox_new(group, _("Path"), (pw->text_fields & TEXT_INFO_FILEPATH), + pref_checkbox_new(subgroup, _("Path"), (pw->text_fields & TEXT_INFO_FILEPATH), G_CALLBACK(print_text_cb_path), pw); - pref_checkbox_new(group, _("Date"), (pw->text_fields & TEXT_INFO_FILEDATE), + pref_checkbox_new(subgroup, _("Date"), (pw->text_fields & TEXT_INFO_FILEDATE), G_CALLBACK(print_text_cb_date), pw); - pref_checkbox_new(group, _("Size"), (pw->text_fields & TEXT_INFO_FILESIZE), + pref_checkbox_new(subgroup, _("Size"), (pw->text_fields & TEXT_INFO_FILESIZE), G_CALLBACK(print_text_cb_size), pw); - pref_checkbox_new(group, _("Dimensions"), (pw->text_fields & TEXT_INFO_DIMENSIONS), + pref_checkbox_new(subgroup, _("Dimensions"), (pw->text_fields & TEXT_INFO_DIMENSIONS), G_CALLBACK(print_text_cb_dims), pw); - group = pref_group_new(box, FALSE, _("Font"), GTK_ORIENTATION_VERTICAL); + hbox = pref_box_new(subgroup, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP); + +#if GTK_CHECK_VERSION(3,4,0) + button = pref_button_new(NULL, GTK_STOCK_SELECT_FONT, _("Font"), FALSE, + G_CALLBACK(print_set_font_cb), "Image text font"); +#else + button = gtk_font_button_new(); + gtk_font_button_set_title(GTK_FONT_BUTTON(button), "Image text Font"); + gtk_font_button_set_font_name(GTK_FONT_BUTTON(button), options->printer.image_font); + g_signal_connect(G_OBJECT(button), "font-set", + G_CALLBACK(print_set_font_cb), "Image text font"); +#endif + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + pref_spacer(group, PREF_PAD_GAP); + + group = pref_group_new(box, FALSE, _("Page text"), GTK_ORIENTATION_VERTICAL); + + page_text_button = pref_checkbox_new_int(group, _("Show page text"), + options->printer.show_page_text, &options->printer.show_page_text); + + subgroup = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP); + pref_checkbox_link_sensitivity(page_text_button, subgroup); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(subgroup), hbox, FALSE, FALSE, 0); - pref_spin_new(group, _("Size:"), _("points"), - 8.0, 100.0, 1.0, 0, pw->text_points, - G_CALLBACK(print_text_cb_points), pw); + /* order is important */ + button2 = pref_radiobutton_new(hbox, NULL, "Header 1", + options->printer.page_text_position == HEADER_1, + G_CALLBACK(page_text_position_h1_cb), pw); + button2 = pref_radiobutton_new(hbox, button2, "Header 2", + options->printer.page_text_position == HEADER_2, + G_CALLBACK(page_text_position_h2_cb), pw); + button2 = pref_radiobutton_new(hbox, button2, "Footer 1", + options->printer.page_text_position == FOOTER_1, + G_CALLBACK(page_text_position_f1_cb), pw); + button2 = pref_radiobutton_new(hbox, button2, "Footer 2", + options->printer.page_text_position == FOOTER_2, + G_CALLBACK(page_text_position_f2_cb), pw); + gtk_widget_show(hbox); + pw->page_group = (gtk_radio_button_get_group(GTK_RADIO_BUTTON(button2))); + + GtkWidget *scrolled; + + scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_set_size_request(scrolled, 50, 50); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start(GTK_BOX(subgroup), scrolled, TRUE, TRUE, 5); + gtk_widget_show(scrolled); + + page_text_view = gtk_text_view_new(); + pw->page_text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page_text_view )); + gtk_text_buffer_set_text(GTK_TEXT_BUFFER(pw->page_text), options->printer.page_text, -1); + g_object_ref(pw->page_text); + + gtk_widget_set_tooltip_markup(page_text_view, ("Text shown on each page of a single or multi-page print job")); + gtk_container_add(GTK_CONTAINER(scrolled), page_text_view); + gtk_widget_show(page_text_view); + + hbox = pref_box_new(subgroup, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP); + +#if GTK_CHECK_VERSION(3,4,0) + button = pref_button_new(NULL, GTK_STOCK_SELECT_FONT, _("Font"), FALSE, + G_CALLBACK(print_set_font_cb), "Page text font"); +#else + button = gtk_font_button_new(); + gtk_font_button_set_title(GTK_FONT_BUTTON(button), "Page text Font"); + gtk_font_button_set_font_name(GTK_FONT_BUTTON(button), options->printer.page_font); + g_signal_connect(G_OBJECT(button), "font-set", + G_CALLBACK(print_set_font_cb), "Page text font"); +#endif + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); } -/* - *----------------------------------------------------------------------------- - * print window - *----------------------------------------------------------------------------- - */ - -static void print_window_close(PrintWindow *pw) -{ - print_window_layout_render_stop(pw); - - generic_dialog_close(pw->dialog); - pw->dialog = NULL; - - print_job_close(pw, FALSE); - - file_data_unref(pw->source_fd); - filelist_free(pw->source_selection); - filelist_free(pw->source_list); - - g_free(pw->output_path); - g_free(pw->output_custom); - - g_free(pw); -} - -static void print_window_print_cb(GenericDialog *gd, gpointer data) +static gboolean paginate_cb(GtkPrintOperation *operation, + GtkPrintContext *context, + gpointer data) { PrintWindow *pw = data; - switch (pw->output) + if (pw->job_render_finished) { - case PRINT_OUTPUT_RGB_FILE: - case PRINT_OUTPUT_PS_FILE: - g_free(pw->output_path); - pw->output_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->path_entry))); - break; - case PRINT_OUTPUT_PS_CUSTOM: - g_free(pw->output_custom); - pw->output_custom = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->custom_entry))); - break; - case PRINT_OUTPUT_PS_LPR: - default: - break; + return TRUE; } - - print_window_print_start(pw); + else + { + return FALSE; + } } -static void print_window_cancel_cb(GenericDialog *gd, gpointer data) +static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context, + gint page_nr, gpointer data) { PrintWindow *pw = data; + FileData *fd; + cairo_t *cr; + gdouble context_width, context_height; + gdouble pixbuf_image_width, pixbuf_image_height; + gdouble width_offset; + gdouble height_offset; + GdkPixbuf *pixbuf; + GdkPixbuf *pixbuf_scaled; + GdkPixbuf *rotated = NULL; + PangoLayout *layout_image = NULL; + PangoLayout *layout_page = NULL; + PangoFontDescription *desc; + GString *image_text = g_string_new(NULL); + GString *page_text = g_string_new(NULL); + PangoRectangle ink_rect, logical_rect; + gdouble w, h, scale; + gdouble image_text_width, image_text_height, page_text_width, page_text_height; + gint image_y; + gint incr_y; + gdouble pango_height; + gdouble pango_image_height; + gdouble pango_page_height; + GtkTextIter start, end; + gchar *tmp; - print_window_close(pw); + fd = g_list_nth_data(pw->source_selection, page_nr); + + pixbuf = g_list_nth_data(pw->print_pixbuf_queue, page_nr); + if (fd->exif_orientation != EXIF_ORIENTATION_TOP_LEFT) + { + rotated = pixbuf_apply_orientation(pixbuf, fd->exif_orientation); + pixbuf = rotated; + } + + pixbuf_image_width = gdk_pixbuf_get_width(pixbuf); + pixbuf_image_height = gdk_pixbuf_get_height(pixbuf); + + if (options->printer.show_image_text) + { + if (pw->text_fields & TEXT_INFO_FILENAME) + { + image_text = g_string_append(image_text, g_strdup(fd->name)); + image_text = g_string_append(image_text, "\n"); + } + if (pw->text_fields & TEXT_INFO_FILEDATE) + { + image_text = g_string_append(image_text, g_strdup(text_from_time(fd->date))); + image_text = g_string_append(image_text, "\n"); + } + if (pw->text_fields & TEXT_INFO_FILESIZE) + { + image_text = g_string_append(image_text, g_strdup(text_from_size(fd->size))); + image_text = g_string_append(image_text, "\n"); + } + if (pw->text_fields & TEXT_INFO_DIMENSIONS) + { + g_string_append_printf(image_text, "%d x %d", (gint)pixbuf_image_width, + (gint)pixbuf_image_height); + image_text = g_string_append(image_text, "\n"); + } + if (pw->text_fields & TEXT_INFO_FILEPATH) + { + image_text = g_string_append(image_text, g_strdup(fd->path)); + image_text = g_string_append(image_text, "\n"); + } + if (image_text->len > 0) + { + image_text = g_string_truncate(image_text, image_text->len - 1); + } + } + + if (options->printer.show_page_text) + { + gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(pw->page_text), &start, &end); + + tmp = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(pw->page_text), &start, &end, FALSE); + page_text = g_string_append(page_text, tmp); + + g_free(tmp); + } + + cr = gtk_print_context_get_cairo_context(context); + context_width = gtk_print_context_get_width(context); + context_height = gtk_print_context_get_height(context); + + pango_image_height = 0; + pango_page_height = 0; + image_text_width = 0; + page_text_width = 0; + + if (image_text->len > 0) + { + layout_image = pango_cairo_create_layout(cr); + + pango_layout_set_text(layout_image, image_text->str, -1); + desc = pango_font_description_from_string(options->printer.image_font); + pango_layout_set_font_description(layout_image, desc); + + pango_layout_get_extents(layout_image, &ink_rect, &logical_rect); + image_text_width = ((gdouble)logical_rect.width / PANGO_SCALE) ; + image_text_height = ((gdouble)logical_rect.height / PANGO_SCALE); + + pango_layout_set_alignment(layout_image, PANGO_ALIGN_CENTER); + pango_layout_set_text(layout_image, image_text->str, -1); + + pango_image_height = image_text_height + PRINT_TEXT_PADDING * 2; + + pango_font_description_free(desc); + } + + if (page_text->len > 0) + { + layout_page = pango_cairo_create_layout(cr); + + pango_layout_set_text(layout_page, page_text->str, -1); + desc = pango_font_description_from_string(options->printer.page_font); + pango_layout_set_font_description(layout_page, desc); + + pango_layout_get_extents(layout_page, &ink_rect, &logical_rect); + page_text_width = ((gdouble)logical_rect.width / PANGO_SCALE) ; + page_text_height = ((gdouble)logical_rect.height / PANGO_SCALE); + + pango_layout_set_alignment(layout_page, PANGO_ALIGN_CENTER); + pango_layout_set_text(layout_page, page_text->str, -1); + + pango_page_height = page_text_height + PRINT_TEXT_PADDING * 2; + + pango_font_description_free(desc); + } + + pango_height = pango_image_height + pango_page_height; + + if ((context_width / pixbuf_image_width) < ((context_height - pango_height) / pixbuf_image_height)) + { + w = context_width; + scale = context_width / pixbuf_image_width; + h = pixbuf_image_height * scale; + height_offset = (context_height - (h + pango_height)) / 2; + width_offset = 0; + } + else + { + h = context_height - pango_height ; + scale = (context_height - pango_height) / pixbuf_image_height; + w = pixbuf_image_width * scale; + height_offset = 0; + width_offset = (context_width - (pixbuf_image_width * scale)) / 2; + } + + incr_y = height_offset + PRINT_TEXT_PADDING; + + if (options->printer.page_text_position == HEADER_1 && page_text->len > 0) + { + cairo_move_to(cr, (w / 2) - (page_text_width / 2) + width_offset, incr_y); + pango_cairo_show_layout(cr, layout_page); + + incr_y = incr_y + PRINT_TEXT_PADDING + pango_page_height; + } + + if (options->printer.image_text_position == HEADER_1 && image_text->len > 0) + { + cairo_move_to(cr, (w / 2) - (image_text_width / 2) + width_offset, incr_y); + pango_cairo_show_layout(cr, layout_image); + + incr_y = incr_y + PRINT_TEXT_PADDING + pango_image_height; + } + + if (options->printer.page_text_position == HEADER_2 && page_text->len > 0) + { + cairo_move_to(cr, (w / 2) - (page_text_width / 2) + width_offset, incr_y); + pango_cairo_show_layout(cr, layout_page); + + incr_y = incr_y + PRINT_TEXT_PADDING + pango_page_height; + } + + if (options->printer.image_text_position == HEADER_2 && image_text->len > 0) + { + cairo_move_to(cr, (w / 2) - (image_text_width / 2) + width_offset, incr_y); + pango_cairo_show_layout(cr, layout_image); + + incr_y = incr_y + PRINT_TEXT_PADDING + pango_image_height; + } + + image_y = incr_y; + incr_y = incr_y + h + PRINT_TEXT_PADDING; + + if (options->printer.page_text_position == FOOTER_1 && page_text->len > 0) + { + cairo_move_to(cr, (w / 2) - (page_text_width / 2) + width_offset, incr_y); + pango_cairo_show_layout(cr, layout_page); + + incr_y = incr_y + PRINT_TEXT_PADDING + pango_page_height; + } + + if (options->printer.image_text_position == FOOTER_1 && image_text->len > 0) + { + cairo_move_to(cr, (w / 2) - (image_text_width / 2) + width_offset, incr_y); + pango_cairo_show_layout(cr, layout_image); + + incr_y = incr_y + PRINT_TEXT_PADDING + pango_image_height; + } + + if (options->printer.page_text_position == FOOTER_2 && page_text->len > 0) + { + cairo_move_to(cr, (w / 2) - (page_text_width / 2) + width_offset, incr_y); + pango_cairo_show_layout(cr, layout_page); + + incr_y = incr_y + PRINT_TEXT_PADDING + pango_page_height; + } + + if (options->printer.image_text_position == FOOTER_2 && image_text->len > 0) + { + cairo_move_to(cr, (w / 2) - (image_text_width / 2) + width_offset, incr_y); + pango_cairo_show_layout(cr, layout_image); + } + + pixbuf_scaled = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h); + gdk_pixbuf_scale(pixbuf, pixbuf_scaled, 0, 0, w, h, 0, 0, scale, scale, PRINT_MAX_INTERP); + + cairo_rectangle(cr, width_offset, image_y, w, h); + + gdk_cairo_set_source_pixbuf(cr, pixbuf_scaled, width_offset, image_y); + + cairo_fill(cr); + + if (image_text->len > 0) + { + g_object_unref(layout_image); + g_string_free(image_text, TRUE); + } + if (page_text->len > 0) + { + g_object_unref(layout_page); + g_string_free(page_text, TRUE); + } + + g_object_unref(pixbuf_scaled); + if (rotated) g_object_unref(rotated); + + return; } -static gint print_pref_int(const gchar *key, gint fallback) +static void begin_print(GtkPrintOperation *operation, + GtkPrintContext *context, + gpointer user_data) +{ + PrintWindow *pw = user_data; + gint page_count; + + page_count = print_layout_page_count(pw); + gtk_print_operation_set_n_pages (operation, page_count); + + print_job_render_image(pw); +} + + +GObject *option_tab_cb(GtkPrintOperation *operation, gpointer user_data) { - gint value; + PrintWindow *pw = user_data; + + return G_OBJECT(pw->vbox); +} - if (pref_list_int_get(PRINT_PREF_GROUP, key, &value)) return value; - return fallback; +static void print_pref_store(PrintWindow *pw) +{ + gchar *tmp; + GtkTextIter start, end; + + options->printer.text_fields = pw->text_fields; + + gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(pw->page_text), &start, &end); + tmp = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(pw->page_text), &start, &end, FALSE); + g_free(options->printer.page_text); + options->printer.page_text = g_strdup(tmp); + g_free(tmp); } -static gdouble print_pref_double(const gchar *key, gdouble fallback) +static void end_print_cb(GtkPrintOperation *operation, + GtkPrintContext *context, gpointer data) { - gdouble value; + PrintWindow *pw = data; + GList *work; + GdkPixbuf *pixbuf; + gchar *path; + GtkPrintSettings *print_settings; + GtkPageSetup *page_setup; + GError *error = NULL; + + print_settings = gtk_print_operation_get_print_settings(operation); + path = g_build_filename(get_rc_dir(), PRINT_SETTINGS, NULL); + + gtk_print_settings_to_file(print_settings, path, &error); + if (error) + { + log_printf("Error: Print settings save failed:\n%s", error->message); + g_error_free(error); + error = NULL; + } + g_free(path); + g_object_unref(print_settings); + + page_setup = gtk_print_operation_get_default_page_setup(operation); + path = g_build_filename(get_rc_dir(), PAGE_SETUP, NULL); - if (pref_list_double_get(PRINT_PREF_GROUP, key, &value)) return value; - return fallback; + gtk_page_setup_to_file(page_setup, path, &error); + if (error) + { + log_printf("Error: Print page setup save failed:\n%s", error->message); + g_error_free(error); + error = NULL; + } + g_free(path); + g_object_unref(page_setup); + + print_pref_store(pw); + + work = pw->print_pixbuf_queue; + while (work) + { + pixbuf = work->data; + if (pixbuf) + { + g_object_unref(pixbuf); + } + work = work->next; + } + g_list_free(pw->print_pixbuf_queue); + g_object_unref(pw->page_text); + g_free(pw); } void print_window_new(FileData *fd, GList *selection, GList *list, GtkWidget *parent) { PrintWindow *pw; - GdkGeometry geometry; - GtkWidget *main_box; GtkWidget *vbox; - GtkWidget *label; - GtkWidget *combo; - GtkWidget *box; - GtkWidget *table; + GtkPrintOperation *operation; + GtkPageSetup *page_setup; + gchar *uri; + const gchar *dir; + GError *error = NULL; + gchar *path; + GtkPrintSettings *settings; pw = g_new0(PrintWindow, 1); - pw->source_fd = file_data_ref(fd); pw->source_selection = file_data_process_groups_in_selection(selection, FALSE, NULL); - pw->source_list = list; - - pw->source = print_pref_int(PRINT_PREF_SOURCE, PRINT_SOURCE_SELECTION); - pw->layout = print_pref_int(PRINT_PREF_LAYOUT, PRINT_LAYOUT_IMAGE); - - pw->image_scale = print_pref_double(PRINT_PREF_IMAGE_SCALE, 100.0); - - pw->output = print_pref_int(PRINT_PREF_OUTPUT, PRINT_OUTPUT_PS_LPR); - pw->output_format = print_pref_int(PRINT_PREF_FORMAT, PRINT_FILE_JPG_NORMAL); - - pw->max_dpi = print_pref_double(PRINT_PREF_DPI, PRINT_PS_DPI_DEFAULT); - - pw->paper_units = print_pref_int(PRINT_PREF_UNITS, paper_unit_default()); - pw->paper_size = print_pref_int(PRINT_PREF_SIZE, 1); - if (pw->paper_size == 0 || - !print_paper_size_lookup(pw->paper_size, &pw->paper_width, &pw->paper_height)) - { - pw->paper_width = print_pref_double(PRINT_PREF_CUSTOM_WIDTH, 360.0); - pw->paper_height = print_pref_double(PRINT_PREF_CUSTOM_HEIGHT, 720.0); - } - pw->paper_orientation = print_pref_int(PRINT_PREF_ORIENTATION, PAPER_ORIENTATION_PORTRAIT); - - pw->margin_left = print_pref_double(PRINT_PREF_MARGIN_LEFT, PRINT_MARGIN_DEFAULT); - pw->margin_right = print_pref_double(PRINT_PREF_MARGIN_RIGHT, PRINT_MARGIN_DEFAULT); - pw->margin_top = print_pref_double(PRINT_PREF_MARGIN_TOP, PRINT_MARGIN_DEFAULT); - pw->margin_bottom = print_pref_double(PRINT_PREF_MARGIN_BOTTOM, PRINT_MARGIN_DEFAULT); + pw->text_fields = options->printer.text_fields; - pw->proof_width = print_pref_double(PRINT_PREF_PROOF_WIDTH, PRINT_PROOF_DEFAULT_SIZE); - pw->proof_height = print_pref_double(PRINT_PREF_PROOF_HEIGHT, PRINT_PROOF_DEFAULT_SIZE); - - pw->text_fields = print_pref_int(PRINT_PREF_TEXT, TEXT_INFO_FILENAME); - pw->text_points = print_pref_int(PRINT_PREF_TEXTSIZE, 10); - pw->text_r = print_pref_int(PRINT_PREF_TEXTCOLOR_R, 0); - pw->text_g = print_pref_int(PRINT_PREF_TEXTCOLOR_G, 0); - pw->text_b = print_pref_int(PRINT_PREF_TEXTCOLOR_B, 0); - - pw->save_settings = print_pref_int(PRINT_PREF_SAVE, TRUE); - - pw->dialog = file_util_gen_dlg(_("Print"), "print_dialog", - parent, FALSE, - print_window_cancel_cb, pw); - - geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE; - geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE; - geometry.base_width = PRINT_DLG_WIDTH; - geometry.base_height = PRINT_DLG_HEIGHT; - gtk_window_set_geometry_hints(GTK_WINDOW(pw->dialog->dialog), NULL, &geometry, - GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE); - - pw->print_button = generic_dialog_add_button(pw->dialog, GTK_STOCK_PRINT, NULL, print_window_print_cb, TRUE); - - main_box = pref_box_new(pw->dialog->vbox, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_GAP); - - pw->notebook = gtk_notebook_new(); - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(pw->notebook), GTK_POS_TOP); - gtk_box_pack_start(GTK_BOX(main_box), pw->notebook, FALSE, FALSE, 0); - - /* layout tab */ + if (print_layout_page_count(pw) == 0) + { + return; + } vbox = gtk_vbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER); gtk_widget_show(vbox); - label = gtk_label_new(_("Layout")); - gtk_notebook_append_page(GTK_NOTEBOOK(pw->notebook), vbox, label); - - print_misc_menu(vbox, pw->source, _("Source"), "print_source", - PRINT_SOURCE_COUNT, print_source_text, - G_CALLBACK(print_source_select_cb), pw); - - box = print_misc_menu(vbox, pw->layout, _("Layout"), "print_layout", - PRINT_LAYOUT_COUNT, print_layout_text, - G_CALLBACK(print_layout_select_cb), pw); - - pref_spacer(box, PREF_PAD_GROUP); - - table = pref_table_new(box, 2, 2, FALSE, FALSE); - - pw->image_scale_spin = pref_table_spin(table, 0, 0, _("Image size:"), "%", - 5.0, 100.0, 1.0, 0, pw->image_scale, - G_CALLBACK(print_image_scale_cb), pw); - - label = pref_table_label(table, 0, 1, _("Proof size:"), 1.0); - pw->proof_group = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL); - pref_link_sensitivity(label, pw->proof_group); - - pw->proof_width_spin = pref_spin_new(pw->proof_group, NULL, NULL, - 0.0, 50.0, 0.1, 3, 0.0, - G_CALLBACK(print_proof_size_cb), pw); - pw->proof_height_spin = pref_spin_new(pw->proof_group, "x", NULL, - 0.0, 50.0, 0.1, 3, 0.0, - G_CALLBACK(print_proof_size_cb), pw); - - /* text tab */ - - vbox = gtk_vbox_new(FALSE, 0); - gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER); - gtk_widget_show(vbox); - label = gtk_label_new(_("Text")); - gtk_notebook_append_page(GTK_NOTEBOOK(pw->notebook), vbox, label); print_text_menu(vbox, pw); - - /* paper tab */ - - vbox = gtk_vbox_new(FALSE, 0); - gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER); - gtk_widget_show(vbox); - label = gtk_label_new(_("Paper")); - gtk_notebook_append_page(GTK_NOTEBOOK(pw->notebook), vbox, label); - - table = pref_table_new(vbox, 2, 4, FALSE, FALSE); + pw->vbox = vbox; - print_paper_menu(table, 0, 0, pw->paper_size, G_CALLBACK(print_paper_select_cb), pw); + pw->print_pixbuf_queue = NULL; + pw->job_render_finished = FALSE; + pw->job_page = 0; - label = pref_table_label(table, 0, 1, (_("Size:")), 1.0); - box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL); - pw->paper_width_spin = pref_spin_new(box, NULL, NULL, - 1.0, 10000.0, 1.0, 2, 66, - G_CALLBACK(print_paper_size_cb), pw); - pw->paper_height_spin = pref_spin_new(box, "x", NULL, - 1.0, 10000.0, 1.0, 2, 66, - G_CALLBACK(print_paper_size_cb), pw); - pref_link_sensitivity(label, pw->paper_width_spin); + operation = gtk_print_operation_new(); + settings = gtk_print_settings_new(); - pw->paper_units_menu = print_paper_units_menu(table, 0, 2, pw->paper_units, - G_CALLBACK(print_paper_units_cb), pw); - - print_paper_orientation_menu(table, 0, 3, pw->paper_orientation, - G_CALLBACK(print_paper_orientation_cb), pw); + gtk_print_operation_set_custom_tab_label(operation, "Options"); + gtk_print_operation_set_use_full_page(operation, TRUE); + gtk_print_operation_set_unit(operation, GTK_UNIT_POINTS); + gtk_print_operation_set_embed_page_setup(operation, TRUE); + gtk_print_operation_set_allow_async (operation, TRUE); + dir = g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS); + if (dir == NULL) + { + dir = g_get_home_dir(); + } - box = pref_group_new(vbox, FALSE, _("Margins"), GTK_ORIENTATION_VERTICAL); - table = pref_table_new(box, 4, 2, FALSE, FALSE); - pw->margin_left_spin = pref_table_spin(table, 0, 0, _("Left:"), NULL, - 0.0, 50.0, 0.1, 3, 0.0, - G_CALLBACK(print_paper_margin_cb), pw); - pw->margin_right_spin = pref_table_spin(table, 2, 0, _("Right:"), NULL, - 0.0, 50.0, 0.1, 3, 0.0, - G_CALLBACK(print_paper_margin_cb), pw); - pw->margin_top_spin = pref_table_spin(table, 0, 1, _("Top:"), NULL, - 0.0, 50.0, 0.1, 3, 0.0, - G_CALLBACK(print_paper_margin_cb), pw); - pw->margin_bottom_spin = pref_table_spin(table, 2, 1, _("Bottom:"), NULL, - 0.0, 50.0, 0.1, 3, 0.0, - G_CALLBACK(print_paper_margin_cb), pw); + uri = g_build_filename("file:/", dir, "geeqie-file.pdf", NULL); + gtk_print_settings_set(settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri); + g_free(uri); - /* printer tab */ + path = g_build_filename(get_rc_dir(), PRINT_SETTINGS, NULL); + gtk_print_settings_load_file(settings, path, &error); + if (error) + { + log_printf("Error: Printer settings load failed:\n%s", error->message); + g_error_free(error); + error = NULL; + } + gtk_print_operation_set_print_settings(operation, settings); + g_free(path); - vbox = gtk_vbox_new(FALSE, 0); - gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_BORDER); - gtk_widget_show(vbox); - label = gtk_label_new(_("Printer")); - gtk_notebook_append_page(GTK_NOTEBOOK(pw->notebook), vbox, label); - - table = pref_table_new(vbox, 2, 5, FALSE, FALSE); - print_output_menu(table, 0, 0, pw->output, G_CALLBACK(print_output_cb), pw); + page_setup = gtk_page_setup_new(); + path = g_build_filename(get_rc_dir(), PAGE_SETUP, NULL); + gtk_page_setup_load_file(page_setup, path, &error); + if (error) + { + log_printf("Error: Print page setup load failed:\n%s", error->message); + g_error_free(error); + error = NULL; + } + gtk_print_operation_set_default_page_setup(operation, page_setup); + g_free(path); - label = pref_table_label(table, 0, 1, _("Custom printer:"), 1.0); - combo = history_combo_new(&pw->custom_entry, NULL, "print_custom", -1); - print_custom_entry_set(pw, combo); - gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 1, 2, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_widget_show(combo); - - pref_link_sensitivity(label, combo); + g_signal_connect (G_OBJECT (operation), "begin-print", + G_CALLBACK (begin_print), pw); + g_signal_connect (G_OBJECT (operation), "draw-page", + G_CALLBACK (draw_page), pw); + g_signal_connect (G_OBJECT (operation), "end-print", + G_CALLBACK (end_print_cb), pw); + g_signal_connect (G_OBJECT (operation), "create-custom-widget", + G_CALLBACK (option_tab_cb), pw); + g_signal_connect (G_OBJECT (operation), "paginate", + G_CALLBACK (paginate_cb), pw); - label = pref_table_label(table, 0, 2, _("File:"), 1.0); - combo = tab_completion_new_with_history(&pw->path_entry, NULL, "print_path", -1, NULL, pw); - tab_completion_add_select_button(pw->path_entry, NULL, FALSE); - gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 2, 3, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_widget_show(combo); + gtk_print_operation_set_n_pages(operation, print_layout_page_count(pw)); - pref_link_sensitivity(label, combo); + gtk_print_operation_run(operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + GTK_WINDOW (parent), &error); - label = pref_table_label(table, 0, 3, _("File format:"), 1.0); - pw->path_format_menu = print_output_format_menu(table, 1, 3, pw->output_format, - G_CALLBACK(print_output_format_cb), pw); - pref_link_sensitivity(label, pw->path_format_menu); + if (error) + { + GtkWidget *dialog; - label = pref_table_label(table, 0, 4, _("DPI:"), 1.0); - pw->max_dpi_menu = print_output_dpi_menu(table, 1, 4, pw->max_dpi, - G_CALLBACK(print_output_dpi_cb), pw); - pref_link_sensitivity(label, pw->max_dpi_menu); - - print_output_set(pw, pw->output); + dialog = gtk_message_dialog_new(GTK_WINDOW (parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", error->message); + g_error_free (error); - vbox = print_window_layout_setup(pw, main_box); - pref_checkbox_new_int(vbox, _("Remember print settings"), pw->save_settings, &pw->save_settings); + g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL); - print_window_layout_sync_layout(pw); - print_window_layout_sync_paper(pw); + gtk_widget_show (dialog); + } - gtk_widget_show(pw->notebook); - gtk_widget_show(pw->dialog->dialog); + g_object_unref(page_setup); + g_object_unref(settings); } /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff -r 5705ded05a19 -r 7e769ff09997 src/rcfile.c --- a/src/rcfile.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/rcfile.c Tue Feb 05 21:18:18 2019 +0100 @@ -308,6 +308,7 @@ WRITE_NL(); WRITE_BOOL(*options, show_icon_names); WRITE_NL(); WRITE_BOOL(*options, show_star_rating); WRITE_NL(); WRITE_BOOL(*options, show_guidelines); + WRITE_NL(); WRITE_BOOL(*options, show_predefined_keyword_tree); WRITE_SEPARATOR(); WRITE_NL(); WRITE_BOOL(*options, tree_descend_subdirs); @@ -493,6 +494,16 @@ WRITE_NL(); WRITE_INT(*options, cp_mv_rn.auto_padding); WRITE_NL(); WRITE_CHAR(*options, cp_mv_rn.auto_end); WRITE_NL(); WRITE_INT(*options, cp_mv_rn.formatted_start); + + /* printer */ + WRITE_NL(); WRITE_CHAR(*options, printer.image_font); + WRITE_NL(); WRITE_CHAR(*options, printer.page_font); + WRITE_NL(); WRITE_CHAR(*options, printer.page_text); + WRITE_NL(); WRITE_INT(*options, printer.text_fields); + WRITE_NL(); WRITE_INT(*options, printer.image_text_position); + WRITE_NL(); WRITE_INT(*options, printer.page_text_position); + WRITE_NL(); WRITE_BOOL(*options, printer.show_image_text); + WRITE_NL(); WRITE_BOOL(*options, printer.show_page_text); } static void write_color_profile(GString *outstr, gint indent) @@ -647,6 +658,7 @@ if (READ_BOOL(*options, show_icon_names)) continue; if (READ_BOOL(*options, show_star_rating)) continue; if (READ_BOOL(*options, show_guidelines)) continue; + if (READ_BOOL(*options, show_predefined_keyword_tree)) continue; if (READ_BOOL(*options, tree_descend_subdirs)) continue; if (READ_BOOL(*options, view_dir_list_single_click_enter)) continue; @@ -827,6 +839,16 @@ if (READ_CHAR(*options, cp_mv_rn.auto_end)) continue; if (READ_INT(*options, cp_mv_rn.formatted_start)) continue; + /* printer */ + if (READ_CHAR(*options, printer.image_font)) continue; + if (READ_CHAR(*options, printer.page_font)) continue; + if (READ_CHAR(*options, printer.page_text)) continue; + if (READ_INT(*options, printer.text_fields)) continue; + if (READ_INT(*options, printer.image_text_position)) continue; + if (READ_INT(*options, printer.page_text_position)) continue; + if (READ_BOOL(*options, printer.show_image_text)) continue; + if (READ_BOOL(*options, printer.show_page_text)) continue; + /* Dummy options */ if (READ_DUMMY(*options, image.dither_quality, "deprecated since 2012-08-13")) continue; diff -r 5705ded05a19 -r 7e769ff09997 src/remote.c --- a/src/remote.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/remote.c Tue Feb 05 21:18:18 2019 +0100 @@ -697,6 +697,7 @@ LayoutWindow *lw = NULL; gint x1, y1, x2, y2; + if (!options->draw_rectangle) return; if (!layout_valid(&lw_id)) return; pr = (PixbufRenderer*)lw_id->image->pr; diff -r 5705ded05a19 -r 7e769ff09997 src/toolbar.c --- a/src/toolbar.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/toolbar.c Tue Feb 05 21:18:18 2019 +0100 @@ -94,6 +94,7 @@ {"SelectAll", N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL}, {"SelectNone", N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE}, {"SelectInvert", N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT}, + {"ShowFileFilter", N_("Show file filter"), PIXBUF_INLINE_ICON_FILE_FILTER}, {"RectangularSelection", N_("Select rectangle"), PIXBUF_INLINE_ICON_SELECT_RECTANGLE}, {"Print", N_("Print"), GTK_STOCK_PRINT}, {"Preferences", N_("Preferences"), GTK_STOCK_PREFERENCES}, diff -r 5705ded05a19 -r 7e769ff09997 src/typedefs.h --- a/src/typedefs.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/typedefs.h Tue Feb 05 21:18:18 2019 +0100 @@ -619,6 +619,7 @@ gboolean show_thumbnails; gboolean show_marks; + gboolean show_file_filter; gboolean show_directory_date; gboolean show_info_pixel; @@ -862,6 +863,13 @@ GtkWidget *filter; GtkWidget *filter_check[FILEDATA_MARKS_SIZE]; + struct { + GtkWidget *combo; + GtkWidget *frame; + gint count; + gint last_selected; + } file_filter; + FileData *dir_fd; GList *list; diff -r 5705ded05a19 -r 7e769ff09997 src/ui_misc.c --- a/src/ui_misc.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/ui_misc.c Tue Feb 05 21:18:18 2019 +0100 @@ -1732,4 +1732,34 @@ } +gchar *text_widget_text_pull_selected(GtkWidget *text_widget) +{ + if (GTK_IS_TEXT_VIEW(text_widget)) + { + GtkTextBuffer *buffer; + GtkTextIter start, end; + GtkTextIter selection_start, selection_end; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_widget)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + + if (gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) + { + gtk_text_iter_set_line_offset(&start, 0); + gtk_text_iter_forward_to_line_end(&end); + } + + return gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + } + else if (GTK_IS_ENTRY(text_widget)) + { + return g_strdup(gtk_entry_get_text(GTK_ENTRY(text_widget))); + } + else + { + return NULL; + } + +} + /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff -r 5705ded05a19 -r 7e769ff09997 src/ui_misc.h --- a/src/ui_misc.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/ui_misc.h Tue Feb 05 21:18:18 2019 +0100 @@ -199,6 +199,7 @@ GCallback func, gpointer data); gchar *text_widget_text_pull(GtkWidget *text_widget); +gchar *text_widget_text_pull_selected(GtkWidget *text_widget); #endif /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff -r 5705ded05a19 -r 7e769ff09997 src/view_file.h --- a/src/view_file.h Tue Feb 05 21:18:18 2019 +0100 +++ b/src/view_file.h Tue Feb 05 21:18:18 2019 +0100 @@ -54,7 +54,6 @@ guint vf_count(ViewFile *vf, gint64 *bytes); GList *vf_get_list(ViewFile *vf); -gint vf_index_is_selected(ViewFile *vf, gint row); guint vf_selection_count(ViewFile *vf, gint64 *bytes); GList *vf_selection_get_list(ViewFile *vf); GList *vf_selection_get_list_by_index(ViewFile *vf); @@ -74,5 +73,7 @@ void vf_thumb_cleanup(ViewFile *vf); void vf_thumb_stop(ViewFile *vf); void vf_read_metadata_in_idle(ViewFile *vf); +void vf_file_filter_set(ViewFile *vf, gboolean enable); +GRegex *vf_file_filter_get_filter(ViewFile *vf); #endif /* VIEW_FILE_H */ /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff -r 5705ded05a19 -r 7e769ff09997 src/view_file/view_file.c --- a/src/view_file/view_file.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/view_file/view_file.c Tue Feb 05 21:18:18 2019 +0100 @@ -25,6 +25,7 @@ #include "collect.h" #include "collect-table.h" #include "editors.h" +#include "history_list.h" #include "layout.h" #include "menu.h" #include "thumb.h" @@ -169,16 +170,6 @@ *----------------------------------------------------------------------------- */ -gboolean vf_index_is_selected(ViewFile *vf, gint row) -{ - switch (vf->type) - { - case FILEVIEW_LIST: return vflist_index_is_selected(vf, row); - case FILEVIEW_ICON: return vficon_index_is_selected(vf, row); - } -} - - guint vf_selection_count(ViewFile *vf, gint64 *bytes) { switch (vf->type) @@ -844,6 +835,81 @@ return FALSE; } +static void vf_file_filter_save_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + gchar *entry_text; + gchar *remove_text = NULL; + gchar *index_text = NULL; + gboolean text_found = FALSE; + gint i; + + entry_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(vf->file_filter.combo))))); + + if (entry_text[0] == '\0' && vf->file_filter.last_selected >= 0) + { + gtk_combo_box_set_active(GTK_COMBO_BOX(vf->file_filter.combo), vf->file_filter.last_selected); + remove_text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(vf->file_filter.combo)); + history_list_item_remove("file_filter", remove_text); + gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(vf->file_filter.combo), vf->file_filter.last_selected); + g_free(remove_text); + + gtk_combo_box_set_active(GTK_COMBO_BOX(vf->file_filter.combo), -1); + vf->file_filter.last_selected = - 1; + gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(vf->file_filter.combo))), ""); + vf->file_filter.count--; + } + else + { + if (entry_text[0] != '\0') + { + for (i = 0; i < vf->file_filter.count; i++) + { + if (index_text) + { + g_free(index_text); + } + gtk_combo_box_set_active(GTK_COMBO_BOX(vf->file_filter.combo), i); + index_text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(vf->file_filter.combo)); + + if (g_strcmp0(index_text, entry_text) == 0) + { + text_found = TRUE; + break; + } + } + + g_free(index_text); + if (!text_found) + { + history_list_add_to_key("file_filter", entry_text, 10); + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(vf->file_filter.combo), entry_text); + vf->file_filter.count++; + gtk_combo_box_set_active(GTK_COMBO_BOX(vf->file_filter.combo), vf->file_filter.count - 1); + } + } + } + vf_refresh(vf); + + g_free(entry_text); +} + +static void vf_file_filter_cb(GtkWidget *widget, gpointer data) +{ + ViewFile *vf = data; + + vf_refresh(vf); +} + +static gboolean vf_file_filter_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) +{ + ViewFile *vf = data; + vf->file_filter.last_selected = gtk_combo_box_get_active(GTK_COMBO_BOX(vf->file_filter.combo)); + + gtk_widget_grab_focus(widget); + + return TRUE; +} static GtkWidget *vf_marks_filter_init(ViewFile *vf) { @@ -870,6 +936,63 @@ return frame; } +void vf_file_filter_set(ViewFile *vf, gboolean enable) +{ + if (enable) + { + gtk_widget_show(vf->file_filter.combo); + gtk_widget_show(vf->file_filter.frame); + } + else + { + gtk_widget_hide(vf->file_filter.combo); + gtk_widget_hide(vf->file_filter.frame); + } + + vf_refresh(vf); +} + +static GtkWidget *vf_file_filter_init(ViewFile *vf) +{ + GtkWidget *frame = gtk_frame_new(NULL); + GtkWidget *hbox = gtk_hbox_new(FALSE, 0); + GList *work; + gint n = 0; + GtkWidget *combo_entry; + + vf->file_filter.combo = gtk_combo_box_text_new_with_entry(); + combo_entry = gtk_bin_get_child(GTK_BIN(vf->file_filter.combo)); + gtk_widget_show(gtk_bin_get_child(GTK_BIN(vf->file_filter.combo))); + gtk_widget_show((GTK_WIDGET(vf->file_filter.combo))); + gtk_widget_set_tooltip_text(GTK_WIDGET(vf->file_filter.combo), "Use regular expressions"); + + work = history_list_get_by_key("file_filter"); + while (work) + { + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(vf->file_filter.combo), (gchar *)work->data); + work = work->next; + n++; + vf->file_filter.count = n; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(vf->file_filter.combo), 0); + + g_signal_connect(G_OBJECT(combo_entry), "activate", + G_CALLBACK(vf_file_filter_save_cb), vf); + + g_signal_connect(G_OBJECT(vf->file_filter.combo), "changed", + G_CALLBACK(vf_file_filter_cb), vf); + + g_signal_connect(G_OBJECT(combo_entry), "button_press_event", + G_CALLBACK(vf_file_filter_press_cb), vf); + + gtk_box_pack_start(GTK_BOX(hbox), vf->file_filter.combo, FALSE, FALSE, 0); + gtk_widget_show(vf->file_filter.combo); + gtk_container_add(GTK_CONTAINER(frame), hbox); + gtk_widget_show(hbox); + + return frame; +} + void vf_mark_filter_toggle(ViewFile *vf, gint mark) { gint n = mark - 1; @@ -894,9 +1017,11 @@ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); vf->filter = vf_marks_filter_init(vf); + vf->file_filter.frame = vf_file_filter_init(vf); vf->widget = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vf->widget), vf->filter, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vf->widget), vf->file_filter.frame, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vf->widget), vf->scrolled, TRUE, TRUE, 0); gtk_widget_show(vf->scrolled); @@ -1171,6 +1296,39 @@ return ret; } +GRegex *vf_file_filter_get_filter(ViewFile *vf) +{ + GRegex *ret = NULL; + GError *error = NULL; + gchar *file_filter_text = NULL; + + if (!gtk_widget_get_visible(vf->file_filter.combo)) + { + return g_regex_new("", 0, 0, NULL); + } + + file_filter_text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(vf->file_filter.combo)); + + if (file_filter_text[0] != '\0') + { + ret = g_regex_new(file_filter_text, 0, 0, &error); + if (error) + { + log_printf("Error: could not compile regular expression %s\n%s\n", file_filter_text, error->message); + g_error_free(error); + error = NULL; + ret = g_regex_new("", 0, 0, NULL); + } + g_free(file_filter_text); + } + else + { + ret = g_regex_new("", 0, 0, NULL); + } + + return ret; +} + void vf_set_layout(ViewFile *vf, LayoutWindow *layout) { vf->layout = layout; diff -r 5705ded05a19 -r 7e769ff09997 src/view_file/view_file_icon.c --- a/src/view_file/view_file_icon.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/view_file/view_file_icon.c Tue Feb 05 21:18:18 2019 +0100 @@ -1842,6 +1842,8 @@ { ret = filelist_read(vf->dir_fd, &new_filelist, NULL); new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf)); + new_filelist = g_list_first(new_filelist); + new_filelist = file_data_filter_file_filter_list(new_filelist, vf_file_filter_get_filter(vf)); } vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */ diff -r 5705ded05a19 -r 7e769ff09997 src/view_file/view_file_list.c --- a/src/view_file/view_file_list.c Tue Feb 05 21:18:18 2019 +0100 +++ b/src/view_file/view_file_list.c Tue Feb 05 21:18:18 2019 +0100 @@ -1801,6 +1801,8 @@ } vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf)); + vf->list = g_list_first(vf->list); + vf->list = file_data_filter_file_filter_list(vf->list, vf_file_filter_get_filter(vf)); file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); DEBUG_1("%s vflist_refresh: sort", get_exec_time()); diff -r 5705ded05a19 -r 7e769ff09997 web/geeqie-install-debian.sh --- a/web/geeqie-install-debian.sh Tue Feb 05 21:18:18 2019 +0100 +++ b/web/geeqie-install-debian.sh Tue Feb 05 21:18:18 2019 +0100 @@ -1,5 +1,5 @@ #!/bin/bash -version="2018-08-06" +version="2018-08-20" description=$' Geeqie is an image viewer. This script will download, compile, and install Geeqie on Debian-based systems. @@ -14,6 +14,7 @@ -c --commit=ID Checkout and compile commit ID -t --tag=TAG Checkout and compile TAG (e.g. v1.4 or v1.3) -b --back=N Checkout commit -N (e.g. "-b 1" for last-but-one commit) +-d --debug=yes Compile with debug output ' # Essential for compiling @@ -23,6 +24,7 @@ "autoconf" "libglib2.0-0" "intltool" +"libtool" ) # Optional for both GTK2 and GTK3 @@ -151,190 +153,190 @@ install_essential() { -arraylength=${#essential_array[@]} -for (( i=0; i<${arraylength}; i=i+1 )); -do - package_query ${essential_array[$i]} - if [ $? != 0 ] - then - package_install ${essential_array[$i]} - fi -done + arraylength=${#essential_array[@]} + for (( i=0; i<${arraylength}; i=i+1 )); + do + package_query ${essential_array[$i]} + if [ $? != 0 ] + then + package_install ${essential_array[$i]} + fi + done -if [[ $1 == "GTK3" ]] -then - package_query "libgtk-3-dev" - if [ $? != 0 ] + if [[ $1 == "GTK3" ]] then - package_install libgtk-3-dev + package_query "libgtk-3-dev" + if [ $? != 0 ] + then + package_install libgtk-3-dev + fi + else + package_query "libgtk2.0-dev" + if [ $? != 0 ] + then + package_install libgtk2.0-dev + fi fi -else - package_query "libgtk2.0-dev" - if [ $? != 0 ] - then - package_install libgtk2.0-dev - fi -fi } install_options() { -if [ -n "$options" ] -then - OLDIFS=$IFS - IFS='|' - set $options - while [ $# -gt 0 ]; - do - package_install $1 - shift - done - IFS=$OLDIFS -fi + if [ -n "$options" ] + then + OLDIFS=$IFS + IFS='|' + set $options + while [ $# -gt 0 ]; + do + package_install $1 + shift + done + IFS=$OLDIFS + fi } install_webp() { -rm -rf webp-pixbuf-loader-master -package_install libglib2.0-dev libgdk-pixbuf2.0-dev libwebp-dev python-minimal -wget https://github.com/aruiz/webp-pixbuf-loader/archive/master.zip -unzip master.zip -cd webp-pixbuf-loader-master -./waf configure -./waf build -sudo --askpass ./waf install -sudo --askpass gdk-pixbuf-query-loaders --update-cache -cd - -rm -rf webp-pixbuf-loader-master -rm master.zip + rm -rf webp-pixbuf-loader-master + package_install libglib2.0-dev libgdk-pixbuf2.0-dev libwebp-dev python-minimal + wget https://github.com/aruiz/webp-pixbuf-loader/archive/master.zip + unzip master.zip + cd webp-pixbuf-loader-master + ./waf configure + ./waf build + sudo --askpass ./waf install + sudo --askpass gdk-pixbuf-query-loaders --update-cache + cd - + rm -rf webp-pixbuf-loader-master + rm master.zip } install_psd() { -rm -rf gdk-pixbuf-psd -git clone https://github.com/and-rom/gdk-pixbuf-psd.git -cd gdk-pixbuf-psd -./autogen.sh -make -sudo --askpass make install -sudo --askpass gdk-pixbuf-query-loaders --update-cache -cd - -rm -rf gdk-pixbuf-psd + rm -rf gdk-pixbuf-psd + git clone https://github.com/and-rom/gdk-pixbuf-psd.git + cd gdk-pixbuf-psd + ./autogen.sh + make + sudo --askpass make install + sudo --askpass gdk-pixbuf-query-loaders --update-cache + cd - + rm -rf gdk-pixbuf-psd } install_xcf() { -rm -rf xcf-pixbuf-loader -package_install libbz2-dev -git clone https://github.com/StephaneDelcroix/xcf-pixbuf-loader.git -cd xcf-pixbuf-loader -./autogen.sh -make + rm -rf xcf-pixbuf-loader + package_install libbz2-dev + git clone https://github.com/StephaneDelcroix/xcf-pixbuf-loader.git + cd xcf-pixbuf-loader + ./autogen.sh + make -# There must be a better way... -loader_locn=$(gdk-pixbuf-query-loaders | grep "LoaderDir" | tr -d '#[:space:]') + # There must be a better way... + loader_locn=$(gdk-pixbuf-query-loaders | grep "LoaderDir" | tr -d '#[:space:]') -OLDIFS=$IFS -IFS='=' -set $loader_locn -OLDIFS=$IFS + OLDIFS=$IFS + IFS='=' + set $loader_locn + OLDIFS=$IFS -if [ -d $2 ] -then - sudo --askpass cp .libs/libioxcf.so $2 - sudo --askpass gdk-pixbuf-query-loaders --update-cache -fi -cd - -rm -rf xcf-pixbuf-loader + if [ -d $2 ] + then + sudo --askpass cp .libs/libioxcf.so $2 + sudo --askpass gdk-pixbuf-query-loaders --update-cache + fi + cd - + rm -rf xcf-pixbuf-loader } install_extra_loaders() { -if [ -n "$extra_loaders" ] -then - OLDIFS=$IFS - IFS='|' - set $extra_loaders - while [ $# -gt 0 ]; - do - case $1 in - "webp" ) - install_webp - ;; - "psd" ) - install_psd - ;; - "xcf" ) - install_xcf - ;; - esac + if [ -n "$extra_loaders" ] + then + OLDIFS=$IFS + IFS='|' + set $extra_loaders + while [ $# -gt 0 ]; + do + case $1 in + "webp" ) + install_webp + ;; + "psd" ) + install_psd + ;; + "xcf" ) + install_xcf + ;; + esac - shift - done - IFS=$OLDIFS -fi -return + shift + done + IFS=$OLDIFS + fi + return } uninstall() { -current_dir=$(basename $PWD) -if [[ $current_dir == "geeqie" ]] -then - sudo --askpass make uninstall - zenity --title="Uninstall Geeqie" --width=370 --text="WARNING.\nThis will delete folder:\n\n$PWD\n\nand all sub-folders!" --question --ok-label="Cancel" --cancel-label="OK" 2>/dev/null - - if [[ $? == 1 ]] + current_dir=$(basename $PWD) + if [[ $current_dir == "geeqie" ]] then - cd .. - sudo --askpass rm -rf geeqie + sudo --askpass make uninstall + zenity --title="Uninstall Geeqie" --width=370 --text="WARNING.\nThis will delete folder:\n\n$PWD\n\nand all sub-folders!" --question --ok-label="Cancel" --cancel-label="OK" 2>/dev/null + + if [[ $? == 1 ]] + then + cd .. + sudo --askpass rm -rf geeqie + fi + else + zenity --title="Uninstall Geeqie" --width=370 --text="This is not a geeqie installation folder!\n\n$PWD" --warning 2>/dev/null fi -else - zenity --title="Uninstall Geeqie" --width=370 --text="This is not a geeqie installation folder!\n\n$PWD" --warning 2>/dev/null -fi -exit_install + exit_install } package_query() { -if [[ $DistroBasedOn == "debian" ]] -then - res=$(dpkg-query --show --showformat='${Status}' $1 2>>$install_log) - if [[ "$res" == "install ok installed"* ]] + if [[ $DistroBasedOn == "debian" ]] then - status=0 - else - status=1 + res=$(dpkg-query --show --showformat='${Status}' $1 2>>$install_log) + if [[ "$res" == "install ok installed"* ]] + then + status=0 + else + status=1 + fi fi -fi -return $status + return $status } package_install() { -if [[ $DistroBasedOn == "debian" ]] -then - sudo --askpass apt-get --assume-yes install $@ >>$install_log 2>&1 -fi + if [[ $DistroBasedOn == "debian" ]] + then + sudo --askpass apt-get --assume-yes install $@ >>$install_log 2>&1 + fi } exit_install() { -rm $install_pass_script >/dev/null 2>&1 -#~ rm $install_log >/dev/null 2>&1 + rm $install_pass_script >/dev/null 2>&1 + #~ rm $install_log >/dev/null 2>&1 -if [[ -p $zen_pipe ]] -then - echo "100" > $zen_pipe - echo "#End" > $zen_pipe -fi + if [[ -p $zen_pipe ]] + then + echo "100" > $zen_pipe + echo "#End" > $zen_pipe + fi -zenity --title="$title" --width=370 --text=$'Geeqie is not installed\nLog file: '$install_log --info 2>/dev/null + zenity --title="$title" --width=370 --text=$'Geeqie is not installed\nLog file: '$install_log --info 2>/dev/null -rm $zen_pipe >/dev/null 2>&1 + rm $zen_pipe >/dev/null 2>&1 -exit 1 + exit 1 } # Entry point @@ -365,7 +367,7 @@ fi # Parse the comand line -OPTS=$(getopt -o vhc:t:b: --long version,help,commit:,tag:,back: -- "$@") +OPTS=$(getopt -o vhc:t:b:d: --long version,help,commit:,tag:,back:,debug: -- "$@") eval set -- "$OPTS" while true; @@ -394,6 +396,11 @@ shift; shift ;; + -d | --debug ) + DEBUG="$2" + shift; + shift + ;; * ) break ;; esac @@ -674,15 +681,21 @@ exit fi fi +if [[ "$DEBUG" == "yes" ]] +then + debug_opt="" +else + debug_opt="--disable-debug-log" +fi echo "40" > $zen_pipe echo "#Creating configuration files..." > $zen_pipe if [[ $gtk_version == "GTK3"* ]] then - ./autogen.sh >>$install_log 2>&1 + ./autogen.sh "$debug_opt" >>$install_log 2>&1 else - ./autogen.sh --disable-gtk3 >>$install_log 2>&1 + ./autogen.sh "$debug_opt" --disable-gtk3 >>$install_log 2>&1 fi echo "60" > $zen_pipe @@ -692,17 +705,40 @@ export CXXFLAGS=$CXXFLAGS" -Wno-deprecated-declarations" make -j >>$install_log 2>&1 +if [[ $? != 0 ]] +then + zenity --title="$title" --width=370 --height=400 --error --text=$'Compile error' 2>/dev/null + exit_install + exit +fi + echo "90 " > $zen_pipe echo "#Installing Geeqie..." > $zen_pipe sudo --askpass make install >>$install_log 2>&1 +# This is a temporary fix until the ZoneDetect project releases its source code +zonedetect_message="ZoneDetect database not loaded" +if [[ -d "/usr/local/lib/geeqie" ]] +then + if [[ ! -f "/usr/local/lib/geeqie/timezone21.bin" ]] + then + sudo --askpass wget --directory-prefix=/usr/local/lib/geeqie/ https://github.com/BertoldVdb/ZoneDetect/raw/master/database/timezone21.bin >>$install_log 2>&1 + if [[ $? == 0 ]] + then + zonedetect_message="" + fi + else + zonedetect_message="" + fi +fi + rm $install_pass_script mv -f $install_log install.log; echo "100 " > $zen_pipe rm $zen_pipe -(for i in $(seq 0 4 100); do echo "$i"; sleep 0.1; done) | zenity --progress --title="$title" --width=370 --text="Geeqie installation complete...\n" --auto-close 2>/dev/null +(for i in $(seq 0 4 100); do echo "$i"; sleep 0.1; done) | zenity --progress --title="$title" --width=370 --text="Geeqie installation complete...\n$zonedetect_message" --auto-close --percentage=0 2>/dev/null exit diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideIndex.html --- a/web/help/GuideIndex.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideIndex.html Tue Feb 05 21:18:18 2019 +0100 @@ -580,28 +580,34 @@ 11.2. Image Options
  • -11.3. Window Options +11.3. OverlayScreen Display
  • -11.4. Keyboard Options +11.4. Window Options
  • -11.5. Files Options +11.5. Keyboard Options +
  • +
  • +11.6. Files Options
  • -11.6. Metadata +11.7. Metadata
  • -11.7. Color management options +11.8. Keywords
  • -11.8. Stereo image management +11.9. Color management options
  • -11.9. Behavior Options +11.10. Stereo image management
  • -11.10. Toolbar +11.11. Behavior Options +
  • +
  • +11.12. Toolbar
  • diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideMainWindowFilePane.html --- a/web/help/GuideMainWindowFilePane.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideMainWindowFilePane.html Tue Feb 05 21:18:18 2019 +0100 @@ -468,16 +468,19 @@ 2.1.2. Icon view
  • -2.1.3. Multiple selection +2.1.3. File Filter +
  • +
  • +2.1.4. Multiple selection
  • -2.1.4. Sorting +2.1.5. Sorting
  • -2.1.5. Context menu +2.1.6. Context menu
  • -2.1.6. Drag and Drop +2.1.7. Drag and Drop
  • @@ -530,19 +533,45 @@

    -

    2.1.3. Multiple selection

    +

    2.1.3. File Filter

    +

    + A filter box can be opened by selecting + Show File Filter + from the View menu. +

    +

    + Characters you type into this box will be used for pattern matching to select the files displayed. + Regular expressions + are used in this field. +

    + For basic pattern matching "JPG" will match any filename containing those characters. +

    + To make a case-insensitive search, use "(?i)JPG" +

    + If you type a newline, the text will be saved in the dropdown box list. The last 10 items are saved. +

    + To delete an item, select it from the drop-down list, click on the text box to select the text, and then press + Delete + followed by + Enter + . +

    +

    +
    +
    +

    2.1.4. Multiple selection

    It is possible to select more than one image from the file pane. Multiple files can be selected with several methods:

    -

    2.1.3.1. Mouse

    +

    2.1.4.1. Mouse

    • Ctrl + Primary mouse button will add or remove the file from the selection. @@ -560,7 +589,7 @@

    -

    2.1.3.2. Keyboard

    +

    2.1.4.2. Keyboard

    • Ctrl + Arrows will move the focus without changing the selection. @@ -593,13 +622,13 @@
    -

    2.1.4. Sorting

    +

    2.1.5. Sorting

    The order of the images can be changed by clicking the sort area of the status bar or from the context menu. The sort methods are by file name, number, file date, or file size. The number method will sort file names by their natural order, for example files with names of file_10, file_12, and file_9 will appear in order file_9, file_10, and file_12.

    Selecting the ascending menu item will toggle between increasing and decreasing sort order.

    -

    2.1.5. Context menu

    +

    2.1.6. Context menu

    Right clicking the mouse or pressing the menu key while the file pane has focus will display a menu. The menu functions will perform the same as those that match the window's menu bar @@ -646,7 +675,7 @@

    -

    2.1.6. Drag and Drop

    +

    2.1.7. Drag and Drop

    Drag and drop can be initialized with the primary or middle mouse buttons in the file pane. Dragging a file that is selected will include all selected files in the drag. Dragging a file that is not selected will first change the selection to the dragged file, and clear the previous selection.

    diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideMainWindowImagePane.html --- a/web/help/GuideMainWindowImagePane.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideMainWindowImagePane.html Tue Feb 05 21:18:18 2019 +0100 @@ -516,7 +516,7 @@

    Data the overlay displays is customizable through the - Image Overlay Template + Image Overlay Template option in the Windows tab in the Preferences dialog.

    diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideOptionsBehavior.html --- a/web/help/GuideOptionsBehavior.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideOptionsBehavior.html Tue Feb 05 21:18:18 2019 +0100 @@ -431,10 +431,12 @@ Preferences
    • General Options
    • Image Options
    • +
    • OverlayScreen Display
    • Window Options
    • Keyboard Options
    • Files Options
    • Metadata
    • +
    • Keywords
    • Color management options
    • Stereo image management
    • Behavior Options
    • @@ -459,20 +461,20 @@

      This section describes the options presented under the Behavior Tab of the preferences dialog.

      -

      11.9.1. Delete

      +

      11.11.1. Delete

      Confirm file delete @@ -550,7 +552,7 @@
      -

      11.9.2. Behavior

      +

      11.11.2. Behavior

      Descend folders in tree view @@ -628,7 +630,7 @@
      -

      11.9.3. Navigation

      +

      11.11.3. Navigation

      Progressive keyboard scrolling @@ -669,7 +671,7 @@
      -

      11.9.4. Debugging

      +

      11.11.4. Debugging

      Debug level diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideOptionsColor.html --- a/web/help/GuideOptionsColor.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideOptionsColor.html Tue Feb 05 21:18:18 2019 +0100 @@ -3,7 +3,7 @@ Color management options - + + + + + +
      +

      Keywords

      + +

      This section describes the keywords list used for autocompletion.

      +

      +
      +
      +

      11.8.1. Keyword Search

      +

      +

      Pressing the Search button will open a dialog which permits a recursive search to be made for keywords already attached to images. The result of the search is automatically appended to the existing list.

      +
      +
      +

      11.8.2. Keyword List

      +

      +

      + The list shows all keywords currently used for autocompletion. Text may be copy-pasted into the list or deleted from the list. +

      + When the list is saved, duplicates will autmatically be removed and the list sorted into alphabetical order. +

      +
      +
      + + + diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideOptionsMain.html --- a/web/help/GuideOptionsMain.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideOptionsMain.html Tue Feb 05 21:18:18 2019 +0100 @@ -430,10 +430,12 @@
    • Preferences
    • diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideOptionsMetadata.html --- a/web/help/GuideOptionsMetadata.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideOptionsMetadata.html Tue Feb 05 21:18:18 2019 +0100 @@ -4,7 +4,7 @@ Metadata - + + + + + +
      +

      OverlayScreen Display

      + +

      This section describes the options presented under the OSD Tab of the preferences dialog.

      + +
      +

      11.3.1. Overlay Screen Display

      +

      The contents of the Overlay Screen Display is defined by the Image Overlay Template. This template is easily customised to display a wide range of data.

      +

      + The format of each entry is: +

      %tag[:max_length][:extra]%
      +

      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Tag + + Replaced by +
      nameFilename of the picture
      collectionName of the collection
      numberCurrent number of image in the list
      totalTotal number of images
      dateFile date
      sizeFile size
      widthImage width
      heightImage height
      resImage resolution
      keywordsImage keywords from metadata
      commentImage comment from XMP metadata
      imagecommentJPEG image comment
      ratingImage rating
      <meta_tag>The Exif, XMP, or IPTC tag from metadata
      lua/<lua_script>/The output of a Lua script file
      lua//<lua_command>The output of a Lua command
      +

      + As an aide, in addition to standard metadata tags, Geeqie provides a number of + pre-formatted tags + . +

      +

      Examples of usage are:

      +

      +

              %keywords%
      +        %Exif.Photo.DateTimeOriginal%
      +        %lua/jpeg_comment.lua/:12%
      +        %lua//return(os.date())%
      +      
      +

      +

      + Refer to + Lua Extensions + for further information. +

      +

      + The length of displayed data can be limited by using the + :max_length + parameter. The following example will truncate the displayed data to 20 characters and will add 3 dots at the end to denote the truncation. +

      %formatted.Camera:20%
      +

      +

      + If two or more variables are connected with the + | + character, the variables are displayed with a separator. For example:: +

      %formatted.ShutterSpeed%|%formatted.ISOSpeedRating%|%formatted.FocalLength%
      + could show: +
      "1/20s - 400 - 80 mm"
      + Or, if there is no ISO information in the Exif data: +
      "1/200 - 80 mm"
      +

      +

      If there is no data for a requested tag, the line is not displayed.

      +

      + The + :extra + parameter may be used to format the output by prepending and appending a text string to the displayed item. +

      +

      + The special character + * + is used to mark the position of the Tag data item. If no + * + is present, the extra string is just appended to the standard data displayed. Any "\n" is replaced by a newline on display. + Pango mark up + is accepted in both left and right parts. If the data item is empty, nothing will be displayed. +

      +

      Examples:

      +
      + + + + + + + + + + + + + + + + + + + + +
      + Template + + Example display +
      + %name: + <i>*</i>\n% + + filename001.jpg +
      %size:\n%123456
      %formatted.ISOSpeedRating:ISO *%ISO 100
      + %collection:Collection: + <b>*</b> + \n% + + Collection: + my_new_collection +
      +

      +
      +
      + + + + + + Font + , + + + + + + Text + , + + + + + + Background +
      +
      The font used for the Overlay Screen Display, as well as the font colour and background colour, may be set via these buttons. Colour transparency can also be set.
      +
      +
      +
      + + + + + + Help +
      +
      +

      Show syntax help

      +
      +
      +
      +
      + Defaults +
      +
      +

      Restore default image overlay template

      +
      +
      +
      +
      + + + diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideOptionsStereo.html --- a/web/help/GuideOptionsStereo.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideOptionsStereo.html Tue Feb 05 21:18:18 2019 +0100 @@ -431,10 +431,12 @@ Preferences
      • General Options
      • Image Options
      • +
      • OverlayScreen Display
      • Window Options
      • Keyboard Options
      • Files Options
      • Metadata
      • +
      • Keywords
      • Color management options
      • Stereo image management
      • Behavior Options
      • diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideOptionsToolbar.html --- a/web/help/GuideOptionsToolbar.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideOptionsToolbar.html Tue Feb 05 21:18:18 2019 +0100 @@ -431,10 +431,12 @@ Preferences
        • General Options
        • Image Options
        • +
        • OverlayScreen Display
        • Window Options
        • Keyboard Options
        • Files Options
        • Metadata
        • +
        • Keywords
        • Color management options
        • Stereo image management
        • Behavior Options
        • diff -r 5705ded05a19 -r 7e769ff09997 web/help/GuideOptionsWindow.html --- a/web/help/GuideOptionsWindow.html Tue Feb 05 21:18:18 2019 +0100 +++ b/web/help/GuideOptionsWindow.html Tue Feb 05 21:18:18 2019 +0100 @@ -3,7 +3,7 @@ Window Options - +