Compare commits

..

16 Commits

Author SHA1 Message Date
c204e4e094 Bump liberty and termo
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
2025-04-23 22:00:17 +02:00
16e0ff2188 Make README.adoc a bit less confusing
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
2024-12-19 15:29:58 +01:00
2d6855445f Update the screenshot
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
2024-12-19 15:24:53 +01:00
531f18d827 GUI: add basic configuration
It is simply not feasible to write the text file by hand on Windows.
2024-12-19 14:38:19 +01:00
862cde36ae CMakeLists.txt: quote more paths 2024-12-07 22:50:53 +01:00
661dc85d45 Fix macOS build
All checks were successful
Alpine 3.20 Success
Arch Linux AUR Success
2024-12-04 18:10:35 +01:00
2fe846f09f Fix running tests in Windows builds
All checks were successful
Arch Linux AUR Success
Alpine 3.19 Success
2024-04-10 12:48:05 +02:00
33426992ec Bump liberty, moving the Win64 toolchain file
All checks were successful
Arch Linux AUR Success
2024-04-09 17:04:45 +02:00
197d071160 Enable cross-compiled tests
All checks were successful
Arch Linux AUR Success
2024-04-09 13:04:32 +02:00
fafac22d60 Bump liberty
All checks were successful
Arch Linux AUR Success
2024-02-10 05:46:13 +01:00
58f7ba55b3 CMakeLists.txt: declare compatibility with 3.27
Sadly, the 3.5 deprecation warning doesn't go away after this.
2023-08-01 03:09:31 +02:00
d2cfc2ee81 Deduplicate CMake scripts 2023-07-24 11:47:33 +02:00
5a9a446b9c Fix build on OpenIndiana 2023-07-24 08:24:52 +02:00
39e2fc5142 Find ncursesw on OpenIndiana 2023-07-24 08:07:26 +02:00
f94bb77091 Reflect the recent renaming of tabfile in scripts 2023-07-07 12:15:37 +02:00
d3cfb12e16 README.adoc: update package information 2023-07-01 22:00:54 +02:00
17 changed files with 403 additions and 123 deletions

View File

@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.0) cmake_minimum_required (VERSION 3.0...3.27)
project (tdv VERSION 0.1.0 LANGUAGES C) project (tdv VERSION 0.1.0 LANGUAGES C)
# Adjust warnings # Adjust warnings
@@ -11,7 +11,8 @@ endif ()
add_definitions (-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_38) add_definitions (-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_38)
# For custom modules # For custom modules
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) set (CMAKE_MODULE_PATH
"${PROJECT_SOURCE_DIR}/cmake;${PROJECT_SOURCE_DIR}/liberty/cmake")
# Cross-compilation for Windows, as a proof-of-concept pulled in from logdiag # Cross-compilation for Windows, as a proof-of-concept pulled in from logdiag
if (WIN32) if (WIN32)
@@ -77,7 +78,7 @@ if (USE_SYSTEM_TERMO)
if (NOT Termo_FOUND) if (NOT Termo_FOUND)
message (FATAL_ERROR "System termo library not found") message (FATAL_ERROR "System termo library not found")
endif () endif ()
else () elseif (NOT WIN32)
# We don't want the library to install, but EXCLUDE_FROM_ALL ignores tests # We don't want the library to install, but EXCLUDE_FROM_ALL ignores tests
add_subdirectory (termo EXCLUDE_FROM_ALL) add_subdirectory (termo EXCLUDE_FROM_ALL)
file (WRITE ${PROJECT_BINARY_DIR}/CTestCustom.cmake file (WRITE ${PROJECT_BINARY_DIR}/CTestCustom.cmake
@@ -126,9 +127,9 @@ CHECK_FUNCTION_EXISTS ("resizeterm" HAVE_RESIZETERM)
# Localization # Localization
find_package (Gettext REQUIRED) find_package (Gettext REQUIRED)
file (GLOB project_PO_FILES ${PROJECT_SOURCE_DIR}/po/*.po) file (GLOB project_PO_FILES "${PROJECT_SOURCE_DIR}/po/*.po")
GETTEXT_CREATE_TRANSLATIONS ( GETTEXT_CREATE_TRANSLATIONS (
${PROJECT_SOURCE_DIR}/po/${PROJECT_NAME}.pot "${PROJECT_SOURCE_DIR}/po/${PROJECT_NAME}.pot"
ALL ${project_PO_FILES}) ALL ${project_PO_FILES})
# Documentation # Documentation
@@ -143,7 +144,7 @@ foreach (page "${PROJECT_NAME}.1")
set (page_output "${PROJECT_BINARY_DIR}/${page}") set (page_output "${PROJECT_BINARY_DIR}/${page}")
list (APPEND project_MAN_PAGES "${page_output}") list (APPEND project_MAN_PAGES "${page_output}")
if (ASCIIDOCTOR_EXECUTABLE) if (ASCIIDOCTOR_EXECUTABLE)
add_custom_command (OUTPUT ${page_output} add_custom_command (OUTPUT "${page_output}"
COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage
-a release-version=${PROJECT_VERSION} -a release-version=${PROJECT_VERSION}
-o "${page_output}" -o "${page_output}"
@@ -151,7 +152,7 @@ foreach (page "${PROJECT_NAME}.1")
DEPENDS "docs/${page}.adoc" DEPENDS "docs/${page}.adoc"
COMMENT "Generating man page for ${page}" VERBATIM) COMMENT "Generating man page for ${page}" VERBATIM)
elseif (A2X_EXECUTABLE) elseif (A2X_EXECUTABLE)
add_custom_command (OUTPUT ${page_output} add_custom_command (OUTPUT "${page_output}"
COMMAND ${A2X_EXECUTABLE} --doctype manpage --format manpage COMMAND ${A2X_EXECUTABLE} --doctype manpage --format manpage
-a release-version=${PROJECT_VERSION} -a release-version=${PROJECT_VERSION}
-D "${PROJECT_BINARY_DIR}" -D "${PROJECT_BINARY_DIR}"
@@ -160,10 +161,10 @@ foreach (page "${PROJECT_NAME}.1")
COMMENT "Generating man page for ${page}" VERBATIM) COMMENT "Generating man page for ${page}" VERBATIM)
else () else ()
set (ASCIIMAN ${PROJECT_SOURCE_DIR}/liberty/tools/asciiman.awk) set (ASCIIMAN ${PROJECT_SOURCE_DIR}/liberty/tools/asciiman.awk)
add_custom_command (OUTPUT ${page_output} add_custom_command (OUTPUT "${page_output}"
COMMAND env LC_ALL=C asciidoc-release-version=${PROJECT_VERSION} COMMAND env LC_ALL=C asciidoc-release-version=${PROJECT_VERSION}
awk -f ${ASCIIMAN} "${PROJECT_SOURCE_DIR}/docs/${page}.adoc" awk -f ${ASCIIMAN} "${PROJECT_SOURCE_DIR}/docs/${page}.adoc"
> ${page_output} > "${page_output}"
DEPENDS "docs/${page}.adoc" ${ASCIIMAN} DEPENDS "docs/${page}.adoc" ${ASCIIMAN}
COMMENT "Generating man page for ${page}" VERBATIM) COMMENT "Generating man page for ${page}" VERBATIM)
endif () endif ()
@@ -180,7 +181,7 @@ if (WIN32)
endif (WIN32) endif (WIN32)
set (project_common_headers set (project_common_headers
${PROJECT_BINARY_DIR}/config.h "${PROJECT_BINARY_DIR}/config.h"
src/dictzip-input-stream.h src/dictzip-input-stream.h
src/stardict.h src/stardict.h
src/stardict-private.h src/stardict-private.h
@@ -197,37 +198,9 @@ add_library (stardict OBJECT
set (project_common_sources $<TARGET_OBJECTS:stardict>) set (project_common_sources $<TARGET_OBJECTS:stardict>)
# Generate a configuration file # Generate a configuration file
configure_file (${PROJECT_SOURCE_DIR}/config.h.in configure_file ("${PROJECT_SOURCE_DIR}/config.h.in"
${PROJECT_BINARY_DIR}/config.h) "${PROJECT_BINARY_DIR}/config.h")
include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) include_directories ("${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}")
# Icon generation utilities
if (NOT ${CMAKE_VERSION} VERSION_LESS 3.18.0)
set (find_program_REQUIRE REQUIRED)
endif ()
function (icon_to_png svg size output_dir output)
set (_dimensions ${size}x${size})
set (_png_path ${output_dir}/hicolor/${_dimensions}/apps)
set (_png ${_png_path}/${PROJECT_NAME}.png)
set (${output} ${_png} PARENT_SCOPE)
find_program (rsvg_convert_EXECUTABLE rsvg-convert ${find_program_REQUIRE})
add_custom_command (OUTPUT ${_png}
COMMAND ${CMAKE_COMMAND} -E make_directory ${_png_path}
COMMAND ${rsvg_convert_EXECUTABLE} --output=${_png}
--width=${size} --height=${size} ${svg}
DEPENDS ${svg}
COMMENT "Generating ${_dimensions} application icon" VERBATIM)
endfunction ()
function (icon_for_win32 pngs ico)
find_program (icotool_EXECUTABLE icotool ${find_program_REQUIRE})
add_custom_command (OUTPUT ${ico}
COMMAND ${icotool_EXECUTABLE} -c -o ${ico} ${pngs}
DEPENDS ${pngs}
COMMENT "Generating Windows program icon" VERBATIM)
endfunction ()
# Build the main executable and link it # Build the main executable and link it
set (project_libraries set (project_libraries
@@ -239,31 +212,34 @@ set (project_headers
${project_common_headers}) ${project_common_headers})
if (WITH_GUI) if (WITH_GUI)
set (icon_svg ${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.svg) include (IconUtils)
set (icon_base ${PROJECT_BINARY_DIR}/icons)
# The largest size is mainly for an appropriately sized Windows icon # The largest size is mainly for an appropriately sized Windows icon
set (icon_base "${PROJECT_BINARY_DIR}/icons")
set (icon_png_list) set (icon_png_list)
foreach (icon_size 16 32 48 256) foreach (icon_size 16 32 48 256)
icon_to_png (${icon_svg} ${icon_size} ${icon_base} icon_png) icon_to_png (${PROJECT_NAME} "${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.svg"
list (APPEND icon_png_list ${icon_png}) ${icon_size} "${icon_base}" icon_png)
list (APPEND icon_png_list "${icon_png}")
endforeach () endforeach ()
add_custom_target (icons ALL DEPENDS ${icon_png_list}) add_custom_target (icons ALL DEPENDS ${icon_png_list})
endif () endif ()
if (WIN32) if (WIN32)
set (icon_ico ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.ico) list (REMOVE_ITEM icon_png_list "${icon_png}")
icon_for_win32 ("${icon_png_list}" ${icon_ico}) set (icon_ico "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.ico")
icon_for_win32 ("${icon_ico}" "${icon_png_list}" "${icon_png}")
set (resource_file ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.rc) set (resource_file "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.rc")
list (APPEND project_sources ${resource_file}) list (APPEND project_sources "${resource_file}")
add_custom_command (OUTPUT ${resource_file} add_custom_command (OUTPUT "${resource_file}"
COMMAND ${CMAKE_COMMAND} -E echo "1 ICON \"${PROJECT_NAME}.ico\"" COMMAND ${CMAKE_COMMAND} -E echo "1 ICON \"${PROJECT_NAME}.ico\""
> ${resource_file} VERBATIM) > "${resource_file}" VERBATIM)
set_property (SOURCE ${resource_file} set_property (SOURCE "${resource_file}"
APPEND PROPERTY OBJECT_DEPENDS ${icon_ico}) APPEND PROPERTY OBJECT_DEPENDS "${icon_ico}")
else () else ()
list (APPEND project_libraries ${Ncursesw_LIBRARIES} termo-static) list (APPEND project_libraries ${Ncursesw_LIBRARIES} ${Termo_LIBRARIES})
list (APPEND project_sources list (APPEND project_sources
src/${PROJECT_NAME}-tui.c) src/${PROJECT_NAME}-tui.c)
endif () endif ()
@@ -324,7 +300,7 @@ if (NOT WIN32)
if (WITH_GUI) if (WITH_GUI)
install (FILES ${PROJECT_NAME}.svg install (FILES ${PROJECT_NAME}.svg
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
install (DIRECTORY ${PROJECT_BINARY_DIR}/icons install (DIRECTORY ${icon_base}
DESTINATION ${CMAKE_INSTALL_DATADIR}) DESTINATION ${CMAKE_INSTALL_DATADIR})
install (FILES ${PROJECT_NAME}.desktop install (FILES ${PROJECT_NAME}.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
@@ -361,7 +337,7 @@ elseif (WITH_GUI)
install (FILES install (FILES
${win32_deps_prefix}/share/icons/hicolor/index.theme ${win32_deps_prefix}/share/icons/hicolor/index.theme
DESTINATION share/icons/hicolor) DESTINATION share/icons/hicolor)
install (DIRECTORY ${icon_base} DESTINATION share) install (DIRECTORY "${icon_base}" DESTINATION share)
install (SCRIPT cmake/Win32Cleanup.cmake) install (SCRIPT cmake/Win32Cleanup.cmake)
@@ -388,11 +364,11 @@ if (BUILD_TESTING)
foreach (xml ${PROJECT_NAME}.xml ${PROJECT_NAME}.svg) foreach (xml ${PROJECT_NAME}.xml ${PROJECT_NAME}.svg)
if (xmlwf_EXECUTABLE) if (xmlwf_EXECUTABLE)
add_test (test-xmlwf-${xml} ${xmlwf_EXECUTABLE} add_test (test-xmlwf-${xml} ${xmlwf_EXECUTABLE}
${PROJECT_SOURCE_DIR}/${xml}) "${PROJECT_SOURCE_DIR}/${xml}")
endif () endif ()
if (xmllint_EXECUTABLE) if (xmllint_EXECUTABLE)
add_test (test-xmllint-${xml} ${xmllint_EXECUTABLE} --noout add_test (test-xmllint-${xml} ${xmllint_EXECUTABLE} --noout
${PROJECT_SOURCE_DIR}/${xml}) "${PROJECT_SOURCE_DIR}/${xml}")
endif () endif ()
endforeach () endforeach ()
@@ -400,7 +376,7 @@ if (BUILD_TESTING)
if (dfv_EXECUTABLE) if (dfv_EXECUTABLE)
foreach (df ${PROJECT_NAME}.desktop) foreach (df ${PROJECT_NAME}.desktop)
add_test (test-dfv-${df} ${dfv_EXECUTABLE} add_test (test-dfv-${df} ${dfv_EXECUTABLE}
${PROJECT_SOURCE_DIR}/${df}) "${PROJECT_SOURCE_DIR}/${df}")
endforeach () endforeach ()
endif () endif ()
@@ -408,7 +384,7 @@ if (BUILD_TESTING)
add_executable (test-${name} add_executable (test-${name}
src/test-${name}.c ${project_common_sources}) src/test-${name}.c ${project_common_sources})
target_link_libraries (test-${name} ${project_common_libraries}) target_link_libraries (test-${name} ${project_common_libraries})
add_test (test-${name} test-${name}) add_test (NAME test-${name} COMMAND test-${name})
endforeach () endforeach ()
endif () endif ()

View File

@@ -1,4 +1,4 @@
Copyright (c) 2013 - 2023, Přemysl Eric Janouch <p@janouch.name> Copyright (c) 2013 - 2024, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted. purpose with or without fee is hereby granted.

View File

@@ -6,19 +6,24 @@ of StarDict dictionaries, and is inspired by the dictionary component
of PC Translator. I was unsuccessful in finding any free software of this kind, of PC Translator. I was unsuccessful in finding any free software of this kind,
and thus decided to write my own. and thus decided to write my own.
The program offers both a terminal user interface, and a GTK+ 3 based UI.
The styling of the latter will follow your theme, and may be customized
from 'gtk.css'.
The project is covered by a permissive license, unlike vast majority of other The project is covered by a permissive license, unlike vast majority of other
similar projects, and can serve as a base for implementing other dictionary similar projects, and can serve as a base for implementing other dictionary
software. software.
Screenshot
----------
image::tdv.png[align="center"] image::tdv.png[align="center"]
As a recent addition, the program also offers a GTK+ 3 based user interface,
whose styling will follow your theme, and may be customized from 'gtk.css'.
Packages Packages
-------- --------
Regular releases are sporadic. git master should be stable enough. You can get Regular releases are sporadic. git master should be stable enough.
a package with the latest development version from Archlinux's AUR. You can get a package with the latest development version using Arch Linux's
https://aur.archlinux.org/packages/tdv-git[AUR],
or as a https://git.janouch.name/p/nixexprs[Nix derivation].
Documentation Documentation
------------- -------------
@@ -65,7 +70,7 @@ selection watching is a very X11/Wayland-specific feature. Beware that build
dependencies take up almost a gigabyte of disk space. dependencies take up almost a gigabyte of disk space.
$ sh -e cmake/Win64Depends.sh $ sh -e cmake/Win64Depends.sh
$ cmake -DCMAKE_TOOLCHAIN_FILE=cmake/Win64CrossToolchain.cmake \ $ cmake -DCMAKE_TOOLCHAIN_FILE=liberty/cmake/toolchains/MinGW-w64-x64.cmake \
-DCMAKE_BUILD_TYPE=Release -B build -DCMAKE_BUILD_TYPE=Release -B build
$ cmake --build build -- package $ cmake --build build -- package
@@ -90,14 +95,8 @@ https://mega.co.nz/#!axtD0QRK!sbtBgizksyfkPqKvKEgr8GQ11rsWhtqyRgUUV0B7pwg[CZ <--
Further Development Further Development
------------------- -------------------
While I've been successfully using 'tdv' for many years now, some issues Lacking configuration, standard StarDict locations should be scanned.
should be addressed before including the software in regular Linux and/or We should try harder to display arbitrary dictionaries sensibly.
BSD distributions:
- The GUI is awkward to configure.
- Lacking configuration, standard StarDict locations should be scanned.
Given all issues with the file format, it might be better to start anew.
Contributing and Support Contributing and Support
------------------------ ------------------------

View File

@@ -1,17 +0,0 @@
# Public Domain
find_package (PkgConfig REQUIRED)
pkg_check_modules (Ncursesw QUIET ncursesw)
# OpenBSD doesn't provide a pkg-config file
set (required_vars Ncursesw_LIBRARIES)
if (NOT Ncursesw_FOUND)
find_library (Ncursesw_LIBRARIES NAMES ncursesw)
find_path (Ncursesw_INCLUDE_DIRS ncurses.h)
list (APPEND required_vars Ncursesw_INCLUDE_DIRS)
endif (NOT Ncursesw_FOUND)
include (FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS (Ncursesw DEFAULT_MSG ${required_vars})
mark_as_advanced (Ncursesw_LIBRARIES Ncursesw_INCLUDE_DIRS)

View File

@@ -1,15 +0,0 @@
set (CMAKE_SYSTEM_NAME "Windows")
set (CMAKE_SYSTEM_PROCESSOR "x86_64")
set (CMAKE_C_COMPILER "x86_64-w64-mingw32-gcc")
set (CMAKE_CXX_COMPILER "x86_64-w64-mingw32-g++")
set (CMAKE_RC_COMPILER "x86_64-w64-mingw32-windres")
# Not needed to crosscompile an installation package
#set (CMAKE_CROSSCOMPILING_EMULATOR "wine64")
set (CMAKE_FIND_ROOT_PATH "/usr/x86_64-w64-mingw32")
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@@ -55,7 +55,7 @@ while (my ($id, $synset) = each %synsets) {
# Output synsets exploded to individual words, with expanded relationships # Output synsets exploded to individual words, with expanded relationships
close($doc) or die $?; close($doc) or die $?;
open(my $tabfile, '|-', 'tabfile', 'czech-wordnet', open(my $tabfile, '|-', 'tdv-tabfile', 'czech-wordnet',
'--book-name=Czech WordNet 1.9 PDT', "--website=$base/$path", '--book-name=Czech WordNet 1.9 PDT', "--website=$base/$path",
'--date=2011-01-24', '--collation=cs_CZ') or die $!; '--date=2011-01-24', '--collation=cs_CZ') or die $!;

View File

@@ -9,7 +9,7 @@ grep -v ^# | sed 's/\\//g' | perl -CSD -F\\t -le '
sub tabesc { shift =~ s/\\/\\\\/gr =~ s/\n/\\n/gr =~ s/\t/\\t/gr } sub tabesc { shift =~ s/\\/\\\\/gr =~ s/\n/\\n/gr =~ s/\t/\\t/gr }
sub w { sub w {
my ($name, $dict, $collation) = @_; my ($name, $dict, $collation) = @_;
open(my $f, "|-", "tabfile", "--pango", "--collation=$collation", open(my $f, "|-", "tdv-tabfile", "--pango", "--collation=$collation",
"--website=https://gnu.nemeckoceskyslovnik.cz", "--website=https://gnu.nemeckoceskyslovnik.cz",
"gnu-fdl-$name") or die $!; "gnu-fdl-$name") or die $!;
print $f tabesc($keyword) . "\t" . tabesc(join("\n", @$defs)) print $f tabesc($keyword) . "\t" . tabesc(join("\n", @$defs))

View File

@@ -5,7 +5,7 @@ zcat | grep -v ^# | sed 's/\\//g' | perl -CSD -F\\t -le '
sub tabesc { shift =~ s/\\/\\\\/gr =~ s/\n/\\n/gr =~ s/\t/\\t/gr } sub tabesc { shift =~ s/\\/\\\\/gr =~ s/\n/\\n/gr =~ s/\t/\\t/gr }
sub w { sub w {
my ($name, $dict, $collation) = @_; my ($name, $dict, $collation) = @_;
open(my $f, "|-", "tabfile", "--pango", "--collation=$collation", open(my $f, "|-", "tdv-tabfile", "--pango", "--collation=$collation",
"--website=https://www.svobodneslovniky.cz", "--website=https://www.svobodneslovniky.cz",
"gnu-fdl-$name") or die $!; "gnu-fdl-$name") or die $!;
print $f tabesc($keyword) . "\t" . tabesc(join("\n", @$defs)) print $f tabesc($keyword) . "\t" . tabesc(join("\n", @$defs))

View File

@@ -4,7 +4,7 @@
curl -Lo- https://slovnik-cizich-slov.abz.cz/export.php | \ curl -Lo- https://slovnik-cizich-slov.abz.cz/export.php | \
iconv -f latin2 -t UTF-8 | perl -CSD -F\\\| -le ' iconv -f latin2 -t UTF-8 | perl -CSD -F\\\| -le '
print "$_\t" . $F[2] =~ s/\\/\\\\/gr =~ s/; /\\n/gr for split(", ", $F[0]) print "$_\t" . $F[2] =~ s/\\/\\\\/gr =~ s/; /\\n/gr for split(", ", $F[0])
' | sort -u | tabfile slovnik-cizich-slov \ ' | sort -u | tdv-tabfile slovnik-cizich-slov \
--book-name="Slovník cizích slov" \ --book-name="Slovník cizích slov" \
--website=https://slovnik-cizich-slov.abz.cz \ --website=https://slovnik-cizich-slov.abz.cz \
--date="$(date +%F)" \ --date="$(date +%F)" \

Submodule liberty updated: bd1013f16a...0f20cce9c8

View File

@@ -357,6 +357,20 @@ error:
return ret_val; return ret_val;
} }
/// Read an .ifo file.
/// @return StardictInfo *. Deallocate with stardict_info_free();
StardictInfo *
stardict_info_new (const gchar *filename, GError **error)
{
StardictInfo *ifo = g_new (StardictInfo, 1);
if (!load_ifo (ifo, filename, error))
{
g_free (ifo);
return NULL;
}
return ifo;
}
/// List all dictionary files located in a path. /// List all dictionary files located in a path.
/// @return GList<StardictInfo *>. Deallocate the list with: /// @return GList<StardictInfo *>. Deallocate the list with:
/// @code /// @code
@@ -377,12 +391,10 @@ stardict_list_dictionaries (const gchar *path)
continue; continue;
gchar *filename = g_build_filename (path, name, NULL); gchar *filename = g_build_filename (path, name, NULL);
StardictInfo *ifo = g_new (StardictInfo, 1); StardictInfo *ifo = stardict_info_new (filename, NULL);
if (load_ifo (ifo, filename, NULL))
dicts = g_list_append (dicts, ifo);
else
g_free (ifo);
g_free (filename); g_free (filename);
if (ifo)
dicts = g_list_append (dicts, ifo);
} }
g_dir_close (dir); g_dir_close (dir);
g_pattern_spec_free (ps); g_pattern_spec_free (ps);

View File

@@ -108,6 +108,7 @@ GQuark stardict_error_quark (void);
// --- Dictionary information -------------------------------------------------- // --- Dictionary information --------------------------------------------------
StardictInfo *stardict_info_new (const gchar *filename, GError **error);
const gchar *stardict_info_get_path (StardictInfo *sdi) G_GNUC_PURE; const gchar *stardict_info_get_path (StardictInfo *sdi) G_GNUC_PURE;
const gchar *stardict_info_get_book_name (StardictInfo *sdi) G_GNUC_PURE; const gchar *stardict_info_get_book_name (StardictInfo *sdi) G_GNUC_PURE;
gsize stardict_info_get_word_count (StardictInfo *sd) G_GNUC_PURE; gsize stardict_info_get_word_count (StardictInfo *sd) G_GNUC_PURE;

View File

@@ -1,7 +1,7 @@
/* /*
* StarDict GTK+ UI * StarDict GTK+ UI
* *
* Copyright (c) 2020 - 2022, Přemysl Eric Janouch <p@janouch.name> * Copyright (c) 2020 - 2024, Přemysl Eric Janouch <p@janouch.name>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted. * purpose with or without fee is hereby granted.
@@ -295,6 +295,8 @@ show_error_dialog (GError *error)
g_error_free (error); g_error_free (error);
} }
// --- Loading -----------------------------------------------------------------
static void static void
on_new_dictionaries_loaded (G_GNUC_UNUSED GObject* source_object, on_new_dictionaries_loaded (G_GNUC_UNUSED GObject* source_object,
GAsyncResult* res, G_GNUC_UNUSED gpointer user_data) GAsyncResult* res, G_GNUC_UNUSED gpointer user_data)
@@ -360,8 +362,8 @@ reload_dictionaries (GPtrArray *new_dictionaries, GError **error)
return TRUE; return TRUE;
} }
static void static GtkWidget *
on_open (G_GNUC_UNUSED GtkMenuItem *item, G_GNUC_UNUSED gpointer data) new_open_dialog (void)
{ {
// The default is local-only. Paths are returned absolute. // The default is local-only. Paths are returned absolute.
GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open dictionary"), GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open dictionary"),
@@ -375,7 +377,14 @@ on_open (G_GNUC_UNUSED GtkMenuItem *item, G_GNUC_UNUSED gpointer data)
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog); GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
gtk_file_chooser_add_filter (chooser, filter); gtk_file_chooser_add_filter (chooser, filter);
gtk_file_chooser_set_select_multiple (chooser, TRUE); gtk_file_chooser_set_select_multiple (chooser, TRUE);
return dialog;
}
static void
on_open (G_GNUC_UNUSED GtkMenuItem *item, G_GNUC_UNUSED gpointer data)
{
GtkWidget *dialog = new_open_dialog ();
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
GPtrArray *new_dictionaries = GPtrArray *new_dictionaries =
g_ptr_array_new_with_free_func ((GDestroyNotify) dictionary_destroy); g_ptr_array_new_with_free_func ((GDestroyNotify) dictionary_destroy);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
@@ -431,6 +440,280 @@ on_drag_data_received (G_GNUC_UNUSED GtkWidget *widget, GdkDragContext *context,
show_error_dialog (error); show_error_dialog (error);
} }
// --- Settings ----------------------------------------------------------------
typedef struct settings_data SettingsData;
enum
{
SETTINGS_COLUMN_NAME,
SETTINGS_COLUMN_PATH,
SETTINGS_COLUMN_COUNT
};
struct settings_data
{
GKeyFile *key_file; ///< Configuration file
GtkTreeModel *model; ///< GtkListStore
};
static void
settings_load (SettingsData *data)
{
// We want to keep original comments, as well as any other data.
GError *error = NULL;
data->key_file = load_project_config_file (&error);
if (!data->key_file)
{
if (error)
show_error_dialog (error);
data->key_file = g_key_file_new ();
}
GtkListStore *list_store = gtk_list_store_new (SETTINGS_COLUMN_COUNT,
G_TYPE_STRING, G_TYPE_STRING);
data->model = GTK_TREE_MODEL (list_store);
const gchar *dictionaries = "Dictionaries";
gchar **names =
g_key_file_get_keys (data->key_file, dictionaries, NULL, NULL);
if (!names)
return;
for (gsize i = 0; names[i]; i++)
{
gchar *path = g_key_file_get_string (data->key_file,
dictionaries, names[i], NULL);
if (!path)
continue;
GtkTreeIter iter = { 0 };
gtk_list_store_append (list_store, &iter);
gtk_list_store_set (list_store, &iter,
SETTINGS_COLUMN_NAME, names[i], SETTINGS_COLUMN_PATH, path, -1);
g_free (path);
}
g_strfreev (names);
}
static void
settings_save (SettingsData *data)
{
const gchar *dictionaries = "Dictionaries";
g_key_file_remove_group (data->key_file, dictionaries, NULL);
GtkTreeIter iter = { 0 };
gboolean valid = gtk_tree_model_get_iter_first (data->model, &iter);
while (valid)
{
gchar *name = NULL, *path = NULL;
gtk_tree_model_get (data->model, &iter,
SETTINGS_COLUMN_NAME, &name, SETTINGS_COLUMN_PATH, &path, -1);
if (name && path)
g_key_file_set_string (data->key_file, dictionaries, name, path);
g_free (name);
g_free (path);
valid = gtk_tree_model_iter_next (data->model, &iter);
}
GError *e = NULL;
if (!save_project_config_file (data->key_file, &e))
show_error_dialog (e);
}
static void
on_settings_name_edited (G_GNUC_UNUSED GtkCellRendererText *cell,
const gchar *path_string, const gchar *new_text, gpointer data)
{
GtkTreeModel *model = GTK_TREE_MODEL (data);
GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
GtkTreeIter iter = { 0 };
gtk_tree_model_get_iter (model, &iter, path);
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
SETTINGS_COLUMN_NAME, new_text, -1);
gtk_tree_path_free (path);
}
static void
on_settings_path_edited (G_GNUC_UNUSED GtkCellRendererText *cell,
const gchar *path_string, const gchar *new_text, gpointer data)
{
GtkTreeModel *model = GTK_TREE_MODEL (data);
GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
GtkTreeIter iter = { 0 };
gtk_tree_model_get_iter (model, &iter, path);
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
SETTINGS_COLUMN_PATH, new_text, -1);
gtk_tree_path_free (path);
}
static void
on_settings_add (G_GNUC_UNUSED GtkButton *button, gpointer user_data)
{
GtkWidget *dialog = new_open_dialog ();
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
GSList *paths = NULL;
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
paths = gtk_file_chooser_get_filenames (chooser);
gtk_widget_destroy (dialog);
// When the dialog is aborted, we simply add an empty list.
GtkTreeView *tree_view = GTK_TREE_VIEW (user_data);
gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view));
GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
GtkListStore *list_store = GTK_LIST_STORE (model);
const gchar *home = g_get_home_dir ();
for (GSList *iter = paths; iter; iter = iter->next)
{
GError *error = NULL;
StardictInfo *ifo = stardict_info_new (iter->data, &error);
g_free (iter->data);
if (!ifo)
{
show_error_dialog (error);
continue;
}
// We also expand tildes, even on Windows, so no problem there.
const gchar *path = stardict_info_get_path (ifo);
gchar *tildified = g_str_has_prefix (stardict_info_get_path (ifo), home)
? g_strdup_printf ("~%s", path + strlen (home))
: g_strdup (path);
GtkTreeIter iter = { 0 };
gtk_list_store_append (list_store, &iter);
gtk_list_store_set (list_store, &iter,
SETTINGS_COLUMN_NAME, stardict_info_get_book_name (ifo),
SETTINGS_COLUMN_PATH, tildified, -1);
g_free (tildified);
stardict_info_free (ifo);
}
g_slist_free (paths);
}
static void
on_settings_remove (G_GNUC_UNUSED GtkButton *button, gpointer user_data)
{
GtkTreeView *tree_view = GTK_TREE_VIEW (user_data);
GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
GtkListStore *list_store = GTK_LIST_STORE (model);
GList *selected = gtk_tree_selection_get_selected_rows (selection, &model);
for (GList *iter = selected; iter; iter = iter->next)
{
GtkTreePath *path = iter->data;
iter->data = gtk_tree_row_reference_new (model, path);
gtk_tree_path_free (path);
}
for (GList *iter = selected; iter; iter = iter->next)
{
GtkTreePath *path = gtk_tree_row_reference_get_path (iter->data);
if (path)
{
GtkTreeIter tree_iter = { 0 };
if (gtk_tree_model_get_iter (model, &tree_iter, path))
gtk_list_store_remove (list_store, &tree_iter);
gtk_tree_path_free (path);
}
}
g_list_free_full (selected, (GDestroyNotify) gtk_tree_row_reference_free);
}
static void
on_settings_selection_changed
(GtkTreeSelection* selection, gpointer user_data)
{
GtkWidget *remove = GTK_WIDGET (user_data);
gtk_widget_set_sensitive (remove,
gtk_tree_selection_count_selected_rows (selection) > 0);
}
static void
on_settings (G_GNUC_UNUSED GtkMenuItem *item, G_GNUC_UNUSED gpointer data)
{
SettingsData sd = {};
settings_load (&sd);
GtkWidget *treeview = gtk_tree_view_new_with_model (sd.model);
gtk_tree_view_set_reorderable (GTK_TREE_VIEW (treeview), TRUE);
g_object_unref (sd.model);
GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
g_object_set (renderer, "editable", TRUE, NULL);
g_signal_connect (renderer, "edited",
G_CALLBACK (on_settings_name_edited), sd.model);
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes
(_("Name"), renderer, "text", SETTINGS_COLUMN_NAME, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
renderer = gtk_cell_renderer_text_new ();
g_object_set (renderer, "editable", TRUE, NULL);
g_signal_connect (renderer, "edited",
G_CALLBACK (on_settings_path_edited), sd.model);
column = gtk_tree_view_column_new_with_attributes
(_("Path"), renderer, "text", SETTINGS_COLUMN_PATH, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
GtkWidget *scrolled = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type
(GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_ETCHED_IN);
gtk_container_add (GTK_CONTAINER (scrolled), treeview);
GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Settings"),
GTK_WINDOW (g.window),
GTK_DIALOG_MODAL,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Save"), GTK_RESPONSE_ACCEPT,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 400);
GtkWidget *remove = gtk_button_new_with_mnemonic (_("_Remove"));
gtk_widget_set_sensitive (remove, FALSE);
g_signal_connect (remove, "clicked",
G_CALLBACK (on_settings_remove), treeview);
GtkWidget *add = gtk_button_new_with_mnemonic (_("_Add..."));
g_signal_connect (add, "clicked",
G_CALLBACK (on_settings_add), treeview);
GtkTreeSelection *selection =
gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
g_signal_connect (selection, "changed",
G_CALLBACK (on_settings_selection_changed), remove);
GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (box),
gtk_label_new (_("Here you can configure the default dictionaries.")),
FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (box), remove, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (box), add, FALSE, FALSE, 0);
GtkWidget *content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
g_object_set (content_area, "margin", 12, NULL);
gtk_box_pack_start (GTK_BOX (content_area), box, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (content_area), scrolled, TRUE, TRUE, 12);
gtk_widget_show_all (dialog);
switch (gtk_dialog_run (GTK_DIALOG (dialog)))
{
case GTK_RESPONSE_NONE:
break;
case GTK_RESPONSE_ACCEPT:
settings_save (&sd);
// Fall through
default:
gtk_widget_destroy (dialog);
}
g_key_file_free (sd.key_file);
}
// --- Main --------------------------------------------------------------------
static void static void
on_destroy (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gpointer data) on_destroy (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gpointer data)
{ {
@@ -464,8 +747,19 @@ gui_main (char *argv[])
die_with_dialog (error->message); die_with_dialog (error->message);
if (!new_dictionaries->len) if (!new_dictionaries->len)
die_with_dialog (_("No dictionaries found either in " {
GtkWidget *dialog = gtk_message_dialog_new (NULL, 0,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s",
_("No dictionaries found either in "
"the configuration or on the command line")); "the configuration or on the command line"));
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
// This is better than nothing.
// Our GtkNotebook action widget would be invisible without any tabs.
on_settings (NULL, NULL);
exit (EXIT_SUCCESS);
}
// Some Adwaita stupidity, plus defaults for our own widget. // Some Adwaita stupidity, plus defaults for our own widget.
// All the named colours have been there since GNOME 3.4 // All the named colours have been there since GNOME 3.4
@@ -504,6 +798,10 @@ gui_main (char *argv[])
GtkWidget *item_open = gtk_menu_item_new_with_mnemonic (_("_Open...")); GtkWidget *item_open = gtk_menu_item_new_with_mnemonic (_("_Open..."));
g_signal_connect (item_open, "activate", G_CALLBACK (on_open), NULL); g_signal_connect (item_open, "activate", G_CALLBACK (on_open), NULL);
GtkWidget *item_settings = gtk_menu_item_new_with_mnemonic (_("_Settings"));
g_signal_connect (item_settings, "activate",
G_CALLBACK (on_settings), NULL);
g.watch_selection = TRUE; g.watch_selection = TRUE;
GtkWidget *item_selection = GtkWidget *item_selection =
gtk_check_menu_item_new_with_mnemonic (_("_Follow selection")); gtk_check_menu_item_new_with_mnemonic (_("_Follow selection"));
@@ -515,6 +813,7 @@ gui_main (char *argv[])
GtkWidget *menu = gtk_menu_new (); GtkWidget *menu = gtk_menu_new ();
gtk_widget_set_halign (menu, GTK_ALIGN_END); gtk_widget_set_halign (menu, GTK_ALIGN_END);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_open); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_open);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_settings);
#ifndef G_OS_WIN32 #ifndef G_OS_WIN32
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_selection); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_selection);
#endif // ! G_OS_WIN32 #endif // ! G_OS_WIN32

View File

@@ -16,6 +16,11 @@
* *
*/ */
// getpwnam_r, _SC_GETPW_R_SIZE_MAX
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
#include <glib.h> #include <glib.h>
#include <glib/gprintf.h> #include <glib/gprintf.h>
#include <gio/gio.h> #include <gio/gio.h>
@@ -217,7 +222,7 @@ load_project_config_file (GError **error)
// which is completely undocumented // which is completely undocumented
g_key_file_load_from_dirs (key_file, g_key_file_load_from_dirs (key_file,
PROJECT_NAME G_DIR_SEPARATOR_S PROJECT_NAME ".conf", PROJECT_NAME G_DIR_SEPARATOR_S PROJECT_NAME ".conf",
paths, NULL, 0, &e); paths, NULL, G_KEY_FILE_KEEP_COMMENTS, &e);
g_free (paths); g_free (paths);
if (!e) if (!e)
return key_file; return key_file;
@@ -231,6 +236,25 @@ load_project_config_file (GError **error)
return NULL; return NULL;
} }
gboolean
save_project_config_file (GKeyFile *key_file, GError **error)
{
gchar *dirname =
g_build_filename (g_get_user_config_dir (), PROJECT_NAME, NULL);
(void) g_mkdir_with_parents (dirname, 0755);
gchar *path = g_build_filename (dirname, PROJECT_NAME ".conf", NULL);
g_free (dirname);
gsize length = 0;
gchar *data = g_key_file_to_data (key_file, &length, error);
if (!data)
return FALSE;
gboolean result = g_file_set_contents (path, data, length, error);
g_free (data);
return result;
}
// --- Loading ----------------------------------------------------------------- // --- Loading -----------------------------------------------------------------
void void

View File

@@ -54,6 +54,7 @@ gchar *resolve_relative_config_filename (const gchar *filename);
gchar *resolve_filename gchar *resolve_filename
(const gchar *filename, gchar *(*relative_cb) (const char *)); (const gchar *filename, gchar *(*relative_cb) (const char *));
GKeyFile *load_project_config_file (GError **error); GKeyFile *load_project_config_file (GError **error);
gboolean save_project_config_file (GKeyFile *key_file, GError **error);
// --- Loading ----------------------------------------------------------------- // --- Loading -----------------------------------------------------------------

BIN
tdv.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 36 KiB

2
termo

Submodule termo updated: 2518b53e5a...f9a102456f