Compare commits
	
		
			16 Commits
		
	
	
		
			9aac2511d3
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						c204e4e094
	
				 | 
					
					
						|||
| 
						
						
							
						
						16e0ff2188
	
				 | 
					
					
						|||
| 
						
						
							
						
						2d6855445f
	
				 | 
					
					
						|||
| 
						
						
							
						
						531f18d827
	
				 | 
					
					
						|||
| 
						
						
							
						
						862cde36ae
	
				 | 
					
					
						|||
| 
						
						
							
						
						661dc85d45
	
				 | 
					
					
						|||
| 
						
						
							
						
						2fe846f09f
	
				 | 
					
					
						|||
| 
						
						
							
						
						33426992ec
	
				 | 
					
					
						|||
| 
						
						
							
						
						197d071160
	
				 | 
					
					
						|||
| 
						
						
							
						
						fafac22d60
	
				 | 
					
					
						|||
| 
						
						
							
						
						58f7ba55b3
	
				 | 
					
					
						|||
| 
						
						
							
						
						d2cfc2ee81
	
				 | 
					
					
						|||
| 
						
						
							
						
						5a9a446b9c
	
				 | 
					
					
						|||
| 
						
						
							
						
						39e2fc5142
	
				 | 
					
					
						|||
| 
						
						
							
						
						f94bb77091
	
				 | 
					
					
						|||
| 
						
						
							
						
						d3cfb12e16
	
				 | 
					
					
						
@@ -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)
 | 
			
		||||
 | 
			
		||||
# Adjust warnings
 | 
			
		||||
@@ -11,7 +11,8 @@ endif ()
 | 
			
		||||
add_definitions (-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_38)
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
if (WIN32)
 | 
			
		||||
@@ -77,7 +78,7 @@ if (USE_SYSTEM_TERMO)
 | 
			
		||||
	if (NOT Termo_FOUND)
 | 
			
		||||
		message (FATAL_ERROR "System termo library not found")
 | 
			
		||||
	endif ()
 | 
			
		||||
else ()
 | 
			
		||||
elseif (NOT WIN32)
 | 
			
		||||
	# We don't want the library to install, but EXCLUDE_FROM_ALL ignores tests
 | 
			
		||||
	add_subdirectory (termo EXCLUDE_FROM_ALL)
 | 
			
		||||
	file (WRITE ${PROJECT_BINARY_DIR}/CTestCustom.cmake
 | 
			
		||||
@@ -126,9 +127,9 @@ CHECK_FUNCTION_EXISTS ("resizeterm" HAVE_RESIZETERM)
 | 
			
		||||
 | 
			
		||||
# Localization
 | 
			
		||||
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 (
 | 
			
		||||
	${PROJECT_SOURCE_DIR}/po/${PROJECT_NAME}.pot
 | 
			
		||||
	"${PROJECT_SOURCE_DIR}/po/${PROJECT_NAME}.pot"
 | 
			
		||||
	ALL ${project_PO_FILES})
 | 
			
		||||
 | 
			
		||||
# Documentation
 | 
			
		||||
@@ -143,7 +144,7 @@ foreach (page "${PROJECT_NAME}.1")
 | 
			
		||||
	set (page_output "${PROJECT_BINARY_DIR}/${page}")
 | 
			
		||||
	list (APPEND project_MAN_PAGES "${page_output}")
 | 
			
		||||
	if (ASCIIDOCTOR_EXECUTABLE)
 | 
			
		||||
		add_custom_command (OUTPUT ${page_output}
 | 
			
		||||
		add_custom_command (OUTPUT "${page_output}"
 | 
			
		||||
			COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage
 | 
			
		||||
				-a release-version=${PROJECT_VERSION}
 | 
			
		||||
				-o "${page_output}"
 | 
			
		||||
@@ -151,7 +152,7 @@ foreach (page "${PROJECT_NAME}.1")
 | 
			
		||||
			DEPENDS "docs/${page}.adoc"
 | 
			
		||||
			COMMENT "Generating man page for ${page}" VERBATIM)
 | 
			
		||||
	elseif (A2X_EXECUTABLE)
 | 
			
		||||
		add_custom_command (OUTPUT ${page_output}
 | 
			
		||||
		add_custom_command (OUTPUT "${page_output}"
 | 
			
		||||
			COMMAND ${A2X_EXECUTABLE} --doctype manpage --format manpage
 | 
			
		||||
				-a release-version=${PROJECT_VERSION}
 | 
			
		||||
				-D "${PROJECT_BINARY_DIR}"
 | 
			
		||||
@@ -160,10 +161,10 @@ foreach (page "${PROJECT_NAME}.1")
 | 
			
		||||
			COMMENT "Generating man page for ${page}" VERBATIM)
 | 
			
		||||
	else ()
 | 
			
		||||
		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}
 | 
			
		||||
				awk -f ${ASCIIMAN} "${PROJECT_SOURCE_DIR}/docs/${page}.adoc"
 | 
			
		||||
				> ${page_output}
 | 
			
		||||
				> "${page_output}"
 | 
			
		||||
			DEPENDS "docs/${page}.adoc" ${ASCIIMAN}
 | 
			
		||||
			COMMENT "Generating man page for ${page}" VERBATIM)
 | 
			
		||||
	endif ()
 | 
			
		||||
@@ -180,7 +181,7 @@ if (WIN32)
 | 
			
		||||
endif (WIN32)
 | 
			
		||||
 | 
			
		||||
set (project_common_headers
 | 
			
		||||
	${PROJECT_BINARY_DIR}/config.h
 | 
			
		||||
	"${PROJECT_BINARY_DIR}/config.h"
 | 
			
		||||
	src/dictzip-input-stream.h
 | 
			
		||||
	src/stardict.h
 | 
			
		||||
	src/stardict-private.h
 | 
			
		||||
@@ -197,37 +198,9 @@ add_library (stardict OBJECT
 | 
			
		||||
set (project_common_sources $<TARGET_OBJECTS:stardict>)
 | 
			
		||||
 | 
			
		||||
# Generate a configuration file
 | 
			
		||||
configure_file (${PROJECT_SOURCE_DIR}/config.h.in
 | 
			
		||||
	${PROJECT_BINARY_DIR}/config.h)
 | 
			
		||||
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 ()
 | 
			
		||||
configure_file ("${PROJECT_SOURCE_DIR}/config.h.in"
 | 
			
		||||
	"${PROJECT_BINARY_DIR}/config.h")
 | 
			
		||||
include_directories ("${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}")
 | 
			
		||||
 | 
			
		||||
# Build the main executable and link it
 | 
			
		||||
set (project_libraries
 | 
			
		||||
@@ -239,31 +212,34 @@ set (project_headers
 | 
			
		||||
	${project_common_headers})
 | 
			
		||||
 | 
			
		||||
if (WITH_GUI)
 | 
			
		||||
	set (icon_svg ${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.svg)
 | 
			
		||||
	set (icon_base ${PROJECT_BINARY_DIR}/icons)
 | 
			
		||||
	include (IconUtils)
 | 
			
		||||
 | 
			
		||||
	# The largest size is mainly for an appropriately sized Windows icon
 | 
			
		||||
	set (icon_base "${PROJECT_BINARY_DIR}/icons")
 | 
			
		||||
	set (icon_png_list)
 | 
			
		||||
	foreach (icon_size 16 32 48 256)
 | 
			
		||||
		icon_to_png (${icon_svg} ${icon_size} ${icon_base} icon_png)
 | 
			
		||||
		list (APPEND icon_png_list ${icon_png})
 | 
			
		||||
		icon_to_png (${PROJECT_NAME} "${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.svg"
 | 
			
		||||
			${icon_size} "${icon_base}" icon_png)
 | 
			
		||||
		list (APPEND icon_png_list "${icon_png}")
 | 
			
		||||
	endforeach ()
 | 
			
		||||
 | 
			
		||||
	add_custom_target (icons ALL DEPENDS ${icon_png_list})
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
if (WIN32)
 | 
			
		||||
	set (icon_ico ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.ico)
 | 
			
		||||
	icon_for_win32 ("${icon_png_list}" ${icon_ico})
 | 
			
		||||
	list (REMOVE_ITEM icon_png_list "${icon_png}")
 | 
			
		||||
	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)
 | 
			
		||||
	list (APPEND project_sources ${resource_file})
 | 
			
		||||
	add_custom_command (OUTPUT ${resource_file}
 | 
			
		||||
	set (resource_file "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.rc")
 | 
			
		||||
	list (APPEND project_sources "${resource_file}")
 | 
			
		||||
	add_custom_command (OUTPUT "${resource_file}"
 | 
			
		||||
		COMMAND ${CMAKE_COMMAND} -E echo "1 ICON \"${PROJECT_NAME}.ico\""
 | 
			
		||||
			> ${resource_file} VERBATIM)
 | 
			
		||||
	set_property (SOURCE ${resource_file}
 | 
			
		||||
		APPEND PROPERTY OBJECT_DEPENDS ${icon_ico})
 | 
			
		||||
			> "${resource_file}" VERBATIM)
 | 
			
		||||
	set_property (SOURCE "${resource_file}"
 | 
			
		||||
		APPEND PROPERTY OBJECT_DEPENDS "${icon_ico}")
 | 
			
		||||
else ()
 | 
			
		||||
	list (APPEND project_libraries ${Ncursesw_LIBRARIES} termo-static)
 | 
			
		||||
	list (APPEND project_libraries ${Ncursesw_LIBRARIES} ${Termo_LIBRARIES})
 | 
			
		||||
	list (APPEND project_sources
 | 
			
		||||
		src/${PROJECT_NAME}-tui.c)
 | 
			
		||||
endif ()
 | 
			
		||||
@@ -324,7 +300,7 @@ if (NOT WIN32)
 | 
			
		||||
	if (WITH_GUI)
 | 
			
		||||
		install (FILES ${PROJECT_NAME}.svg
 | 
			
		||||
			DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
 | 
			
		||||
		install (DIRECTORY ${PROJECT_BINARY_DIR}/icons
 | 
			
		||||
		install (DIRECTORY ${icon_base}
 | 
			
		||||
			DESTINATION ${CMAKE_INSTALL_DATADIR})
 | 
			
		||||
		install (FILES ${PROJECT_NAME}.desktop
 | 
			
		||||
			DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
 | 
			
		||||
@@ -361,7 +337,7 @@ elseif (WITH_GUI)
 | 
			
		||||
	install (FILES
 | 
			
		||||
		${win32_deps_prefix}/share/icons/hicolor/index.theme
 | 
			
		||||
		DESTINATION share/icons/hicolor)
 | 
			
		||||
	install (DIRECTORY ${icon_base} DESTINATION share)
 | 
			
		||||
	install (DIRECTORY "${icon_base}" DESTINATION share)
 | 
			
		||||
 | 
			
		||||
	install (SCRIPT cmake/Win32Cleanup.cmake)
 | 
			
		||||
 | 
			
		||||
@@ -388,11 +364,11 @@ if (BUILD_TESTING)
 | 
			
		||||
	foreach (xml ${PROJECT_NAME}.xml ${PROJECT_NAME}.svg)
 | 
			
		||||
		if (xmlwf_EXECUTABLE)
 | 
			
		||||
			add_test (test-xmlwf-${xml} ${xmlwf_EXECUTABLE}
 | 
			
		||||
				${PROJECT_SOURCE_DIR}/${xml})
 | 
			
		||||
				"${PROJECT_SOURCE_DIR}/${xml}")
 | 
			
		||||
		endif ()
 | 
			
		||||
		if (xmllint_EXECUTABLE)
 | 
			
		||||
			add_test (test-xmllint-${xml} ${xmllint_EXECUTABLE} --noout
 | 
			
		||||
				${PROJECT_SOURCE_DIR}/${xml})
 | 
			
		||||
				"${PROJECT_SOURCE_DIR}/${xml}")
 | 
			
		||||
		endif ()
 | 
			
		||||
	endforeach ()
 | 
			
		||||
 | 
			
		||||
@@ -400,7 +376,7 @@ if (BUILD_TESTING)
 | 
			
		||||
	if (dfv_EXECUTABLE)
 | 
			
		||||
		foreach (df ${PROJECT_NAME}.desktop)
 | 
			
		||||
			add_test (test-dfv-${df} ${dfv_EXECUTABLE}
 | 
			
		||||
				${PROJECT_SOURCE_DIR}/${df})
 | 
			
		||||
				"${PROJECT_SOURCE_DIR}/${df}")
 | 
			
		||||
		endforeach ()
 | 
			
		||||
	endif ()
 | 
			
		||||
 | 
			
		||||
@@ -408,7 +384,7 @@ if (BUILD_TESTING)
 | 
			
		||||
		add_executable (test-${name}
 | 
			
		||||
			src/test-${name}.c ${project_common_sources})
 | 
			
		||||
		target_link_libraries (test-${name} ${project_common_libraries})
 | 
			
		||||
		add_test (test-${name} test-${name})
 | 
			
		||||
		add_test (NAME test-${name} COMMAND test-${name})
 | 
			
		||||
	endforeach ()
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							@@ -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
 | 
			
		||||
purpose with or without fee is hereby granted.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								README.adoc
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								README.adoc
									
									
									
									
									
								
							@@ -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,
 | 
			
		||||
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
 | 
			
		||||
similar projects, and can serve as a base for implementing other dictionary
 | 
			
		||||
software.
 | 
			
		||||
 | 
			
		||||
Screenshot
 | 
			
		||||
----------
 | 
			
		||||
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
 | 
			
		||||
--------
 | 
			
		||||
Regular releases are sporadic.  git master should be stable enough.  You can get
 | 
			
		||||
a package with the latest development version from Archlinux's AUR.
 | 
			
		||||
Regular releases are sporadic.  git master should be stable enough.
 | 
			
		||||
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
 | 
			
		||||
-------------
 | 
			
		||||
@@ -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.
 | 
			
		||||
 | 
			
		||||
 $ 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
 | 
			
		||||
 $ cmake --build build -- package
 | 
			
		||||
 | 
			
		||||
@@ -90,14 +95,8 @@ https://mega.co.nz/#!axtD0QRK!sbtBgizksyfkPqKvKEgr8GQ11rsWhtqyRgUUV0B7pwg[CZ <--
 | 
			
		||||
 | 
			
		||||
Further Development
 | 
			
		||||
-------------------
 | 
			
		||||
While I've been successfully using 'tdv' for many years now, some issues
 | 
			
		||||
should be addressed before including the software in regular Linux and/or
 | 
			
		||||
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.
 | 
			
		||||
Lacking configuration, standard StarDict locations should be scanned.
 | 
			
		||||
We should try harder to display arbitrary dictionaries sensibly.
 | 
			
		||||
 | 
			
		||||
Contributing and Support
 | 
			
		||||
------------------------
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
@@ -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)
 | 
			
		||||
@@ -55,7 +55,7 @@ while (my ($id, $synset) = each %synsets) {
 | 
			
		||||
 | 
			
		||||
# Output synsets exploded to individual words, with expanded relationships
 | 
			
		||||
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",
 | 
			
		||||
	'--date=2011-01-24', '--collation=cs_CZ') or die $!;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 w {
 | 
			
		||||
		my ($name, $dict, $collation) = @_;
 | 
			
		||||
		open(my $f, "|-", "tabfile", "--pango", "--collation=$collation",
 | 
			
		||||
		open(my $f, "|-", "tdv-tabfile", "--pango", "--collation=$collation",
 | 
			
		||||
			"--website=https://gnu.nemeckoceskyslovnik.cz",
 | 
			
		||||
			"gnu-fdl-$name") or die $!;
 | 
			
		||||
		print $f tabesc($keyword) . "\t" . tabesc(join("\n", @$defs))
 | 
			
		||||
 
 | 
			
		||||
@@ -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 w {
 | 
			
		||||
		my ($name, $dict, $collation) = @_;
 | 
			
		||||
		open(my $f, "|-", "tabfile", "--pango", "--collation=$collation",
 | 
			
		||||
		open(my $f, "|-", "tdv-tabfile", "--pango", "--collation=$collation",
 | 
			
		||||
			"--website=https://www.svobodneslovniky.cz",
 | 
			
		||||
			"gnu-fdl-$name") or die $!;
 | 
			
		||||
		print $f tabesc($keyword) . "\t" . tabesc(join("\n", @$defs))
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
curl -Lo- https://slovnik-cizich-slov.abz.cz/export.php | \
 | 
			
		||||
iconv -f latin2 -t UTF-8 | perl -CSD -F\\\| -le '
 | 
			
		||||
	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" \
 | 
			
		||||
	--website=https://slovnik-cizich-slov.abz.cz \
 | 
			
		||||
	--date="$(date +%F)" \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								liberty
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								liberty
									
									
									
									
									
								
							 Submodule liberty updated: bd1013f16a...0f20cce9c8
									
								
							@@ -357,6 +357,20 @@ error:
 | 
			
		||||
	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.
 | 
			
		||||
/// @return GList<StardictInfo *>. Deallocate the list with:
 | 
			
		||||
/// @code
 | 
			
		||||
@@ -377,12 +391,10 @@ stardict_list_dictionaries (const gchar *path)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		gchar *filename = g_build_filename (path, name, NULL);
 | 
			
		||||
		StardictInfo *ifo = g_new (StardictInfo, 1);
 | 
			
		||||
		if (load_ifo (ifo, filename, NULL))
 | 
			
		||||
			dicts = g_list_append (dicts, ifo);
 | 
			
		||||
		else
 | 
			
		||||
			g_free (ifo);
 | 
			
		||||
		StardictInfo *ifo = stardict_info_new (filename, NULL);
 | 
			
		||||
		g_free (filename);
 | 
			
		||||
		if (ifo)
 | 
			
		||||
			dicts = g_list_append (dicts, ifo);
 | 
			
		||||
	}
 | 
			
		||||
	g_dir_close (dir);
 | 
			
		||||
	g_pattern_spec_free (ps);
 | 
			
		||||
 
 | 
			
		||||
@@ -108,6 +108,7 @@ GQuark stardict_error_quark (void);
 | 
			
		||||
 | 
			
		||||
// --- 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_book_name (StardictInfo *sdi) G_GNUC_PURE;
 | 
			
		||||
gsize stardict_info_get_word_count (StardictInfo *sd) G_GNUC_PURE;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										307
									
								
								src/tdv-gui.c
									
									
									
									
									
								
							
							
						
						
									
										307
									
								
								src/tdv-gui.c
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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
 | 
			
		||||
 * purpose with or without fee is hereby granted.
 | 
			
		||||
@@ -295,6 +295,8 @@ show_error_dialog (GError *error)
 | 
			
		||||
	g_error_free (error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Loading -----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_new_dictionaries_loaded (G_GNUC_UNUSED GObject* source_object,
 | 
			
		||||
	GAsyncResult* res, G_GNUC_UNUSED gpointer user_data)
 | 
			
		||||
@@ -360,8 +362,8 @@ reload_dictionaries (GPtrArray *new_dictionaries, GError **error)
 | 
			
		||||
	return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_open (G_GNUC_UNUSED GtkMenuItem *item, G_GNUC_UNUSED gpointer data)
 | 
			
		||||
static GtkWidget *
 | 
			
		||||
new_open_dialog (void)
 | 
			
		||||
{
 | 
			
		||||
	// The default is local-only.  Paths are returned absolute.
 | 
			
		||||
	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);
 | 
			
		||||
	gtk_file_chooser_add_filter (chooser, filter);
 | 
			
		||||
	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 =
 | 
			
		||||
		g_ptr_array_new_with_free_func ((GDestroyNotify) dictionary_destroy);
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- 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
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
	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"));
 | 
			
		||||
		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.
 | 
			
		||||
	// 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..."));
 | 
			
		||||
	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;
 | 
			
		||||
	GtkWidget *item_selection =
 | 
			
		||||
		gtk_check_menu_item_new_with_mnemonic (_("_Follow selection"));
 | 
			
		||||
@@ -515,6 +813,7 @@ gui_main (char *argv[])
 | 
			
		||||
	GtkWidget *menu = gtk_menu_new ();
 | 
			
		||||
	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_settings);
 | 
			
		||||
#ifndef G_OS_WIN32
 | 
			
		||||
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item_selection);
 | 
			
		||||
#endif  // ! G_OS_WIN32
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								src/utils.c
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								src/utils.c
									
									
									
									
									
								
							@@ -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/gprintf.h>
 | 
			
		||||
#include <gio/gio.h>
 | 
			
		||||
@@ -217,7 +222,7 @@ load_project_config_file (GError **error)
 | 
			
		||||
	//   which is completely undocumented
 | 
			
		||||
	g_key_file_load_from_dirs (key_file,
 | 
			
		||||
		PROJECT_NAME G_DIR_SEPARATOR_S PROJECT_NAME ".conf",
 | 
			
		||||
		paths, NULL, 0, &e);
 | 
			
		||||
		paths, NULL, G_KEY_FILE_KEEP_COMMENTS, &e);
 | 
			
		||||
	g_free (paths);
 | 
			
		||||
	if (!e)
 | 
			
		||||
		return key_file;
 | 
			
		||||
@@ -231,6 +236,25 @@ load_project_config_file (GError **error)
 | 
			
		||||
	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 -----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,7 @@ gchar *resolve_relative_config_filename (const gchar *filename);
 | 
			
		||||
gchar *resolve_filename
 | 
			
		||||
	(const gchar *filename, gchar *(*relative_cb) (const char *));
 | 
			
		||||
GKeyFile *load_project_config_file (GError **error);
 | 
			
		||||
gboolean save_project_config_file (GKeyFile *key_file, GError **error);
 | 
			
		||||
 | 
			
		||||
// --- Loading -----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								tdv.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tdv.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 36 KiB  | 
							
								
								
									
										2
									
								
								termo
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								termo
									
									
									
									
									
								
							 Submodule termo updated: 2518b53e5a...f9a102456f
									
								
							
		Reference in New Issue
	
	Block a user