goldendict:yeni program eklendi.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Licensed under the GNU General Public License, version 3.
|
||||
# See the file http://www.gnu.org/copyleft/gpl.txt
|
||||
|
||||
from pisi.actionsapi import autotools
|
||||
from pisi.actionsapi import pisitools
|
||||
from pisi.actionsapi import get
|
||||
from pisi.actionsapi import shelltools
|
||||
|
||||
WorkDir = "goldendict-1.5.0-RC2"
|
||||
|
||||
def setup():
|
||||
shelltools.system("PREFIX=/usr qmake-qt5 CONFIG+=no_epwing_support")
|
||||
|
||||
|
||||
def build():
|
||||
autotools.make()
|
||||
|
||||
def install():
|
||||
autotools.rawInstall("INSTALL_ROOT=%s" % get.installDIR())
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,40 @@
|
||||
*.o
|
||||
*~
|
||||
|
||||
mouseover_win32/*.dll
|
||||
mouseover_win32/*.a
|
||||
|
||||
locale/*.qm
|
||||
debug/
|
||||
build/
|
||||
release/
|
||||
tmp/
|
||||
goldendict
|
||||
winlibs/lib32-48/
|
||||
|
||||
Makefile
|
||||
Makefile.Debug
|
||||
Makefile.Release
|
||||
|
||||
goldendict.pro.user
|
||||
goldendict.pro.user.*
|
||||
object_script.goldendict.Debug
|
||||
object_script.goldendict.Release
|
||||
|
||||
version.txt
|
||||
|
||||
.gitattributes
|
||||
|
||||
/goldendict.app/
|
||||
/GoldenDict.app/
|
||||
*.dmg
|
||||
|
||||
.DS_Store
|
||||
Info.plist
|
||||
GoldenDict.xcodeproj/
|
||||
|
||||
# visual studio files
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.suo
|
||||
*.vcxproj.user
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "winlibs/lib/msvc"]
|
||||
path = winlibs/lib/msvc
|
||||
url = git://github.com/Tvangeste/goldendict-winlibs-prebuilt.git
|
||||
@@ -0,0 +1,32 @@
|
||||
Konstantin Isakov <ikm\goldendict.org>: Principal author of the program
|
||||
Jennie Petoumenou <epetoumenou\gmail.com>: Greek transliteration and Greek translation
|
||||
Mindaugas Baranauskas <embar\super.lt>: Lithuanian translation
|
||||
Vit Pelcak <vit\pelcak.org>: Czech translation
|
||||
Hero Phương <herophuong93\gmail.com> , Trần Đình Anh Tuấn <tuantda.vn\gmail.com>: Vietnamese translation
|
||||
Daniele Di Pisa <daniele\systems.it>: Italian translation
|
||||
Daniel Kaneider: German translation
|
||||
Zhang Jinsong <zhangsong023\gmail.com>: Simplified Chinese translation
|
||||
Usama Akkad <uahello\gmail.com>, Linux Arab Community Team <linuxac.org>: Arabic translation
|
||||
Korostil Daniel <ted.korostiled\gmail.com>: Ukrainian translation
|
||||
Grzegorz Karpowicz <greg.karpowicz\gmail.com>: Polish translation
|
||||
Nardog <alphisation\gmail.com>: Japanese translation
|
||||
Maha 吳寶原, Ray Chou 周邦信, Marcus Bingenheimer, 黃文龍 <wlhunag\gmail.com>: Traditional Chinese translation
|
||||
Besmir Godole <bgodole\gmail.com>: Albanian translation
|
||||
Leonardo Montenegro <thesolidone\gmail.com>, MCHAL <MCHAL@anomail.de>, Alexandro Casanova <shorterfire\gmail.com>: Brazilian Portuguese translation
|
||||
Julian Depetris Chauvin <jdepetris\gmail.com>: Interface enhancements
|
||||
Yanina Weingast <yjweingast\gmail.com>: Argentinian Spanish translation
|
||||
Tvangeste <i.4m.l33t\yandex.ru>: Interface enhancements
|
||||
Zdenko Podobný <zdenop\gmail.com>: Slovak translation
|
||||
Şükrü Yekta Karabulut <sukruyekta\gmail.com>: Turkish translation
|
||||
Amos Batto <amosbatto\yahoo.com>: Quechua, Aymara and Bolivian Spanish translations
|
||||
Carlos A. Cortijo Bon <carlos_cortijo\yahoo.com>: Spanish from Spain translation
|
||||
Victor Ibragimov <victor.ibragimov\gmail.com>: Tajik translation
|
||||
Maksim Tamkovič <quendimax\gmail.com>: Belarusian translation and transliteration
|
||||
VirtualTam <virtualtam\myopera.com>: French translation
|
||||
Panho Ku <maranamho\gmail.com>: Korean translation
|
||||
Тим Перце <kreativnooko\gmail.com>: Serbian translation
|
||||
Cris van Minnen: Dutch translation
|
||||
Timon Wong <timon86.wang\gmail.com>: MDict (*.mdx/*.mdd) dictionary format
|
||||
PICTT Turkmenistan: Turkmen translation
|
||||
Nasrollah Noori <nanopqr@gmail.com>: Persian translation
|
||||
Vladimir Gerovski: Macedonian Translation
|
||||
@@ -0,0 +1,14 @@
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" publicKeyToken="6595b64144ccf1df" language="*" processorArchitecture="*"></assemblyIdentity>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
||||
@@ -0,0 +1,691 @@
|
||||
GoldenDict, a dictionary lookup program.
|
||||
Copyright (C) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
|
||||
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
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
The text of the license follows.
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura and Tvangeste.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemDefinitionGroup>
|
||||
<QtTranslate>
|
||||
<OutputFile Condition="'%(QtLrelease.OutputFile)' == ''">%(Filename).qm</OutputFile>
|
||||
<Compress Condition="'%(QtLrelease.Compress)' == ''">false</Compress>
|
||||
<LRELEASE Condition="'%(QtLrelease.LRELEASE)' == ''">$(QTDIR)\bin\lrelease.exe</LRELEASE>
|
||||
<CommandLineTemplate>%(LRELEASE) [AllOptions] [AdditionalOptions] [Inputs]</CommandLineTemplate>
|
||||
</QtTranslate>
|
||||
</ItemDefinitionGroup>
|
||||
<PropertyGroup>
|
||||
<BuildGenerateSourcesTargets>$(BuildGenerateSourcesTargets);_QtTranslate</BuildGenerateSourcesTargets>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura and Tvangeste.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
|
||||
<AvailableItemName Include="QtTranslate">
|
||||
<Targets>QtTranslate</Targets>
|
||||
</AvailableItemName>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask
|
||||
TaskName="QtLrelease"
|
||||
TaskFactory="XamlTaskFactory"
|
||||
AssemblyName="Microsoft.Build.Tasks.v4.0">
|
||||
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
|
||||
</UsingTask>
|
||||
|
||||
<Target
|
||||
Name="SelectQtTranslate"
|
||||
DependsOnTargets="_SelectedFiles;SelectCustomBuild"
|
||||
>
|
||||
<ItemGroup Condition="'@(SelectedFiles)' != ''">
|
||||
<QtTranslate
|
||||
Remove="@(QtTranslate)"
|
||||
Condition="'%(Identity)' != '@(SelectedFiles)'"
|
||||
/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="QtTranslate"
|
||||
Condition="'@(QtTranslate)' != ''"
|
||||
DependsOnTargets="SelectQtTranslate"
|
||||
Inputs="@(QtTranslate)"
|
||||
Outputs="%(OutputFile)"
|
||||
>
|
||||
<Message Text="LRELEASE %(QtTranslate.Identity) -> $([System.IO.Path]::GetFileName('%(QtTranslate.OutputFile)'))"
|
||||
Importance="high"/>
|
||||
<ItemGroup>
|
||||
<QtTranslate_tlog
|
||||
Include="%(QtTranslate.OutputFile)"
|
||||
Condition="'%(QtTranslate.OutputFile)' != '' and '%(QtTranslate.ExcludedFromBuild)' != 'true'"
|
||||
>
|
||||
<Source>@(QtTranslate, '|')</Source>
|
||||
</QtTranslate_tlog>
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile
|
||||
Condition="'@(QtTranslate_tlog)' != '' and '%(QtTranslate_tlog.ExcludedFromBuild)' != 'true'"
|
||||
File="$(IntDir)$(ProjectName).write.1.tlog"
|
||||
Lines="^%(QtTranslate_tlog.Source);@(QtTranslate_tlog->'%(Fullpath)')"
|
||||
/>
|
||||
<MakeDir
|
||||
Directories="$([System.IO.Path]::GetDirectoryName('%(QtTranslate.OutputFile)'))"
|
||||
/>
|
||||
<QtLrelease
|
||||
Condition="'%(QtTranslate.ExcludedFromBuild)' != 'true'"
|
||||
OutputFile="%(QtTranslate.OutputFile)"
|
||||
Inputs="%(QtTranslate.Identity)"
|
||||
AdditionalOptions="%(QtTranslate.AdditionalOptions)"
|
||||
CommandLineTemplate="%(QtTranslate.CommandLineTemplate)"
|
||||
/>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="QtTranslateAndInclude"
|
||||
Condition="'@(QtTranslate)' != ''"
|
||||
DependsOnTargets="SelectQtTranslate;QtTranslate"
|
||||
>
|
||||
<ItemGroup>
|
||||
<ClInclude
|
||||
Include="%(QtTranslate.OutputFile)"
|
||||
Condition="'%(QtTranslate.ExcludedFromBuild)' != 'true'"
|
||||
>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>%(QtTranslate.Identity)</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_QtTranslate"
|
||||
BeforeTargets="ClCompile"
|
||||
DependsOnTargets="$(BeforeQtTranslateTargets);QtTranslateAndInclude;$(AfterQtTranslateTargets)"
|
||||
/>
|
||||
</Project>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura and Tvangeste.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<ProjectSchemaDefinitions
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<Rule
|
||||
Name="QtLrelease"
|
||||
DisplayName="Qt Lrelease Tool"
|
||||
PageTemplate="tool"
|
||||
SwitchPrefix="-"
|
||||
Order="220"
|
||||
>
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="QtTranslate"
|
||||
HasConfigurationCondition="true"
|
||||
/>
|
||||
</Rule.DataSource>
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General"
|
||||
/>
|
||||
<Category
|
||||
Name="Command Line"
|
||||
DisplayName="Command Line"
|
||||
Subtype="CommandLine"
|
||||
/>
|
||||
</Rule.Categories>
|
||||
<StringProperty
|
||||
Name="OutputFile"
|
||||
Subtype="file"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtlinguist/linguist-manager.html#using-lrelease"
|
||||
DisplayName="Output File"
|
||||
Description="Write output to <file>. (-qm <file>)"
|
||||
Switch="qm "[value]""
|
||||
/>
|
||||
<BoolProperty
|
||||
Name="Compress"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtlinguist/linguist-manager.html#using-lrelease"
|
||||
DisplayName="Compress the QM files"
|
||||
Description="Compress the QM files. (-compress)"
|
||||
Switch="compress"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="Inputs"
|
||||
Category="Command Line"
|
||||
IsRequired="true"
|
||||
>
|
||||
<StringProperty.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="QtTranslate"
|
||||
SourceType="Item"
|
||||
/>
|
||||
</StringProperty.DataSource>
|
||||
</StringProperty>
|
||||
<StringProperty
|
||||
Subtype="AdditionalOptions"
|
||||
Name="AdditionalOptions"
|
||||
Category="Command Line"
|
||||
DisplayName="Additional Options"
|
||||
F1Keyword="vc.project.AdditionalOptionsPage"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="LRELEASE"
|
||||
Subtype="file"
|
||||
DisplayName="LRELEASE"
|
||||
Description="Path to lrelease.exe"
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="CommandLineTemplate"
|
||||
Category="Command Line"
|
||||
DisplayName="Command Line"
|
||||
Visible="False"
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
</Rule>
|
||||
<ItemType Name="QtTranslate" DisplayName="Qt Translation" />
|
||||
<FileExtension Name="*.ts" ContentType="QtTranslate" />
|
||||
<ContentType Name="QtTranslate" DisplayName="Qt Translation" ItemType="QtTranslate" />
|
||||
</ProjectSchemaDefinitions>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemDefinitionGroup>
|
||||
<QtMOCCompile>
|
||||
<OutputFile Condition="'%(QtMOCCompile.OutputFile)' == ''">%(Filename)%(Extension).$(Platform).$(Configuration).cpp</OutputFile>
|
||||
<DoNotGenerateInclude Condition="'%(QtMOCCompile.DoNotGenerateInclude)' == ''">false</DoNotGenerateInclude>
|
||||
<NoWarnings Condition="'%(QtMOCCompile.NoWarnings)' == ''">false</NoWarnings>
|
||||
<BaseIncludePath Condition="'%(QtMOCCompile.BaseIncludePath)' == ''"></BaseIncludePath>
|
||||
<DefaultIncludeDirectories Condition="'%(QtMOCCompile.DefaultIncludeDirectories)' == ''"></DefaultIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'%(QtMOCCompile.AdditionalIncludeDirectories)' == ''"></AdditionalIncludeDirectories>
|
||||
<PreprocessOnly Condition="'%(QtMOCCompile.PreprocessOnly)' == ''">false</PreprocessOnly>
|
||||
<DefaultPreprocessorDefinitions Condition="'%(QtMOCCompile.DefaultPreprocessorDefinitions)' == ''"></DefaultPreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'%(QtMOCCompile.PreprocessorDefinitions)' == ''"></PreprocessorDefinitions>
|
||||
<DefaultUndefinePreprocessorDefinitions Condition="'%(QtMOCCompile.DefaultUndefinePreprocessorDefinitions)' == ''"></DefaultUndefinePreprocessorDefinitions>
|
||||
<UndefinePreprocessorDefinitions Condition="'%(QtMOCCompile.PreprocessorDefinitions)' == ''"></UndefinePreprocessorDefinitions>
|
||||
<MOC Condition="'%(QtMOCCompile.MOC)' == ''">$(QTDIR)\bin\moc.exe</MOC>
|
||||
<ClCompileAssemblerListingLocation Condition="'%(QtMOCCompile.ClCompileAssemblerListingLocation)' == ''"></ClCompileAssemblerListingLocation>
|
||||
<ClCompileObjectFileName Condition="'%(QtMOCCompile.ClCompileObjectFileName)' == ''"></ClCompileObjectFileName>
|
||||
<ClCompileXMLDocumentationFileName Condition="'%(QtMOCCompile.ClCompileXMLDocumentationFileName)' == ''"></ClCompileXMLDocumentationFileName>
|
||||
<CommandLineTemplate>%(MOC) [AllOptions] [AdditionalOptions] [Inputs]</CommandLineTemplate>
|
||||
</QtMOCCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<PropertyGroup>
|
||||
<BuildGenerateSourcesTargets>$(BuildGenerateSourcesTargets);_QtMOCCompile</BuildGenerateSourcesTargets>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
|
||||
<AvailableItemName Include="QtMOCCompile">
|
||||
<Targets>QtMOCCompile</Targets>
|
||||
</AvailableItemName>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask
|
||||
TaskName="QtMOC"
|
||||
TaskFactory="XamlTaskFactory"
|
||||
AssemblyName="Microsoft.Build.Tasks.v4.0">
|
||||
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
|
||||
</UsingTask>
|
||||
|
||||
<Target
|
||||
Name="SelectQtMOCCompile"
|
||||
DependsOnTargets="_SelectedFiles;SelectCustomBuild"
|
||||
>
|
||||
<ItemGroup Condition="'@(SelectedFiles)' != ''">
|
||||
<QtMOCCompile
|
||||
Remove="@(QtMOCCompile)"
|
||||
Condition="'%(Identity)' != '@(SelectedFiles)'"
|
||||
/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="QtMOCCompile"
|
||||
Condition="'@(QtMOCCompile)' != ''"
|
||||
DependsOnTargets="SelectQtMOCCompile;QtUICCompile"
|
||||
Inputs="@(QtMOCCompile)"
|
||||
Outputs="%(OutputFile)"
|
||||
>
|
||||
<Message Text="MOC %(QtMOCCompile.Identity) -> $([System.IO.Path]::GetFileName('%(QtMOCCompile.OutputFile)'))"
|
||||
Importance="high"/>
|
||||
<!-- Almost for sure this will cause issues when this target is skipped. -->
|
||||
<ItemGroup>
|
||||
<!-- Take care of other common C++ extensions. -->
|
||||
<ClCompile
|
||||
Include="%(QtMOCCompile.RootDir)%(QtMOCCompile.Directory)%(QtMOCCompile.Filename).cpp"
|
||||
Condition="!exists('%(QtMOCCompile.RootDir)%(QtMOCCompile.Directory)%(QtMOCCompile.Filename).cpp')"
|
||||
>
|
||||
<QtMOCCompileFakeItem>true</QtMOCCompileFakeItem>
|
||||
</ClCompile>
|
||||
<QtMOCCompileNoExtension Include="@(QtMOCCompile->'%(RootDir)%(Directory)%(Filename)')" />
|
||||
<ClCompileNoExtension Include="@(ClCompile->'%(RootDir)%(Directory)%(Filename)')" />
|
||||
<BuddyClCompile Include="@(ClCompileNoExtension)" Condition="'%(Identity)' == '@(QtMOCCompileNoExtension)'" />
|
||||
<QtMOCCompile>
|
||||
<DefaultIncludeDirectories Condition="'%(QtMOCCompile.DefaultIncludeDirectories)' == ''">@(BuddyClCompile->'%(AdditionalIncludeDirectories)')</DefaultIncludeDirectories>
|
||||
<DefaultPreprocessorDefinitions Condition="'%(QtMOCCompile.DefaultPreprocessorDefinitions)' == ''">@(BuddyClCompile->'%(PreprocessorDefinitions)')</DefaultPreprocessorDefinitions>
|
||||
<DefaultUndefinePreprocessorDefinitions Condition="'%(QtMOCCompile.DefaultUndefinePreprocessorDefinitions)' == ''">@(BuddyClCompile->'%(UndefinePreprocessorDefinitions)')</DefaultUndefinePreprocessorDefinitions>
|
||||
</QtMOCCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtMOCCompile_tlog
|
||||
Include="%(QtMOCCompile.OutputFile)"
|
||||
Condition="'%(QtMOCCompile.OutputFile)' != '' and '%(QtMOCCompile.ExcludedFromBuild)' != 'true'"
|
||||
>
|
||||
<Source>@(QtMOCCompile, '|')</Source>
|
||||
</QtMOCCompile_tlog>
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile
|
||||
Condition="'@(QtMOCCompile_tlog)' != '' and '%(QtMOCCompile_tlog.ExcludedFromBuild)' != 'true'"
|
||||
File="$(IntDir)$(ProjectName).write.1.tlog"
|
||||
Lines="^%(QtMOCCompile_tlog.Source);@(QtMOCCompile_tlog->'%(Fullpath)')"
|
||||
/>
|
||||
<MakeDir
|
||||
Directories="$([System.IO.Path]::GetDirectoryName('%(QtMOCCompile.OutputFile)'))"
|
||||
/>
|
||||
<QtMOC
|
||||
Condition="'%(QtMOCCompile.ExcludedFromBuild)' != 'true'"
|
||||
OutputFile="%(QtMOCCompile.OutputFile)"
|
||||
DoNotGenerateInclude="%(QtMOCCompile.DoNotGenerateInclude)"
|
||||
NoWarnings="%(QtMOCCompile.NoWarnings)"
|
||||
BaseIncludePath="%(QtMOCCompile.BaseIncludePath)"
|
||||
DefaultIncludeDirectories="%(QtMOCCompile.DefaultIncludeDirectories)"
|
||||
AdditionalIncludeDirectories="%(QtMOCCompile.AdditionalIncludeDirectories)"
|
||||
PreprocessOnly="%(QtMOCCompile.PreprocessOnly)"
|
||||
DefaultPreprocessorDefinitions="%(QtMOCCompile.DefaultPreprocessorDefinitions)"
|
||||
PreprocessorDefinitions="%(QtMOCCompile.PreprocessorDefinitions)"
|
||||
DefaultUndefinePreprocessorDefinitions="%(QtMOCCompile.DefaultUndefinePreprocessorDefinitions)"
|
||||
UndefinePreprocessorDefinitions="%(QtMOCCompile.UndefinePreprocessorDefinitions)"
|
||||
Inputs="%(QtMOCCompile.Identity)"
|
||||
AdditionalOptions="%(QtMOCCompile.AdditionalOptions)"
|
||||
CommandLineTemplate="%(QtMOCCompile.CommandLineTemplate)"
|
||||
/>
|
||||
<ItemGroup>
|
||||
<ClCompile
|
||||
Remove="@(ClCompile)"
|
||||
Condition="'%(ClCompile.QtMOCCompileFakeItem)' == 'true'"
|
||||
/>
|
||||
<QtMOCCompileNoExtension Remove="*" />
|
||||
<ClCompileNoExtension Remove="*" />
|
||||
<BuddyClCompile Remove="*" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="QtMOCCompileAndInclude"
|
||||
Condition="'@(QtMOCCompile)' != ''"
|
||||
DependsOnTargets="SelectQtMOCCompile;QtMOCCompile"
|
||||
>
|
||||
<ItemGroup>
|
||||
<ClCompile
|
||||
Include="%(QtMOCCompile.OutputFile)"
|
||||
Condition="'%(QtMOCCompile.ExcludedFromBuild)' != 'true'"
|
||||
>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>%(QtMOCCompile.Identity)</DependentUpon>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>%(QtMOCCompile.AdditionalIncludeDirectories);%(QtMOCCompile.DefaultIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>%(QtMOCCompile.DefaultPreprocessorDefinitions);%(QtMOCCompile.PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<UndefinePreprocessorDefinitions>%(QtMOCCompile.DefaultUndefinePreprocessorDefinitions);%(QtMOCCompile.UndefinePreprocessorDefinitions)</UndefinePreprocessorDefinitions>
|
||||
<AssemblerListingLocation Condition="'%(QtMOCCompile.ClCompileAssemblerListingLocation)' != ''">%(QtMOCCompile.ClCompileAssemblerListingLocation)</AssemblerListingLocation>
|
||||
<ObjectFileName Condition="'%(QtMOCCompile.ClCompileObjectFileName)' != ''">%(QtMOCCompile.ClCompileObjectFileName)</ObjectFileName>
|
||||
<XMLDocumentationFileName Condition="'%(QtMOCCompile.ClCompileXMLDocumentationFileName)' != ''">%(QtMOCCompile.ClCompileXMLDocumentationFileName)</XMLDocumentationFileName>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_QtMOCCompile"
|
||||
BeforeTargets="ClCompile"
|
||||
DependsOnTargets="$(BeforeQtMOCCompileTargets);QtMOCCompileAndInclude;$(AfterQtMOCCompileTargets)"
|
||||
/>
|
||||
</Project>
|
||||
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<ProjectSchemaDefinitions
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<Rule
|
||||
Name="QtMOC"
|
||||
DisplayName="Qt Meta Object Compiler"
|
||||
PageTemplate="tool"
|
||||
SwitchPrefix="-"
|
||||
Order="222"
|
||||
>
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="QtMOCCompile"
|
||||
HasConfigurationCondition="true"
|
||||
/>
|
||||
</Rule.DataSource>
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General"
|
||||
/>
|
||||
<Category
|
||||
Name="Command Line"
|
||||
DisplayName="Command Line"
|
||||
Subtype="CommandLine"
|
||||
/>
|
||||
</Rule.Categories>
|
||||
<StringProperty
|
||||
Name="OutputFile"
|
||||
Subtype="file"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/moc.html#command-line-options"
|
||||
DisplayName="Output File"
|
||||
Description="Write output to <file> rather than to standard output. (-o <file>)"
|
||||
Switch="o "[value]""
|
||||
/>
|
||||
<!-- -f -->
|
||||
<BoolProperty
|
||||
Name="DoNotGenerateInclude"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/moc.html#command-line-options"
|
||||
DisplayName="Do Not Generate Include"
|
||||
Description="Do not generate an #include statement in the output. This may be used to run the moc on on a C++ file containing one or more class declarations. You should then #include the meta-object code in the .cpp file. (-i)"
|
||||
Switch="i"
|
||||
/>
|
||||
<BoolProperty
|
||||
Name="NoWarnings"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/moc.html#command-line-options"
|
||||
DisplayName="No Warnings"
|
||||
Description="Do not generate any warnings. (Not recommended.) (-nw)"
|
||||
Switch="nw"
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="BaseIncludePath"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/moc.html#command-line-options"
|
||||
DisplayName="Base Include Path"
|
||||
Description="Makes the moc prepend <path>/ to the file name in the generated #include statement. (-p<path>)"
|
||||
Switch="p "[value]""
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="DefaultIncludeDirectories"
|
||||
Switch="I "[value]""
|
||||
Visible="false"
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="AdditionalIncludeDirectories"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/moc.html#command-line-options"
|
||||
DisplayName="Additional Include Directories"
|
||||
Description="Add dir to the include path for header files. (-I <dir>)"
|
||||
Switch="I "[value]""
|
||||
/>
|
||||
<BoolProperty
|
||||
Name="PreprocessOnly"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/moc.html#command-line-options"
|
||||
DisplayName="Preprocess Only"
|
||||
Description="Preprocess only; do not generate meta-object code. (-E)"
|
||||
Switch="E"
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="DefaultPreprocessorDefinitions"
|
||||
Switch="D "[value]""
|
||||
Visible="false"
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="PreprocessorDefinitions"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/moc.html#command-line-options"
|
||||
DisplayName="Preprocessor Definitions"
|
||||
Description="Define macro, with optional definition. (-D <macro>[=<def>])"
|
||||
Switch="D "[value]""
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="DefaultUndefinePreprocessorDefinitions"
|
||||
Switch="U "[value]""
|
||||
Visible="false"
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="UndefinePreprocessorDefinitions"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/moc.html#command-line-options"
|
||||
DisplayName="Undefine Preprocessor Definitions"
|
||||
Description="Undefine macro. (-U <macro>)"
|
||||
Switch="U "[value]""
|
||||
/>
|
||||
<StringProperty
|
||||
Name="Inputs"
|
||||
Category="Command Line"
|
||||
IsRequired="true"
|
||||
>
|
||||
<StringProperty.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="QtMOCCompile"
|
||||
SourceType="Item"
|
||||
/>
|
||||
</StringProperty.DataSource>
|
||||
</StringProperty>
|
||||
<StringProperty
|
||||
Subtype="AdditionalOptions"
|
||||
Name="AdditionalOptions"
|
||||
Category="Command Line"
|
||||
DisplayName="Additional Options"
|
||||
F1Keyword="vc.project.AdditionalOptionsPage"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="MOC"
|
||||
Subtype="file"
|
||||
DisplayName="MOC"
|
||||
Description="Path to moc.exe"
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="CommandLineTemplate"
|
||||
Category="Command Line"
|
||||
DisplayName="Command Line"
|
||||
Visible="False"
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
</Rule>
|
||||
<ItemType Name="QtMOCCompile" DisplayName="Qt MOC Compile" />
|
||||
<FileExtension Name="*.h;*.hpp;*.hxx;*.hh" ContentType="QtMOCCompile" />
|
||||
<ContentType Name="QtMOCCompile" DisplayName="Qt MOC Compile" ItemType="QtMOCCompile" />
|
||||
</ProjectSchemaDefinitions>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemDefinitionGroup>
|
||||
<QtRCCCompile>
|
||||
<OutputFile Condition="'%(QtRCCCompile.OutputFile)' == ''">%(Filename)%(Extension).cpp</OutputFile>
|
||||
<ExternalInitializationFunction Condition="'%(QtRCCCompile.ExternalInitializationFunction)' == ''">%(Filename)</ExternalInitializationFunction>
|
||||
<Threshold Condition="'%(QtRCCCompile.Threshold)' == ''"></Threshold>
|
||||
<CompressLevel Condition="'%(QtRCCCompile.CompressLevel)' == ''">-1</CompressLevel>
|
||||
<Root Condition="'%(QtRCCCompile.Root)' == ''"></Root>
|
||||
<DisableCompression Condition="'%(QtRCCCompile.DisableCompression)' == ''">true</DisableCompression>
|
||||
<Binary Condition="'%(QtRCCCompile.Binary)' == ''">false</Binary>
|
||||
<RCC Condition="'%(QtRCCCompile.RCC)' == ''">$(QTDIR)\bin\rcc.exe</RCC>
|
||||
<AdditionalDependencies Condition="'%(QtRCCCompile.AdditionalDependencies)' == ''">$(QTDIR)\bin\rcc.exe;$(MSBuildProjectFile)</AdditionalDependencies>
|
||||
<ClCompileAssemblerListingLocation Condition="'%(QtMOCCompile.ClCompileAssemblerListingLocation)' == ''"></ClCompileAssemblerListingLocation>
|
||||
<ClCompileObjectFileName Condition="'%(QtMOCCompile.ClCompileObjectFileName)' == ''"></ClCompileObjectFileName>
|
||||
<ClCompileXMLDocumentationFileName Condition="'%(QtMOCCompile.ClCompileXMLDocumentationFileName)' == ''"></ClCompileXMLDocumentationFileName>
|
||||
<CommandLineTemplate>%(RCC) [AllOptions] [AdditionalOptions] [Inputs]</CommandLineTemplate>
|
||||
</QtRCCCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<PropertyGroup>
|
||||
<BuildGenerateSourcesTargets>$(BuildGenerateSourcesTargets);_QtRCCCompile</BuildGenerateSourcesTargets>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
|
||||
<AvailableItemName Include="QtRCCCompile">
|
||||
<Targets>QtRCCCompile</Targets>
|
||||
</AvailableItemName>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask
|
||||
TaskName="QtRCC"
|
||||
TaskFactory="XamlTaskFactory"
|
||||
AssemblyName="Microsoft.Build.Tasks.v4.0">
|
||||
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
|
||||
</UsingTask>
|
||||
|
||||
<Target
|
||||
Name="SelectQtRCCCompile"
|
||||
DependsOnTargets="_SelectedFiles;SelectCustomBuild"
|
||||
>
|
||||
<ItemGroup Condition="'@(SelectedFiles)' != ''">
|
||||
<QtRCCCompile
|
||||
Remove="@(QtRCCCompile)"
|
||||
Condition="'%(Identity)' != '@(SelectedFiles)'"
|
||||
/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="CalculateQtRCCDeps"
|
||||
DependsOnTargets="SelectQtRCCCompile"
|
||||
Outputs="%(QtRCCCompile.Identity)">
|
||||
<Message Text="Calculating dependencies for %(QtRCCCompile.Identity)" />
|
||||
|
||||
<!-- Look into the qrc file and fetch all the referenced files -->
|
||||
<XmlPeek XmlInputPath="%(QtRCCCompile.FullPath)"
|
||||
Query="//file/text()">
|
||||
<Output TaskParameter="Result" ItemName="QtRCCDeps" />
|
||||
</XmlPeek>
|
||||
|
||||
<!-- Add the calculated dependencies to the AdditionalDependencies -->
|
||||
<ItemGroup>
|
||||
<QtRCCCompile>
|
||||
<AdditionalDependencies>@(QtRCCDeps);%(QtRCCCompile.AdditionalDependencies)</AdditionalDependencies>
|
||||
</QtRCCCompile>
|
||||
</ItemGroup>
|
||||
|
||||
<Message Text="Actual Dependencies: @(QtRCCCompile->'%(AdditionalDependencies)')" />
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="QtRCCCompile"
|
||||
Condition="'@(QtRCCCompile)' != ''"
|
||||
DependsOnTargets="SelectQtRCCCompile;CalculateQtRCCDeps"
|
||||
Inputs="@(QtRCCCompile);%(QtRCCCompile.AdditionalDependencies);"
|
||||
Outputs="%(OutputFile)"
|
||||
>
|
||||
<Message Text="RCC %(QtRCCCompile.Identity) -> $([System.IO.Path]::GetFileName('%(QtRCCCompile.OutputFile)'))"
|
||||
Importance="high"/>
|
||||
<ItemGroup>
|
||||
<QtRCCCompile_tlog
|
||||
Include="%(QtRCCCompile.OutputFile)"
|
||||
Condition="'%(QtRCCCompile.OutputFile)' != '' and '%(QtRCCCompile.ExcludedFromBuild)' != 'true'"
|
||||
>
|
||||
<Source>@(QtRCCCompile, '|')</Source>
|
||||
</QtRCCCompile_tlog>
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile
|
||||
Condition="'@(QtRCCCompile_tlog)' != '' and '%(QtRCCCompile_tlog.ExcludedFromBuild)' != 'true'"
|
||||
File="$(IntDir)$(ProjectName).write.1.tlog"
|
||||
Lines="^%(QtRCCCompile_tlog.Source);@(QtRCCCompile_tlog->'%(Fullpath)')"
|
||||
/>
|
||||
<MakeDir
|
||||
Directories="$([System.IO.Path]::GetDirectoryName('%(QtRCCCompile.OutputFile)'))"
|
||||
/>
|
||||
<QtRCC
|
||||
Condition="'%(QtRCCCompile.ExcludedFromBuild)' != 'true'"
|
||||
OutputFile="%(QtRCCCompile.OutputFile)"
|
||||
ExternalInitializationFunction="%(QtRCCCompile.ExternalInitializationFunction)"
|
||||
Threshold="%(QtRCCCompile.Threshold)"
|
||||
CompressLevel="%(QtRCCCompile.CompressLevel)"
|
||||
Root="%(QtRCCCompile.Root)"
|
||||
DisableCompression="%(QtRCCCompile.DisableCompression)"
|
||||
Binary="%(QtRCCCompile.Binary)"
|
||||
Inputs="%(QtRCCCompile.Identity)"
|
||||
AdditionalOptions="%(QtRCCCompile.AdditionalOptions)"
|
||||
CommandLineTemplate="%(QtRCCCompile.CommandLineTemplate)"
|
||||
/>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="QtRCCCompileAndInclude"
|
||||
Condition="'@(QtRCCCompile)' != ''"
|
||||
DependsOnTargets="SelectQtRCCCompile;QtRCCCompile"
|
||||
>
|
||||
<ItemGroup>
|
||||
<ClCompile
|
||||
Include="%(QtRCCCompile.OutputFile)"
|
||||
Condition="'%(QtRCCCompile.ExcludedFromBuild)' != 'true'"
|
||||
>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>%(QtRCCCompile.Identity)</DependentUpon>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<AssemblerListingLocation Condition="'%(QtRCCCompile.ClCompileAssemblerListingLocation)' != ''">%(QtRCCCompile.ClCompileAssemblerListingLocation)</AssemblerListingLocation>
|
||||
<ObjectFileName Condition="'%(QtRCCCompile.ClCompileObjectFileName)' != ''">%(QtRCCCompile.ClCompileObjectFileName)</ObjectFileName>
|
||||
<XMLDocumentationFileName Condition="'%(QtRCCCompile.ClCompileXMLDocumentationFileName)' != ''">%(QtRCCCompile.ClCompileXMLDocumentationFileName)</XMLDocumentationFileName>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_QtRCCCompile"
|
||||
BeforeTargets="ClCompile"
|
||||
DependsOnTargets="$(BeforeQtRCCCompileTargets);QtRCCCompileAndInclude;$(AfterQtRCCCompileTargets)"
|
||||
/>
|
||||
</Project>
|
||||
@@ -0,0 +1,173 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<ProjectSchemaDefinitions
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<Rule
|
||||
Name="QtRCC"
|
||||
DisplayName="Qt Resource Compiler"
|
||||
PageTemplate="tool"
|
||||
SwitchPrefix="-"
|
||||
Order="223"
|
||||
>
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="QtRCCCompile"
|
||||
HasConfigurationCondition="true"
|
||||
/>
|
||||
</Rule.DataSource>
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General"
|
||||
/>
|
||||
<Category
|
||||
Name="Command Line"
|
||||
DisplayName="Command Line"
|
||||
Subtype="CommandLine"
|
||||
/>
|
||||
</Rule.Categories>
|
||||
<StringProperty
|
||||
Name="OutputFile"
|
||||
Subtype="file"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/rcc.html"
|
||||
DisplayName="Output File"
|
||||
Description="Write output to file rather than to stdout. (-o <file>)"
|
||||
Switch="o "[value]""
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="ExternalInitializationFunction"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/rcc.html"
|
||||
DisplayName="External Initialization Function"
|
||||
Description="Create an external initialization function with name. (-name <name>)"
|
||||
Switch="name "[value]""
|
||||
/>
|
||||
<IntProperty
|
||||
Name="Threshold"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/rcc.html"
|
||||
DisplayName="Threshold"
|
||||
Description="Specifies a threshold level (as a percentage) to use when deciding whether to compress a file. If the reduction in the file size is greater than the threshold level, it is compressed; otherwise, the uncompressed data is stored instead. The default threshold level is 70%, meaning that compressed files which are 30% or less of their original size are stored as compressed data. (-threshold <level>)"
|
||||
Switch="threshold [value]"
|
||||
MinValue="0"
|
||||
MaxValue="100"
|
||||
/>
|
||||
<EnumProperty
|
||||
Name="CompressLevel"
|
||||
DisplayName="Compress Level"
|
||||
Description="Compress input files to the given compression level, which is an integer in the range 1 to 9. Level 1 does the least compression but is fastest. Level 9 does the most compression but is slowest. To turn off compression, use -no-compress. The default value for level is -1, which means use zlib's default compression level. (-compress <level>)"
|
||||
Switch="compress [value]"
|
||||
>
|
||||
<EnumValue
|
||||
Name="-1"
|
||||
DisplayName="-1 (zlib's default)"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="1"
|
||||
DisplayName="1 (least)"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="2"
|
||||
DisplayName="2"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="3"
|
||||
DisplayName="3"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="4"
|
||||
DisplayName="4"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="5"
|
||||
DisplayName="5"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="6"
|
||||
DisplayName="6"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="7"
|
||||
DisplayName="7"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="8"
|
||||
DisplayName="8"
|
||||
/>
|
||||
<EnumValue
|
||||
Name="9"
|
||||
DisplayName="9 (most)"
|
||||
/>
|
||||
</EnumProperty>
|
||||
<StringListProperty
|
||||
Name="Root"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/rcc.html"
|
||||
DisplayName="Root"
|
||||
Description="Prefix the resource access path with path. The default is no prefix. (-root <path>)"
|
||||
Switch="root "[value]""
|
||||
/>
|
||||
<BoolProperty
|
||||
Name="DisableCompression"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/rcc.html"
|
||||
DisplayName="Disable Compression"
|
||||
Description="Disable compression. (-no-compress)"
|
||||
Switch="no-compress"
|
||||
/>
|
||||
<BoolProperty
|
||||
Name="Binary"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/rcc.html"
|
||||
DisplayName="Binary"
|
||||
Description="Output a binary file for use as a dynamic resource. (-binary)"
|
||||
Switch="binary"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="Inputs"
|
||||
Category="Command Line"
|
||||
IsRequired="true"
|
||||
>
|
||||
<StringProperty.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="QtRCCCompile"
|
||||
SourceType="Item"
|
||||
/>
|
||||
</StringProperty.DataSource>
|
||||
</StringProperty>
|
||||
<StringProperty
|
||||
Subtype="AdditionalOptions"
|
||||
Name="AdditionalOptions"
|
||||
Category="Command Line"
|
||||
DisplayName="Additional Options"
|
||||
F1Keyword="vc.project.AdditionalOptionsPage"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="RCC"
|
||||
Subtype="file"
|
||||
DisplayName="RCC"
|
||||
Description="Path to rcc.exe"
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="AdditionalDependencies"
|
||||
Subtype="file"
|
||||
DisplayName="Additional Dependencies"
|
||||
Description="Provide additional dependencies that affect the end result. Note: files, listed inside the QRC are determined automatically and should not be added manually."
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="CommandLineTemplate"
|
||||
Category="Command Line"
|
||||
DisplayName="Command Line"
|
||||
Visible="False"
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
</Rule>
|
||||
<ItemType Name="QtRCCCompile" DisplayName="Qt RRC Compile" />
|
||||
<FileExtension Name="*.qrc" ContentType="QtRCCCompile" />
|
||||
<ContentType Name="QtRCCCompile" DisplayName="Qt RRC Compile" ItemType="QtRCCCompile" />
|
||||
</ProjectSchemaDefinitions>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemDefinitionGroup>
|
||||
<QtUICCompile>
|
||||
<OutputFile Condition="'%(QtUICCompile.OutputFile)' == ''">%(Filename)%(Extension).h</OutputFile>
|
||||
<TranslationFunction Condition="'%(QtUICCompile.TranslationFunction)' == ''"></TranslationFunction>
|
||||
<DontGenerateHeaderGuards Condition="'%(QtUICCompile.DontGenerateHeaderGuards)' == ''">false</DontGenerateHeaderGuards>
|
||||
<UIC Condition="'%(QtUICCompile.UIC)' == ''">$(QTDIR)\bin\uic.exe</UIC>
|
||||
<CommandLineTemplate>%(UIC) [AllOptions] [AdditionalOptions] [Inputs]</CommandLineTemplate>
|
||||
</QtUICCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<PropertyGroup>
|
||||
<BuildGenerateSourcesTargets>$(BuildGenerateSourcesTargets);_QtUICCompile</BuildGenerateSourcesTargets>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
|
||||
<AvailableItemName Include="QtUICCompile">
|
||||
<Targets>QtUICCompile</Targets>
|
||||
</AvailableItemName>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
|
||||
<UsingTask
|
||||
TaskName="QtUIC"
|
||||
TaskFactory="XamlTaskFactory"
|
||||
AssemblyName="Microsoft.Build.Tasks.v4.0">
|
||||
<Task>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml</Task>
|
||||
</UsingTask>
|
||||
|
||||
<Target
|
||||
Name="SelectQtUICCompile"
|
||||
DependsOnTargets="_SelectedFiles;SelectCustomBuild"
|
||||
>
|
||||
<ItemGroup Condition="'@(SelectedFiles)' != ''">
|
||||
<QtUICCompile
|
||||
Remove="@(QtUICCompile)"
|
||||
Condition="'%(Identity)' != '@(SelectedFiles)'"
|
||||
/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="QtUICCompile"
|
||||
Condition="'@(QtUICCompile)' != ''"
|
||||
DependsOnTargets="SelectQtUICCompile"
|
||||
Inputs="@(QtUICCompile)"
|
||||
Outputs="%(OutputFile)"
|
||||
>
|
||||
<Message Text="UIC %(QtUICCompile.Identity) -> $([System.IO.Path]::GetFileName('%(QtUICCompile.OutputFile)'))"
|
||||
Importance="high"/>
|
||||
<ItemGroup>
|
||||
<QtUICCompile_tlog
|
||||
Include="%(QtUICCompile.OutputFile)"
|
||||
Condition="'%(QtUICCompile.OutputFile)' != '' and '%(QtUICCompile.ExcludedFromBuild)' != 'true'"
|
||||
>
|
||||
<Source>@(QtUICCompile, '|')</Source>
|
||||
</QtUICCompile_tlog>
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile
|
||||
Condition="'@(QtUICCompile_tlog)' != '' and '%(QtUICCompile_tlog.ExcludedFromBuild)' != 'true'"
|
||||
File="$(IntDir)$(ProjectName).write.1.tlog"
|
||||
Lines="^%(QtUICCompile_tlog.Source);@(QtUICCompile_tlog->'%(Fullpath)')"
|
||||
/>
|
||||
<MakeDir
|
||||
Directories="$([System.IO.Path]::GetDirectoryName('%(QtUICCompile.OutputFile)'))"
|
||||
/>
|
||||
<QtUIC
|
||||
Condition="'%(QtUICCompile.ExcludedFromBuild)' != 'true'"
|
||||
OutputFile="%(QtUICCompile.OutputFile)"
|
||||
TranslationFunction="%(QtUICCompile.TranslationFunction)"
|
||||
DontGenerateHeaderGuards="%(QtUICCompile.DontGenerateHeaderGuards)"
|
||||
Inputs="%(QtUICCompile.Identity)"
|
||||
AdditionalOptions="%(QtUICCompile.AdditionalOptions)"
|
||||
CommandLineTemplate="%(QtUICCompile.CommandLineTemplate)"
|
||||
/>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="QtUICCompileAndInclude"
|
||||
Condition="'@(QtUICCompile)' != ''"
|
||||
DependsOnTargets="SelectQtUICCompile;QtUICCompile"
|
||||
>
|
||||
<ItemGroup>
|
||||
<ClInclude
|
||||
Include="%(QtUICCompile.OutputFile)"
|
||||
Condition="'%(QtUICCompile.ExcludedFromBuild)' != 'true'"
|
||||
>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>%(QtUICCompile.Identity)</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_QtUICCompile"
|
||||
BeforeTargets="ClCompile"
|
||||
DependsOnTargets="$(BeforeQtUICCompileTargets);QtUICCompileAndInclude;$(AfterQtUICCompileTargets)"
|
||||
/>
|
||||
</Project>
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is (c) 2013 Adam Badura.
|
||||
Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file.
|
||||
-->
|
||||
<ProjectSchemaDefinitions
|
||||
xmlns="http://schemas.microsoft.com/build/2009/properties"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<Rule
|
||||
Name="QtUIC"
|
||||
DisplayName="Qt User Interface Compiler"
|
||||
PageTemplate="tool"
|
||||
SwitchPrefix="-"
|
||||
Order="221"
|
||||
>
|
||||
<Rule.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="QtUICCompile"
|
||||
HasConfigurationCondition="true"
|
||||
/>
|
||||
</Rule.DataSource>
|
||||
<Rule.Categories>
|
||||
<Category
|
||||
Name="General"
|
||||
DisplayName="General"
|
||||
/>
|
||||
<Category
|
||||
Name="Command Line"
|
||||
DisplayName="Command Line"
|
||||
Subtype="CommandLine"
|
||||
/>
|
||||
</Rule.Categories>
|
||||
<StringProperty
|
||||
Name="OutputFile"
|
||||
Subtype="file"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/uic.html"
|
||||
DisplayName="Output File"
|
||||
Description="Write output to <file> instead of to standard output. (-o <file>)"
|
||||
Switch="o "[value]""
|
||||
/>
|
||||
<StringListProperty
|
||||
Name="TranslationFunction"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/uic.html"
|
||||
DisplayName="Translation Function"
|
||||
Description="Use <func> for translating strings instead of tr(). (-tr <func>)"
|
||||
Switch="tr "[value]""
|
||||
/>
|
||||
<BoolProperty
|
||||
Name="DontGenerateHeaderGuards"
|
||||
HelpUrl="http://qt-project.org/doc/qt-5.0/qtdoc/uic.html"
|
||||
DisplayName="Don't Generate Header Guards"
|
||||
Description="Don't generate guards against multiple inclusion (#ifndef FOO_H …). (-p)"
|
||||
Switch="p"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="Inputs"
|
||||
Category="Command Line"
|
||||
IsRequired="true"
|
||||
>
|
||||
<StringProperty.DataSource>
|
||||
<DataSource
|
||||
Persistence="ProjectFile"
|
||||
ItemType="QtUICCompile"
|
||||
SourceType="Item"
|
||||
/>
|
||||
</StringProperty.DataSource>
|
||||
</StringProperty>
|
||||
<StringProperty
|
||||
Subtype="AdditionalOptions"
|
||||
Name="AdditionalOptions"
|
||||
Category="Command Line"
|
||||
DisplayName="Additional Options"
|
||||
F1Keyword="vc.project.AdditionalOptionsPage"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="UIC"
|
||||
Subtype="file"
|
||||
DisplayName="UIC"
|
||||
Description="Path to uic.exe"
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
<StringProperty
|
||||
Name="CommandLineTemplate"
|
||||
Category="Command Line"
|
||||
DisplayName="Command Line"
|
||||
Visible="False"
|
||||
IncludeInCommandLine="False"
|
||||
/>
|
||||
</Rule>
|
||||
<ItemType Name="QtUICCompile" DisplayName="Qt UIC Compile" />
|
||||
<FileExtension Name="*.ui" ContentType="QtUICCompile" />
|
||||
<ContentType Name="QtUICCompile" DisplayName="Qt UIC Compile" ItemType="QtUICCompile" />
|
||||
</ProjectSchemaDefinitions>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- Be careful adding properties and items here, they will be defined *before*
|
||||
most other properties are defined, hence things like $(IntDir) have
|
||||
wrong values, since they are not yet properly defined -->
|
||||
</Project>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionFile>$(ProjectDir)version.txt</VersionFile>
|
||||
<NewVersionFile>$(IntDir)version.txt</NewVersionFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Update the version.txt file, but only if needed, when the version really changes -->
|
||||
<Target Name="GenerateGitVersionFile" AfterTargets="InitializeBuildStatus">
|
||||
<WriteLinesToFile
|
||||
File="$(VersionFile)"
|
||||
Lines="1.5+git"
|
||||
Overwrite="true"
|
||||
Encoding="ASCII"
|
||||
Condition="!Exists($(VersionFile))" />
|
||||
|
||||
<Exec Command="git describe --tags --always --dirty > $(NewVersionFile)"
|
||||
IgnoreExitCode="True" />
|
||||
|
||||
<PropertyGroup>
|
||||
<GoldenDictVersion>$([System.IO.File]::ReadAllText($(VersionFile)).Trim())</GoldenDictVersion>
|
||||
<NewGoldenDictVersion>$([System.IO.File]::ReadAllText($(NewVersionFile)).Trim())</NewGoldenDictVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<Message Text="Version change: $(GoldenDictVersion) ~> $(NewGoldendictVersion)"
|
||||
Importance="high"
|
||||
Condition="('$(NewGoldenDictVersion)' != '') And
|
||||
('$(NewGoldenDictVersion)' != '$(GoldenDictVersion)')" />
|
||||
|
||||
<Copy
|
||||
SourceFiles="$(NewVersionFile)"
|
||||
DestinationFiles="$(VersionFile)"
|
||||
Condition="('$(NewGoldenDictVersion)' != '') And
|
||||
('$(NewGoldenDictVersion)' != '$(GoldenDictVersion)')"
|
||||
/>
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanVersionFiles" AfterTargets="Clean">
|
||||
<Delete Files="$(VersionFile);$(NewVersionFile)" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,118 @@
|
||||
## Introduction
|
||||
|
||||
<b>GoldenDict</b> is a feature-rich dictionary lookup program, supporting multiple dictionary formats (StarDict/Babylon/Lingvo/Dictd/AARD/MDict/SDict) and online dictionaries, featuring perfect article rendering with the complete markup, illustrations and other content retained, and allowing you to type in words without any accents or correct case.
|
||||
|
||||
## Requirements
|
||||
|
||||
This code has been run and tested on Windows XP/Vista/7, Ubuntu Linux, Mac OS X.
|
||||
|
||||
### External Deps
|
||||
|
||||
* Make, GCC, Git
|
||||
* Qt framework. Minimum required version is 4.6 for Windows, 4.5 for all other platforms. But Qt 4.7 or 4.8 is recommended.
|
||||
* If you want to use Qt 5.x then use branch qt4x5
|
||||
* Qt Creator IDE is recommended for development
|
||||
* Various libraries on Linux (png, zlib, etc)
|
||||
* On Mac and Windows all the libraries are already included in the repository
|
||||
|
||||
### Installing External Deps on Ubuntu Linux
|
||||
|
||||
sudo apt-get install git pkg-config build-essential qt4-qmake \
|
||||
libvorbis-dev zlib1g-dev libhunspell-dev x11proto-record-dev \
|
||||
libqt4-dev libqtwebkit-dev libxtst-dev liblzo2-dev libbz2-dev \
|
||||
libao-dev libavutil-dev libavformat-dev libtiff5-dev libeb16-dev
|
||||
|
||||
## How to build
|
||||
|
||||
First, clone this repository, e.g.:
|
||||
|
||||
git clone git://github.com/goldendict/goldendict.git
|
||||
|
||||
And then invoke `qmake-qt4` and `make`:
|
||||
|
||||
cd goldendict && qmake-qt4 && make
|
||||
|
||||
In case when qmake-qt4 does not exist, try using `qmake` but make sure it is indeed from the Qt 4 installation.
|
||||
Alternatively, you might want to load `goldendict.pro` file from within Qt Creator, especially on Windows.
|
||||
|
||||
### Building with Chinese conversion support
|
||||
|
||||
To add Chinese conversion support you need at first install libopencc-dev package:
|
||||
|
||||
sudo apt-get install libopencc-dev
|
||||
|
||||
Then pass `"CONFIG+=chinese_conversion_support"` to `qmake`
|
||||
|
||||
qmake "CONFIG+=chinese_conversion_support"
|
||||
|
||||
### Building with Zim dictionaries support
|
||||
|
||||
To add Zim and Slob formats support you need at first install lzma-dev package:
|
||||
|
||||
sudo apt-get install liblzma-dev
|
||||
|
||||
Then pass `"CONFIG+=zim_support"` to `qmake`
|
||||
|
||||
qmake "CONFIG+=zim_support"
|
||||
|
||||
### Building without extra tiff handler
|
||||
|
||||
If you have problem building with libtiff5-dev package, you can pass
|
||||
`"CONFIG+=no_extra_tiff_handler"` to `qmake` in order to disable extra tiff support
|
||||
(without such extra support some b/w tiff images will not be displayed):
|
||||
|
||||
qmake "CONFIG+=no_extra_tiff_handler"
|
||||
|
||||
### Building without Epwing format support
|
||||
|
||||
If you have problem building with libeb-dev package, you can pass
|
||||
`"CONFIG+=no_epwing_support"` to `qmake` in order to disable Epwing format support
|
||||
|
||||
qmake "CONFIG+=no_epwing_support"
|
||||
|
||||
### Building without internal audio player
|
||||
|
||||
If you have problem building with FFmpeg/libao (for example, Ubuntu older than 12.04), you can pass
|
||||
`"DISABLE_INTERNAL_PLAYER=1"` to `qmake` in order to disable internal audio player completely:
|
||||
|
||||
qmake "DISABLE_INTERNAL_PLAYER=1"
|
||||
|
||||
<b>NB:</b> All additional settings for `qmake` that you need must be combined in one `qmake` launch, for example:
|
||||
|
||||
qmake "CONFIG+=zim_support" "CONFIG+=no_extra_tiff_handler" "DISABLE_INTERNAL_PLAYER=1"
|
||||
|
||||
|
||||
Then, invoke `make clean` before `make` because the setting change:
|
||||
|
||||
make clean && make
|
||||
|
||||
### Building under Windows with MS Visual Studio
|
||||
|
||||
To build GoldenDict with Visual Studio take one of next library packs and unpack it to `"winlibs/lib/msvc"` folder in GoldenDict sources folder.
|
||||
[GoldenDict_libs_VS2013_x86_v2.7z](http://www.mediafire.com/download/lu73ykqmjf7bqjs/GoldenDict_libs_VS2013_x86_v2.7z) - for MS Visual Studio 2013, 32 bit
|
||||
[GoldenDict_libs_VS2013_x64_v2.7z](http://www.mediafire.com/download/wcwx8041ixixq3w/GoldenDict_libs_VS2013_x64_v2.7z) - for MS Visual Studio 2013, 64 bit
|
||||
[GoldenDict_libs_VS2015_x86_v2.7z](http://www.mediafire.com/download/qu7xh7v6ar0x2oc/GoldenDict_libs_VS2015_x86_v2.7z) - for MS Visual Studio 2015, 32 bit
|
||||
[GoldenDict_libs_VS2015_x64_v2.7z](http://www.mediafire.com/download/3jk6j55j6l9w902/GoldenDict_libs_VS2015_x64_v2.7z) - for MS Visual Studio 2015, 64 bit
|
||||
|
||||
To create project files for Visual Studio you can pass `"-tp vc"` option to `qmake`.
|
||||
|
||||
Note: In Qt 5.6.0 and later the `Webkit` module was removed from official release builds. You should to build it from sources to compile GoldenDict.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Installation is an optional step since the built binary can be used as-is without installation. But you can properly install via:
|
||||
|
||||
make install
|
||||
|
||||
<b>NB:</b> Don't do that on Windows!
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the <b>GNU GPLv3+</b> license, a copy of which can be found in the `LICENSE.txt` file.
|
||||
|
||||
## Support
|
||||
|
||||
Users looking for support should file an issue in the official [GoldenDict issue tracker](https://github.com/goldendict/goldendict/issues),
|
||||
or even better: submit a [pull request](https://github.com/goldendict/goldendict/pulls) if you have a fix available.
|
||||
General questions should be asked on the [official GoldenDict forum](http://goldendict.org/forum/).
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __AARD_HH_INCLUDED__
|
||||
#define __AARD_HH_INCLUDED__
|
||||
|
||||
#include "dictionary.hh"
|
||||
|
||||
/// Support for the aard dictionaries.
|
||||
namespace Aard {
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
vector< sptr< Dictionary::Class > > makeDictionaries(
|
||||
vector< string > const & fileNames,
|
||||
string const & indicesDir,
|
||||
Dictionary::Initializing & )
|
||||
throw( std::exception );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "about.hh"
|
||||
#include <QtGui>
|
||||
#include <QSysInfo>
|
||||
|
||||
#include "qt4x5.hh"
|
||||
|
||||
About::About( QWidget * parent ): QDialog( parent )
|
||||
{
|
||||
ui.setupUi( this );
|
||||
|
||||
QFile versionFile( ":/version.txt" );
|
||||
|
||||
QString version;
|
||||
|
||||
if ( !versionFile.open( QFile::ReadOnly ) )
|
||||
version = tr( "[Unknown]" );
|
||||
else
|
||||
version = QString::fromLatin1( versionFile.readAll() ).trimmed();
|
||||
|
||||
ui.version->setText( version );
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
QString compilerVersion = QString( "Visual C++ %1.%2.%3" )
|
||||
.arg( GD_CXX_MSVC_MAJOR )
|
||||
.arg( GD_CXX_MSVC_MINOR )
|
||||
.arg( GD_CXX_MSVC_BUILD );
|
||||
#elif defined (__clang__) && defined (__clang_version__)
|
||||
QString compilerVersion = QLatin1String( "Clang " ) + QLatin1String( __clang_version__ );
|
||||
#else
|
||||
QString compilerVersion = QLatin1String( "GCC " ) + QLatin1String( __VERSION__ );
|
||||
#endif
|
||||
|
||||
ui.qtVersion->setText( tr( "Based on Qt %1 (%2, %3 bit)" ).arg(
|
||||
QLatin1String( qVersion() ),
|
||||
compilerVersion,
|
||||
QString::number( QSysInfo::WordSize ) ) );
|
||||
|
||||
QFile creditsFile( ":/CREDITS.txt" );
|
||||
|
||||
if ( creditsFile.open( QFile::ReadOnly ) )
|
||||
{
|
||||
QStringList creditsList =
|
||||
QString::fromUtf8(
|
||||
creditsFile.readAll() ).split( '\n', QString::SkipEmptyParts );
|
||||
|
||||
QString html = "<html><body style='color: black; background: #f4f4f4;'>";
|
||||
|
||||
for( int x = 0; x < creditsList.size(); ++x )
|
||||
{
|
||||
QString str = creditsList[ x ];
|
||||
|
||||
str.replace( "\\", "@" );
|
||||
|
||||
str = Qt4x5::escape( str );
|
||||
|
||||
int colon = str.indexOf( ":" );
|
||||
|
||||
if ( colon != -1 )
|
||||
{
|
||||
QString name( str.left( colon ) );
|
||||
|
||||
name.replace( ", ", "<br>" );
|
||||
|
||||
str = "<font color='blue'>" + name + "</font><br> "
|
||||
+ str.mid( colon + 1 );
|
||||
}
|
||||
|
||||
html += str;
|
||||
html += "<br>";
|
||||
}
|
||||
|
||||
html += "</body></html>";
|
||||
|
||||
ui.credits->setHtml( html );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef ABOUT_HH
|
||||
#define ABOUT_HH
|
||||
|
||||
#include "ui_about.h"
|
||||
#include <QDialog>
|
||||
|
||||
// Microsoft Visual C++ version
|
||||
#if defined (_MSC_VER)
|
||||
// how many digits does the build number have?
|
||||
# if _MSC_FULL_VER / 10000 == _MSC_VER
|
||||
# define GD_CXX_MSVC_BUILD (_MSC_FULL_VER % 10000) // four digits
|
||||
# elif _MSC_FULL_VER / 100000 == _MSC_VER
|
||||
# define GD_CXX_MSVC_BUILD (_MSC_FULL_VER % 100000) // five digits
|
||||
# else
|
||||
# define GD_CXX_MSVC_BUILD 0
|
||||
# endif
|
||||
# define GD_CXX_MSVC_MAJOR (_MSC_VER/100-6)
|
||||
# define GD_CXX_MSVC_MINOR (_MSC_VER%100)
|
||||
#endif
|
||||
|
||||
class About: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
About( QWidget * parent = 0 );
|
||||
|
||||
private:
|
||||
|
||||
Ui::About ui;
|
||||
};
|
||||
|
||||
#endif // ABOUT_HH
|
||||
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>About</class>
|
||||
<widget class="QDialog" name="About">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="resources.qrc">:/icons/programicon.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>GoldenDict dictionary lookup program, version </string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="version">
|
||||
<property name="accessibleDescription">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">#.#</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextEditorInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>(c) 2008-2013 Konstantin Isakov (ikm@goldendict.org)</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Licensed under GNU GPLv3 or later</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="qtVersion">
|
||||
<property name="text">
|
||||
<string notr="true">Based on Qt #.#.# (GCC #.#, 32/64 bit)</string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextEditorInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Credits:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="credits">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="resources.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>About</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>About</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,14 @@
|
||||
/* This stylesheet is used for printing only, overriding article-style.css */
|
||||
|
||||
body
|
||||
{
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Hide audio icons */
|
||||
|
||||
.dsl_s_wav
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
html
|
||||
{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
background: white;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.gdarticle
|
||||
{
|
||||
background: white;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
::selection
|
||||
{
|
||||
background: #4492E8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Dictionary's name heading */
|
||||
.gddictname
|
||||
{
|
||||
font-weight: normal;
|
||||
font-size: 11px;
|
||||
border: none;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 0px;
|
||||
background: #F0EDED;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.gddicticon
|
||||
{
|
||||
display: inline;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.gddicticon img
|
||||
{
|
||||
border: 0;
|
||||
height: 16px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
/* The 'From ' string which preceeds dictionary name in the heading */
|
||||
.gdfromprefix
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Move the collapse/expand buttons to the right */
|
||||
.collapse_expand_area
|
||||
{
|
||||
float: right;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.gdexpandicon, .gdexpandicon:hover, .gdcollapseicon, .gdcollapseicon:hover
|
||||
{
|
||||
width:15px;
|
||||
background-position: center;
|
||||
background-repeat:no-repeat;
|
||||
vertical-align: text-bottom;
|
||||
padding-left: -5px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.gdexpandicon {
|
||||
background-image: url('qrcx://localhost/icons/expand_article.png');
|
||||
}
|
||||
|
||||
.gdexpandicon:hover {
|
||||
background-image: url('qrcx://localhost/icons/expand_article_hovered.png');
|
||||
}
|
||||
|
||||
.gdcollapseicon {
|
||||
background-image: url('qrcx://localhost/icons/collapse_article.png');
|
||||
}
|
||||
|
||||
.gdcollapseicon:hover {
|
||||
background-image: url('qrcx://localhost/icons/collapse_article_hovered.png');
|
||||
}
|
||||
|
||||
.bglpos { color: black; font-weight: bold; font-size: 11px; background: #F0EDED; display: inline; }
|
||||
|
||||
.bgltrn { color: #808080; font-size: 12px; display: inline; }
|
||||
|
||||
a, .mwiki a
|
||||
{
|
||||
color: #0066CC;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
body {
|
||||
background: #EAF0F8;
|
||||
margin: 0.3em;
|
||||
font-family: "Arial Unicode MS", "Lucida Sans Unicode", Tahoma, Verdana, "Palatino Linotype", sans-serif;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.gdarticle {
|
||||
background: #FFFEF2;
|
||||
margin-top: 0.1em;
|
||||
margin-bottom: 0.35em;
|
||||
padding: 5px;
|
||||
padding-top: -10px;
|
||||
border: 1px solid #C7D4DC;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.gdactivearticle {
|
||||
background: #FFFEF2;
|
||||
border: 1px solid #92B0DD;
|
||||
}
|
||||
|
||||
.gdarticleseparator + script + .gdactivearticle .gddictname {
|
||||
border-top: 1px solid #92B0DD;
|
||||
border-right: 1px solid #92B0DD;
|
||||
}
|
||||
|
||||
.gddictname {
|
||||
display: -webkit-box; /* needed to be able to reorder elements, e.g. icon, dictionary name, collapse icon */
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
float: right;
|
||||
border: 0px;
|
||||
border-top: 1px solid #C7D4DC;
|
||||
border-right: 1px solid #C7D4DC;
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-left-radius: 6px;
|
||||
margin: -6px;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 2px;
|
||||
padding-right: 0.4em;
|
||||
color: #34517D;
|
||||
background: #DDE6F5;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Actual text with the dictionary title */
|
||||
.gddicttitle
|
||||
{
|
||||
display:block;
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-webkit-box-flex: 1;
|
||||
}
|
||||
|
||||
.gdactivearticle .gddictname {
|
||||
border-top: 1px solid #92B0DD;
|
||||
border-right: 1px solid #92B0DD;
|
||||
color: darkblue;
|
||||
background: #CFDDF0;
|
||||
}
|
||||
|
||||
.gdcollapsedarticle .gddictname
|
||||
{
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Nice diagonal pattern for the collapsed article */
|
||||
.gdcollapsedarticle
|
||||
{
|
||||
background-image: -webkit-linear-gradient(left top, #ccc 0%, #ccc 25%, #bbb 25%, #bbb 50%, #ccc 50%, #ccc 75%, #bbb 75%);
|
||||
background-size: 50px 50px;
|
||||
}
|
||||
|
||||
/* Move the collapse/expand buttons to the last, 3rd position */
|
||||
.collapse_expand_area
|
||||
{
|
||||
display: block;
|
||||
-webkit-box-ordinal-group: 3;
|
||||
}
|
||||
|
||||
.gddicticon
|
||||
{
|
||||
display: block;
|
||||
vertical-align: text-bottom;
|
||||
padding-right: -1em;
|
||||
padding-left: 0.3em;
|
||||
-webkit-box-ordinal-group: 2;
|
||||
}
|
||||
|
||||
.gdexpandicon, .gdexpandicon:hover, .gdcollapseicon, .gdcollapseicon:hover
|
||||
{
|
||||
width:15px;
|
||||
background-position: center 25%;
|
||||
background-repeat:no-repeat;
|
||||
vertical-align: top;
|
||||
padding-left: -5px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.gdexpandicon {
|
||||
background-image: url('qrcx://localhost/icons/expand_article.png');
|
||||
}
|
||||
|
||||
.gdexpandicon:hover {
|
||||
background-image: url('qrcx://localhost/icons/expand_article_hovered.png');
|
||||
}
|
||||
|
||||
.gdcollapseicon {
|
||||
background-image: url('qrcx://localhost/icons/collapse_article.png');
|
||||
}
|
||||
|
||||
.gdcollapseicon:hover {
|
||||
background-image: url('qrcx://localhost/icons/collapse_article_hovered.png');
|
||||
}
|
||||
|
||||
.gdarticleseparator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* The 'From ' string which preceeds dictionary name in the heading */
|
||||
.gdfromprefix {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* The first headword in a (possibly) multi-headword DSL article */
|
||||
.gdarticlebody > span:first-child .dsl_headwords,
|
||||
.gdarticlebody > h3:first-child {
|
||||
margin-top: -3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mwiki .toc, .mwiki .metadata mbox-small plainlinks {
|
||||
display:none
|
||||
}
|
||||
|
||||
|
||||
.gddicticon img {
|
||||
border: 0;
|
||||
height: 15px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
/* Text selection */
|
||||
::selection {
|
||||
background: #839EC7;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
code::selection {
|
||||
background: #839EC7;
|
||||
}
|
||||
|
||||
/* Headers */
|
||||
h3 {
|
||||
color: inherit;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
/******** DSL Dictionaries ****************/
|
||||
|
||||
/* DSL headwords */
|
||||
.dsl_headwords {
|
||||
color: inherit;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
/* Formatting */
|
||||
.dsl_b {
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
.dsl_ex, .dsl_ex .dsl_opt
|
||||
{
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.dsl_ref {
|
||||
color: #0000DD;
|
||||
}
|
||||
|
||||
.dsl_url {
|
||||
color: #0000DD;
|
||||
}
|
||||
|
||||
/* Stress settings */
|
||||
.dsl_stress_without_accent,
|
||||
.dsl_opt .dsl_ex .dsl_stress_without_accent {
|
||||
display: none;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.dsl_stress_with_accent,
|
||||
.dsl_opt .dsl_ex .dsl_stress_with_accent {
|
||||
display: inline;
|
||||
color: #DD0000;
|
||||
}
|
||||
.dsl_stress,
|
||||
.dsl_opt .dsl_ex .dsl_stress {
|
||||
display: inline;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
/* Sound icon */
|
||||
.dsl_s_wav img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dsl_s_wav a {
|
||||
text-decoration: none;
|
||||
-webkit-user-select: none;
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
-webkit-border-radius: 100px;
|
||||
text-align: center;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.dsl_s_wav a {
|
||||
color: #FFF;
|
||||
border: 1px solid #798415;
|
||||
-webkit-box-shadow: 1px 1px #CCC, inset -1px -1px #4E7500;
|
||||
|
||||
background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,rgba(191,210,85,1)), color-stop(50%,rgba(142,185,42,1)), color-stop(51%,rgba(114,170,0,1)), color-stop(100%,rgba(158,203,45,1)));
|
||||
}
|
||||
|
||||
.dsl_s_wav a:hover {
|
||||
background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,#e6f0a3), color-stop(50%,#d2e638), color-stop(51%,#c3d825), color-stop(100%,#dbf043));
|
||||
|
||||
border: 1px solid #A2B01C;
|
||||
-webkit-box-shadow: 1px 1px #CCC, inset -1px -1px #8A991A;
|
||||
}
|
||||
|
||||
.dsl_s_wav a:active {
|
||||
-webkit-box-shadow: 1px 1px #CCC, inset 1px 1px #B1C421;
|
||||
}
|
||||
|
||||
.dsl_s_wav a:after {
|
||||
display: inline-block;
|
||||
font-family: "Webdings";
|
||||
content:"X";
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
vertical-align: center;
|
||||
-webkit-background-clip: text;
|
||||
color: #E6EF8F;
|
||||
-webkit-text-stroke: 1px #51580E;
|
||||
}
|
||||
|
||||
.dsl_s_wav a:hover:after {
|
||||
-webkit-text-stroke: 1px #656E11;
|
||||
}
|
||||
|
||||
|
||||
/******** Verbix Dictionaries ****************/
|
||||
|
||||
.sdct_h .normal {
|
||||
font-family: Arial;
|
||||
font-size: 10pt;
|
||||
color:#000000;
|
||||
font-weight: normal;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.sdct_h .irregular {
|
||||
font-family: Arial;
|
||||
font-size: 10pt;
|
||||
color:#FF0000;
|
||||
font-weight: bold;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.sdct_h .orto {
|
||||
font-family: Arial;
|
||||
font-size: 10pt;
|
||||
color:#0000FF;
|
||||
font-weight: normal;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.sdct_h .archaic {
|
||||
font-family: Arial;
|
||||
font-size: 10pt;
|
||||
color:#800080;
|
||||
font-weight: normal;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.sdct_h .notused {
|
||||
font-family: Arial;
|
||||
font-size: 10pt;
|
||||
color:#808080;
|
||||
font-weight: normal;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.sdct_h .pronoun {
|
||||
font-family: Arial;
|
||||
font-size: 10pt;
|
||||
color:#008000;
|
||||
font-weight: normal;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
|
||||
.sdct_h > table {
|
||||
border: 1px solid #444444;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.sdct_h td.verbtable {
|
||||
border: 1px solid #444444;
|
||||
}
|
||||
|
||||
.sdct_h th {
|
||||
background-color: #A6C9EF;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sdct_h .mini {
|
||||
font-size: 8pt; font-weight: normal; color: #808080;
|
||||
}
|
||||
|
||||
.sdct_h div.spacerline {
|
||||
clear: both;
|
||||
margin-left: 8px;
|
||||
width:100%;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.sdct_h div.spacer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.sdct_h div.float {
|
||||
margin-left: 8px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.sdct_h div.float400 {
|
||||
margin-left: 8px;
|
||||
float: left;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.sdct_h div.float250color {
|
||||
margin-left: 8px;
|
||||
float: left;
|
||||
width: 250px;
|
||||
border-style: dashed;
|
||||
border-color:#0033CC;
|
||||
border-collapse:collapse;
|
||||
border-width:1px;
|
||||
background-color:#eeeeee;
|
||||
padding: 8px 8px 8px 8px;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
a
|
||||
{
|
||||
color: #2233bb;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
color: #6677ff;
|
||||
}
|
||||
|
||||
::selection
|
||||
{
|
||||
background: #3399ff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
background: #fffef2;
|
||||
color: black;
|
||||
font-family: Tahoma, Verdana, SimSun, SimSun-ExtB, sans-serif;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.gdarticle
|
||||
{
|
||||
background: #fffef2;
|
||||
margin-top: -8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gddicticon
|
||||
{
|
||||
display: inline;
|
||||
margin-left: -3px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.gddicticon img
|
||||
{
|
||||
border: 0px;
|
||||
height: 16px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.gdfromprefix
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gddictname
|
||||
{
|
||||
display: inline-block;
|
||||
border: 1px solid #92b0dd;
|
||||
padding: 0.2em;
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
margin-top: 1em;
|
||||
margin-bottom: -0.1em;
|
||||
background: #cfddf0;
|
||||
color: #000080;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.collapse_expand_area
|
||||
{
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.gdexpandicon, .gdexpandicon:hover, .gdcollapseicon, .gdcollapseicon:hover
|
||||
{
|
||||
background-position: center 35%;
|
||||
background-repeat:no-repeat;
|
||||
}
|
||||
|
||||
|
||||
.gdexpandicon {
|
||||
background-image: url('qrcx://localhost/icons/expand_article.png');
|
||||
}
|
||||
|
||||
.gdexpandicon:hover {
|
||||
background-image: url('qrcx://localhost/icons/expand_article_hovered.png');
|
||||
}
|
||||
|
||||
.gdcollapseicon {
|
||||
background-image: url('qrcx://localhost/icons/collapse_article.png');
|
||||
}
|
||||
|
||||
.gdcollapseicon:hover {
|
||||
background-image: url('qrcx://localhost/icons/collapse_article_hovered.png');
|
||||
}
|
||||
|
||||
.gddictnamebodyseparator
|
||||
{
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
border-top: 1px solid #92b0dd;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
html
|
||||
{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
background: white;
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
text-decoration: none;
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.gdarticle
|
||||
{
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Dictionary's name heading */
|
||||
.gddictname
|
||||
{
|
||||
border: 1px dotted black; padding: 0.2em; padding-left: 0.5em;
|
||||
margin-top: 1.2em; margin-bottom: 0.1em; font-weight: bold; font-size: 14px;
|
||||
background: #87CEEB;
|
||||
}
|
||||
|
||||
/* The 'From ' string which preceeds dictionary name in the heading */
|
||||
.gdfromprefix
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gddictname
|
||||
{
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
|
||||
float: right;
|
||||
border: 1px solid white;
|
||||
margin-top: 7px;
|
||||
color: #7f7f7f;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.gdarticleseparator + script + .gdarticle .gddictname
|
||||
{
|
||||
border-top: 1px solid #ccc;
|
||||
/* margin-top: 11px;*/
|
||||
}
|
||||
|
||||
.gdactivearticle .gddictname
|
||||
{
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
|
||||
float: right;
|
||||
border: 1px solid #b3d7f7;
|
||||
margin-top: 7px;
|
||||
color: #0066ff;
|
||||
background: #deecf8;
|
||||
}
|
||||
|
||||
.gdarticleseparator + script + .gdactivearticle .gddictname
|
||||
{
|
||||
border-top: 1px solid #b3d7f7;
|
||||
/* margin-top: 11px;*/
|
||||
}
|
||||
|
||||
.gdarticleseparator
|
||||
{
|
||||
margin-top: 10px;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
margin-bottom: 0px;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.gddictname + h3, .gddictname + .dsl_article > .dsl_headwords
|
||||
{
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dsl_ex, .dsl_ex .dsl_opt
|
||||
{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.dsl_com
|
||||
{
|
||||
color: black;
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
html
|
||||
{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin-top: 1px;
|
||||
margin-right: 3px;
|
||||
margin-left: 2px;
|
||||
margin-bottom: 3px;
|
||||
background: white;
|
||||
font-family: Tahoma, Verdana, "Lucida Sans Unicode", "Segoe UI", "Palatino Linotype", "Arial Unicode MS", sans-serif;
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
text-decoration: none;
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.gdarticle
|
||||
{
|
||||
margin-top: 0.1em;
|
||||
margin-bottom: 0.4em;
|
||||
padding: 5px;
|
||||
border: 1px solid #d0dde2;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.gdactivearticle
|
||||
{
|
||||
border: 1px solid #3399FF;
|
||||
}
|
||||
|
||||
.gdarticleseparator + script + .gdactivearticle .gddictname
|
||||
{
|
||||
border-top: 1px solid #3399FF;
|
||||
}
|
||||
|
||||
.gdarticleseparator
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide the 'From ' string which preceeds dictionary name in the heading */
|
||||
.gdfromprefix
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gddictname
|
||||
{
|
||||
display: -webkit-box; /* needed to be able to reorder elements, e.g. icon, dictionary name, collapse icon */
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
float: right;
|
||||
border: 0px;
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-left-radius: 6px;
|
||||
margin: -6px;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 2px;
|
||||
padding-right: 0.3em;
|
||||
color: #4480f8;
|
||||
background: #E0E8F0;
|
||||
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Actual text with the dictionary title */
|
||||
.gddicttitle
|
||||
{
|
||||
display:block;
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-webkit-box-flex: 1;
|
||||
}
|
||||
|
||||
.gdactivearticle .gddictname
|
||||
{
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
margin: -6px;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 2px;
|
||||
|
||||
float: right;
|
||||
border: 1px solid #3399FF;
|
||||
color: #0066cc;
|
||||
background: #deecf8;
|
||||
}
|
||||
|
||||
.gdcollapsedarticle .gddictname
|
||||
{
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Nice diagonal pattern for the collapsed article */
|
||||
.gdcollapsedarticle
|
||||
{
|
||||
background-image: -webkit-linear-gradient(left top, #ccc 0%, #ccc 25%, #bbb 25%, #bbb 50%, #ccc 50%, #ccc 75%, #bbb 75%);
|
||||
background-size: 50px 50px;
|
||||
}
|
||||
|
||||
/* Move the collapse/expand buttons to the last, 3rd position */
|
||||
.collapse_expand_area
|
||||
{
|
||||
display: block;
|
||||
-webkit-box-ordinal-group: 3;
|
||||
}
|
||||
|
||||
.gddicticon
|
||||
{
|
||||
display: block;
|
||||
vertical-align: text-bottom;
|
||||
padding-right: -1em;
|
||||
padding-left: 0.3em;
|
||||
-webkit-box-ordinal-group: 2;
|
||||
}
|
||||
|
||||
.gdexpandicon, .gdexpandicon:hover, .gdcollapseicon, .gdcollapseicon:hover
|
||||
{
|
||||
width:15px;
|
||||
background-position: center 35%;
|
||||
background-repeat:no-repeat;
|
||||
vertical-align: text-bottom;
|
||||
padding-left: -5px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.gdexpandicon {
|
||||
background-image: url('qrcx://localhost/icons/expand_article.png');
|
||||
}
|
||||
|
||||
.gdexpandicon:hover {
|
||||
background-image: url('qrcx://localhost/icons/expand_article_hovered.png');
|
||||
}
|
||||
|
||||
.gdcollapseicon {
|
||||
background-image: url('qrcx://localhost/icons/collapse_article.png');
|
||||
}
|
||||
|
||||
.gdcollapseicon:hover {
|
||||
background-image: url('qrcx://localhost/icons/collapse_article_hovered.png');
|
||||
}
|
||||
|
||||
/* The first headword in a (possibly) multi-headword DSL article */
|
||||
.gdarticlebody > span:first-child .dsl_headwords,
|
||||
.gdarticlebody > h3:first-child
|
||||
{
|
||||
margin-top: -3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.gdspellsuggestion
|
||||
{
|
||||
/* Add some vertical space before the article */
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.dsl_ex, .dsl_ex .dsl_opt
|
||||
{
|
||||
color: steelblue;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background:#cc0000;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
code::selection {
|
||||
background: #333333;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,169 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __ARTICLE_MAKER_HH_INCLUDED__
|
||||
#define __ARTICLE_MAKER_HH_INCLUDED__
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include "dictionary.hh"
|
||||
#include "instances.hh"
|
||||
#include "wordfinder.hh"
|
||||
|
||||
/// This class generates the article's body for the given lookup request
|
||||
class ArticleMaker: public QObject
|
||||
{
|
||||
Q_OBJECT // We make it QObject to use tr() conveniently
|
||||
|
||||
std::vector< sptr< Dictionary::Class > > const & dictionaries;
|
||||
std::vector< Instances::Group > const & groups;
|
||||
|
||||
QString displayStyle, addonStyle;
|
||||
|
||||
bool needExpandOptionalParts;
|
||||
bool collapseBigArticles;
|
||||
int articleLimitSize;
|
||||
|
||||
public:
|
||||
|
||||
/// On construction, a reference to all dictionaries and a reference all
|
||||
/// groups' instances are to be passed. Those references are kept stored as
|
||||
/// references, and as such, any changes to them would reflect on the results
|
||||
/// of the inquiries, although those changes are perfectly legal.
|
||||
ArticleMaker( std::vector< sptr< Dictionary::Class > > const & dictionaries,
|
||||
std::vector< Instances::Group > const & groups,
|
||||
QString const & displayStyle,
|
||||
QString const & addonStyle);
|
||||
|
||||
/// Sets the display style to use for any new requests. This affects the
|
||||
/// choice of the stylesheet file.
|
||||
void setDisplayStyle( QString const &, QString const & addonStyle );
|
||||
|
||||
/// Looks up the given word within the given group, and creates a full html
|
||||
/// page text containing its definition.
|
||||
/// The result is returned as Dictionary::DataRequest just like dictionaries
|
||||
/// themselves do. The difference is that the result is a complete html page
|
||||
/// with all definitions from all the relevant dictionaries.
|
||||
/// Contexts is a map of context values to be passed to each dictionary, where
|
||||
/// the keys are dictionary ids.
|
||||
/// If mutedDicts is not empty, the search would be limited only to those
|
||||
/// dictionaries in group which aren't listed there.
|
||||
sptr< Dictionary::DataRequest > makeDefinitionFor( QString const & word, unsigned groupId,
|
||||
QMap< QString, QString > const & contexts,
|
||||
QSet< QString > const & mutedDicts =
|
||||
QSet< QString >(),
|
||||
QStringList const & dictIDs = QStringList() ) const;
|
||||
|
||||
/// Makes up a text which states that no translation for the given word
|
||||
/// was found. Sometimes it's better to call this directly when it's already
|
||||
/// known that there's no translation.
|
||||
sptr< Dictionary::DataRequest > makeNotFoundTextFor( QString const & word, QString const & group ) const;
|
||||
|
||||
/// Creates an 'untitled' page. The result is guaranteed to be instant.
|
||||
sptr< Dictionary::DataRequest > makeEmptyPage() const;
|
||||
|
||||
/// Create page with one picture
|
||||
sptr< Dictionary::DataRequest > makePicturePage( std::string const & url ) const;
|
||||
|
||||
/// Set auto expanding optional parts of articles
|
||||
void setExpandOptionalParts( bool expand );
|
||||
|
||||
/// Add base path to file path if it's relative and file not found
|
||||
/// Return true if path successfully adjusted
|
||||
static bool adjustFilePath( QString & fileName );
|
||||
|
||||
/// Set collapse articles parameters
|
||||
void setCollapseParameters( bool autoCollapse, int articleSize );
|
||||
|
||||
private:
|
||||
|
||||
/// Makes everything up to and including the opening body tag.
|
||||
std::string makeHtmlHeader( QString const & word, QString const & icon,
|
||||
bool expandOptionalParts ) const;
|
||||
|
||||
/// Makes the html body for makeNotFoundTextFor()
|
||||
static std::string makeNotFoundBody( QString const & word, QString const & group );
|
||||
|
||||
friend class ArticleRequest; // Allow it calling makeNotFoundBody()
|
||||
};
|
||||
|
||||
/// The request specific to article maker. This should really be private,
|
||||
/// but we need it to be handled by moc.
|
||||
class ArticleRequest: public Dictionary::DataRequest
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QString word, group;
|
||||
QMap< QString, QString > contexts;
|
||||
std::vector< sptr< Dictionary::Class > > activeDicts;
|
||||
|
||||
std::set< gd::wstring > alts; // Accumulated main forms
|
||||
std::list< sptr< Dictionary::WordSearchRequest > > altSearches;
|
||||
bool altsDone, bodyDone;
|
||||
std::list< sptr< Dictionary::DataRequest > > bodyRequests;
|
||||
bool foundAnyDefinitions;
|
||||
bool closePrevSpan; // Indicates whether the last opened article span is to
|
||||
// be closed after the article ends.
|
||||
sptr< WordFinder > stemmedWordFinder; // Used when there're no results
|
||||
|
||||
/// A sequence of words and spacings between them, including the initial
|
||||
/// spacing before the first word and the final spacing after the last word.
|
||||
typedef QList< QString > Words;
|
||||
typedef QList< QString > Spacings;
|
||||
|
||||
/// Splits the given string into words and spacings between them.
|
||||
QPair< Words, Spacings > splitIntoWords( QString const & );
|
||||
|
||||
QPair< Words, Spacings > splittedWords;
|
||||
int currentSplittedWordStart;
|
||||
int currentSplittedWordEnd;
|
||||
QString currentSplittedWordCompound;
|
||||
QString lastGoodCompoundResult;
|
||||
bool firstCompoundWasFound;
|
||||
int articleSizeLimit;
|
||||
bool needExpandOptionalParts;
|
||||
|
||||
public:
|
||||
|
||||
ArticleRequest( QString const & word, QString const & group,
|
||||
QMap< QString, QString > const & contexts,
|
||||
std::vector< sptr< Dictionary::Class > > const & activeDicts,
|
||||
std::string const & header,
|
||||
int sizeLimit, bool needExpandOptionalParts_ );
|
||||
|
||||
virtual void cancel();
|
||||
// { finish(); } // Add our own requests cancellation here
|
||||
|
||||
private slots:
|
||||
|
||||
void altSearchFinished();
|
||||
void bodyFinished();
|
||||
void stemmedSearchFinished();
|
||||
void individualWordFinished();
|
||||
|
||||
private:
|
||||
|
||||
/// Appends the given string to 'data', with locking its mutex.
|
||||
void appendToData( std::string const & );
|
||||
|
||||
/// Uses stemmedWordFinder to perform the next step of looking up word
|
||||
/// combinations.
|
||||
void compoundSearchNextStep( bool lastSearchSucceeded );
|
||||
|
||||
/// Creates a single word out of the [currentSplittedWordStart..End] range.
|
||||
QString makeSplittedWordCompound();
|
||||
|
||||
/// Makes an html link to the given word.
|
||||
std::string linkWord( QString const & );
|
||||
|
||||
/// Escapes the spacing between the words to include in html.
|
||||
std::string escapeSpacing( QString const & );
|
||||
|
||||
/// Find end of corresponding </div> tag
|
||||
int findEndOfCloseDiv( QString const &, int pos );
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,396 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#if defined( _MSC_VER ) && _MSC_VER < 1800 // VS2012 and older
|
||||
#include <stdint_msvc.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
#include "article_netmgr.hh"
|
||||
#include "wstring_qt.hh"
|
||||
#include "gddebug.hh"
|
||||
#include "qt4x5.hh"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace
|
||||
{
|
||||
/// Uses some heuristics to chop off the first domain name from the host name,
|
||||
/// but only if it's not too base. Returns the resulting host name.
|
||||
QString getHostBase( QUrl const & url )
|
||||
{
|
||||
QString host = url.host();
|
||||
|
||||
QStringList domains = host.split( '.' );
|
||||
|
||||
int left = domains.size();
|
||||
|
||||
// Skip last <=3-letter domain name
|
||||
if ( left && domains[ left - 1 ].size() <= 3 )
|
||||
--left;
|
||||
|
||||
// Skip another <=3-letter domain name
|
||||
if ( left && domains[ left - 1 ].size() <= 3 )
|
||||
--left;
|
||||
|
||||
if ( left > 1 )
|
||||
{
|
||||
// We've got something like www.foobar.co.uk -- we can chop off the first
|
||||
// domain
|
||||
|
||||
return host.mid( domains[ 0 ].size() + 1 );
|
||||
}
|
||||
else
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op,
|
||||
QNetworkRequest const & req,
|
||||
QIODevice * outgoingData )
|
||||
{
|
||||
if ( op == GetOperation )
|
||||
{
|
||||
if ( req.url().scheme() == "qrcx" )
|
||||
{
|
||||
// We have to override the local load policy for the qrc scheme, hence
|
||||
// we use qrcx and redirect it here back to qrc
|
||||
QUrl newUrl( req.url() );
|
||||
|
||||
newUrl.setScheme( "qrc" );
|
||||
newUrl.setHost( "" );
|
||||
|
||||
QNetworkRequest newReq( req );
|
||||
newReq.setUrl( newUrl );
|
||||
|
||||
return QNetworkAccessManager::createRequest( op, newReq, outgoingData );
|
||||
}
|
||||
|
||||
QString contentType;
|
||||
|
||||
sptr< Dictionary::DataRequest > dr = getResource( req.url(), contentType );
|
||||
|
||||
if ( dr.get() )
|
||||
return new ArticleResourceReply( this, req, dr, contentType );
|
||||
}
|
||||
|
||||
// Check the Referer. If the user has opted-in to block elements from external
|
||||
// pages, we block them.
|
||||
|
||||
if ( disallowContentFromOtherSites && req.hasRawHeader( "Referer" ) )
|
||||
{
|
||||
QByteArray referer = req.rawHeader( "Referer" );
|
||||
|
||||
//DPRINTF( "Referer: %s\n", referer.data() );
|
||||
|
||||
QUrl refererUrl = QUrl::fromEncoded( referer );
|
||||
|
||||
//DPRINTF( "Considering %s vs %s\n", getHostBase( req.url() ).toUtf8().data(),
|
||||
// getHostBase( refererUrl ).toUtf8().data() );
|
||||
|
||||
if ( !req.url().host().endsWith( refererUrl.host() ) &&
|
||||
getHostBase( req.url() ) != getHostBase( refererUrl ) && !req.url().scheme().startsWith("data") )
|
||||
{
|
||||
gdWarning( "Blocking element \"%s\"\n", req.url().toEncoded().data() );
|
||||
|
||||
return new BlockedNetworkReply( this );
|
||||
}
|
||||
}
|
||||
|
||||
if( req.url().scheme() == "file" )
|
||||
{
|
||||
// Check file presence and adjust path if necessary
|
||||
QString fileName = req.url().toLocalFile();
|
||||
if( req.url().host().isEmpty() && articleMaker.adjustFilePath( fileName ) )
|
||||
{
|
||||
QUrl newUrl( req.url() );
|
||||
newUrl.setPath( Qt4x5::Url::ensureLeadingSlash( QUrl::fromLocalFile( fileName ).path() ) );
|
||||
|
||||
QNetworkRequest newReq( req );
|
||||
newReq.setUrl( newUrl );
|
||||
|
||||
return QNetworkAccessManager::createRequest( op, newReq, outgoingData );
|
||||
}
|
||||
}
|
||||
|
||||
// spoof User-Agent
|
||||
if ( hideGoldenDictHeader && req.url().scheme().startsWith("http", Qt::CaseInsensitive))
|
||||
{
|
||||
QNetworkRequest newReq( req );
|
||||
newReq.setRawHeader("User-Agent", req.rawHeader("User-Agent").replace(qApp->applicationName(), ""));
|
||||
return QNetworkAccessManager::createRequest( op, newReq, outgoingData );
|
||||
}
|
||||
|
||||
return QNetworkAccessManager::createRequest( op, req, outgoingData );
|
||||
}
|
||||
|
||||
sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource(
|
||||
QUrl const & url, QString & contentType )
|
||||
{
|
||||
GD_DPRINTF( "getResource: %ls\n", url.toString().toStdWString().c_str() );
|
||||
GD_DPRINTF( "scheme: %ls\n", url.scheme().toStdWString().c_str() );
|
||||
GD_DPRINTF( "host: %ls\n", url.host().toStdWString().c_str() );
|
||||
|
||||
if ( url.scheme() == "gdlookup" )
|
||||
{
|
||||
if( !url.host().isEmpty() && url.host() != "localhost" )
|
||||
{
|
||||
// Strange request - ignore it
|
||||
return new Dictionary::DataRequestInstant( false );
|
||||
}
|
||||
|
||||
contentType = "text/html";
|
||||
|
||||
if ( Qt4x5::Url::queryItemValue( url, "blank" ) == "1" )
|
||||
return articleMaker.makeEmptyPage();
|
||||
|
||||
bool groupIsValid = false;
|
||||
|
||||
QString word = Qt4x5::Url::queryItemValue( url, "word" );
|
||||
unsigned group = Qt4x5::Url::queryItemValue( url, "group" ).toUInt( &groupIsValid );
|
||||
|
||||
QString dictIDs = Qt4x5::Url::queryItemValue( url, "dictionaries" );
|
||||
if( !dictIDs.isEmpty() )
|
||||
{
|
||||
// Individual dictionaries set from full-text search
|
||||
QStringList dictIDList = dictIDs.split( "," );
|
||||
return articleMaker.makeDefinitionFor( word, 0, QMap< QString, QString >(), QSet< QString >(), dictIDList );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// See if we have some dictionaries muted
|
||||
|
||||
QSet< QString > mutedDicts =
|
||||
QSet< QString >::fromList( Qt4x5::Url::queryItemValue( url, "muted" ).split( ',' ) );
|
||||
|
||||
// Unpack contexts
|
||||
|
||||
QMap< QString, QString > contexts;
|
||||
|
||||
QString contextsEncoded = Qt4x5::Url::queryItemValue( url, "contexts" );
|
||||
|
||||
if ( contextsEncoded.size() )
|
||||
{
|
||||
QByteArray ba = QByteArray::fromBase64( contextsEncoded.toLatin1() );
|
||||
|
||||
QBuffer buf( & ba );
|
||||
|
||||
buf.open( QBuffer::ReadOnly );
|
||||
|
||||
QDataStream stream( &buf );
|
||||
|
||||
stream >> contexts;
|
||||
}
|
||||
|
||||
if ( groupIsValid && word.size() ) // Require group and word to be passed
|
||||
return articleMaker.makeDefinitionFor( word, group, contexts, mutedDicts );
|
||||
}
|
||||
|
||||
if ( ( url.scheme() == "bres" || url.scheme() == "gdau" || url.scheme() == "gdvideo" || url.scheme() == "gico" ) &&
|
||||
url.path().size() )
|
||||
{
|
||||
//DPRINTF( "Get %s\n", req.url().host().toLocal8Bit().data() );
|
||||
//DPRINTF( "Get %s\n", req.url().path().toLocal8Bit().data() );
|
||||
|
||||
string id = url.host().toStdString();
|
||||
|
||||
bool search = ( id == "search" );
|
||||
|
||||
if ( !search )
|
||||
{
|
||||
for( unsigned x = 0; x < dictionaries.size(); ++x )
|
||||
if ( dictionaries[ x ]->getId() == id )
|
||||
{
|
||||
if( url.scheme() == "gico" )
|
||||
{
|
||||
QByteArray bytes;
|
||||
QBuffer buffer(&bytes);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
dictionaries[ x ]->getIcon().pixmap( 16 ).save(&buffer, "PNG");
|
||||
buffer.close();
|
||||
sptr< Dictionary::DataRequestInstant > ico = new Dictionary::DataRequestInstant( true );
|
||||
ico->getData().resize( bytes.size() );
|
||||
memcpy( &( ico->getData().front() ), bytes.data(), bytes.size() );
|
||||
return ico;
|
||||
}
|
||||
try
|
||||
{
|
||||
return dictionaries[ x ]->getResource( Qt4x5::Url::path( url ).mid( 1 ).toUtf8().data() );
|
||||
}
|
||||
catch( std::exception & e )
|
||||
{
|
||||
gdWarning( "getResource request error (%s) in \"%s\"\n", e.what(),
|
||||
dictionaries[ x ]->getName().c_str() );
|
||||
return sptr< Dictionary::DataRequest >();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't do search requests for now
|
||||
#if 0
|
||||
for( unsigned x = 0; x < dictionaries.size(); ++x )
|
||||
{
|
||||
if ( search || dictionaries[ x ]->getId() == id )
|
||||
{
|
||||
try
|
||||
{
|
||||
dictionaries[ x ]->getResource( url.path().mid( 1 ).toUtf8().data(),
|
||||
data );
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( Dictionary::exNoSuchResource & )
|
||||
{
|
||||
if ( !search )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if ( url.scheme() == "gdpicture" )
|
||||
{
|
||||
contentType = "text/html";
|
||||
QUrl imgUrl ( url );
|
||||
imgUrl.setScheme( "bres" );
|
||||
return articleMaker.makePicturePage( imgUrl.toEncoded().data() );
|
||||
}
|
||||
|
||||
return sptr< Dictionary::DataRequest >();
|
||||
}
|
||||
|
||||
ArticleResourceReply::ArticleResourceReply( QObject * parent,
|
||||
QNetworkRequest const & netReq,
|
||||
sptr< Dictionary::DataRequest > const & req_,
|
||||
QString const & contentType ):
|
||||
QNetworkReply( parent ), req( req_ ), alreadyRead( 0 )
|
||||
{
|
||||
setRequest( netReq );
|
||||
|
||||
setOpenMode( ReadOnly );
|
||||
|
||||
if ( contentType.size() )
|
||||
setHeader( QNetworkRequest::ContentTypeHeader, contentType );
|
||||
|
||||
connect( req.get(), SIGNAL( updated() ),
|
||||
this, SLOT( reqUpdated() ) );
|
||||
|
||||
connect( req.get(), SIGNAL( finished() ),
|
||||
this, SLOT( reqFinished() ) );
|
||||
|
||||
if ( req->isFinished() || req->dataSize() > 0 )
|
||||
{
|
||||
connect( this, SIGNAL( readyReadSignal() ),
|
||||
this, SLOT( readyReadSlot() ), Qt::QueuedConnection );
|
||||
connect( this, SIGNAL( finishedSignal() ),
|
||||
this, SLOT( finishedSlot() ), Qt::QueuedConnection );
|
||||
|
||||
emit readyReadSignal();
|
||||
|
||||
if ( req->isFinished() )
|
||||
{
|
||||
emit finishedSignal();
|
||||
GD_DPRINTF( "In-place finish.\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArticleResourceReply::~ArticleResourceReply()
|
||||
{
|
||||
req->cancel();
|
||||
}
|
||||
|
||||
void ArticleResourceReply::reqUpdated()
|
||||
{
|
||||
emit readyRead();
|
||||
}
|
||||
|
||||
void ArticleResourceReply::reqFinished()
|
||||
{
|
||||
emit readyRead();
|
||||
finishedSlot();
|
||||
}
|
||||
|
||||
qint64 ArticleResourceReply::bytesAvailable() const
|
||||
{
|
||||
qint64 avail = req->dataSize();
|
||||
|
||||
if ( avail < 0 )
|
||||
return 0;
|
||||
|
||||
return avail - alreadyRead + QNetworkReply::bytesAvailable();
|
||||
}
|
||||
|
||||
qint64 ArticleResourceReply::readData( char * out, qint64 maxSize )
|
||||
{
|
||||
// From the doc: "This function might be called with a maxSize of 0,
|
||||
// which can be used to perform post-reading operations".
|
||||
if ( maxSize == 0 )
|
||||
return 0;
|
||||
|
||||
GD_DPRINTF( "====reading %d bytes\n", (int)maxSize );
|
||||
|
||||
bool finished = req->isFinished();
|
||||
|
||||
qint64 avail = req->dataSize();
|
||||
|
||||
if ( avail < 0 )
|
||||
return finished ? -1 : 0;
|
||||
|
||||
qint64 left = avail - alreadyRead;
|
||||
|
||||
qint64 toRead = maxSize < left ? maxSize : left;
|
||||
|
||||
try
|
||||
{
|
||||
req->getDataSlice( alreadyRead, toRead, out );
|
||||
}
|
||||
catch( std::exception & e )
|
||||
{
|
||||
qWarning( "getDataSlice error: %s\n", e.what() );
|
||||
}
|
||||
|
||||
alreadyRead += toRead;
|
||||
|
||||
if ( !toRead && finished )
|
||||
return -1;
|
||||
else
|
||||
return toRead;
|
||||
}
|
||||
|
||||
void ArticleResourceReply::readyReadSlot()
|
||||
{
|
||||
readyRead();
|
||||
}
|
||||
|
||||
void ArticleResourceReply::finishedSlot()
|
||||
{
|
||||
if ( req->dataSize() < 0 )
|
||||
error( ContentNotFoundError );
|
||||
|
||||
finished();
|
||||
}
|
||||
|
||||
BlockedNetworkReply::BlockedNetworkReply( QObject * parent ): QNetworkReply( parent )
|
||||
{
|
||||
setError( QNetworkReply::ContentOperationNotPermittedError, "Content Blocked" );
|
||||
|
||||
connect( this, SIGNAL( finishedSignal() ), this, SLOT( finishedSlot() ),
|
||||
Qt::QueuedConnection );
|
||||
|
||||
emit finishedSignal(); // This way we call readyRead()/finished() sometime later
|
||||
}
|
||||
|
||||
|
||||
void BlockedNetworkReply::finishedSlot()
|
||||
{
|
||||
emit readyRead();
|
||||
emit finished();
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __ARTICLE_NETMGR_HH_INCLUDED__
|
||||
#define __ARTICLE_NETMGR_HH_INCLUDED__
|
||||
|
||||
#include <QtNetwork>
|
||||
#include "dictionary.hh"
|
||||
#include "article_maker.hh"
|
||||
|
||||
using std::vector;
|
||||
|
||||
/// A custom QNetworkAccessManager version which fetches images from the
|
||||
/// dictionaries when requested.
|
||||
|
||||
class ArticleNetworkAccessManager: public QNetworkAccessManager
|
||||
{
|
||||
vector< sptr< Dictionary::Class > > const & dictionaries;
|
||||
ArticleMaker const & articleMaker;
|
||||
bool const & disallowContentFromOtherSites;
|
||||
bool const & hideGoldenDictHeader;
|
||||
|
||||
public:
|
||||
|
||||
ArticleNetworkAccessManager( QObject * parent,
|
||||
vector< sptr< Dictionary::Class > > const &
|
||||
dictionaries_,
|
||||
ArticleMaker const & articleMaker_,
|
||||
bool const & disallowContentFromOtherSites_,
|
||||
bool const & hideGoldenDictHeader_ ):
|
||||
QNetworkAccessManager( parent ), dictionaries( dictionaries_ ),
|
||||
articleMaker( articleMaker_ ),
|
||||
disallowContentFromOtherSites( disallowContentFromOtherSites_ ),
|
||||
hideGoldenDictHeader( hideGoldenDictHeader_ )
|
||||
{}
|
||||
|
||||
/// Tries handling any kind of internal resources referenced by dictionaries.
|
||||
/// If it succeeds, the result is a dictionary request object. Otherwise, an
|
||||
/// empty pointer is returned.
|
||||
/// The function can optionally set the Content-Type header correspondingly.
|
||||
sptr< Dictionary::DataRequest > getResource( QUrl const & url,
|
||||
QString & contentType );
|
||||
|
||||
protected:
|
||||
|
||||
virtual QNetworkReply * createRequest( Operation op,
|
||||
QNetworkRequest const & req,
|
||||
QIODevice * outgoingData );
|
||||
};
|
||||
|
||||
class ArticleResourceReply: public QNetworkReply
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
sptr< Dictionary::DataRequest > req;
|
||||
qint64 alreadyRead;
|
||||
|
||||
public:
|
||||
|
||||
ArticleResourceReply( QObject * parent,
|
||||
QNetworkRequest const &,
|
||||
sptr< Dictionary::DataRequest > const &,
|
||||
QString const & contentType );
|
||||
|
||||
~ArticleResourceReply();
|
||||
|
||||
protected:
|
||||
|
||||
virtual qint64 bytesAvailable() const;
|
||||
|
||||
virtual void abort()
|
||||
{}
|
||||
virtual qint64 readData( char * data, qint64 maxSize );
|
||||
|
||||
// We use the hackery below to work around the fact that we need to emit
|
||||
// ready/finish signals after we've been constructed.
|
||||
signals:
|
||||
|
||||
void readyReadSignal();
|
||||
void finishedSignal();
|
||||
|
||||
private slots:
|
||||
|
||||
void reqUpdated();
|
||||
void reqFinished();
|
||||
|
||||
void readyReadSlot();
|
||||
void finishedSlot();
|
||||
};
|
||||
|
||||
class BlockedNetworkReply: public QNetworkReply
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
BlockedNetworkReply( QObject * parent );
|
||||
|
||||
virtual qint64 readData( char *, qint64 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual void abort()
|
||||
{}
|
||||
|
||||
protected:
|
||||
|
||||
// We use the hackery below to work around the fact that we need to emit
|
||||
// ready/finish signals after we've been constructed.
|
||||
|
||||
signals:
|
||||
|
||||
void finishedSignal();
|
||||
|
||||
private slots:
|
||||
|
||||
void finishedSlot();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "articleinspector.hh"
|
||||
|
||||
#if QT_VERSION >= 0x040600
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using std::list;
|
||||
|
||||
list< ArticleInspector * > ArticleInspector::openedInspectors;
|
||||
|
||||
ArticleInspector::ArticleInspector( Config::Class * cfg, QWidget* parent ) :
|
||||
QWebInspector( parent ),
|
||||
cfg( cfg )
|
||||
{
|
||||
if ( cfg == NULL )
|
||||
throw exInit();
|
||||
}
|
||||
|
||||
ArticleInspector::~ArticleInspector()
|
||||
{
|
||||
}
|
||||
|
||||
void ArticleInspector::beforeClosed()
|
||||
{
|
||||
list< ArticleInspector * >::iterator itemIter = std::find( openedInspectors.begin(),
|
||||
openedInspectors.end(), this );
|
||||
if ( itemIter != openedInspectors.end() )
|
||||
{
|
||||
openedInspectors.erase( itemIter );
|
||||
// Save geometry of the recent closed inspector window
|
||||
QByteArray geometry = saveGeometry();
|
||||
cfg->inspectorGeometry = geometry;
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleInspector::showEvent( QShowEvent * event )
|
||||
{
|
||||
if ( openedInspectors.empty() )
|
||||
{
|
||||
// Restore geometry from config, if no inspector opened
|
||||
restoreGeometry( cfg->inspectorGeometry );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load geometry from first inspector opened
|
||||
ArticleInspector * p = openedInspectors.front();
|
||||
setGeometry( p->geometry() );
|
||||
}
|
||||
|
||||
openedInspectors.push_back( this );
|
||||
|
||||
QWebInspector::showEvent( event );
|
||||
}
|
||||
|
||||
#endif // QT_VERSION
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef ARTICLEINSPECTOR_HH
|
||||
#define ARTICLEINSPECTOR_HH
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if QT_VERSION >= 0x040600
|
||||
|
||||
#include <QWebInspector>
|
||||
#include <list>
|
||||
#include "config.hh"
|
||||
#include "ex.hh"
|
||||
|
||||
class ArticleInspector : public QWebInspector
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DEF_EX( exInit, "Article inspector failed to init", std::exception )
|
||||
|
||||
explicit ArticleInspector( Config::Class * cfg, QWidget* parent = 0 );
|
||||
~ArticleInspector();
|
||||
|
||||
public slots:
|
||||
void beforeClosed();
|
||||
|
||||
protected:
|
||||
void showEvent( QShowEvent *event );
|
||||
|
||||
private:
|
||||
Config::Class * cfg;
|
||||
|
||||
static std::list< ArticleInspector * > openedInspectors;
|
||||
};
|
||||
|
||||
#endif // QT_VERSION
|
||||
|
||||
#endif // ARTICLEINSPECTOR_HH
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,382 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __ARTICLEVIEW_HH_INCLUDED__
|
||||
#define __ARTICLEVIEW_HH_INCLUDED__
|
||||
|
||||
#include <QWebView>
|
||||
#include <QMap>
|
||||
#include <QUrl>
|
||||
#include <list>
|
||||
#include "article_netmgr.hh"
|
||||
#include "instances.hh"
|
||||
#include "groupcombobox.hh"
|
||||
#include "ui_articleview.h"
|
||||
|
||||
class ResourceToSaveHandler;
|
||||
|
||||
/// A widget with the web view tailored to view and handle articles -- it
|
||||
/// uses the appropriate netmgr, handles link clicks, rmb clicks etc
|
||||
class ArticleView: public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
ArticleNetworkAccessManager & articleNetMgr;
|
||||
std::vector< sptr< Dictionary::Class > > const & allDictionaries;
|
||||
Instances::Groups const & groups;
|
||||
bool popupView;
|
||||
Config::Class const & cfg;
|
||||
|
||||
Ui::ArticleView ui;
|
||||
|
||||
QAction pasteAction, articleUpAction, articleDownAction,
|
||||
goBackAction, goForwardAction, selectCurrentArticleAction,
|
||||
copyAsTextAction, inspectAction;
|
||||
QAction & openSearchAction;
|
||||
bool searchIsOpened;
|
||||
bool expandOptionalParts;
|
||||
QString articleToJump;
|
||||
QString rangeVarName;
|
||||
|
||||
/// Any resource we've decided to download off the dictionary gets stored here.
|
||||
/// Full vector capacity is used for search requests, where we have to make
|
||||
/// a multitude of requests.
|
||||
std::list< sptr< Dictionary::DataRequest > > resourceDownloadRequests;
|
||||
/// Url of the resourceDownloadRequests
|
||||
QUrl resourceDownloadUrl;
|
||||
|
||||
/// For resources opened via desktop services
|
||||
QString desktopOpenedTempFile;
|
||||
|
||||
QAction * dictionaryBarToggled;
|
||||
GroupComboBox const * groupComboBox;
|
||||
|
||||
/// Search in results of full-text search
|
||||
QStringList allMatches;
|
||||
QStringList uniqueMatches;
|
||||
bool ftsSearchIsOpened, ftsSearchMatchCase;
|
||||
int ftsPosition;
|
||||
|
||||
void highlightFTSResults();
|
||||
void performFtsFindOperation( bool backwards );
|
||||
void showFindButtons();
|
||||
|
||||
public:
|
||||
/// The popupView flag influences contents of the context menus to be
|
||||
/// appropriate to the context of the view.
|
||||
/// The groups aren't copied -- rather than that, the reference is kept
|
||||
ArticleView( QWidget * parent,
|
||||
ArticleNetworkAccessManager &,
|
||||
std::vector< sptr< Dictionary::Class > > const & allDictionaries,
|
||||
Instances::Groups const &,
|
||||
bool popupView,
|
||||
Config::Class const & cfg,
|
||||
QAction & openSearchAction_,
|
||||
QAction * dictionaryBarToggled = 0,
|
||||
GroupComboBox const * groupComboBox = 0 );
|
||||
|
||||
/// Sets the currently active group combo box. When looking up selections,
|
||||
/// this allows presenting a choice of looking up in the currently chosen
|
||||
/// group. Setting this to 0 disables this. It is 0 by default.
|
||||
void setGroupComboBox( GroupComboBox const * );
|
||||
|
||||
virtual QSize minimumSizeHint() const;
|
||||
|
||||
~ArticleView();
|
||||
|
||||
typedef QMap< QString, QString > Contexts;
|
||||
|
||||
/// Shows the definition of the given word with the given group.
|
||||
/// scrollTo can be optionally set to a "gdfrom-xxxx" identifier to position
|
||||
/// the page to that article on load.
|
||||
/// contexts is an optional map of context values to be passed for dictionaries.
|
||||
/// The only values to pass here are ones obtained from showDefinitionInNewTab()
|
||||
/// signal or none at all.
|
||||
void showDefinition( QString const & word, unsigned group,
|
||||
QString const & scrollTo = QString(),
|
||||
Contexts const & contexts = Contexts() );
|
||||
|
||||
void showDefinition( QString const & word, QStringList const & dictIDs,
|
||||
QRegExp const & searchRegExp, unsigned group );
|
||||
|
||||
/// Clears the view and sets the application-global waiting cursor,
|
||||
/// which will be restored when some article loads eventually.
|
||||
void showAnticipation();
|
||||
|
||||
/// Opens the given link. Supposed to be used in response to
|
||||
/// openLinkInNewTab() signal. The link scheme is therefore supposed to be
|
||||
/// one of the internal ones.
|
||||
/// contexts is an optional map of context values to be passed for dictionaries.
|
||||
/// The only values to pass here are ones obtained from showDefinitionInNewTab()
|
||||
/// signal or none at all.
|
||||
void openLink( QUrl const & url, QUrl const & referrer,
|
||||
QString const & scrollTo = QString(),
|
||||
Contexts const & contexts = Contexts() );
|
||||
|
||||
/// Called when the state of dictionary bar changes and the view is active.
|
||||
/// The function reloads content if the change affects it.
|
||||
void updateMutedContents();
|
||||
|
||||
bool canGoBack();
|
||||
bool canGoForward();
|
||||
|
||||
/// Called when preference changes
|
||||
void setSelectionBySingleClick( bool set );
|
||||
|
||||
public slots:
|
||||
|
||||
/// Goes back in history
|
||||
void back();
|
||||
|
||||
/// Goes forward in history
|
||||
void forward();
|
||||
|
||||
public:
|
||||
|
||||
/// Takes the focus to the view
|
||||
void focus()
|
||||
{ ui.definition->setFocus( Qt::ShortcutFocusReason ); }
|
||||
|
||||
/// Reloads the view
|
||||
void reload()
|
||||
{ ui.definition->reload(); }
|
||||
|
||||
/// Returns true if there's an audio reference on the page, false otherwise.
|
||||
bool hasSound();
|
||||
|
||||
/// Plays the first audio reference on the page, if any.
|
||||
void playSound();
|
||||
|
||||
void setZoomFactor( qreal factor )
|
||||
{ ui.definition->setZoomFactor( factor ); }
|
||||
|
||||
/// Returns current article's text in .html format
|
||||
QString toHtml();
|
||||
|
||||
/// Returns current article's title
|
||||
QString getTitle();
|
||||
|
||||
/// Prints current article
|
||||
void print( QPrinter * ) const;
|
||||
|
||||
/// Closes search if it's open and returns true. Returns false if it
|
||||
/// wasn't open.
|
||||
bool closeSearch();
|
||||
|
||||
bool isSearchOpened();
|
||||
|
||||
/// Jumps to the article specified by the dictionary id,
|
||||
/// by executing a javascript code.
|
||||
void jumpToDictionary( QString const &, bool force );
|
||||
|
||||
/// Returns all articles currently present in view, as a list of dictionary
|
||||
/// string ids.
|
||||
QStringList getArticlesList();
|
||||
|
||||
/// Returns the dictionary id of the currently active article in the view.
|
||||
QString getActiveArticleId();
|
||||
|
||||
std::vector< ResourceToSaveHandler * > saveResource( const QUrl & url, const QString & fileName );
|
||||
std::vector< ResourceToSaveHandler * > saveResource( const QUrl & url, const QUrl & ref, const QString & fileName );
|
||||
|
||||
signals:
|
||||
|
||||
void iconChanged( ArticleView *, QIcon const & icon );
|
||||
|
||||
void titleChanged( ArticleView *, QString const & title );
|
||||
|
||||
void pageLoaded( ArticleView * );
|
||||
|
||||
/// Signals that the following link was requested to be opened in new tab
|
||||
void openLinkInNewTab( QUrl const &, QUrl const & referrer,
|
||||
QString const & fromArticle,
|
||||
ArticleView::Contexts const & contexts );
|
||||
/// Signals that the following definition was requested to be showed in new tab
|
||||
void showDefinitionInNewTab( QString const & word, unsigned group,
|
||||
QString const & fromArticle,
|
||||
ArticleView::Contexts const & contexts );
|
||||
|
||||
/// Put translated word into history
|
||||
void sendWordToHistory( QString const & word );
|
||||
|
||||
/// Emitted when user types a text key. This should typically be used to
|
||||
/// switch focus to word input.
|
||||
void typingEvent( QString const & text );
|
||||
|
||||
void statusBarMessage( QString const & message, int timeout = 0, QPixmap const & pixmap = QPixmap());
|
||||
|
||||
/// Signals that the dictionaries pane was requested to be showed
|
||||
void showDictsPane( );
|
||||
|
||||
/// Emitted when an article becomes active,
|
||||
/// typically in response to user actions
|
||||
/// (clicking on the article or using shortcuts).
|
||||
/// id - the dictionary id of the active article.
|
||||
void activeArticleChanged ( ArticleView const *, QString const & id );
|
||||
|
||||
/// Signal to add word to history even if history is disabled
|
||||
void forceAddWordToHistory( const QString & word);
|
||||
|
||||
/// Signal to close popup menu
|
||||
void closePopupMenu();
|
||||
|
||||
/// Signal to set optional parts expand mode
|
||||
void setExpandMode ( bool expand );
|
||||
|
||||
void sendWordToInputLine( QString const & word );
|
||||
|
||||
void storeResourceSavePath(QString const & );
|
||||
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
|
||||
public slots:
|
||||
|
||||
void on_searchPrevious_clicked();
|
||||
void on_searchNext_clicked();
|
||||
|
||||
void onJsActiveArticleChanged(QString const & id);
|
||||
|
||||
/// Handles F3 and Shift+F3 for search navigation
|
||||
bool handleF3( QObject * obj, QEvent * ev );
|
||||
|
||||
/// Control optional parts expanding
|
||||
void receiveExpandOptionalParts( bool expand );
|
||||
void switchExpandOptionalParts();
|
||||
|
||||
/// Selects an entire text of the current article
|
||||
void selectCurrentArticle();
|
||||
|
||||
private slots:
|
||||
|
||||
void loadFinished( bool ok );
|
||||
void handleTitleChanged( QString const & title );
|
||||
void handleUrlChanged( QUrl const & url );
|
||||
void attachToJavaScript();
|
||||
void linkClicked( QUrl const & );
|
||||
void linkHovered( const QString & link, const QString & title, const QString & textContent );
|
||||
void contextMenuRequested( QPoint const & );
|
||||
|
||||
void resourceDownloadFinished();
|
||||
|
||||
/// We handle pasting by attempting to define the word in clipboard.
|
||||
void pasteTriggered();
|
||||
|
||||
/// Nagivates to the previous article relative to the active one.
|
||||
void moveOneArticleUp();
|
||||
|
||||
/// Nagivates to the next article relative to the active one.
|
||||
void moveOneArticleDown();
|
||||
|
||||
/// Opens the search area
|
||||
void openSearch();
|
||||
|
||||
void on_searchText_textEdited();
|
||||
void on_searchText_returnPressed();
|
||||
void on_searchCloseButton_clicked();
|
||||
void on_searchCaseSensitive_clicked();
|
||||
void on_highlightAllButton_clicked();
|
||||
|
||||
void on_ftsSearchPrevious_clicked();
|
||||
void on_ftsSearchNext_clicked();
|
||||
|
||||
/// Handles the double-click from the definition.
|
||||
void doubleClicked();
|
||||
|
||||
/// Handles audio player error message
|
||||
void audioPlayerError( QString const & message );
|
||||
|
||||
/// Copy current selection as plain text
|
||||
void copyAsText();
|
||||
|
||||
/// Inspect element
|
||||
void inspect();
|
||||
|
||||
private:
|
||||
|
||||
/// Deduces group from the url. If there doesn't seem to be any group,
|
||||
/// returns 0.
|
||||
unsigned getGroup( QUrl const & );
|
||||
|
||||
|
||||
/// Returns current article in the view, in the form of "gdfrom-xxx" id.
|
||||
QString getCurrentArticle();
|
||||
|
||||
/// Sets the current article by executing a javascript code.
|
||||
/// If moveToIt is true, it moves the focus to it as well.
|
||||
void setCurrentArticle( QString const &, bool moveToIt = false );
|
||||
|
||||
/// Checks if the given article in form of "gdfrom-xxx" is inside a "website"
|
||||
/// frame.
|
||||
bool isFramedArticle( QString const & );
|
||||
|
||||
/// Checks if the given link is to be opened externally, as opposed to opening
|
||||
/// it in-place.
|
||||
bool isExternalLink( QUrl const & url );
|
||||
|
||||
/// Sees if the last clicked link is from a website frame. If so, changes url
|
||||
/// to point to url text translation instead, and saves the original
|
||||
/// url to the appropriate "contexts" entry.
|
||||
void tryMangleWebsiteClickedUrl( QUrl & url, Contexts & contexts );
|
||||
|
||||
/// Use the known information about the current frame to update the current
|
||||
/// article's value.
|
||||
void updateCurrentArticleFromCurrentFrame( QWebFrame * frame = 0 );
|
||||
|
||||
/// Saves current article and scroll position for the current history item.
|
||||
/// Should be used when leaving the page.
|
||||
void saveHistoryUserData();
|
||||
|
||||
/// Attempts removing last temporary file created.
|
||||
void cleanupTemp();
|
||||
|
||||
bool eventFilter( QObject * obj, QEvent * ev );
|
||||
|
||||
void performFindOperation( bool restart, bool backwards, bool checkHighlight = false );
|
||||
|
||||
void reloadStyleSheet();
|
||||
|
||||
/// Returns the comma-separated list of dictionary ids which should be muted
|
||||
/// for the given group. If there are none, returns empty string.
|
||||
QString getMutedForGroup( unsigned group );
|
||||
|
||||
protected:
|
||||
|
||||
// We need this to hide the search bar when we're showed
|
||||
void showEvent( QShowEvent * );
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
|
||||
/// Search inside web page for word under cursor
|
||||
|
||||
private:
|
||||
QString insertSpans( QString const & html );
|
||||
void readTag( QString const & from, QString & to, int & count );
|
||||
QString checkElement( QWebElement & elem, const QPoint & pt );
|
||||
public:
|
||||
QString wordAtPoint( int x, int y );
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
class ResourceToSaveHandler: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ResourceToSaveHandler( ArticleView * view, sptr< Dictionary::DataRequest > req,
|
||||
QString const & fileName );
|
||||
|
||||
signals:
|
||||
void done();
|
||||
void statusBarMessage( QString const & message, int timeout = 0, QPixmap const & pixmap = QPixmap() );
|
||||
|
||||
private slots:
|
||||
void downloadFinished();
|
||||
|
||||
private:
|
||||
sptr< Dictionary::DataRequest > req;
|
||||
QString fileName;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,255 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ArticleView</class>
|
||||
<widget class="QWidget" name="ArticleView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>833</width>
|
||||
<height>634</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1000,0,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ArticleWebView" name="definition" native="true">
|
||||
<property name="url" stdset="0">
|
||||
<url>
|
||||
<string>about:blank</string>
|
||||
</url>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="ftsSearchFrame">
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QToolButton" name="ftsSearchPrevious">
|
||||
<property name="text">
|
||||
<string>&Previous</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icons/previous.png</normaloff>:/icons/previous.png</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="ftsSearchNext">
|
||||
<property name="text">
|
||||
<string>&Next</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icons/next.png</normaloff>:/icons/next.png</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="searchFrame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Find:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="searchText"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="searchCloseButton">
|
||||
<property name="text">
|
||||
<string>x</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icons/closetab.png</normaloff>:/icons/closetab.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="searchPrevious">
|
||||
<property name="text">
|
||||
<string>&Previous</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icons/previous.png</normaloff>:/icons/previous.png</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="searchNext">
|
||||
<property name="text">
|
||||
<string>&Next</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icons/next.png</normaloff>:/icons/next.png</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+G</string>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="highlightAllButton">
|
||||
<property name="text">
|
||||
<string>Highlight &all</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icons/highlighter.png</normaloff>:/icons/highlighter.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="searchCaseSensitive">
|
||||
<property name="text">
|
||||
<string>&Case Sensitive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>782</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ArticleWebView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>articlewebview.hh</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,180 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "articlewebview.hh"
|
||||
#include <QMouseEvent>
|
||||
#include <QWebFrame>
|
||||
#include <QApplication>
|
||||
#include "articleinspector.hh"
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <qt_windows.h>
|
||||
#endif
|
||||
|
||||
ArticleWebView::ArticleWebView( QWidget *parent ):
|
||||
QWebView( parent ),
|
||||
#if QT_VERSION >= 0x040600
|
||||
inspector( NULL ),
|
||||
#endif
|
||||
midButtonPressed( false ),
|
||||
selectionBySingleClick( false ),
|
||||
showInspectorDirectly( true )
|
||||
{
|
||||
}
|
||||
|
||||
ArticleWebView::~ArticleWebView()
|
||||
{
|
||||
#if QT_VERSION >= 0x040600
|
||||
if ( inspector )
|
||||
inspector->deleteLater();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ArticleWebView::setUp( Config::Class * cfg )
|
||||
{
|
||||
this->cfg = cfg;
|
||||
}
|
||||
|
||||
void ArticleWebView::triggerPageAction( QWebPage::WebAction action, bool checked )
|
||||
{
|
||||
#if QT_VERSION >= 0x040600
|
||||
if ( action == QWebPage::InspectElement )
|
||||
{
|
||||
// Get or create inspector instance for current view.
|
||||
if ( !inspector )
|
||||
{
|
||||
inspector = new ArticleInspector( cfg );
|
||||
inspector->setPage( page() );
|
||||
connect( this, SIGNAL( destroyed() ), inspector, SLOT( beforeClosed() ) );
|
||||
}
|
||||
|
||||
if ( showInspectorDirectly )
|
||||
{
|
||||
showInspectorDirectly = false;
|
||||
// Bring up the inspector window and set focus
|
||||
inspector->show();
|
||||
inspector->activateWindow();
|
||||
inspector->raise();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QWebView::triggerPageAction( action, checked );
|
||||
}
|
||||
|
||||
bool ArticleWebView::event( QEvent * event )
|
||||
{
|
||||
switch ( event->type() )
|
||||
{
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
showInspectorDirectly = true;
|
||||
break;
|
||||
|
||||
case QEvent::ContextMenu:
|
||||
showInspectorDirectly = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QWebView::event( event );
|
||||
}
|
||||
|
||||
void ArticleWebView::mousePressEvent( QMouseEvent * event )
|
||||
{
|
||||
if ( event->buttons() & Qt::MidButton )
|
||||
midButtonPressed = true;
|
||||
|
||||
QWebView::mousePressEvent( event );
|
||||
|
||||
if ( selectionBySingleClick && ( event->buttons() & Qt::LeftButton ) )
|
||||
{
|
||||
findText(""); // clear the selection first, if any
|
||||
QMouseEvent ev( QEvent::MouseButtonDblClick, event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers() );
|
||||
QApplication::sendEvent( page(), &ev );
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleWebView::mouseReleaseEvent( QMouseEvent * event )
|
||||
{
|
||||
bool noMidButton = !( event->buttons() & Qt::MidButton );
|
||||
|
||||
QWebView::mouseReleaseEvent( event );
|
||||
|
||||
if ( midButtonPressed & noMidButton )
|
||||
midButtonPressed = false;
|
||||
}
|
||||
|
||||
void ArticleWebView::mouseDoubleClickEvent( QMouseEvent * event )
|
||||
{
|
||||
QWebView::mouseDoubleClickEvent( event );
|
||||
#if QT_VERSION >= 0x040600
|
||||
int scrollBarWidth = page()->mainFrame()->scrollBarGeometry( Qt::Vertical ).width();
|
||||
int scrollBarHeight = page()->mainFrame()->scrollBarGeometry( Qt::Horizontal ).height();
|
||||
#else
|
||||
int scrollBarWidth = 0;
|
||||
int scrollBarHeight = 0;
|
||||
#endif
|
||||
|
||||
// emit the signal only if we are not double-clicking on scrollbars
|
||||
if ( ( event->x() < width() - scrollBarWidth ) &&
|
||||
( event->y() < height() - scrollBarHeight ) )
|
||||
{
|
||||
emit doubleClicked();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ArticleWebView::focusInEvent( QFocusEvent * event )
|
||||
{
|
||||
QWebView::focusInEvent( event );
|
||||
|
||||
switch( event->reason() )
|
||||
{
|
||||
case Qt::MouseFocusReason:
|
||||
case Qt::TabFocusReason:
|
||||
case Qt::BacktabFocusReason:
|
||||
page()->mainFrame()->evaluateJavaScript("top.focus();");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleWebView::wheelEvent( QWheelEvent *ev )
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
|
||||
// Avoid wrong mouse wheel handling in QWebView
|
||||
// if system preferences is set to "scroll by page"
|
||||
|
||||
if( ev->modifiers() == Qt::NoModifier )
|
||||
{
|
||||
unsigned nLines;
|
||||
SystemParametersInfo( SPI_GETWHEELSCROLLLINES, 0, &nLines, 0 );
|
||||
if( nLines == WHEEL_PAGESCROLL )
|
||||
{
|
||||
QKeyEvent kev( QEvent::KeyPress, ev->delta() > 0 ? Qt::Key_PageUp : Qt::Key_PageDown,
|
||||
Qt::NoModifier );
|
||||
QApplication::sendEvent( this, &kev );
|
||||
|
||||
ev->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( ev->modifiers().testFlag( Qt::ControlModifier ) )
|
||||
{
|
||||
ev->ignore();
|
||||
}
|
||||
else
|
||||
{
|
||||
QWebView::wheelEvent( ev );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __ARTICLEWEBVIEW_HH_INCLUDED__
|
||||
#define __ARTICLEWEBVIEW_HH_INCLUDED__
|
||||
|
||||
#include <QWebView>
|
||||
#include "config.hh"
|
||||
|
||||
class ArticleInspector;
|
||||
|
||||
/// A thin wrapper around QWebView to accomodate to some ArticleView's needs.
|
||||
/// Currently the only added features:
|
||||
/// 1. Ability to know if the middle mouse button is pressed or not according
|
||||
/// to the view's current state. This is used to open links in new tabs when
|
||||
/// they are clicked with middle button. There's also an added posibility to
|
||||
/// get double-click events after the fact with the doubleClicked() signal.
|
||||
/// 2. Manage our own QWebInspector instance. In order to show inspector correctly,
|
||||
/// use triggerPageAction( QWebPage::InspectElement ) instead.
|
||||
class ArticleWebView: public QWebView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ArticleWebView( QWidget * parent );
|
||||
~ArticleWebView();
|
||||
|
||||
void setUp( Config::Class * cfg );
|
||||
|
||||
bool isMidButtonPressed() const
|
||||
{ return midButtonPressed; }
|
||||
void setSelectionBySingleClick( bool set )
|
||||
{ selectionBySingleClick = set; }
|
||||
|
||||
void triggerPageAction( QWebPage::WebAction action, bool checked = false );
|
||||
|
||||
signals:
|
||||
|
||||
/// Signals that the user has just double-clicked. The signal is delivered
|
||||
/// after the event was processed by the view -- that's the difference from
|
||||
/// installing an event filter. This is used for translating the double-clicked
|
||||
/// word, which gets selected by the view in response to double-click.
|
||||
void doubleClicked();
|
||||
|
||||
protected:
|
||||
|
||||
bool event( QEvent * event );
|
||||
void mousePressEvent( QMouseEvent * event );
|
||||
void mouseReleaseEvent( QMouseEvent * event );
|
||||
void mouseDoubleClickEvent( QMouseEvent * event );
|
||||
void focusInEvent( QFocusEvent * event );
|
||||
void wheelEvent( QWheelEvent * event );
|
||||
|
||||
private:
|
||||
|
||||
Config::Class * cfg;
|
||||
ArticleInspector * inspector;
|
||||
|
||||
bool midButtonPressed;
|
||||
bool selectionBySingleClick;
|
||||
bool showInspectorDirectly;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "atomic_rename.hh"
|
||||
#include <QtGlobal>
|
||||
#include <QVector>
|
||||
#include <string> // for wchar_t
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
bool renameAtomically( QString const & oldName, QString const & newName )
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
|
||||
QString srcFile( QDir::toNativeSeparators( oldName ) );
|
||||
QVector< wchar_t > srcFileW( srcFile.size() + 1 );
|
||||
srcFileW[ srcFile.toWCharArray( srcFileW.data() ) ] = 0;
|
||||
|
||||
QString destFile( QDir::toNativeSeparators( newName ) );
|
||||
QVector< wchar_t > destFileW( destFile.size() + 1 );
|
||||
destFileW[ destFile.toWCharArray( destFileW.data() ) ] = 0;
|
||||
|
||||
if ( !MoveFileExW( srcFileW.data(), destFileW.data(), MOVEFILE_REPLACE_EXISTING ) )
|
||||
return false;
|
||||
|
||||
#else
|
||||
|
||||
if ( rename( QFile::encodeName( QDir::toNativeSeparators( oldName ) ).data(),
|
||||
QFile::encodeName( QDir::toNativeSeparators( newName ) ).data() ) )
|
||||
return false;
|
||||
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __ATOMIC_RENAME_HH_INCLUDED__
|
||||
#define __ATOMIC_RENAME_HH_INCLUDED__
|
||||
|
||||
#include <QString>
|
||||
|
||||
/// Performs an atomic rename of file, from oldBame to newName. If newName
|
||||
/// exists, it gets overwritten. Names should feature Qt-style separators
|
||||
/// (straight slashes). Returns true on success, false on failure.
|
||||
bool renameAtomically( QString const & oldName, QString const & newName );
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "audiolink.hh"
|
||||
|
||||
std::string addAudioLink( std::string const & url,
|
||||
std::string const & dictionaryId )
|
||||
{
|
||||
return std::string( "<script type=\"text/javascript\">" +
|
||||
makeAudioLinkScript( url, dictionaryId ) +
|
||||
"</script>" );
|
||||
}
|
||||
|
||||
std::string makeAudioLinkScript( std::string const & url,
|
||||
std::string const & dictionaryId )
|
||||
{
|
||||
/// Convert "'" to "\'" - this char broke autoplay of audiolinks
|
||||
|
||||
std::string ref;
|
||||
bool escaped = false;
|
||||
for( unsigned x = 0; x < url.size(); x++ )
|
||||
{
|
||||
char ch = url[ x ];
|
||||
if( escaped )
|
||||
{
|
||||
ref += ch;
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if( ch == '\'' )
|
||||
ref += '\\';
|
||||
ref += ch;
|
||||
escaped = ( ch == '\\' );
|
||||
}
|
||||
|
||||
std::string audioLinkForDict = "gdAudioLinks['" + dictionaryId + "']";
|
||||
return "gdAudioLinks.first = gdAudioLinks.first || " + ref + ";" +
|
||||
audioLinkForDict + " = " + audioLinkForDict + " || " + ref + ";";
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __AUDIOLINK_HH_INCLUDED__
|
||||
#define __AUDIOLINK_HH_INCLUDED__
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
/// Adds a piece of javascript to save the given audiolink to a special
|
||||
/// javascript variable. Embed this into article's html to enable the
|
||||
/// 'say sound' functionality.
|
||||
/// The url should be escaped and surrounded by quotes.
|
||||
/// The dictionary id is used to make active dictionary feature work.
|
||||
std::string addAudioLink( std::string const & url,
|
||||
std::string const & dictionaryId );
|
||||
|
||||
std::string makeAudioLinkScript( std::string const & url,
|
||||
std::string const & dictionaryId );
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>389</width>
|
||||
<height>120</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Proxy authentication required</string>
|
||||
</property>
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>You need to supply a Username and a Password to access via proxy</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Username:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="userEdit"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Password:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="passwordEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,429 @@
|
||||
/* This file is (c) 2013 Maksim Tamkovicz <quendimax@gmail.com>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "belarusiantranslit.hh"
|
||||
#include "transliteration.hh"
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace BelarusianTranslit {
|
||||
|
||||
class BelarusianLatinToClassicTable: public Transliteration::Table
|
||||
{
|
||||
public:
|
||||
BelarusianLatinToClassicTable()
|
||||
{
|
||||
// Utf8ins()
|
||||
|
||||
// latin to cyrillic
|
||||
ins( "a", "а" );
|
||||
ins( "b", "б" );
|
||||
ins( "c", "ц" );
|
||||
ins( "ć", "ць" );
|
||||
ins( "č", "ч" );
|
||||
ins( "cz", "ч" );
|
||||
ins( "d", "д" );
|
||||
ins( "e", "э" );
|
||||
ins( "f", "ф" );
|
||||
ins( "g", "ґ" );
|
||||
ins( "h", "г" );
|
||||
ins( "i", "і" );
|
||||
ins( "j", "й" );
|
||||
ins( "k", "к" );
|
||||
ins( "l", "ль" );
|
||||
ins( "ł", "л" );
|
||||
ins( "m", "м" );
|
||||
ins( "n", "н" );
|
||||
ins( "ń", "нь" );
|
||||
ins( "o", "о" );
|
||||
ins( "p", "п" );
|
||||
ins( "r", "р" );
|
||||
ins( "s", "с" );
|
||||
ins( "ś", "сь" );
|
||||
ins( "š", "ш" );
|
||||
ins( "sz", "ш" );
|
||||
ins( "t", "т" );
|
||||
ins( "u", "у" );
|
||||
ins( "ŭ", "ў" );
|
||||
ins( "v", "в" );
|
||||
ins( "w", "в" );
|
||||
ins( "y", "ы" );
|
||||
ins( "z", "з" );
|
||||
ins( "ź", "зь" );
|
||||
ins( "ž", "ж" );
|
||||
ins( "ż", "ж" );
|
||||
ins( "ch", "х" );
|
||||
|
||||
ins( "ja", "я" ); ins( "je", "е" ); ins( "jo", "ё" ); ins( "ju", "ю" );
|
||||
ins( "la", "ля" ); ins( "le", "ле" ); ins( "lo", "лё" ); ins( "lu", "лю" ); ins( "li", "лі" );
|
||||
|
||||
ins( "bia", "бя" ); ins( "bie", "бе" ); ins( "bio", "бё" ); ins( "biu", "бю" );
|
||||
ins( "cia", "ця" ); ins( "cie", "це" ); ins( "cio", "цё" ); ins( "ciu", "цю" );
|
||||
ins( "dzia","дзя" ); ins( "dzie","дзе" ); ins( "dzio","дзё" ); ins( "dziu","дзю" );
|
||||
ins( "fia", "фя" ); ins( "fie", "фе" ); ins( "fio", "фё" ); ins( "fiu", "фю" );
|
||||
ins( "gia", "ґя" ); ins( "gie", "ґе" ); ins( "gio", "ґё" ); ins( "giu", "ґю" );
|
||||
ins( "hia", "гя" ); ins( "hie", "ге" ); ins( "hio", "гё" ); ins( "hiu", "гю" );
|
||||
ins( "kia", "кя" ); ins( "kie", "ке" ); ins( "kio", "кё" ); ins( "kiu", "кю" );
|
||||
ins( "lia", "ліa" ); ins( "lie", "ліэ" ); ins( "lio", "ліо" ); ins( "liu", "ліу" );
|
||||
ins( "mia", "мя" ); ins( "mie", "ме" ); ins( "mio", "мё" ); ins( "miu", "мю" );
|
||||
ins( "nia", "ня" ); ins( "nie", "не" ); ins( "nio", "нё" ); ins( "niu", "ню" );
|
||||
ins( "pia", "пя" ); ins( "pie", "пе" ); ins( "pio", "пё" ); ins( "piu", "пю" );
|
||||
ins( "sia", "ся" ); ins( "sie", "се" ); ins( "sio", "сё" ); ins( "siu", "сю" );
|
||||
ins( "via", "вя" ); ins( "vie", "ве" ); ins( "vio", "вё" ); ins( "viu", "вю" );
|
||||
ins( "wia", "вя" ); ins( "wie", "ве" ); ins( "wio", "вё" ); ins( "wiu", "вю" );
|
||||
ins( "zia", "зя" ); ins( "zie", "зе" ); ins( "zio", "зё" ); ins( "ziu", "зю" );
|
||||
ins( "chia", "хя" ); ins( "chie", "хе" ); ins( "chio", "хё" ); ins( "chiu", "хю" );
|
||||
|
||||
ins( "bja", "б'я" ); ins( "bje", "б'е" ); ins( "bjo", "б'ё" ); ins( "bju", "б'ю" );
|
||||
ins( "cia", "ц'я" ); ins( "cje", "ц'е" ); ins( "cjo", "ц'ё" ); ins( "cju", "ц'ю" );
|
||||
ins( "fja", "ф'я" ); ins( "fje", "ф'е" ); ins( "fjo", "ф'ё" ); ins( "fju", "ф'ю" );
|
||||
ins( "hja", "г'я" ); ins( "hje", "г'е" ); ins( "hjo", "г'ё" ); ins( "hju", "г'ю" );
|
||||
ins( "kja", "к'я" ); ins( "kje", "к'е" ); ins( "kjo", "к'ё" ); ins( "kju", "к'ю" );
|
||||
ins( "łja", "л'я" ); ins( "łje", "л'е" ); ins( "łjo", "л'ё" ); ins( "łju", "л'ю" );
|
||||
ins( "mja", "м'я" ); ins( "mje", "м'е" ); ins( "mjo", "м'ё" ); ins( "mju", "м'ю" );
|
||||
ins( "nja", "н'я" ); ins( "nje", "н'е" ); ins( "njo", "н'ё" ); ins( "nju", "н'ю" );
|
||||
ins( "pja", "п'я" ); ins( "pje", "п'е" ); ins( "pjo", "п'ё" ); ins( "pju", "п'ю" );
|
||||
ins( "sja", "с'я" ); ins( "sje", "с'е" ); ins( "sjo", "с'ё" ); ins( "sju", "с'ю" );
|
||||
ins( "vja", "в'я" ); ins( "vje", "в'е" ); ins( "vjo", "в'ё" ); ins( "vju", "в'ю" );
|
||||
ins( "wja", "в'я" ); ins( "wje", "в'е" ); ins( "wjo", "в'ё" ); ins( "wju", "в'ю" );
|
||||
ins( "zja", "з'я" ); ins( "zje", "з'е" ); ins( "zjo", "з'ё" ); ins( "zju", "з'ю" );
|
||||
ins( "chja", "х'я" ); ins( "chje", "х'е" ); ins( "chjo", "х'ё" ); ins( "chju", "х'ю" );
|
||||
|
||||
ins( "nnia", "ньня" ); ins( "nnie", "ньне" ); ins( "nnio", "ньнё" ); ins( "nniu", "ньню" );ins( "nni", "ньні" );
|
||||
|
||||
// cyrillic to latin
|
||||
ins( "а", "a" ); ins( "б", "b" ); ins( "в", "v" ); ins( "г", "h" );
|
||||
ins( "ґ", "g" ); ins( "д", "d" ); ins( "е", "je" ); ins( "ё", "jo" );
|
||||
ins( "ж", "ž" ); ins( "з", "z" ); ins( "і", "i" ); ins( "ї", "ï" );
|
||||
ins( "й", "j" ); ins( "к", "k" ); ins( "л", "ł" ); ins( "м", "m" );
|
||||
ins( "н", "n" ); ins( "о", "o" ); ins( "п", "p" ); ins( "р", "r" );
|
||||
ins( "с", "s" ); ins( "т", "t" ); ins( "у", "u" ); ins( "ў", "ŭ" );
|
||||
ins( "ф", "f" ); ins( "х", "ch" ); ins( "ц", "c" ); ins( "ч", "č" );
|
||||
ins( "ш", "š" ); ins( "ы", "y" ); ins( "э", "e" ); ins( "ю", "ju" );
|
||||
ins( "я", "ja" ); ins( "кг", "g" );
|
||||
ins( "бе", "bie" ); ins( "бё", "bio" ); ins( "бю", "biu" ); ins( "бя", "bia" );
|
||||
ins( "ве", "vie" ); ins( "вё", "vio" ); ins( "вю", "viu" ); ins( "вя", "via" );
|
||||
ins( "ге", "hie" ); ins( "гё", "hio" ); ins( "гю", "hiu" ); ins( "гя", "hia" );
|
||||
ins( "ґе", "gie" ); ins( "ґё", "gio" ); ins( "ґю", "giu" ); ins( "ґя", "gia" );
|
||||
ins( "кге", "gie" ); ins( "кгё", "gio" ); ins( "кгю", "giu" ); ins( "кгя", "gia" );
|
||||
ins( "зе", "zie" ); ins( "зё", "zio" ); ins( "зю", "ziu" ); ins( "зя", "zia" ); ins( "зь", "ź" );
|
||||
ins( "ке", "kie" ); ins( "кё", "kio" ); ins( "кю", "kiu" ); ins( "кя", "kia" );
|
||||
ins( "ле", "le" ); ins( "лё", "lo" ); ins( "лю", "lu" ); ins( "ля", "la" ); ins( "ль", "l" ); ins( "лі", "li" );
|
||||
ins( "ме", "mie" ); ins( "мё", "mio" ); ins( "мю", "miu" ); ins( "мя", "mia" ); ins( "мь", "m" );
|
||||
ins( "не", "nie" ); ins( "нё", "nio" ); ins( "ню", "niu" ); ins( "ня", "nia" ); ins( "нь", "ń" );
|
||||
ins( "пе", "pie" ); ins( "пё", "pio" ); ins( "пю", "piu" ); ins( "пя", "pia" ); ins( "пь", "p" );
|
||||
ins( "се", "sie" ); ins( "сё", "sio" ); ins( "сю", "siu" ); ins( "ся", "sia" ); ins( "сь", "ś" );
|
||||
ins( "фе", "fie" ); ins( "фё", "fio" ); ins( "фю", "fiu" ); ins( "фя", "fia" ); ins( "фь", "f" );
|
||||
ins( "хе", "chie" ); ins( "хё", "chio" ); ins( "хю", "chiu" ); ins( "хя", "chia" );
|
||||
ins( "це", "cie" ); ins( "цё", "cio" ); ins( "цю", "ciu" ); ins( "ця", "cia" ); ins( "ць", "ć" );
|
||||
ins( "бʼ", "b" ); ins( "б'", "b" ); ins( "б’", "b" );
|
||||
ins( "вʼ", "v" ); ins( "в'", "v" ); ins( "в’", "v" );
|
||||
ins( "гʼ", "h" ); ins( "г'", "h" ); ins( "г’", "h" );
|
||||
ins( "ґʼ", "g" ); ins( "ґ'", "g" ); ins( "ґ’", "g" );
|
||||
ins( "дʼ", "d" ); ins( "д'", "d" ); ins( "д’", "d" );
|
||||
ins( "жʼ", "ž" ); ins( "ж'", "ž" ); ins( "ж’", "ž" );
|
||||
ins( "зʼ", "z" ); ins( "з'", "z" ); ins( "з’", "z" );
|
||||
ins( "кʼ", "k" ); ins( "к'", "k" ); ins( "к’", "k" );
|
||||
ins( "лʼ", "ł" ); ins( "л'", "ł" ); ins( "л’", "ł" );
|
||||
ins( "мʼ", "m" ); ins( "м'", "m" ); ins( "м’", "m" );
|
||||
ins( "нʼ", "n" ); ins( "н'", "n" ); ins( "н’", "n" );
|
||||
ins( "пʼ", "p" ); ins( "п'", "p" ); ins( "п’", "p" );
|
||||
ins( "рʼ", "r" ); ins( "р'", "r" ); ins( "р’", "r" );
|
||||
ins( "сʼ", "s" ); ins( "с'", "s" ); ins( "с’", "s" );
|
||||
ins( "тʼ", "t" ); ins( "т'", "t" ); ins( "т’", "t" );
|
||||
ins( "фʼ", "f" ); ins( "ф'", "f" ); ins( "ф’", "f" );
|
||||
ins( "хʼ", "ch" ); ins( "х'", "ch" ); ins( "х’", "ch" );
|
||||
ins( "цʼ", "c" ); ins( "ц'", "c" ); ins( "ц’", "c" );
|
||||
ins( "чʼ", "č" ); ins( "ч'", "č" ); ins( "ч’", "č" );
|
||||
ins( "шʼ", "š" ); ins( "ш'", "š" ); ins( "ш’", "š" );
|
||||
}
|
||||
};
|
||||
|
||||
class BelarusianLatinToSchoolTable: public Transliteration::Table
|
||||
{
|
||||
public:
|
||||
BelarusianLatinToSchoolTable()
|
||||
{
|
||||
// Utf8ins()
|
||||
|
||||
// latin to cyrillic
|
||||
ins( "a", "а" );
|
||||
ins( "b", "б" );
|
||||
ins( "c", "ц" );
|
||||
ins( "ć", "ць" );
|
||||
ins( "č", "ч" );
|
||||
ins( "cz", "ч" );
|
||||
ins( "d", "д" );
|
||||
ins( "e", "э" );
|
||||
ins( "f", "ф" );
|
||||
ins( "g", "г" );
|
||||
ins( "h", "г" );
|
||||
ins( "i", "і" );
|
||||
ins( "j", "й" );
|
||||
ins( "k", "к" );
|
||||
ins( "l", "ль" );
|
||||
ins( "ł", "л" );
|
||||
ins( "m", "м" );
|
||||
ins( "n", "н" );
|
||||
ins( "ń", "нь" );
|
||||
ins( "o", "о" );
|
||||
ins( "p", "п" );
|
||||
ins( "r", "р" );
|
||||
ins( "s", "с" );
|
||||
ins( "ś", "сь" );
|
||||
ins( "š", "ш" );
|
||||
ins( "sz", "ш" );
|
||||
ins( "t", "т" );
|
||||
ins( "u", "у" );
|
||||
ins( "ŭ", "ў" );
|
||||
ins( "v", "в" );
|
||||
ins( "w", "в" );
|
||||
ins( "y", "ы" );
|
||||
ins( "z", "з" );
|
||||
ins( "ź", "зь" );
|
||||
ins( "ž", "ж" );
|
||||
ins( "ż", "ж" );
|
||||
ins( "ch", "х" );
|
||||
|
||||
ins( "ja", "я" ); ins( "je", "е" ); ins( "jo", "ё" ); ins( "ju", "ю" );
|
||||
ins( "la", "ля" ); ins( "le", "ле" ); ins( "lo", "лё" ); ins( "lu", "лю" ); ins( "li", "лі" );
|
||||
|
||||
ins( "bia", "бя" ); ins( "bie", "бе" ); ins( "bio", "бё" ); ins( "biu", "бю" );
|
||||
ins( "cia", "ця" ); ins( "cie", "це" ); ins( "cio", "цё" ); ins( "ciu", "цю" );
|
||||
ins( "dzia","дзя" ); ins( "dzie","дзе" ); ins( "dzio","дзё" ); ins( "dziu","дзю" );
|
||||
ins( "fia", "фя" ); ins( "fie", "фе" ); ins( "fio", "фё" ); ins( "fiu", "фю" );
|
||||
ins( "gia", "гя" ); ins( "gie", "ге" ); ins( "gio", "гё" ); ins( "giu", "гю" );
|
||||
ins( "hia", "гя" ); ins( "hie", "ге" ); ins( "hio", "гё" ); ins( "hiu", "гю" );
|
||||
ins( "kia", "кя" ); ins( "kie", "ке" ); ins( "kio", "кё" ); ins( "kiu", "кю" );
|
||||
ins( "lia", "ліa" ); ins( "lie", "ліэ" ); ins( "lio", "ліо" ); ins( "liu", "ліу" );
|
||||
ins( "mia", "мя" ); ins( "mie", "ме" ); ins( "mio", "мё" ); ins( "miu", "мю" );
|
||||
ins( "nia", "ня" ); ins( "nie", "не" ); ins( "nio", "нё" ); ins( "niu", "ню" );
|
||||
ins( "pia", "пя" ); ins( "pie", "пе" ); ins( "pio", "пё" ); ins( "piu", "пю" );
|
||||
ins( "sia", "ся" ); ins( "sie", "се" ); ins( "sio", "сё" ); ins( "siu", "сю" );
|
||||
ins( "via", "вя" ); ins( "vie", "ве" ); ins( "vio", "вё" ); ins( "viu", "вю" );
|
||||
ins( "wia", "вя" ); ins( "wie", "ве" ); ins( "wio", "вё" ); ins( "wiu", "вю" );
|
||||
ins( "zia", "зя" ); ins( "zie", "зе" ); ins( "zio", "зё" ); ins( "ziu", "зю" );
|
||||
ins( "chia", "хя" ); ins( "chie", "хе" ); ins( "chio", "хё" ); ins( "chiu", "хю" );
|
||||
|
||||
ins( "bja", "б'я" ); ins( "bje", "б'е" ); ins( "bjo", "б'ё" ); ins( "bju", "б'ю" );
|
||||
ins( "cia", "ц'я" ); ins( "cje", "ц'е" ); ins( "cjo", "ц'ё" ); ins( "cju", "ц'ю" );
|
||||
ins( "fja", "ф'я" ); ins( "fje", "ф'е" ); ins( "fjo", "ф'ё" ); ins( "fju", "ф'ю" );
|
||||
ins( "hja", "г'я" ); ins( "hje", "г'е" ); ins( "hjo", "г'ё" ); ins( "hju", "г'ю" );
|
||||
ins( "kja", "к'я" ); ins( "kje", "к'е" ); ins( "kjo", "к'ё" ); ins( "kju", "к'ю" );
|
||||
ins( "łja", "л'я" ); ins( "łje", "л'е" ); ins( "łjo", "л'ё" ); ins( "łju", "л'ю" );
|
||||
ins( "mja", "м'я" ); ins( "mje", "м'е" ); ins( "mjo", "м'ё" ); ins( "mju", "м'ю" );
|
||||
ins( "nja", "н'я" ); ins( "nje", "н'е" ); ins( "njo", "н'ё" ); ins( "nju", "н'ю" );
|
||||
ins( "pja", "п'я" ); ins( "pje", "п'е" ); ins( "pjo", "п'ё" ); ins( "pju", "п'ю" );
|
||||
ins( "sja", "с'я" ); ins( "sje", "с'е" ); ins( "sjo", "с'ё" ); ins( "sju", "с'ю" );
|
||||
ins( "vja", "в'я" ); ins( "vje", "в'е" ); ins( "vjo", "в'ё" ); ins( "vju", "в'ю" );
|
||||
ins( "wja", "в'я" ); ins( "wje", "в'е" ); ins( "wjo", "в'ё" ); ins( "wju", "в'ю" );
|
||||
ins( "zja", "з'я" ); ins( "zje", "з'е" ); ins( "zjo", "з'ё" ); ins( "zju", "з'ю" );
|
||||
ins( "źja", "з'я" ); ins( "źje", "з'е" ); ins( "źjo", "з'ё" ); ins( "źju", "з'ю" );
|
||||
ins( "chja", "х'я" ); ins( "chje", "х'е" ); ins( "chjo", "х'ё" ); ins( "chju", "х'ю" );
|
||||
|
||||
ins( "śbia", "сбя" ); ins( "śbie", "сбе" ); ins( "śbio", "сбё" ); ins( "śbiu", "сбю" ); ins( "śbi", "сбі" );
|
||||
ins( "ścia", "сця" ); ins( "ście", "сце" ); ins( "ścio", "сцё" ); ins( "ściu", "сцю" ); ins( "ści", "сці" ); ins( "ść", "сць" );
|
||||
ins( "śdzia","сдзя" ); ins( "śdzie","сдзе" ); ins( "śdzio","сдзё" ); ins( "śdziu","сдзю" ); ins( "śdzi","сдзі" ); ins( "śdź","сдзь" );
|
||||
ins( "śfia", "сфя" ); ins( "śfie", "сфе" ); ins( "śfio", "сфё" ); ins( "śfiu", "сфю" ); ins( "śfi", "сфі" );
|
||||
ins( "ślia", "сліa" ); ins( "ślie", "сліэ" ); ins( "ślio", "сліо" ); ins( "śliu", "сліу" ); ins( "śli", "слі" ); ins( "śl", "сль" );
|
||||
ins( "śmia", "смя" ); ins( "śmie", "сме" ); ins( "śmio", "смё" ); ins( "śmiu", "смю" ); ins( "śmi", "смі" );
|
||||
ins( "śnia", "сня" ); ins( "śnie", "сне" ); ins( "śnio", "снё" ); ins( "śniu", "сню" ); ins( "śni", "сні" ); ins( "śń", "снь" );
|
||||
ins( "śpia", "спя" ); ins( "śpie", "спе" ); ins( "śpio", "спё" ); ins( "śpiu", "спю" ); ins( "śpi", "спі" );
|
||||
ins( "śsia", "сся" ); ins( "śsie", "ссе" ); ins( "śsio", "ссё" ); ins( "śsiu", "ссю" ); ins( "śsi", "ссі" );
|
||||
ins( "śvia", "свя" ); ins( "śvie", "све" ); ins( "śvio", "свё" ); ins( "śviu", "свю" ); ins( "śvi", "сві" );
|
||||
ins( "świa", "свя" ); ins( "świe", "све" ); ins( "świo", "свё" ); ins( "świu", "свю" ); ins( "świ", "сві" );
|
||||
ins( "śzia", "сзя" ); ins( "śzie", "сзе" ); ins( "śzio", "сзё" ); ins( "śziu", "сзю" ); ins( "śzi", "сзі" ); ins( "śź", "сзь" );
|
||||
|
||||
ins( "ćbia", "цбя" ); ins( "ćbie", "цбе" ); ins( "ćbio", "цбё" ); ins( "ćbiu", "цбю" ); ins( "ćbi", "цбі" );
|
||||
ins( "ćcia", "цця" ); ins( "ćcie", "цце" ); ins( "ćcio", "ццё" ); ins( "ćciu", "ццю" ); ins( "ćci", "цці" );
|
||||
ins( "ćdzia","цдзя" ); ins( "ćdzie","цдзе" ); ins( "ćdzio","цдзё" ); ins( "ćdziu","цдзю" ); ins( "ćdzi","цдзі" );
|
||||
ins( "ćfia", "цфя" ); ins( "ćfie", "цфе" ); ins( "ćfio", "цфё" ); ins( "ćfiu", "цфю" ); ins( "ćfi", "цфі" );
|
||||
ins( "ćlia", "цліa" ); ins( "ćlie", "цліэ" ); ins( "ćlio", "цліо" ); ins( "ćliu", "цліу" ); ins( "ćli", "цлі" ); ins( "ćl", "цль" );
|
||||
ins( "ćmia", "цмя" ); ins( "ćmie", "цме" ); ins( "ćmio", "цмё" ); ins( "ćmiu", "цмю" ); ins( "ćmi", "цмі" );
|
||||
ins( "ćnia", "цня" ); ins( "ćnie", "цне" ); ins( "ćnio", "цнё" ); ins( "ćniu", "цню" ); ins( "ćni", "цні" ); ins( "ćń", "цнь" );
|
||||
ins( "ćpia", "цпя" ); ins( "ćpie", "цпе" ); ins( "ćpio", "цпё" ); ins( "ćpiu", "цпю" ); ins( "ćpi", "цпі" );
|
||||
ins( "ćsia", "цся" ); ins( "ćsie", "цсе" ); ins( "ćsio", "цсё" ); ins( "ćsiu", "цсю" ); ins( "ćsi", "цсі" ); ins( "ćś", "цсь" );
|
||||
ins( "ćvia", "цвя" ); ins( "ćvie", "цве" ); ins( "ćvio", "цвё" ); ins( "ćviu", "цвю" ); ins( "ćvi", "цві" );
|
||||
ins( "ćwia", "цвя" ); ins( "ćwie", "цве" ); ins( "ćwio", "цвё" ); ins( "ćwiu", "цвю" ); ins( "ćwi", "цві" );
|
||||
ins( "ćzia", "цзя" ); ins( "ćzie", "цзе" ); ins( "ćzio", "цзё" ); ins( "ćziu", "цзю" ); ins( "ćzi", "цзі" ); ins( "ćź", "цзь" );
|
||||
|
||||
ins( "źbia", "збя" ); ins( "źbie", "збе" ); ins( "źbio", "збё" ); ins( "źbiu", "збю" ); ins( "źbi", "збі" );
|
||||
ins( "źcia", "зця" ); ins( "źcie", "зце" ); ins( "źcio", "зцё" ); ins( "źciu", "зцю" ); ins( "źci", "зці" ); ins( "źć", "зць" );
|
||||
ins( "ździa","здзя" ); ins( "ździe","здзе" ); ins( "ździo","здзё" ); ins( "ździu","здзю" ); ins( "ździ","здзі" ); ins( "źdź","здзь" );
|
||||
ins( "źfia", "зфя" ); ins( "źfie", "зфе" ); ins( "źfio", "зфё" ); ins( "źfiu", "зфю" ); ins( "źfi", "зфі" );
|
||||
ins( "źlia", "зліa" ); ins( "źlie", "зліэ" ); ins( "źlio", "зліо" ); ins( "źliu", "зліу" ); ins( "źli", "злі" ); ins( "źl", "зль" );
|
||||
ins( "źmia", "змя" ); ins( "źmie", "зме" ); ins( "źmio", "змё" ); ins( "źmiu", "змю" ); ins( "źmi", "змі" );
|
||||
ins( "źnia", "зня" ); ins( "źnie", "зне" ); ins( "źnio", "знё" ); ins( "źniu", "зню" ); ins( "źni", "зні" );
|
||||
ins( "źpia", "зпя" ); ins( "źpie", "зпе" ); ins( "źpio", "зпё" ); ins( "źpiu", "зпю" ); ins( "źpi", "зпі" );
|
||||
ins( "źsia", "зся" ); ins( "źsie", "зсе" ); ins( "źsio", "зсё" ); ins( "źsiu", "зсю" ); ins( "źsi", "зсі" );
|
||||
ins( "źvia", "звя" ); ins( "źvie", "зве" ); ins( "źvio", "звё" ); ins( "źviu", "звю" ); ins( "źvi", "зві" );
|
||||
ins( "źwia", "звя" ); ins( "źwie", "зве" ); ins( "źwio", "звё" ); ins( "źwiu", "звю" ); ins( "źwi", "зві" );
|
||||
ins( "źzia", "ззя" ); ins( "źzie", "ззе" ); ins( "źzio", "ззё" ); ins( "źziu", "ззю" ); ins( "źzi", "ззі" );
|
||||
|
||||
ins( "ńnia", "ння" ); ins( "ńnie", "нне" ); ins( "ńnio", "ннё" ); ins( "ńniu", "нню" ); ins( "ńni", "нні" );
|
||||
ins( "dździa", "ддзя" ); ins( "dździe", "ддзе" ); ins( "dździo", "ддзё" ); ins( "dździu", "ддзю" ); ins( "dździ", "ддзі" );
|
||||
|
||||
// cyrillic to latin
|
||||
ins( "а", "a" ); ins( "б", "b" ); ins( "в", "v" ); ins( "г", "h" );
|
||||
ins( "ґ", "g" ); ins( "д", "d" ); ins( "е", "je" ); ins( "ё", "jo" );
|
||||
ins( "ж", "ž" ); ins( "з", "z" ); ins( "і", "i" ); ins( "ї", "ï" );
|
||||
ins( "й", "j" ); ins( "к", "k" ); ins( "л", "ł" ); ins( "м", "m" );
|
||||
ins( "н", "n" ); ins( "о", "o" ); ins( "п", "p" ); ins( "р", "r" );
|
||||
ins( "с", "s" ); ins( "т", "t" ); ins( "у", "u" ); ins( "ў", "ŭ" );
|
||||
ins( "ф", "f" ); ins( "х", "ch" ); ins( "ц", "c" ); ins( "ч", "č" );
|
||||
ins( "ш", "š" ); ins( "ы", "y" ); ins( "э", "e" ); ins( "ю", "ju" );
|
||||
ins( "я", "ja" ); ins( "кг", "g" );
|
||||
ins( "бе", "bie" ); ins( "бё", "bio" ); ins( "бю", "biu" ); ins( "бя", "bia" );
|
||||
ins( "ве", "vie" ); ins( "вё", "vio" ); ins( "вю", "viu" ); ins( "вя", "via" );
|
||||
ins( "ге", "hie" ); ins( "гё", "hio" ); ins( "гю", "hiu" ); ins( "гя", "hia" );
|
||||
ins( "ґе", "gie" ); ins( "ґё", "gio" ); ins( "ґю", "giu" ); ins( "ґя", "gia" );
|
||||
ins( "кге", "gie" ); ins( "кгё", "gio" ); ins( "кгю", "giu" ); ins( "кгя", "gia" );
|
||||
ins( "зе", "zie" ); ins( "зё", "zio" ); ins( "зю", "ziu" ); ins( "зя", "zia" ); ins( "зь", "ź" );
|
||||
ins( "ке", "kie" ); ins( "кё", "kio" ); ins( "кю", "kiu" ); ins( "кя", "kia" );
|
||||
ins( "ле", "le" ); ins( "лё", "lo" ); ins( "лю", "lu" ); ins( "ля", "la" ); ins( "ль", "l" ); ins( "лі", "li" );
|
||||
ins( "ме", "mie" ); ins( "мё", "mio" ); ins( "мю", "miu" ); ins( "мя", "mia" ); ins( "мь", "m" );
|
||||
ins( "не", "nie" ); ins( "нё", "nio" ); ins( "ню", "niu" ); ins( "ня", "nia" ); ins( "нь", "ń" );
|
||||
ins( "пе", "pie" ); ins( "пё", "pio" ); ins( "пю", "piu" ); ins( "пя", "pia" ); ins( "пь", "p" );
|
||||
ins( "се", "sie" ); ins( "сё", "sio" ); ins( "сю", "siu" ); ins( "ся", "sia" ); ins( "сь", "ś" );
|
||||
ins( "фе", "fie" ); ins( "фё", "fio" ); ins( "фю", "fiu" ); ins( "фя", "fia" ); ins( "фь", "f" );
|
||||
ins( "хе", "chie" ); ins( "хё", "chio" ); ins( "хю", "chiu" ); ins( "хя", "chia" );
|
||||
ins( "це", "cie" ); ins( "цё", "cio" ); ins( "цю", "ciu" ); ins( "ця", "cia" ); ins( "ць", "ć" );
|
||||
ins( "бʼ", "b" ); ins( "б'", "b" ); ins( "б’", "b" );
|
||||
ins( "вʼ", "v" ); ins( "в'", "v" ); ins( "в’", "v" );
|
||||
ins( "гʼ", "h" ); ins( "г'", "h" ); ins( "г’", "h" );
|
||||
ins( "ґʼ", "g" ); ins( "ґ'", "g" ); ins( "ґ’", "g" );
|
||||
ins( "дʼ", "d" ); ins( "д'", "d" ); ins( "д’", "d" );
|
||||
ins( "жʼ", "ž" ); ins( "ж'", "ž" ); ins( "ж’", "ž" );
|
||||
ins( "зʼ", "z" ); ins( "з'", "z" ); ins( "з’", "z" );
|
||||
ins( "кʼ", "k" ); ins( "к'", "k" ); ins( "к’", "k" );
|
||||
ins( "лʼ", "ł" ); ins( "л'", "ł" ); ins( "л’", "ł" );
|
||||
ins( "мʼ", "m" ); ins( "м'", "m" ); ins( "м’", "m" );
|
||||
ins( "нʼ", "n" ); ins( "н'", "n" ); ins( "н’", "n" );
|
||||
ins( "пʼ", "p" ); ins( "п'", "p" ); ins( "п’", "p" );
|
||||
ins( "рʼ", "r" ); ins( "р'", "r" ); ins( "р’", "r" );
|
||||
ins( "сʼ", "s" ); ins( "с'", "s" ); ins( "с’", "s" );
|
||||
ins( "тʼ", "t" ); ins( "т'", "t" ); ins( "т’", "t" );
|
||||
ins( "фʼ", "f" ); ins( "ф'", "f" ); ins( "ф’", "f" );
|
||||
ins( "хʼ", "ch" ); ins( "х'", "ch" ); ins( "х’", "ch" );
|
||||
ins( "цʼ", "c" ); ins( "ц'", "c" ); ins( "ц’", "c" );
|
||||
ins( "чʼ", "č" ); ins( "ч'", "č" ); ins( "ч’", "č" );
|
||||
ins( "шʼ", "š" ); ins( "ш'", "š" ); ins( "ш’", "š" );
|
||||
}
|
||||
};
|
||||
|
||||
class BelarusianSchoolToClassicTable: public Transliteration::Table
|
||||
{
|
||||
public:
|
||||
BelarusianSchoolToClassicTable()
|
||||
{
|
||||
ins( "ньне", "нне" ); ins( "ньня", "ння" ); ins( "ньню", "нню" ); ins( "ньнё", "ннё" ); ins( "ньні", "нні" );
|
||||
ins( "нне", "ньне" ); ins( "ння", "ньня" ); ins( "нню", "ньню" ); ins( "ннё", "ньнё" ); ins( "нні", "ньні" );
|
||||
|
||||
ins( "льле", "лле" ); ins( "льля", "лля" ); ins( "льлю", "ллю" ); ins( "льлё", "ллё" ); ins( "льлі", "ллі" );
|
||||
ins( "лле", "льле" ); ins( "лля", "льля" ); ins( "ллю", "льлю" ); ins( "ллё", "льлё" ); ins( "ллі", "льлі" );
|
||||
|
||||
ins( "зьбе", "збе" ); ins( "зьбя", "збя" ); ins( "зьбю", "збю" ); ins( "зьбё", "збё" ); ins( "зьбі", "збі" );
|
||||
ins( "збе", "зьбе" ); ins( "збя", "зьбя" ); ins( "збю", "зьбю" ); ins( "збё", "зьбё" ); ins( "збі", "зьбі" );
|
||||
ins( "зьве", "зве" ); ins( "зьвя", "звя" ); ins( "зьвю", "звю" ); ins( "зьвё", "звё" ); ins( "зьві", "зві" );
|
||||
ins( "зве", "зьве" ); ins( "звя", "зьвя" ); ins( "звю", "зьвю" ); ins( "звё", "зьвё" ); ins( "зві", "зьві" );
|
||||
ins( "зьдзе", "здзе" ); ins( "зьдзя", "здзя" ); ins( "зьдзю", "здзю" ); ins( "зьдзё", "здзё" ); ins( "зьдзі", "здзі" ); ins( "зьдзь", "здзь" );
|
||||
ins( "здзе", "зьдзе" ); ins( "здзя", "зьдзя" ); ins( "здзю", "зьдзю" ); ins( "здзё", "зьдзё" ); ins( "здзі", "зьдзі" ); ins( "здзь", "здзь" );
|
||||
ins( "зьзе", "ззе" ); ins( "зьзя", "ззя" ); ins( "зьзю", "ззю" ); ins( "зьзё", "ззё" ); ins( "зьзі", "ззі" ); ins( "зьзь", "ззь" );
|
||||
ins( "ззе", "зьзе" ); ins( "ззя", "зьзя" ); ins( "ззю", "зьзю" ); ins( "ззё", "зьзё" ); ins( "ззі", "зьзі" ); ins( "ззь", "зьзь" );
|
||||
ins( "зьле", "зле" ); ins( "зьля", "зля" ); ins( "зьлю", "злю" ); ins( "зьлё", "злё" ); ins( "зьлі", "злі" ); ins( "зьль", "зль" );
|
||||
ins( "зле", "зьле" ); ins( "зля", "зьля" ); ins( "злю", "зьлю" ); ins( "злё", "зьлё" ); ins( "злі", "зьлі" ); ins( "зль", "зьль" );
|
||||
ins( "зьме", "зме" ); ins( "зьмя", "змя" ); ins( "зьмю", "змю" ); ins( "зьмё", "змё" ); ins( "зьмі", "змі" );
|
||||
ins( "зме", "зьме" ); ins( "змя", "зьмя" ); ins( "змю", "зьмю" ); ins( "змё", "зьмё" ); ins( "змі", "зьмі" );
|
||||
ins( "зьне", "зне" ); ins( "зьня", "зня" ); ins( "зьню", "зню" ); ins( "зьнё", "знё" ); ins( "зьні", "зні" ); ins( "зьнь", "знь" );
|
||||
ins( "зне", "зьне" ); ins( "зня", "зьня" ); ins( "зню", "зьню" ); ins( "знё", "зьнё" ); ins( "зні", "зьні" ); ins( "знь", "зьнь" );
|
||||
ins( "зьпе", "зпе" ); ins( "зьпя", "зпя" ); ins( "зьпю", "зпю" ); ins( "зьпё", "зпё" ); ins( "зьпі", "зпі" );
|
||||
ins( "зпе", "зьпе" ); ins( "зпя", "зьпя" ); ins( "зпю", "зьпю" ); ins( "зпё", "зьпё" ); ins( "зпі", "зьпі" );
|
||||
ins( "зьсе", "зсе" ); ins( "зься", "зся" ); ins( "зьсю", "зсю" ); ins( "зьсё", "зсё" ); ins( "зьсі", "зсі" ); ins( "зьсь", "зсь" );
|
||||
ins( "зсе", "зьсе" ); ins( "зся", "зься" ); ins( "зсю", "зьсю" ); ins( "зсё", "зьсё" ); ins( "зсі", "зьсі" ); ins( "зсь", "зьсь" );
|
||||
ins( "зьфе", "зфе" ); ins( "зьфя", "зфя" ); ins( "зьфю", "зфю" ); ins( "зьфё", "зфё" ); ins( "зьфі", "зфі" );
|
||||
ins( "зфе", "зьфе" ); ins( "зфя", "зьфя" ); ins( "зфю", "зьфю" ); ins( "зфё", "зьфё" ); ins( "зфі", "зьфі" );
|
||||
ins( "зьхе", "зхе" ); ins( "зьхя", "зхя" ); ins( "зьхю", "зхю" ); ins( "зьхё", "зхё" ); ins( "зьхі", "зхі" );
|
||||
ins( "зхе", "зьхе" ); ins( "зхя", "зьхя" ); ins( "зхю", "зьхю" ); ins( "зхё", "зьхё" ); ins( "зхі", "зьхі" );
|
||||
ins( "зьце", "зце" ); ins( "зьця", "зця" ); ins( "зьцю", "зцю" ); ins( "зьцё", "зцё" ); ins( "зьці", "зці" ); ins( "зьць", "зць" );
|
||||
ins( "зце", "зьце" ); ins( "зця", "зьця" ); ins( "зцю", "зьцю" ); ins( "зцё", "зьцё" ); ins( "зці", "зьці" ); ins( "зць", "зьць" );
|
||||
|
||||
ins( "сьбе", "сбе" ); ins( "сьбя", "сбя" ); ins( "сьбю", "сбю" ); ins( "сьбё", "сбё" ); ins( "сьбі", "сбі" );
|
||||
ins( "сбе", "сьбе" ); ins( "сбя", "сьбя" ); ins( "сбю", "сьбю" ); ins( "сбё", "сьбё" ); ins( "сбі", "сьбі" );
|
||||
ins( "сьве", "све" ); ins( "сьвя", "свя" ); ins( "сьвю", "свю" ); ins( "сьвё", "свё" ); ins( "сьві", "сві" );
|
||||
ins( "све", "сьве" ); ins( "свя", "сьвя" ); ins( "свю", "сьвю" ); ins( "свё", "сьвё" ); ins( "сві", "зьві" );
|
||||
ins( "сьдзе", "сдзе" ); ins( "сьдзя", "сдзя" ); ins( "сьдзю", "сдзю" ); ins( "сьдзё", "сдзё" ); ins( "сьдзі", "сдзі" ); ins( "сьдзь", "сдзь" );
|
||||
ins( "сдзе", "сьдзе" ); ins( "сдзя", "сьдзя" ); ins( "сдзю", "сьдзю" ); ins( "сдзё", "сьдзё" ); ins( "сдзі", "сьдзі" ); ins( "сдзь", "сьдзь" );
|
||||
ins( "сьзе", "сзе" ); ins( "сьзя", "сзя" ); ins( "сьзю", "сзю" ); ins( "сьзё", "сзё" ); ins( "сьзі", "сзі" ); ins( "сьзь", "сзь" );
|
||||
ins( "сзе", "сьзе" ); ins( "сзя", "сьзя" ); ins( "сзю", "сьзю" ); ins( "сзё", "сьзё" ); ins( "сзі", "сьзі" ); ins( "сзь", "сьзь" );
|
||||
ins( "сьле", "сле" ); ins( "сьля", "сля" ); ins( "сьлю", "слю" ); ins( "сьлё", "слё" ); ins( "сьлі", "слі" ); ins( "сьль", "сль" );
|
||||
ins( "сле", "сьле" ); ins( "сля", "сьля" ); ins( "слю", "сьлю" ); ins( "слё", "сьлё" ); ins( "слі", "сьлі" ); ins( "сль", "сьль" );
|
||||
ins( "сьме", "сме" ); ins( "сьмя", "смя" ); ins( "сьмю", "смю" ); ins( "сьмё", "смё" ); ins( "сьмі", "смі" );
|
||||
ins( "сме", "сьме" ); ins( "смя", "сьмя" ); ins( "смю", "сьмю" ); ins( "смё", "сьмё" ); ins( "смі", "сьмі" );
|
||||
ins( "сьне", "сне" ); ins( "сьня", "сня" ); ins( "сьню", "сню" ); ins( "сьнё", "снё" ); ins( "сьні", "сні" ); ins( "сьнь", "снь" );
|
||||
ins( "сне", "сьне" ); ins( "сня", "сьня" ); ins( "сню", "сьню" ); ins( "снё", "сьнё" ); ins( "сні", "сьні" ); ins( "снь", "сьнь" );
|
||||
ins( "сьпе", "спе" ); ins( "сьпя", "спя" ); ins( "сьпю", "спю" ); ins( "сьпё", "спё" ); ins( "сьпі", "спі" );
|
||||
ins( "спе", "сьпе" ); ins( "спя", "сьпя" ); ins( "спю", "сьпю" ); ins( "спё", "сьпё" ); ins( "спі", "сьпі" );
|
||||
ins( "сьсе", "ссе" ); ins( "сься", "сся" ); ins( "сьсю", "ссю" ); ins( "сьсё", "ссё" ); ins( "сьсі", "ссі" ); ins( "сьсь", "ссь" );
|
||||
ins( "ссе", "сьсе" ); ins( "сся", "сься" ); ins( "ссю", "сьсю" ); ins( "ссё", "сьсё" ); ins( "ссі", "сьсі" ); ins( "ссь", "сьсь" );
|
||||
ins( "сьфе", "сфе" ); ins( "сьфя", "сфя" ); ins( "сьфю", "сфю" ); ins( "сьфё", "сфё" ); ins( "сьфі", "сфі" );
|
||||
ins( "сфе", "сьфе" ); ins( "сфя", "сьфя" ); ins( "сфю", "сьфю" ); ins( "сфё", "сьфё" ); ins( "сфі", "сьфі" );
|
||||
ins( "сьхе", "схе" ); ins( "сьхя", "схя" ); ins( "сьхю", "схю" ); ins( "сьхё", "схё" ); ins( "сьхі", "схі" );
|
||||
ins( "схе", "сьхе" ); ins( "схя", "сьхя" ); ins( "схю", "сьхю" ); ins( "схё", "сьхё" ); ins( "схі", "сьхі" );
|
||||
ins( "сьце", "сце" ); ins( "сьця", "сця" ); ins( "сьцю", "сцю" ); ins( "сьцё", "сцё" ); ins( "сьці", "сці" ); ins( "сьць", "сць" );
|
||||
ins( "сце", "сьце" ); ins( "сця", "сьця" ); ins( "сцю", "сьцю" ); ins( "сцё", "сьцё" ); ins( "сці", "сьці" ); ins( "сць", "сьць" );
|
||||
|
||||
ins( "цьбе", "цбе" ); ins( "цьбя", "цбя" ); ins( "цьбю", "цбю" ); ins( "цьбё", "цбё" ); ins( "цьбі", "цбі" );
|
||||
ins( "цбе", "цьбе" ); ins( "цбя", "цьбя" ); ins( "цбю", "цьбю" ); ins( "цбё", "цьбё" ); ins( "цбі", "цьбі" );
|
||||
ins( "цьве", "цве" ); ins( "цьвя", "цвя" ); ins( "цьвю", "цвю" ); ins( "цьвё", "цвё" ); ins( "цьві", "цві" );
|
||||
ins( "цве", "цьве" ); ins( "цвя", "цьвя" ); ins( "цвю", "цьвю" ); ins( "цвё", "цьвё" ); ins( "цві", "цьві" );
|
||||
ins( "цьдзе", "цдзе" ); ins( "цьдзя", "цдзя" ); ins( "цьдзю", "цдзю" ); ins( "цьдзё", "цдзё" ); ins( "цьдзі", "цдзі" ); ins( "цьдзь", "цдзь" );
|
||||
ins( "цдзе", "цьдзе" ); ins( "цдзя", "цьдзя" ); ins( "цдзю", "цьдзю" ); ins( "цдзё", "цьдзё" ); ins( "цдзі", "цьдзі" ); ins( "цдзь", "цьдзь" );
|
||||
ins( "цьзе", "цзе" ); ins( "цьзя", "цзя" ); ins( "цьзю", "цзю" ); ins( "цьзё", "цзё" ); ins( "цьзі", "цзі" ); ins( "цьзь", "цзь" );
|
||||
ins( "цзе", "цьзе" ); ins( "цзя", "цьзя" ); ins( "цзю", "цьзю" ); ins( "цзё", "цьзё" ); ins( "цзі", "цьзі" ); ins( "цзь", "цьзь" );
|
||||
ins( "цьле", "цле" ); ins( "цьля", "цля" ); ins( "цьлю", "цлю" ); ins( "цьлё", "цлё" ); ins( "цьлі", "цлі" ); ins( "цьль", "цль" );
|
||||
ins( "цле", "цьле" ); ins( "цля", "цьля" ); ins( "цлю", "цьлю" ); ins( "цлё", "цьлё" ); ins( "цлі", "цьлі" ); ins( "цль", "цьль" );
|
||||
ins( "цьме", "цме" ); ins( "цьмя", "цмя" ); ins( "цьмю", "цмю" ); ins( "цьмё", "цмё" ); ins( "цьмі", "цмі" );
|
||||
ins( "цме", "цьме" ); ins( "цмя", "цьмя" ); ins( "цмю", "цьмю" ); ins( "цмё", "цьмё" ); ins( "цмі", "цьмі" );
|
||||
ins( "цьне", "цне" ); ins( "цьня", "цня" ); ins( "цьню", "цню" ); ins( "цьнё", "цнё" ); ins( "цьні", "цні" ); ins( "цьнь", "цнь" );
|
||||
ins( "цне", "цьне" ); ins( "цня", "цьня" ); ins( "цню", "цьню" ); ins( "цнё", "цьнё" ); ins( "цні", "цьні" ); ins( "цнь", "цьнь" );
|
||||
ins( "цьпе", "цпе" ); ins( "цьпя", "цпя" ); ins( "цьпю", "цпю" ); ins( "цьпё", "цпё" ); ins( "цьпі", "цпі" );
|
||||
ins( "цпе", "цьпе" ); ins( "цпя", "цьпя" ); ins( "цпю", "цьпю" ); ins( "цпё", "цьпё" ); ins( "цпі", "цьпі" );
|
||||
ins( "цьсе", "цсе" ); ins( "цься", "цся" ); ins( "цьсю", "цсю" ); ins( "цьсё", "цсё" ); ins( "цьсі", "цсі" ); ins( "цьсь", "цсь" );
|
||||
ins( "цсе", "цьсе" ); ins( "цся", "цься" ); ins( "цсю", "цьсю" ); ins( "цсё", "цьсё" ); ins( "цсі", "цьсі" ); ins( "цсь", "цьсь" );
|
||||
ins( "цьфе", "цфе" ); ins( "цьфя", "цфя" ); ins( "цьфю", "цфю" ); ins( "цьфё", "цфё" ); ins( "цьфі", "цфі" );
|
||||
ins( "цфе", "цьфе" ); ins( "цфя", "цьфя" ); ins( "цфю", "цьфю" ); ins( "цфё", "цьфё" ); ins( "цфі", "цьфі" );
|
||||
ins( "цьце", "цце" ); ins( "цьця", "цця" ); ins( "цьцю", "ццю" ); ins( "цьцё", "ццё" ); ins( "цьці", "цці" ); ins( "цьць", "цць" );
|
||||
ins( "цце", "цьце" ); ins( "цця", "цьця" ); ins( "ццю", "цьцю" ); ins( "ццё", "цьцё" ); ins( "цці", "цьці" ); ins( "цць", "цьць" );
|
||||
|
||||
ins( "дзьдзе", "ддзе" ); ins( "дзьдзя", "ддзя" ); ins( "дзьдзю", "ддзю" ); ins( "дзьдзё", "ддзё" ); ins( "дзьдзі", "ддзі" );
|
||||
ins( "ддзе", "дзьдзе" ); ins( "ддзя", "дзьдзя" ); ins( "ддзю", "дзьдзю" ); ins( "ддзё", "дзьдзё" ); ins( "ддзі", "дзьдзі" );
|
||||
|
||||
ins( "з'е", "зье" ); ins( "з'я", "зья" ); ins( "з'ю", "зью" ); ins( "з'ё", "зьё" ); ins( "з'і", "зьі" );
|
||||
ins( "зье", "з'е" ); ins( "зья", "з'я" ); ins( "зью", "з'ю" ); ins( "зьё", "з'ё" ); ins( "зьі", "з'і" );
|
||||
ins( "зʼе", "зье" ); ins( "зʼя", "зья" ); ins( "зʼю", "зью" ); ins( "зʼё", "зьё" ); ins( "зʼі", "зьі" );
|
||||
ins( "зье", "зʼе" ); ins( "зья", "зʼя" ); ins( "зью", "зʼю" ); ins( "зьё", "зʼё" ); ins( "зьі", "зʼі" );
|
||||
ins( "з’е", "зье" ); ins( "з’я", "зья" ); ins( "з’ю", "зью" ); ins( "з’ё", "зьё" ); ins( "з’і", "зьі" );
|
||||
ins( "зье", "з’е" ); ins( "зья", "з’я" ); ins( "зью", "з’ю" ); ins( "зьё", "з’ё" ); ins( "зьі", "з’і" );
|
||||
}
|
||||
};
|
||||
|
||||
std::vector< sptr< Dictionary::Class > > makeDictionaries() throw( std::exception )
|
||||
{
|
||||
static BelarusianLatinToClassicTable t0;
|
||||
static BelarusianLatinToSchoolTable t1;
|
||||
static BelarusianSchoolToClassicTable t2;
|
||||
std::vector< sptr< Dictionary::Class > > dicts;
|
||||
|
||||
dicts.push_back( new Transliteration::TransliterationDictionary( "c31b24abf412abc9b23bb40a898f1040",
|
||||
QCoreApplication::translate( "BelarusianTranslit", "Belarusian transliteration from latin to cyrillic (classic orthography)" ).toUtf8().data(),
|
||||
QIcon( ":/flags/by.png" ), t0, false ) );
|
||||
dicts.push_back( new Transliteration::TransliterationDictionary( "c31b24abf412abc9b23bb40a898f1041",
|
||||
QCoreApplication::translate( "BelarusianTranslit", "Belarusian transliteration from latin to cyrillic (school orthography)" ).toUtf8().data(),
|
||||
QIcon( ":/flags/by.png" ), t1, false ) );
|
||||
dicts.push_back( new Transliteration::TransliterationDictionary( "c31b24abf412abc9b23bb40a898f1042",
|
||||
QCoreApplication::translate( "BelarusianTranslit", "Belarusian transliteration (smoothes out the difference\n"
|
||||
"between classic and school orthography in cyrillic)" ).toUtf8().data(),
|
||||
QIcon( ":/flags/by.png" ), t2, false ) );
|
||||
return dicts;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/* This file is (c) 2013 Maksim Tamkovicz <quendimax@gmail.com>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __BELARUSIANTRANSLIT_HH_INCLUDED__
|
||||
#define __BELARUSIANTRANSLIT_HH_INCLUDED__
|
||||
|
||||
#include <vector>
|
||||
#include "dictionary.hh"
|
||||
|
||||
// Support for Belarusian transliteration
|
||||
namespace BelarusianTranslit {
|
||||
|
||||
std::vector< sptr< Dictionary::Class > > makeDictionaries() throw( std::exception );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __BGL_HH_INCLUDED__
|
||||
#define __BGL_HH_INCLUDED__
|
||||
|
||||
#include "dictionary.hh"
|
||||
|
||||
/// Support for the Babylon's .bgl dictionaries.
|
||||
namespace Bgl {
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
/// Goes through the given list of file names, trying each one as a possible
|
||||
/// dictionary. Upon finding one, creates a corresponding dictionary instance.
|
||||
/// As a result, a list of dictionaries is created.
|
||||
/// indicesDir indicates a directory where index files can be created, should
|
||||
/// there be need for them. The index file name would be the same as the
|
||||
/// dictionary's id, made by makeDictionaryId() from the list of file names.
|
||||
/// Any exception thrown would terminate the program with an error.
|
||||
vector< sptr< Dictionary::Class > > makeDictionaries(
|
||||
vector< string > const & fileNames,
|
||||
string const & indicesDir,
|
||||
Dictionary::Initializing & )
|
||||
throw( std::exception );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,834 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Raul Fernandes and Karl Grill *
|
||||
* rgbr@yahoo.com.br *
|
||||
* *
|
||||
* 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 *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
/* Various improvements were made by Konstantin Isakov for the GoldenDict
|
||||
* program. */
|
||||
|
||||
#include "bgl_babylon.hh"
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <iconv.h>
|
||||
#include <QTextDocument>
|
||||
#include "gddebug.hh"
|
||||
#include "ufile.hh"
|
||||
#include "iconv.hh"
|
||||
#include "htmlescape.hh"
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include "dictionary.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#define DUP _dup
|
||||
#else
|
||||
#define DUP dup
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
|
||||
DEF_EX_STR( exCantReadFile, "Can't read file", Dictionary::Ex )
|
||||
DEF_EX( exUserAbort, "User abort", Dictionary::Ex )
|
||||
DEF_EX( exIconv, "Iconv library error", Dictionary::Ex )
|
||||
DEF_EX( exAllocation, "Error memory allocation", Dictionary::Ex )
|
||||
|
||||
Babylon::Babylon( std::string filename ) :
|
||||
m_filename( filename )
|
||||
{
|
||||
file = NULL;
|
||||
}
|
||||
|
||||
|
||||
Babylon::~Babylon()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
bool Babylon::open()
|
||||
{
|
||||
FILE *f;
|
||||
unsigned char buf[6];
|
||||
int i;
|
||||
|
||||
f = gd_fopen( m_filename.c_str(), "rb" );
|
||||
if( f == NULL )
|
||||
return false;
|
||||
|
||||
i = fread( buf, 1, 6, f );
|
||||
|
||||
/* First four bytes: BGL signature 0x12340001 or 0x12340002 (big-endian) */
|
||||
if( i < 6 || memcmp( buf, "\x12\x34\x00", 3 ) || buf[3] == 0 || buf[3] > 2 )
|
||||
{
|
||||
fclose( f );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Calculate position of gz header */
|
||||
|
||||
i = buf[4] << 8 | buf[5];
|
||||
|
||||
if( i < 6 )
|
||||
{
|
||||
fclose( f );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( fseek( f, i, SEEK_SET ) ) /* can't seek - emulate */
|
||||
for(int j=0;j < i - 6;j++) fgetc( f );
|
||||
|
||||
if( ferror( f ) || feof( f ) )
|
||||
{
|
||||
fclose( f );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we need to flush the file because otherwise some nfs mounts don't seem
|
||||
* to properly update the file position for the following reopen */
|
||||
|
||||
fflush( f );
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
/* Under Mac OS X the above technique don't set reopen position properly */
|
||||
int fn = DUP( fileno( f ) );
|
||||
lseek( fn, i, SEEK_SET );
|
||||
file = gzdopen( fn, "r" );
|
||||
#else
|
||||
file = gzdopen( DUP( fileno( f ) ), "r" );
|
||||
#endif
|
||||
|
||||
fclose( f );
|
||||
|
||||
if( file == NULL )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Babylon::close()
|
||||
{
|
||||
if ( file )
|
||||
{
|
||||
gzclose( file );
|
||||
file = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Babylon::readBlock( bgl_block &block )
|
||||
{
|
||||
if( gzeof( file ) || file == NULL )
|
||||
return false;
|
||||
|
||||
block.length = bgl_readnum( 1 );
|
||||
block.type = block.length & 0xf;
|
||||
if( block.type == 4 ) return false; // end of file marker
|
||||
block.length >>= 4;
|
||||
block.length = block.length < 4 ? bgl_readnum( block.length + 1 ) : block.length - 4 ;
|
||||
if( block.length )
|
||||
{
|
||||
block.data = (char *)malloc( block.length );
|
||||
if( !block.data )
|
||||
throw exAllocation();
|
||||
|
||||
unsigned res = gzread( file, block.data, block.length );
|
||||
if( block.length != res )
|
||||
{
|
||||
free( block.data );
|
||||
block.length = 0;
|
||||
gzclearerr( file );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
unsigned int Babylon::bgl_readnum( int bytes )
|
||||
{
|
||||
unsigned char buf[4];
|
||||
unsigned val = 0;
|
||||
|
||||
if ( bytes < 1 || bytes > 4 ) return (0);
|
||||
|
||||
int res = gzread( file, buf, bytes );
|
||||
|
||||
if( res != bytes )
|
||||
{
|
||||
gzclearerr( file );
|
||||
return 4; // Read error - return end of file marker
|
||||
}
|
||||
|
||||
for(int i=0;i<bytes;i++) val= (val << 8) | buf[i];
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
bool Babylon::read(std::string &source_charset, std::string &target_charset)
|
||||
{
|
||||
if( file == NULL ) return false;
|
||||
|
||||
bgl_block block;
|
||||
unsigned int pos;
|
||||
unsigned int type;
|
||||
std::string headword;
|
||||
std::string definition;
|
||||
|
||||
bool isUtf8File = false;
|
||||
|
||||
m_sourceCharset = source_charset;
|
||||
m_targetCharset = target_charset;
|
||||
m_numEntries = 0;
|
||||
while( readBlock( block ) )
|
||||
{
|
||||
headword.clear();
|
||||
definition.clear();
|
||||
switch( block.type )
|
||||
{
|
||||
case 0:
|
||||
switch( block.data[0] )
|
||||
{
|
||||
case 8:
|
||||
type = (unsigned int)block.data[2];
|
||||
if( type == 67 ) type = 1;
|
||||
if( type > 64 ) type -= 65;
|
||||
|
||||
if ( type >= 14 )
|
||||
type = 0;
|
||||
|
||||
m_defaultCharset = bgl_charset[type];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 7:
|
||||
case 10:
|
||||
case 11:
|
||||
// Only count entries
|
||||
m_numEntries++;
|
||||
break;
|
||||
case 3:
|
||||
pos = 2;
|
||||
switch( block.data[1] )
|
||||
{
|
||||
case 1:
|
||||
headword.reserve( block.length - 2 );
|
||||
for(unsigned int a=0;a<block.length-2;a++) headword += block.data[pos++];
|
||||
m_title = headword;
|
||||
break;
|
||||
case 2:
|
||||
headword.reserve( block.length - 2 );
|
||||
for(unsigned int a=0;a<block.length-2;a++) headword += block.data[pos++];
|
||||
m_author = headword;
|
||||
break;
|
||||
case 3:
|
||||
headword.reserve( block.length - 2 );
|
||||
for(unsigned int a=0;a<block.length-2;a++) headword += block.data[pos++];
|
||||
m_email = headword;
|
||||
break;
|
||||
case 4:
|
||||
headword.reserve( block.length - 2 );
|
||||
for(unsigned int a=0;a<block.length-2;a++) headword += block.data[pos++];
|
||||
m_copyright = headword;
|
||||
break;
|
||||
case 7:
|
||||
m_sourceLang = bgl_language[(unsigned char)(block.data[5])];
|
||||
//m_sourceLang = headword;
|
||||
break;
|
||||
case 8:
|
||||
m_targetLang = bgl_language[(unsigned char)(block.data[5])];
|
||||
//m_targetLang = headword;
|
||||
break;
|
||||
case 9:
|
||||
headword.reserve( block.length - 2 );
|
||||
for(unsigned int a=0;a<block.length-2;a++) {
|
||||
if (block.data[pos] == '\r') {
|
||||
} else if (block.data[pos] == '\n') {
|
||||
headword += "<br>";
|
||||
} else {
|
||||
headword += block.data[pos];
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
m_description = headword;
|
||||
break;
|
||||
case 11:
|
||||
icon.resize( block.length - 2 );
|
||||
memcpy( &icon.front(), &(block.data[ 2 ]), icon.size() );
|
||||
break;
|
||||
case 17:
|
||||
if ( block.length >= 5 && ( (unsigned char) block.data[ 4 ] & 0x80 ) != 0 )
|
||||
isUtf8File = true;
|
||||
break;
|
||||
case 26:
|
||||
type = (unsigned int)block.data[2];
|
||||
if( type == 67 ) type = 1;
|
||||
if( type > 64 ) type -= 65;
|
||||
if ( type >= 14 )
|
||||
type = 0;
|
||||
if (m_sourceCharset.empty())
|
||||
m_sourceCharset = bgl_charset[type];
|
||||
break;
|
||||
case 27:
|
||||
type = (unsigned int)block.data[2];
|
||||
if( type == 67 ) type = 1;
|
||||
if( type > 64 ) type -= 65;
|
||||
if ( type >= 14 )
|
||||
type = 0;
|
||||
if (m_targetCharset.empty())
|
||||
m_targetCharset = bgl_charset[type];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
if( block.length ) free( block.data );
|
||||
}
|
||||
gzseek( file, 0, SEEK_SET );
|
||||
|
||||
if ( isUtf8File )
|
||||
{
|
||||
//FDPRINTF( stderr, "%s: utf8 file.\n", m_title.c_str() );
|
||||
m_defaultCharset = "UTF-8";
|
||||
m_sourceCharset = "UTF-8";
|
||||
m_targetCharset = "UTF-8";
|
||||
}
|
||||
|
||||
convertToUtf8( m_title, BGL_TARGET_CHARSET );
|
||||
convertToUtf8( m_author, BGL_TARGET_CHARSET );
|
||||
convertToUtf8( m_email, BGL_TARGET_CHARSET );
|
||||
convertToUtf8( m_copyright, BGL_TARGET_CHARSET );
|
||||
convertToUtf8( m_description, BGL_TARGET_CHARSET );
|
||||
GD_DPRINTF("Default charset: %s\nSource Charset: %s\nTargetCharset: %s\n", m_defaultCharset.c_str(), m_sourceCharset.c_str(), m_targetCharset.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bgl_entry Babylon::readEntry( ResourceHandler * resourceHandler )
|
||||
{
|
||||
bgl_entry entry;
|
||||
|
||||
if( file == NULL )
|
||||
{
|
||||
entry.headword = "";
|
||||
return entry;
|
||||
}
|
||||
|
||||
bgl_block block;
|
||||
unsigned int len, pos;
|
||||
unsigned int alts_num;
|
||||
std::string headword, displayedHeadword;
|
||||
std::string definition;
|
||||
std::string temp;
|
||||
std::vector<std::string> alternates;
|
||||
std::string alternate;
|
||||
std::string root;
|
||||
bool defBodyEnded = false;
|
||||
std::string transcription;
|
||||
|
||||
while( readBlock( block ) )
|
||||
{
|
||||
switch( block.type )
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
pos = 0;
|
||||
len = (unsigned char)block.data[pos++];
|
||||
if( pos + len > block.length )
|
||||
break;
|
||||
std::string filename( block.data+pos, len );
|
||||
//if (filename != "8EAF66FD.bmp" && filename != "C2EEF3F6.html") {
|
||||
pos += len;
|
||||
if ( resourceHandler )
|
||||
resourceHandler->handleBabylonResource( filename,
|
||||
block.data + pos,
|
||||
block.length - pos );
|
||||
#if 0
|
||||
FILE *ifile = gd_fopen(filename.c_str(), "w");
|
||||
fwrite(block.data + pos, 1, block.length -pos, ifile);
|
||||
fclose(ifile);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 7:
|
||||
case 10:
|
||||
case 11:
|
||||
alternate.clear();
|
||||
headword.clear();
|
||||
displayedHeadword.clear();
|
||||
root.clear();
|
||||
definition.clear();
|
||||
temp.clear();
|
||||
pos = 0;
|
||||
|
||||
// Headword
|
||||
if( block.type == 11 )
|
||||
{
|
||||
pos = 1;
|
||||
len = 0;
|
||||
if( pos + 4 > block.length )
|
||||
break;
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
len = len << 8;
|
||||
len |= (unsigned char)block.data[ pos++ ];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
len = (unsigned char)block.data[pos++];
|
||||
}
|
||||
|
||||
if( pos + len > block.length )
|
||||
break;
|
||||
|
||||
headword.reserve( len );
|
||||
for(unsigned int a=0;a<len;a++)
|
||||
headword += block.data[pos++];
|
||||
|
||||
convertToUtf8( headword, BGL_SOURCE_CHARSET );
|
||||
|
||||
// Try to repair malformed headwords
|
||||
if( headword.find( "&#" ) != string::npos )
|
||||
headword = Html::unescapeUtf8( headword );
|
||||
|
||||
if( block.type == 11 )
|
||||
{
|
||||
// Alternate forms
|
||||
if( pos + 4 >= block.length )
|
||||
break;
|
||||
|
||||
alts_num = 0;
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
alts_num = alts_num << 8;
|
||||
alts_num |= (unsigned char)block.data[ pos++ ];
|
||||
}
|
||||
|
||||
for( unsigned j = 0; j < alts_num; j++ )
|
||||
{
|
||||
len = 0;
|
||||
if( pos + 4 > block.length )
|
||||
break;
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
len = len << 8;
|
||||
len |= (unsigned char)block.data[ pos++ ];
|
||||
}
|
||||
if( pos + len >= block.length )
|
||||
break;
|
||||
alternate.reserve( len );
|
||||
for(unsigned int a=0;a<len;a++) alternate += block.data[pos++];
|
||||
convertToUtf8( alternate, BGL_SOURCE_CHARSET );
|
||||
|
||||
// Try to repair malformed forms
|
||||
if( alternate.find( "&#" ) != string::npos )
|
||||
alternate = Html::unescapeUtf8( alternate );
|
||||
|
||||
alternates.push_back( alternate );
|
||||
alternate.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Definition
|
||||
|
||||
if( block.type == 11 )
|
||||
{
|
||||
len = 0;
|
||||
if( pos + 4 > block.length )
|
||||
break;
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
len = len << 8;
|
||||
len |= (unsigned char)block.data[ pos++ ];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
len = (unsigned char)block.data[pos++] << 8;
|
||||
len |= (unsigned char)block.data[pos++];
|
||||
}
|
||||
|
||||
if( pos + len > block.length )
|
||||
break;
|
||||
|
||||
definition.reserve( len );
|
||||
|
||||
for(unsigned int a=0;a<len;a++)
|
||||
{
|
||||
if( (unsigned char)block.data[pos] == 0x0a )
|
||||
{
|
||||
definition += "<br>";
|
||||
pos++;
|
||||
}
|
||||
else if ( (unsigned char)block.data[pos] == 6 )
|
||||
{
|
||||
// Something
|
||||
pos += 2;
|
||||
++a;
|
||||
definition += " ";
|
||||
}
|
||||
else if ( (unsigned char)block.data[pos] >= 0x40 &&
|
||||
len - a >= 2 &&
|
||||
(unsigned char)block.data[pos + 1 ] == 0x18 )
|
||||
{
|
||||
// Hidden displayed headword (a displayed headword which
|
||||
// contains some garbage and shouldn't probably be visible).
|
||||
unsigned length = (unsigned char)block.data[ pos ] - 0x3F;
|
||||
|
||||
if ( length > len - a - 2 )
|
||||
{
|
||||
GD_FDPRINTF( stderr, "Hidden displayed headword is too large %s\n", headword.c_str() );
|
||||
pos += len - a;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += length + 2;
|
||||
a += length + 1;
|
||||
}
|
||||
else if ( (unsigned char)block.data[pos] == 0x18 )
|
||||
{
|
||||
// Displayed headword
|
||||
unsigned length = (unsigned char)block.data[ pos + 1 ];
|
||||
|
||||
if ( length > len - a - 2 )
|
||||
{
|
||||
GD_FDPRINTF( stderr, "Displayed headword's length is too large for headword %s\n", headword.c_str() );
|
||||
pos += len - a;
|
||||
break;
|
||||
}
|
||||
|
||||
displayedHeadword = std::string( block.data + pos + 2, length );
|
||||
pos += length + 2;
|
||||
a += length + 1;
|
||||
}
|
||||
else
|
||||
if ( block.data[ pos ] == 0x28 && defBodyEnded &&
|
||||
len - a >= 3 )
|
||||
{
|
||||
// 2-byte sized displayed headword
|
||||
unsigned length = (unsigned char)block.data[ pos + 1 ];
|
||||
length <<= 8;
|
||||
length += (unsigned char)block.data[ pos + 2 ];
|
||||
|
||||
if ( length > len - a - 3 )
|
||||
{
|
||||
GD_FDPRINTF( stderr, "2-byte sized displayed headword for %s is too large\n", headword.c_str() );
|
||||
pos += len - a;
|
||||
break;
|
||||
}
|
||||
|
||||
displayedHeadword = std::string( block.data + pos + 3, length );
|
||||
|
||||
pos += length + 3;
|
||||
a += length + 2;
|
||||
}
|
||||
else if ( (unsigned char)block.data[pos] == 0x50 && len - a - 1 >= 2 &&
|
||||
(unsigned char)block.data[pos + 1 ] == 0x1B )
|
||||
{
|
||||
// 1-byte-sized transcription
|
||||
unsigned length = (unsigned char)block.data[pos + 2 ];
|
||||
|
||||
if ( length > len - a - 3 )
|
||||
{
|
||||
GD_FDPRINTF( stderr, "1-byte-sized transcription's length is too large for headword %s\n", headword.c_str() );
|
||||
pos += len - a;
|
||||
break;
|
||||
}
|
||||
|
||||
if( m_targetCharset.compare( "UTF-8" ) != 0 )
|
||||
{
|
||||
try
|
||||
{
|
||||
transcription = Iconv::toUtf8( "CP1252", block.data + pos + 3, length );
|
||||
}
|
||||
catch( Iconv::Ex & e )
|
||||
{
|
||||
qWarning( "Bgl: charset convertion error, no trancription processing's done: %s\n", e.what() );
|
||||
transcription = std::string( block.data + pos + 3, length );
|
||||
}
|
||||
}
|
||||
else
|
||||
transcription = std::string( block.data + pos + 3, length );
|
||||
|
||||
pos += length + 3;
|
||||
a += length + 2;
|
||||
}
|
||||
else if ( (unsigned char)block.data[pos] == 0x60 && len - a - 1 >= 3 &&
|
||||
(unsigned char)block.data[pos + 1 ] == 0x1B )
|
||||
{
|
||||
// 2-byte-sized transcription
|
||||
unsigned length = (unsigned char)block.data[pos + 2 ];
|
||||
length <<= 8;
|
||||
length += (unsigned char)block.data[pos + 3 ];
|
||||
|
||||
if ( length > len - a - 4)
|
||||
{
|
||||
GD_FDPRINTF( stderr, "2-byte-sized transcription's length is too large for headword %s\n", headword.c_str() );
|
||||
pos += len - a;
|
||||
break;
|
||||
}
|
||||
|
||||
if( m_targetCharset.compare( "UTF-8" ) != 0 )
|
||||
{
|
||||
try
|
||||
{
|
||||
transcription = Iconv::toUtf8( "CP1252", block.data + pos + 4, length );
|
||||
}
|
||||
catch( Iconv::Ex & e )
|
||||
{
|
||||
qWarning( "Bgl: charset convertion error, no trancription processing's done: %s\n", e.what() );
|
||||
transcription = std::string( block.data + pos + 4, length );
|
||||
}
|
||||
}
|
||||
else
|
||||
transcription = std::string( block.data + pos + 4, length );
|
||||
|
||||
pos += length + 4;
|
||||
a += length + 3;
|
||||
}
|
||||
else if ( (unsigned char)block.data[pos] >= 0x40 &&
|
||||
len - a >= 2 &&
|
||||
(unsigned char)block.data[pos + 1 ] == 0x1B )
|
||||
{
|
||||
// Hidden transcription (a transcription which is usually the same
|
||||
// as the headword and shouldn't probably be visible).
|
||||
unsigned length = (unsigned char)block.data[ pos ] - 0x3F;
|
||||
|
||||
if ( length > len - a - 2 )
|
||||
{
|
||||
GD_FDPRINTF( stderr, "Hidden transcription is too large %s\n", headword.c_str() );
|
||||
pos += len - a;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += length + 2;
|
||||
a += length + 1;
|
||||
}
|
||||
else if ( (unsigned char)block.data[pos] == 0x1E )
|
||||
{
|
||||
// Resource reference begin marker
|
||||
definition += m_resourcePrefix;
|
||||
++pos;
|
||||
}
|
||||
else if ( (unsigned char)block.data[pos] == 0x1F )
|
||||
{
|
||||
// Resource reference end marker
|
||||
++pos;
|
||||
}
|
||||
else if( (unsigned char)block.data[pos] < 0x20 )
|
||||
{
|
||||
if( a <= len - 3 && block.data[pos] == 0x14 && block.data[pos+1] == 0x02 ) {
|
||||
int index = (unsigned char)block.data[pos+2] - 0x30;
|
||||
if (index >= 0 && index <= 10) {
|
||||
definition = "<span class=\"bglpos\">" + partOfSpeech[index] + "</span> " + definition;
|
||||
}
|
||||
pos += 3;
|
||||
a += 2;
|
||||
//pos += len - a;
|
||||
//break;
|
||||
}
|
||||
else
|
||||
if (block.data[pos] == 0x14) {
|
||||
defBodyEnded = true; // Presumably
|
||||
pos++;
|
||||
} else if ((unsigned char)block.data[pos] == 0x1A){
|
||||
unsigned length = (unsigned char)block.data[ pos + 1 ];
|
||||
if (length <= 10){// 0x1A identifies two different data types.
|
||||
// data about the Hebrew root should be shorter then
|
||||
// 10 bytes, and in the other data type the byte
|
||||
// after 0x1A is > 10 (at least it is in Bybylon's
|
||||
// Hebrew dictionaries).
|
||||
root = std::string( block.data + pos + 2, length );
|
||||
std::reverse(root.begin(),root.end());
|
||||
definition += " (" + root + ")";
|
||||
pos += length + 2;
|
||||
a += length + 1;
|
||||
}
|
||||
else
|
||||
pos++;
|
||||
} else {
|
||||
definition += block.data[pos++];
|
||||
}
|
||||
}else definition += block.data[pos++];
|
||||
}
|
||||
convertToUtf8( definition, BGL_TARGET_CHARSET );
|
||||
if( !transcription.empty() )
|
||||
definition = std::string( "<span class=\"bgltrn\">" ) + transcription + "</span>" + definition;
|
||||
|
||||
if ( displayedHeadword.size() )
|
||||
convertToUtf8( displayedHeadword, BGL_TARGET_CHARSET );
|
||||
|
||||
// Alternate forms
|
||||
while( pos < block.length )
|
||||
{
|
||||
len = (unsigned char)block.data[pos++];
|
||||
if( pos + len > block.length ) break;
|
||||
alternate.reserve( len );
|
||||
for(unsigned int a=0;a<len;a++) alternate += block.data[pos++];
|
||||
convertToUtf8( alternate, BGL_SOURCE_CHARSET );
|
||||
|
||||
// Try to repair malformed forms
|
||||
if( alternate.find( "&#" ) != string::npos )
|
||||
alternate = Html::unescapeUtf8( alternate );
|
||||
|
||||
alternates.push_back( alternate );
|
||||
alternate.clear();
|
||||
}
|
||||
|
||||
// Try adding displayed headword to the list of alts
|
||||
if ( headword != displayedHeadword )
|
||||
{
|
||||
// Only add displayed headword if the normal one has two or more digits.
|
||||
// This would indicate some irregularity in it (like e.g. if it serves
|
||||
// as some kind of an identifier instead of being an actual headword)
|
||||
int totalDigits = 0;
|
||||
|
||||
for( char const * p = headword.c_str(); *p; ++p )
|
||||
if ( *p >= '0' && *p <= '9' )
|
||||
{
|
||||
if ( ++totalDigits > 1 )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( totalDigits > 1 )
|
||||
{
|
||||
// Ok, let's add it.
|
||||
|
||||
// Does it contain HTML? If it does, we need to strip it
|
||||
if ( displayedHeadword.find( '<' ) != string::npos ||
|
||||
displayedHeadword.find( '&' ) != string::npos )
|
||||
{
|
||||
string result = Html::unescapeUtf8( displayedHeadword );
|
||||
|
||||
if ( result != headword )
|
||||
alternates.push_back( result );
|
||||
}
|
||||
else
|
||||
alternates.push_back(displayedHeadword);
|
||||
}
|
||||
}
|
||||
|
||||
entry.headword = headword;
|
||||
|
||||
entry.displayedHeadword = displayedHeadword;
|
||||
entry.definition = definition;
|
||||
|
||||
entry.alternates = alternates;
|
||||
|
||||
if( block.length ) free( block.data );
|
||||
|
||||
// Some dictionaries can in fact have an empty headword, so we
|
||||
// make it non-empty here to differentiate between the end of entries.
|
||||
if ( entry.headword.empty() )
|
||||
entry.headword += ' ';
|
||||
|
||||
return entry;
|
||||
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
if( block.length ) free( block.data );
|
||||
}
|
||||
entry.headword = "";
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Babylon::convertToUtf8( std::string &s, unsigned int type )
|
||||
{
|
||||
if( s.size() < 1 ) return;
|
||||
if( type > 2 ) return;
|
||||
|
||||
if( s.compare( 0, 13, "<charset c=U>") == 0 )
|
||||
return;
|
||||
|
||||
std::string charset;
|
||||
switch( type )
|
||||
{
|
||||
case BGL_DEFAULT_CHARSET:
|
||||
if(!m_defaultCharset.empty()) charset = m_defaultCharset;
|
||||
else charset = m_sourceCharset;
|
||||
break;
|
||||
case BGL_SOURCE_CHARSET:
|
||||
if(!m_sourceCharset.empty()) charset = m_sourceCharset;
|
||||
else charset = m_defaultCharset;
|
||||
break;
|
||||
case BGL_TARGET_CHARSET:
|
||||
if(!m_targetCharset.empty()) charset = m_targetCharset;
|
||||
else charset = m_defaultCharset;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
if( charset == "UTF-8" )
|
||||
return;
|
||||
|
||||
iconv_t cd = iconv_open( "UTF-8", charset.c_str() );
|
||||
if( cd == (iconv_t)(-1) )
|
||||
throw exIconv();
|
||||
|
||||
char *outbuf, *defbuf;
|
||||
size_t inbufbytes, outbufbytes;
|
||||
|
||||
inbufbytes = s.size();
|
||||
outbufbytes = s.size() * 6;
|
||||
|
||||
char *inbuf;
|
||||
inbuf = (char *)s.data();
|
||||
outbuf = (char*)malloc( outbufbytes + 1 );
|
||||
if( !outbuf )
|
||||
{
|
||||
iconv_close( cd );
|
||||
throw exAllocation();
|
||||
}
|
||||
|
||||
memset( outbuf, '\0', outbufbytes + 1 );
|
||||
defbuf = outbuf;
|
||||
while (inbufbytes) {
|
||||
if (iconv(cd, &inbuf, &inbufbytes, &outbuf, &outbufbytes) == (size_t)-1) {
|
||||
gdWarning( "\"%s\" - error in iconv conversion\n", inbuf );
|
||||
break;
|
||||
// inbuf++;
|
||||
// inbufbytes--;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the state. This fixes CP1255 problems.
|
||||
iconv( cd, 0, 0, &outbuf, &outbufbytes );
|
||||
|
||||
if( inbufbytes == 0 )
|
||||
s = std::string( defbuf );
|
||||
|
||||
free( defbuf );
|
||||
iconv_close( cd );
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2007 by Raul Fernandes and Karl Grill *
|
||||
* rgbr@yahoo.com.br *
|
||||
* *
|
||||
* 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 *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef BABYLON_H
|
||||
#define BABYLON_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <qglobal.h>
|
||||
#if defined( _MSC_VER ) && _MSC_VER < 1800 // VS2012 and older
|
||||
#include <stdint_msvc.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
//const std::string bgl_language[] = {
|
||||
#ifndef blgCode2Int
|
||||
#define blgCode2Int( index, code0, code1 ) (((uint32_t)index) << 16 ) + (((uint32_t)code1) << 8 ) + (uint32_t)code0
|
||||
#endif
|
||||
const quint32 bgl_language[] = {
|
||||
blgCode2Int( 0, 'e', 'n' ),// "English",
|
||||
blgCode2Int( 0, 'f', 'r' ),//"French",
|
||||
blgCode2Int( 0, 'i', 't' ),//"Italian",
|
||||
blgCode2Int( 0, 'e', 's' ),//"Spanish",
|
||||
blgCode2Int( 0, 'n', 'l' ),//"Dutch",
|
||||
blgCode2Int( 0, 'p', 't' ),//"Portuguese",
|
||||
blgCode2Int( 0, 'd', 'e' ),//"German",
|
||||
blgCode2Int( 0, 'r', 'u' ),//"Russian",
|
||||
blgCode2Int( 0, 'j', 'a' ),//"Japanese",
|
||||
blgCode2Int( 1, 'z', 'h' ),//"\x01",//"Traditional Chinese",
|
||||
blgCode2Int( 2, 'z', 'h' ),//"\x02",//"Simplified Chinese",
|
||||
blgCode2Int( 0, 'e', 'l' ),//"Greek",
|
||||
blgCode2Int( 0, 'k', 'o' ),//"Korean",
|
||||
blgCode2Int( 0, 't', 'r' ),//"Turkish",
|
||||
blgCode2Int( 0, 'h', 'e' ),//"Hebrew",
|
||||
blgCode2Int( 0, 'a', 'r' ),//"Arabic",
|
||||
blgCode2Int( 0, 't', 'h' ),//"Thai",
|
||||
blgCode2Int( 3, 0, 0 ),//"\x03",//"Other",
|
||||
blgCode2Int( 4, 'z', 'h' ),//"\x04",//"Other Simplified Chinese dialects",
|
||||
blgCode2Int( 5, 'z', 'h' ),//"\x05",//Other Traditional Chinese dialects",
|
||||
blgCode2Int( 6, 0, 0 ),//"\x06",//Other Eastern-European languages",
|
||||
blgCode2Int( 7, 0, 0 ),//"\x07",//Other Western-European languages",
|
||||
blgCode2Int( 8, 'r', 'u' ),//"\x08",//Other Russian languages",
|
||||
blgCode2Int( 9, 'j', 'a' ),//"\x09",//Other Japanese languages",
|
||||
blgCode2Int( 10, 0, 0 ),//"\x0A",//"Other Baltic languages",
|
||||
blgCode2Int( 11, 'e', 'l' ),//"\x0B",//Other Greek languages",
|
||||
blgCode2Int( 12, 'k', 'o' ),//"\x0C",//"Other Korean dialects",
|
||||
blgCode2Int( 13, 't', 'r' ),//"\x0D",//Other Turkish dialects",
|
||||
blgCode2Int( 14, 't', 'h' ),//"\x0E",//"Other Thai dialects",
|
||||
blgCode2Int( 0, 'p', 'l' ),//"Polish",
|
||||
blgCode2Int( 0, 'h', 'u' ),//"Hungarian",
|
||||
blgCode2Int( 0, 'c', 's' ),//"Czech",
|
||||
blgCode2Int( 0, 'l', 't' ),//"Lithuanian",
|
||||
blgCode2Int( 0, 'l', 'v' ),//"Latvian",
|
||||
blgCode2Int( 0, 'c', 'a' ),//"Catalan",
|
||||
blgCode2Int( 0, 'h', 'r' ),//"Croatian",
|
||||
blgCode2Int( 0, 's', 'r' ),//"Serbian",
|
||||
blgCode2Int( 0, 's', 'k' ),//"Slovak",
|
||||
blgCode2Int( 0, 's', 'q' ),//"Albanian",
|
||||
blgCode2Int( 0, 'u', 'r' ),//"Urdu",
|
||||
blgCode2Int( 0, 's', 'l' ),//"Slovenian",
|
||||
blgCode2Int( 0, 'e', 't' ),//"Estonian",
|
||||
blgCode2Int( 0, 'b', 'g' ),//"Bulgarian",
|
||||
blgCode2Int( 0, 'd', 'a' ),//"Danish",
|
||||
blgCode2Int( 0, 'f', 'i' ),//"Finnish",
|
||||
blgCode2Int( 0, 'i', 's' ),//"Icelandic",
|
||||
blgCode2Int( 0, 'n', 'o' ),//"Norwegian",
|
||||
blgCode2Int( 0, 'r', 'o' ),//"Romanian",
|
||||
blgCode2Int( 0, 's', 'v' ),//"Swedish",
|
||||
blgCode2Int( 0, 'u', 'k' ),//"Ukrainian",
|
||||
blgCode2Int( 0, 'b', 'e' ),//"Belarusian",
|
||||
blgCode2Int( 0, 'f', 'a' ),//"Farsi"=Persian,
|
||||
blgCode2Int( 0, 'e', 'u' ),//"Basque",
|
||||
blgCode2Int( 0, 'm', 'k' ),//"Macedonian",
|
||||
blgCode2Int( 0, 'a', 'f' ),//"Afrikaans",
|
||||
blgCode2Int( 0, 'f', 'o' ),//"Faeroese"=Faroese,
|
||||
blgCode2Int( 0, 'l', 'a' ),//"Latin",
|
||||
blgCode2Int( 0, 'e', 'o' ),//"Esperanto",
|
||||
blgCode2Int( 15, 0, 0 ),//"Tamazight",
|
||||
blgCode2Int( 0, 'h', 'y' )//"Armenian"
|
||||
};
|
||||
|
||||
|
||||
const std::string bgl_charsetname[] = {
|
||||
"Default" ,
|
||||
"Latin",
|
||||
"Eastern European",
|
||||
"Cyrillic",
|
||||
"Japanese",
|
||||
"Traditional Chinese",
|
||||
"Simplified Chinese",
|
||||
"Baltic",
|
||||
"Greek",
|
||||
"Korean",
|
||||
"Turkish",
|
||||
"Hebrew",
|
||||
"Arabic",
|
||||
"Thai" };
|
||||
|
||||
const std::string bgl_charset[] = {
|
||||
"WINDOWS-1252", /*Default*/
|
||||
"WINDOWS-1252", /*Latin*/
|
||||
"ISO-8859-2", /*Eastern European*/
|
||||
"WINDOWS-1251", /*Cyriilic*/
|
||||
"CP932", /*Japanese*/
|
||||
"BIG5", /*Traditional Chinese*/
|
||||
"GB18030", /*Simplified Chinese*/
|
||||
"CP1257", /*Baltic*/
|
||||
"CP1253", /*Greek*/
|
||||
"EUC-KR", /*Korean*/
|
||||
"ISO-8859-9", /*Turkish*/
|
||||
"WINDOWS-1255", /*Hebrew*/
|
||||
"CP1256", /*Arabic*/
|
||||
"CP874" /*Thai*/ };
|
||||
|
||||
const std::string partOfSpeech[] = {
|
||||
"n.",
|
||||
"adj.",
|
||||
"v.",
|
||||
"adv.",
|
||||
"interj.",
|
||||
"pron.",
|
||||
"prep.",
|
||||
"conj.",
|
||||
"suff.",
|
||||
"pref.",
|
||||
"art." };
|
||||
|
||||
typedef struct {
|
||||
unsigned type;
|
||||
unsigned length;
|
||||
char * data;
|
||||
} bgl_block;
|
||||
|
||||
typedef struct {
|
||||
std::string headword;
|
||||
std::string definition;
|
||||
std::string displayedHeadword;
|
||||
std::vector<std::string> alternates;
|
||||
} bgl_entry;
|
||||
|
||||
class Babylon
|
||||
{
|
||||
public:
|
||||
Babylon( std::string );
|
||||
~Babylon();
|
||||
|
||||
// Subclass this to store resources
|
||||
class ResourceHandler
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void handleBabylonResource( std::string const & filename,
|
||||
char const * data, size_t size )=0;
|
||||
|
||||
virtual ~ResourceHandler()
|
||||
{}
|
||||
};
|
||||
|
||||
/// Sets a prefix string to append to each resource reference in hyperlinks.
|
||||
void setResourcePrefix( std::string const & prefix )
|
||||
{ m_resourcePrefix = prefix; }
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
bool readBlock( bgl_block& );
|
||||
bool read(std::string &source_charset, std::string &target_charset);
|
||||
bgl_entry readEntry( ResourceHandler * = 0 );
|
||||
|
||||
inline std::string title() const { return m_title; }
|
||||
inline std::string author() const { return m_author; }
|
||||
inline std::string email() const { return m_email; }
|
||||
inline std::string description() const { return m_description; }
|
||||
inline std::string copyright() const { return m_copyright; }
|
||||
inline quint32 sourceLang() const { return m_sourceLang; }//std::string sourceLang() const { return m_sourceLang; }
|
||||
inline quint32 targetLang() const { return m_targetLang; }//inline std::string targetLang() const { return m_targetLang; }
|
||||
inline unsigned int numEntries() const { return m_numEntries; }
|
||||
inline std::string charset() const { return m_defaultCharset; }
|
||||
|
||||
inline std::string filename() const { return m_filename; }
|
||||
|
||||
std::vector< char > const & getIcon() const
|
||||
{ return icon; }
|
||||
|
||||
enum
|
||||
{
|
||||
ParserVersion = 17
|
||||
};
|
||||
|
||||
private:
|
||||
unsigned int bgl_readnum( int );
|
||||
void convertToUtf8( std::string &, unsigned int = 0 );
|
||||
|
||||
std::string m_filename;
|
||||
gzFile file;
|
||||
|
||||
std::string m_title;
|
||||
std::string m_author;
|
||||
std::string m_email;
|
||||
std::string m_description;
|
||||
std::string m_copyright;
|
||||
quint32 m_sourceLang; //std::string m_sourceLang;
|
||||
quint32 m_targetLang;//std::string m_targetLang;
|
||||
unsigned int m_numEntries;
|
||||
std::string m_defaultCharset;
|
||||
std::string m_sourceCharset;
|
||||
std::string m_targetCharset;
|
||||
std::vector< char > icon;
|
||||
|
||||
std::string m_resourcePrefix;
|
||||
|
||||
enum CHARSET { BGL_DEFAULT_CHARSET, BGL_SOURCE_CHARSET, BGL_TARGET_CHARSET };
|
||||
};
|
||||
|
||||
#endif // BABYLON_H
|
||||
@@ -0,0 +1,30 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "broken_xrecord.hh"
|
||||
|
||||
#include <QtGui>
|
||||
|
||||
#ifdef HAVE_X11
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/record.h>
|
||||
#include <QX11Info>
|
||||
#endif
|
||||
|
||||
bool isRECORDBroken()
|
||||
{
|
||||
#ifdef HAVE_X11
|
||||
|
||||
char const * vendor = ServerVendor( QX11Info::display() );
|
||||
|
||||
if ( vendor && strstr( vendor, "X.Org" ) )
|
||||
{
|
||||
int release = VendorRelease( QX11Info::display() );
|
||||
|
||||
return release >= 10600000 && release < 10701000;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __BROKEN_XRECORD_HH_INCLUDED__
|
||||
#define __BROKEN_XRECORD_HH_INCLUDED__
|
||||
|
||||
/// Returns true if the RECORD extension is likely to be broken. Under Windows
|
||||
/// it always returns false.
|
||||
/// This function is to be removed once the RECORD extension is working again
|
||||
/// on all the major distributions.
|
||||
bool isRECORDBroken();
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,268 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __BTREEIDX_HH_INCLUDED__
|
||||
#define __BTREEIDX_HH_INCLUDED__
|
||||
|
||||
#include "dictionary.hh"
|
||||
#include "file.hh"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <QVector>
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
|
||||
#if defined( _MSC_VER ) && _MSC_VER < 1800 // VS2012 and older
|
||||
#include <stdint_msvc.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/// A base for the dictionary which creates a btree index to look up
|
||||
/// the words.
|
||||
namespace BtreeIndexing {
|
||||
|
||||
using std::string;
|
||||
using gd::wstring;
|
||||
using std::vector;
|
||||
using std::map;
|
||||
|
||||
enum
|
||||
{
|
||||
/// This is to be bumped up each time the internal format changes.
|
||||
/// The value isn't used here by itself, it is supposed to be added
|
||||
/// to each dictionary's internal format version.
|
||||
FormatVersion = 4
|
||||
};
|
||||
|
||||
// These exceptions which might be thrown during the index traversal
|
||||
|
||||
DEF_EX( exIndexWasNotOpened, "The index wasn't opened", Dictionary::Ex )
|
||||
DEF_EX( exFailedToDecompressNode, "Failed to decompress a btree's node", Dictionary::Ex )
|
||||
DEF_EX( exCorruptedChainData, "Corrupted chain data in the leaf of a btree encountered", Dictionary::Ex )
|
||||
|
||||
/// This structure describes a word linked to its translation. The
|
||||
/// translation is represented as an abstract 32-bit offset.
|
||||
struct WordArticleLink
|
||||
{
|
||||
string word, prefix; // in utf8
|
||||
uint32_t articleOffset;
|
||||
|
||||
WordArticleLink()
|
||||
{}
|
||||
|
||||
WordArticleLink( string const & word_, uint32_t articleOffset_, string const & prefix_ = string() ):
|
||||
word( word_ ), prefix( prefix_ ), articleOffset( articleOffset_ )
|
||||
{}
|
||||
};
|
||||
|
||||
/// Information needed to open the index
|
||||
struct IndexInfo
|
||||
{
|
||||
uint32_t btreeMaxElements, rootOffset;
|
||||
|
||||
IndexInfo( uint32_t btreeMaxElements_, uint32_t rootOffset_ ):
|
||||
btreeMaxElements( btreeMaxElements_ ), rootOffset( rootOffset_ )
|
||||
{}
|
||||
};
|
||||
|
||||
/// Base btree indexing class which allows using what buildIndex() function
|
||||
/// created. It's quite low-lovel and is basically a set of 'bulding blocks'
|
||||
/// functions.
|
||||
class BtreeIndex
|
||||
{
|
||||
public:
|
||||
|
||||
BtreeIndex();
|
||||
|
||||
/// Opens the index. The file reference is saved to be used for
|
||||
/// subsequent lookups.
|
||||
/// The mutex is the one to be locked when working with the file.
|
||||
void openIndex( IndexInfo const &, File::Class &, Mutex & );
|
||||
|
||||
/// Finds articles that match the given string. A case-insensitive search
|
||||
/// is performed.
|
||||
vector< WordArticleLink > findArticles( wstring const & );
|
||||
|
||||
/// Find all unique article links in the index
|
||||
void findAllArticleLinks( QVector< WordArticleLink > & articleLinks );
|
||||
|
||||
/// Retrieve all unique headwors from index
|
||||
void getAllHeadwords( QSet< QString > & headwords );
|
||||
|
||||
/// Find all article links and/or headwords in the index
|
||||
void findArticleLinks( QVector< WordArticleLink > * articleLinks,
|
||||
QSet< uint32_t > * offsets,
|
||||
QSet< QString > * headwords,
|
||||
QAtomicInt * isCancelled = 0 );
|
||||
|
||||
/// Retrieve headwords for presented article adresses
|
||||
void getHeadwordsFromOffsets( QList< uint32_t > & offsets,
|
||||
QVector< QString > & headwords,
|
||||
QAtomicInt * isCancelled = 0 );
|
||||
|
||||
protected:
|
||||
|
||||
/// Finds the offset in the btree leaf for the given word, either matching
|
||||
/// by an exact match, or by finding the smallest entry that might match
|
||||
/// by prefix. It can return zero if there isn't even a possible prefx
|
||||
/// match. The input string must already be folded. The exactMatch is set
|
||||
/// to true when an exact match is located, and to false otherwise.
|
||||
/// The located leaf is loaded to 'leaf', and the pointer to the next
|
||||
/// leaf is saved to 'nextLeaf'.
|
||||
/// However, due to root node being permanently cached, the 'leaf' passed
|
||||
/// might not get used at all if the root node was the terminal one. In that
|
||||
/// case, the returned pointer wouldn't belong to 'leaf' at all. To that end,
|
||||
/// the leafEnd pointer always holds the pointer to the first byte outside
|
||||
/// the node data.
|
||||
char const * findChainOffsetExactOrPrefix( wstring const & target,
|
||||
bool & exactMatch,
|
||||
vector< char > & leaf,
|
||||
uint32_t & nextLeaf,
|
||||
char const * & leafEnd );
|
||||
|
||||
/// Reads a node or leaf at the given offset. Just uncompresses its data
|
||||
/// to the given vector and does nothing more.
|
||||
void readNode( uint32_t offset, vector< char > & out );
|
||||
|
||||
/// Reads the word-article links' chain at the given offset. The pointer
|
||||
/// is updated to point to the next chain, if there's any.
|
||||
vector< WordArticleLink > readChain( char const * & );
|
||||
|
||||
/// Drops any alises which arose due to folding. Only case-folded aliases
|
||||
/// are left.
|
||||
void antialias( wstring const &, vector< WordArticleLink > & );
|
||||
|
||||
protected:
|
||||
|
||||
Mutex * idxFileMutex;
|
||||
File::Class * idxFile;
|
||||
|
||||
private:
|
||||
|
||||
uint32_t indexNodeSize;
|
||||
uint32_t rootOffset;
|
||||
bool rootNodeLoaded;
|
||||
vector< char > rootNode; // We load root note here and keep it at all times,
|
||||
// since all searches always start with it.
|
||||
};
|
||||
|
||||
/// A base for the dictionary that utilizes a btree index build using
|
||||
/// buildIndex() function declared below.
|
||||
class BtreeDictionary: public Dictionary::Class, public BtreeIndex
|
||||
{
|
||||
public:
|
||||
|
||||
BtreeDictionary( string const & id, vector< string > const & dictionaryFiles );
|
||||
|
||||
/// Btree-indexed dictionaries are usually a good source for compound searches.
|
||||
virtual Dictionary::Features getFeatures() const throw()
|
||||
{ return Dictionary::SuitableForCompoundSearching; }
|
||||
|
||||
/// This function does the search using the btree index. Derivatives usually
|
||||
/// need not to implement this function.
|
||||
virtual sptr< Dictionary::WordSearchRequest > prefixMatch( wstring const &,
|
||||
unsigned long )
|
||||
throw( std::exception );
|
||||
|
||||
virtual sptr< Dictionary::WordSearchRequest > stemmedMatch( wstring const &,
|
||||
unsigned minLength,
|
||||
unsigned maxSuffixVariation,
|
||||
unsigned long maxResults )
|
||||
throw( std::exception );
|
||||
|
||||
virtual bool isLocalDictionary()
|
||||
{ return true; }
|
||||
|
||||
virtual bool getHeadwords( QStringList &headwords );
|
||||
|
||||
virtual void getArticleText( uint32_t articleAddress, QString & headword, QString & text );
|
||||
|
||||
string const & ftsIndexName() const
|
||||
{ return ftsIdxName; }
|
||||
|
||||
Mutex & getFtsMutex()
|
||||
{ return ftsIdxMutex; }
|
||||
|
||||
virtual uint32_t getFtsIndexVersion()
|
||||
{ return 0; }
|
||||
|
||||
/// Called before each matching operation to ensure that any child init
|
||||
/// has completed. Mainly used for deferred init. The default implementation
|
||||
/// does nothing.
|
||||
/// The function returns an empty string if the initialization is or was
|
||||
/// successful, or a human-readable error string otherwise.
|
||||
virtual string const & ensureInitDone();
|
||||
|
||||
protected:
|
||||
Mutex ftsIdxMutex;
|
||||
string ftsIdxName;
|
||||
|
||||
friend class BtreeWordSearchRequest;
|
||||
friend class FTSResultsRequest;
|
||||
};
|
||||
|
||||
class BtreeWordSearchRequest: public Dictionary::WordSearchRequest
|
||||
{
|
||||
friend class BtreeWordSearchRunnable;
|
||||
protected:
|
||||
BtreeDictionary & dict;
|
||||
wstring str;
|
||||
unsigned long maxResults;
|
||||
unsigned minLength;
|
||||
int maxSuffixVariation;
|
||||
bool allowMiddleMatches;
|
||||
QAtomicInt isCancelled;
|
||||
QSemaphore hasExited;
|
||||
|
||||
public:
|
||||
|
||||
BtreeWordSearchRequest( BtreeDictionary & dict_,
|
||||
wstring const & str_,
|
||||
unsigned minLength_,
|
||||
int maxSuffixVariation_,
|
||||
bool allowMiddleMatches_,
|
||||
unsigned long maxResults_,
|
||||
bool startRunnable = true );
|
||||
|
||||
virtual void findMatches();
|
||||
|
||||
void run(); // Run from another thread by BtreeWordSearchRunnable
|
||||
|
||||
virtual void cancel()
|
||||
{
|
||||
isCancelled.ref();
|
||||
}
|
||||
|
||||
~BtreeWordSearchRequest();
|
||||
};
|
||||
|
||||
// Everything below is for building the index data.
|
||||
|
||||
/// This represents the index in its source form, as a map which binds folded
|
||||
/// words to sequences of their unfolded source forms and the corresponding
|
||||
/// article offsets. The words are utf8-encoded -- it doesn't break Unicode
|
||||
/// sorting, but conserves space.
|
||||
struct IndexedWords: public map< string, vector< WordArticleLink > >
|
||||
{
|
||||
/// Instead of adding to the map directly, use this function. It does folding
|
||||
/// itself, and for phrases/sentences it adds additional entries beginning with
|
||||
/// each new word.
|
||||
void addWord( wstring const & word, uint32_t articleOffset, unsigned int maxHeadwordSize = 256U );
|
||||
|
||||
/// Differs from addWord() in that it only adds a single entry. We use this
|
||||
/// for zip's file names.
|
||||
void addSingleWord( wstring const & word, uint32_t articleOffset );
|
||||
};
|
||||
|
||||
/// Builds the index, as a compressed btree. Returns IndexInfo.
|
||||
/// All the data is stored to the given file, beginning from its current
|
||||
/// position.
|
||||
IndexInfo buildIndex( IndexedWords const &, File::Class & file );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/* This file is (c) 2015 Zhe Wang <0x1997@gmail.com>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "chinese.hh"
|
||||
#include <stdexcept>
|
||||
#include <QCoreApplication>
|
||||
#ifdef Q_OS_MAC
|
||||
#include <opencc/opencc.h>
|
||||
#endif
|
||||
#include <opencc/Export.hpp>
|
||||
#include <opencc/SimpleConverter.hpp>
|
||||
#include "folding.hh"
|
||||
#include "gddebug.hh"
|
||||
#include "transliteration.hh"
|
||||
#include "utf8.hh"
|
||||
|
||||
namespace Chinese {
|
||||
|
||||
class CharacterConversionDictionary: public Transliteration::BaseTransliterationDictionary
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
opencc_t converter;
|
||||
#else
|
||||
opencc::SimpleConverter* converter;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
CharacterConversionDictionary( std::string const & id, std::string const & name,
|
||||
QIcon icon, QString const & openccConfig);
|
||||
~CharacterConversionDictionary();
|
||||
|
||||
std::vector< gd::wstring > getAlternateWritings( gd::wstring const & )
|
||||
throw();
|
||||
};
|
||||
|
||||
CharacterConversionDictionary::CharacterConversionDictionary( std::string const & id,
|
||||
std::string const & name_,
|
||||
QIcon icon_,
|
||||
QString const & openccConfig):
|
||||
Transliteration::BaseTransliterationDictionary( id, name_, icon_, false ),
|
||||
converter( NULL )
|
||||
{
|
||||
try {
|
||||
#ifdef Q_OS_MAC
|
||||
converter = opencc_open( openccConfig.toLocal8Bit().constData() );
|
||||
if( converter == reinterpret_cast< opencc_t >( -1 ) )
|
||||
{
|
||||
gdWarning( "CharacterConversionDictionary: failed to initialize OpenCC from config %s: %s\n",
|
||||
openccConfig.toLocal8Bit().constData(), opencc_error() );
|
||||
}
|
||||
#else
|
||||
converter = new opencc::SimpleConverter( openccConfig.toLocal8Bit().constData() );
|
||||
#endif
|
||||
} catch ( std::runtime_error& e ) {
|
||||
gdWarning( "CharacterConversionDictionary: failed to initialize OpenCC from config %s: %s\n",
|
||||
openccConfig.toLocal8Bit().constData(), e.what() );
|
||||
}
|
||||
}
|
||||
|
||||
CharacterConversionDictionary::~CharacterConversionDictionary()
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
if ( converter != NULL && converter != reinterpret_cast< opencc_t >( -1 ) )
|
||||
opencc_close( converter );
|
||||
#else
|
||||
if ( converter != NULL )
|
||||
delete converter;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector< gd::wstring > CharacterConversionDictionary::getAlternateWritings( gd::wstring const & str )
|
||||
throw()
|
||||
{
|
||||
std::vector< gd::wstring > results;
|
||||
|
||||
if ( converter != NULL ) {
|
||||
gd::wstring folded = Folding::applySimpleCaseOnly( str );
|
||||
std::string input = Utf8::encode( folded );
|
||||
std::string output;
|
||||
gd::wstring result;
|
||||
|
||||
try {
|
||||
#ifdef Q_OS_MAC
|
||||
if ( converter != NULL && converter != reinterpret_cast< opencc_t >( -1 ) )
|
||||
{
|
||||
char * tmp = opencc_convert_utf8( converter, input.c_str(), input.length() );
|
||||
if( tmp )
|
||||
{
|
||||
output = std::string( tmp );
|
||||
opencc_convert_utf8_free( tmp );
|
||||
}
|
||||
else
|
||||
gdWarning( "OpenCC: convertion failed %s\n", opencc_error() );
|
||||
}
|
||||
#else
|
||||
output = converter->Convert( input );
|
||||
#endif
|
||||
result = Utf8::decode( output );
|
||||
} catch ( std::exception& ex ) {
|
||||
gdWarning( "OpenCC: convertion failed %s\n", ex.what() );
|
||||
}
|
||||
|
||||
if ( !result.empty() && result != folded )
|
||||
results.push_back( result );
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector< sptr< Dictionary::Class > > makeDictionaries( Config::Chinese const & cfg )
|
||||
throw( std::exception )
|
||||
{
|
||||
std::vector< sptr< Dictionary::Class > > result;
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
QString configDir = "";
|
||||
#else
|
||||
QString configDir = Config::getOpenCCDir();
|
||||
if( !configDir.isEmpty() )
|
||||
configDir += "/";
|
||||
#endif
|
||||
|
||||
if ( cfg.enable )
|
||||
{
|
||||
if ( cfg.enableSCToTWConversion )
|
||||
{
|
||||
result.push_back( new CharacterConversionDictionary( "bf1c33a59cbacea8f39b5b5475787cfd",
|
||||
QCoreApplication::translate( "ChineseConversion", "Simplified to traditional Chinese (Taiwan variant) conversion" ).toUtf8().data(),
|
||||
QIcon( ":/flags/tw.png" ), configDir + "s2tw.json" ) );
|
||||
}
|
||||
|
||||
if ( cfg.enableSCToHKConversion )
|
||||
{
|
||||
result.push_back( new CharacterConversionDictionary( "9e0681fb9e1c0b6c90e6fb46111d96b5",
|
||||
QCoreApplication::translate( "ChineseConversion", "Simplified to traditional Chinese (Hong Kong variant) conversion" ).toUtf8().data(),
|
||||
QIcon( ":/flags/hk.png" ), configDir + "s2hk.json" ) );
|
||||
}
|
||||
|
||||
if ( cfg.enableTCToSCConversion )
|
||||
{
|
||||
result.push_back( new CharacterConversionDictionary( "0db536ce0bdc52ea30d11a82c5db4a27",
|
||||
QCoreApplication::translate( "ChineseConversion", "Traditional to simplified Chinese conversion" ).toUtf8().data(),
|
||||
QIcon( ":/flags/cn.png" ), configDir + "t2s.json" ) );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* This file is (c) 2015 Zhe Wang <0x1997@gmail.com>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __CHINESE_HH_INCLUDED__
|
||||
#define __CHINESE_HH_INCLUDED__
|
||||
|
||||
#include <map>
|
||||
#include "config.hh"
|
||||
#include "dictionary.hh"
|
||||
|
||||
/// Chinese character conversion support.
|
||||
namespace Chinese {
|
||||
|
||||
std::vector< sptr< Dictionary::Class > > makeDictionaries( Config::Chinese const & )
|
||||
throw( std::exception );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
/* This file is (c) 2015 Zhe Wang <0x1997@gmail.com>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "chineseconversion.hh"
|
||||
#include "ui_chineseconversion.h"
|
||||
|
||||
ChineseConversion::ChineseConversion(QWidget * parent, Config::Chinese const & cfg) :
|
||||
QGroupBox(parent),
|
||||
ui(new Ui::ChineseConversion)
|
||||
{
|
||||
ui->setupUi( this );
|
||||
|
||||
setChecked( cfg.enable );
|
||||
ui->enableSCToTWConversion->setChecked( cfg.enableSCToTWConversion );
|
||||
ui->enableSCToHKConversion->setChecked( cfg.enableSCToHKConversion );
|
||||
ui->enableTCToSCConversion->setChecked( cfg.enableTCToSCConversion );
|
||||
}
|
||||
|
||||
ChineseConversion::~ChineseConversion()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ChineseConversion::getConfig( Config::Chinese & cfg ) const
|
||||
{
|
||||
cfg.enable = isChecked();
|
||||
cfg.enableSCToTWConversion = ui->enableSCToTWConversion->isChecked();
|
||||
cfg.enableSCToHKConversion = ui->enableSCToHKConversion->isChecked();
|
||||
cfg.enableTCToSCConversion = ui->enableTCToSCConversion->isChecked();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* This file is (c) 2015 Zhe Wang <0x1997@gmail.com>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __CHINESE_CONVERSION_HH_INCLUDED__
|
||||
#define __CHINESE_CONVERSION_HH_INCLUDED__
|
||||
|
||||
#include <QGroupBox>
|
||||
#include "config.hh"
|
||||
|
||||
namespace Ui {
|
||||
class ChineseConversion;
|
||||
}
|
||||
|
||||
class ChineseConversion : public QGroupBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ChineseConversion( QWidget * parent, Config::Chinese const & );
|
||||
~ChineseConversion();
|
||||
|
||||
void getConfig( Config::Chinese & ) const;
|
||||
|
||||
private:
|
||||
Ui::ChineseConversion *ui;
|
||||
};
|
||||
|
||||
#endif // __CHINESE_CONVERSION_HH_INCLUDED__
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ChineseConversion</class>
|
||||
<widget class="QGroupBox" name="ChineseConversion">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>689</width>
|
||||
<height>128</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>GroupBox</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enable conversion between simplified and traditional Chinese characters</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Chinese Con&version</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="enableSCToTWConversion">
|
||||
<property name="toolTip">
|
||||
<string>Enable conversion from simplified characters to traditional (Taiwan variant) characters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SC to TC (Taiwan variant)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="enableSCToHKConversion">
|
||||
<property name="toolTip">
|
||||
<string>Enable conversion from simplified characters to traditional (Hong Kong variant) characters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SC to TC (Hong Kong variant)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QCheckBox" name="enableTCToSCConversion">
|
||||
<property name="toolTip">
|
||||
<string>Enable conversion from traditional characters to simplified characters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TC to SC</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,166 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "chunkedstorage.hh"
|
||||
#include <zlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace ChunkedStorage {
|
||||
|
||||
enum
|
||||
{
|
||||
ChunkMaxSize = 65536 // Can't be more since it would overflow the address
|
||||
};
|
||||
|
||||
Writer::Writer( File::Class & f ):
|
||||
file( f ), chunkStarted( false ), bufferUsed( 0 )
|
||||
{
|
||||
// Create a sratchpad at the beginning of file. We use it to write chunk
|
||||
// table if it would fit, in order to save some seek times.
|
||||
|
||||
char zero[ 4096 ];
|
||||
|
||||
memset( zero, 0, sizeof( zero ) );
|
||||
|
||||
scratchPadOffset = file.tell();
|
||||
scratchPadSize = sizeof( zero );
|
||||
|
||||
file.write( zero, sizeof( zero ) );
|
||||
}
|
||||
|
||||
uint32_t Writer::startNewBlock()
|
||||
{
|
||||
if ( bufferUsed >= ChunkMaxSize )
|
||||
{
|
||||
// Need to flush first.
|
||||
saveCurrentChunk();
|
||||
}
|
||||
|
||||
chunkStarted = true;
|
||||
|
||||
// The address is comprised of the offset within the chunk (in lower
|
||||
// 16 bits, always fits there since ChunkMaxSize-1 does) and the
|
||||
// number of the chunk, which is therefore limited to be 65535 max.
|
||||
return bufferUsed | ( (uint32_t)offsets.size() << 16 );
|
||||
}
|
||||
|
||||
void Writer::addToBlock( void const * data, size_t size )
|
||||
{
|
||||
if ( !size )
|
||||
return;
|
||||
|
||||
if ( buffer.size() - bufferUsed < size )
|
||||
buffer.resize( bufferUsed + size );
|
||||
|
||||
memcpy( &buffer.front() + bufferUsed, data, size );
|
||||
|
||||
bufferUsed += size;
|
||||
|
||||
chunkStarted = false;
|
||||
}
|
||||
|
||||
void Writer::saveCurrentChunk()
|
||||
{
|
||||
size_t maxCompressedSize = compressBound( bufferUsed );
|
||||
|
||||
if ( bufferCompressed.size() < maxCompressedSize )
|
||||
bufferCompressed.resize( maxCompressedSize );
|
||||
|
||||
unsigned long compressedSize = bufferCompressed.size();
|
||||
|
||||
if ( compress( &bufferCompressed.front(), &compressedSize,
|
||||
&buffer.front(), bufferUsed ) != Z_OK )
|
||||
throw exFailedToCompressChunk();
|
||||
|
||||
offsets.push_back( file.tell() );
|
||||
|
||||
file.write( (uint32_t) bufferUsed );
|
||||
file.write( (uint32_t) compressedSize );
|
||||
file.write( &bufferCompressed.front(), compressedSize );
|
||||
|
||||
bufferUsed = 0;
|
||||
|
||||
chunkStarted = false;
|
||||
}
|
||||
|
||||
uint32_t Writer::finish()
|
||||
{
|
||||
if ( bufferUsed || chunkStarted )
|
||||
saveCurrentChunk();
|
||||
|
||||
bool useScratchPad = false;
|
||||
uint32_t savedOffset = 0;
|
||||
|
||||
if ( scratchPadSize >= offsets.size() * sizeof( uint32_t ) + sizeof( uint32_t ) )
|
||||
{
|
||||
useScratchPad = true;
|
||||
savedOffset = file.tell();
|
||||
file.seek( scratchPadOffset );
|
||||
}
|
||||
|
||||
uint32_t offset = file.tell();
|
||||
|
||||
file.write( (uint32_t) offsets.size() );
|
||||
|
||||
if ( offsets.size() )
|
||||
file.write( &offsets.front(), offsets.size() * sizeof( uint32_t ) );
|
||||
|
||||
if ( useScratchPad )
|
||||
file.seek( savedOffset );
|
||||
|
||||
offsets.clear();
|
||||
chunkStarted = false;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
Reader::Reader( File::Class & f, uint32_t offset ): file( f )
|
||||
{
|
||||
file.seek( offset );
|
||||
|
||||
uint32_t size = file.read< uint32_t >();
|
||||
if ( size == 0 )
|
||||
return;
|
||||
offsets.resize( size );
|
||||
file.read( &offsets.front(), offsets.size() * sizeof( uint32_t ) );
|
||||
}
|
||||
|
||||
char * Reader::getBlock( uint32_t address, vector< char > & chunk )
|
||||
{
|
||||
size_t chunkIdx = address >> 16;
|
||||
|
||||
if ( chunkIdx >= offsets.size() )
|
||||
throw exAddressOutOfRange();
|
||||
|
||||
// Read and decompress the chunk
|
||||
{
|
||||
file.seek( offsets[ chunkIdx ] );
|
||||
|
||||
uint32_t uncompressedSize = file.read< uint32_t >();
|
||||
uint32_t compressedSize = file.read< uint32_t >();
|
||||
|
||||
chunk.resize( uncompressedSize );
|
||||
|
||||
vector< unsigned char > compressedData( compressedSize );
|
||||
|
||||
file.read( &compressedData.front(), compressedData.size() );
|
||||
|
||||
unsigned long decompressedLength = chunk.size();
|
||||
|
||||
if ( uncompress( (unsigned char *)&chunk.front(),
|
||||
&decompressedLength,
|
||||
&compressedData.front(),
|
||||
compressedData.size() ) != Z_OK ||
|
||||
decompressedLength != chunk.size() )
|
||||
throw exFailedToDecompressChunk();
|
||||
}
|
||||
|
||||
size_t offsetInChunk = address & 0xffFF;
|
||||
|
||||
if ( offsetInChunk > chunk.size() ) // It can be equal to for 0-sized blocks
|
||||
throw exAddressOutOfRange();
|
||||
|
||||
return &chunk.front() + offsetInChunk;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __CHUNKEDSTORAGE_HH_INCLUDED__
|
||||
#define __CHUNKEDSTORAGE_HH_INCLUDED__
|
||||
|
||||
#include "ex.hh"
|
||||
#include "file.hh"
|
||||
|
||||
#include <vector>
|
||||
#if defined( _MSC_VER ) && _MSC_VER < 1800 // VS2012 and older
|
||||
#include <stdint_msvc.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/// A chunked compression storage. We use this for articles' bodies. The idea
|
||||
/// is to store data in a separately-compressed chunks, much like in dictzip,
|
||||
/// but without any fancy gzip-compatibility or whatever. Another difference
|
||||
/// is that any block of data saved is always contained within one chunk,
|
||||
/// even if its size does exceed its maximum allowed size. This is very
|
||||
/// handy since we're retrieving the data by the same blocks we used to save
|
||||
/// it as, that' the only kind of seek we support, really.
|
||||
namespace ChunkedStorage {
|
||||
|
||||
using std::vector;
|
||||
|
||||
DEF_EX( Ex, "Chunked storage exception", std::exception )
|
||||
DEF_EX( exFailedToCompressChunk, "Failed to compress a chunk", Ex )
|
||||
DEF_EX( exAddressOutOfRange, "The given chunked address is out of range", Ex )
|
||||
DEF_EX( exFailedToDecompressChunk, "Failed to decompress a chunk", Ex )
|
||||
|
||||
/// This class writes data blocks in chunks.
|
||||
class Writer
|
||||
{
|
||||
vector< uint32_t > offsets;
|
||||
File::Class & file;
|
||||
size_t scratchPadOffset, scratchPadSize;
|
||||
|
||||
public:
|
||||
|
||||
Writer( File::Class & );
|
||||
|
||||
/// Starts new block. Returns its address.
|
||||
uint32_t startNewBlock();
|
||||
|
||||
/// Add data to the previously started block.
|
||||
void addToBlock( void const * data, size_t size );
|
||||
|
||||
/// Finishes writing chunks and returns the offset to the chunk table which
|
||||
/// gets written at the moment of finishing.
|
||||
uint32_t finish();
|
||||
|
||||
private:
|
||||
|
||||
/// Indicates that an address was allocated, which would mean the writeout
|
||||
/// of the pending chunk is required even if its size is zero.
|
||||
bool chunkStarted;
|
||||
|
||||
// This buffer accumulates the chunk data until either enough data is
|
||||
// stored (>=ChunkMaxSize), or there's no more data left to store.
|
||||
vector< unsigned char > buffer;
|
||||
|
||||
// Here we compress the chunk before writing it out to file.
|
||||
vector< unsigned char > bufferCompressed;
|
||||
|
||||
// The amount of data stored in buffer so far. We keep it separate
|
||||
// from buffer.size() for performance reasons; the latter one only
|
||||
// grows, but never shrinks.
|
||||
size_t bufferUsed;
|
||||
|
||||
void saveCurrentChunk();
|
||||
};
|
||||
|
||||
/// This class reads data blocks previously written by Writer.
|
||||
class Reader
|
||||
{
|
||||
vector< uint32_t > offsets;
|
||||
File::Class & file;
|
||||
|
||||
public:
|
||||
/// Creates reader by giving it a file to read from and the offset returned
|
||||
/// by Writer::finish().
|
||||
Reader( File::Class &, uint32_t );
|
||||
|
||||
/// Reads the block previously written by Writer, identified by its address.
|
||||
/// Uses the user-provided storage to load the entire chunk, and then to
|
||||
/// return a pointer to the requested block inside it.
|
||||
char * getBlock( uint32_t address, vector< char > & );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,702 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __CONFIG_HH_INCLUDED__
|
||||
#define __CONFIG_HH_INCLUDED__
|
||||
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QSize>
|
||||
#include <QDateTime>
|
||||
#include <QKeySequence>
|
||||
#include <QSet>
|
||||
#include "ex.hh"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QRect>
|
||||
#endif
|
||||
|
||||
/// GoldenDict's configuration
|
||||
namespace Config {
|
||||
|
||||
/// Dictionaries which are temporarily disabled via the dictionary bar.
|
||||
typedef QSet< QString > MutedDictionaries;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#pragma pack(push,4)
|
||||
#endif
|
||||
|
||||
/// A path where to search for the dictionaries
|
||||
struct Path
|
||||
{
|
||||
QString path;
|
||||
bool recursive;
|
||||
|
||||
Path(): recursive( false ) {}
|
||||
Path( QString const & path_, bool recursive_ ):
|
||||
path( path_ ), recursive( recursive_ ) {}
|
||||
|
||||
bool operator == ( Path const & other ) const
|
||||
{ return path == other.path && recursive == other.recursive; }
|
||||
};
|
||||
|
||||
/// A list of paths where to search for the dictionaries
|
||||
typedef QVector< Path > Paths;
|
||||
|
||||
/// A directory holding bunches of audiofiles, which is indexed into a separate
|
||||
/// dictionary.
|
||||
struct SoundDir
|
||||
{
|
||||
QString path, name;
|
||||
QString iconFilename;
|
||||
|
||||
SoundDir()
|
||||
{}
|
||||
|
||||
SoundDir( QString const & path_, QString const & name_, QString iconFilename_ = "" ):
|
||||
path( path_ ), name( name_ ), iconFilename( iconFilename_ )
|
||||
{}
|
||||
|
||||
bool operator == ( SoundDir const & other ) const
|
||||
{ return path == other.path && name == other.name && iconFilename == other.iconFilename; }
|
||||
};
|
||||
|
||||
/// A list of SoundDirs
|
||||
typedef QVector< SoundDir > SoundDirs;
|
||||
|
||||
struct DictionaryRef
|
||||
{
|
||||
QString id; // Dictionrary id, which is usually an md5 hash
|
||||
QString name; // Dictionary name, used to recover when its id changes
|
||||
|
||||
DictionaryRef()
|
||||
{}
|
||||
|
||||
DictionaryRef( QString const & id_, QString const & name_ ):
|
||||
id( id_ ), name( name_ ) {}
|
||||
|
||||
bool operator == ( DictionaryRef const & other ) const
|
||||
{ return id == other.id && name == other.name; }
|
||||
};
|
||||
|
||||
/// A dictionary group
|
||||
struct Group
|
||||
{
|
||||
unsigned id;
|
||||
QString name, icon;
|
||||
QByteArray iconData;
|
||||
QKeySequence shortcut;
|
||||
QVector< DictionaryRef > dictionaries;
|
||||
Config::MutedDictionaries mutedDictionaries; // Disabled via dictionary bar
|
||||
Config::MutedDictionaries popupMutedDictionaries; // Disabled via dictionary bar in popup
|
||||
|
||||
Group(): id( 0 ) {}
|
||||
|
||||
bool operator == ( Group const & other ) const
|
||||
{ return id == other.id && name == other.name && icon == other.icon &&
|
||||
dictionaries == other.dictionaries && shortcut == other.shortcut &&
|
||||
mutedDictionaries == other.mutedDictionaries &&
|
||||
popupMutedDictionaries == other.popupMutedDictionaries &&
|
||||
iconData == other.iconData; }
|
||||
|
||||
bool operator != ( Group const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
};
|
||||
|
||||
/// All the groups
|
||||
struct Groups: public QVector< Group >
|
||||
{
|
||||
unsigned nextId; // Id to use to create the group next time
|
||||
|
||||
Groups(): nextId( 1 )
|
||||
{}
|
||||
};
|
||||
|
||||
/// Proxy server configuration
|
||||
struct ProxyServer
|
||||
{
|
||||
bool enabled;
|
||||
bool useSystemProxy;
|
||||
|
||||
enum Type
|
||||
{
|
||||
Socks5 = 0,
|
||||
HttpConnect,
|
||||
HttpGet
|
||||
} type;
|
||||
|
||||
QString host;
|
||||
unsigned port;
|
||||
QString user, password;
|
||||
QString systemProxyUser, systemProxyPassword;
|
||||
|
||||
ProxyServer();
|
||||
};
|
||||
|
||||
// A hotkey -- currently qt modifiers plus one or two keys
|
||||
struct HotKey
|
||||
{
|
||||
Qt::KeyboardModifiers modifiers;
|
||||
int key1, key2;
|
||||
|
||||
HotKey();
|
||||
|
||||
/// We use the first two keys of QKeySequence, with modifiers being stored
|
||||
/// in the first one.
|
||||
HotKey( QKeySequence const & );
|
||||
|
||||
QKeySequence toKeySequence() const;
|
||||
};
|
||||
|
||||
struct FullTextSearch
|
||||
{
|
||||
int searchMode;
|
||||
bool matchCase;
|
||||
int maxArticlesPerDictionary;
|
||||
int maxDistanceBetweenWords;
|
||||
bool useMaxDistanceBetweenWords;
|
||||
bool useMaxArticlesPerDictionary;
|
||||
bool enabled;
|
||||
quint32 maxDictionarySize;
|
||||
QByteArray dialogGeometry;
|
||||
QString disabledTypes;
|
||||
|
||||
FullTextSearch() :
|
||||
searchMode( 0 ), matchCase( false ),
|
||||
maxArticlesPerDictionary( 100 ),
|
||||
maxDistanceBetweenWords( 2 ),
|
||||
useMaxDistanceBetweenWords( true ),
|
||||
useMaxArticlesPerDictionary( false ),
|
||||
enabled( true ),
|
||||
maxDictionarySize( 0 )
|
||||
{}
|
||||
};
|
||||
|
||||
/// Various user preferences
|
||||
struct Preferences
|
||||
{
|
||||
QString interfaceLanguage; // Empty value corresponds to system default
|
||||
QString helpLanguage; // Empty value corresponds to interface language
|
||||
QString displayStyle; // Empty value corresponds to the default one
|
||||
bool newTabsOpenAfterCurrentOne;
|
||||
bool newTabsOpenInBackground;
|
||||
bool hideSingleTab;
|
||||
bool mruTabOrder;
|
||||
bool hideMenubar;
|
||||
bool enableTrayIcon;
|
||||
bool startToTray;
|
||||
bool closeToTray;
|
||||
bool autoStart;
|
||||
bool doubleClickTranslates;
|
||||
bool selectWordBySingleClick;
|
||||
bool escKeyHidesMainWindow;
|
||||
bool alwaysOnTop;
|
||||
|
||||
/// An old UI mode when tranlateLine and wordList
|
||||
/// are in the dockable side panel, not on the toolbar.
|
||||
bool searchInDock;
|
||||
|
||||
bool enableMainWindowHotkey;
|
||||
HotKey mainWindowHotkey;
|
||||
bool enableClipboardHotkey;
|
||||
HotKey clipboardHotkey;
|
||||
|
||||
bool enableScanPopup;
|
||||
bool startWithScanPopupOn;
|
||||
bool enableScanPopupModifiers;
|
||||
unsigned long scanPopupModifiers; // Combination of KeyboardState::Modifier
|
||||
bool scanPopupAltMode; // When you press modifier shortly after the selection
|
||||
unsigned scanPopupAltModeSecs;
|
||||
bool scanPopupUseUIAutomation;
|
||||
bool scanPopupUseIAccessibleEx;
|
||||
bool scanPopupUseGDMessage;
|
||||
bool scanToMainWindow;
|
||||
|
||||
// Whether the word should be pronounced on page load, in main window/popup
|
||||
bool pronounceOnLoadMain, pronounceOnLoadPopup;
|
||||
QString audioPlaybackProgram;
|
||||
bool useExternalPlayer;
|
||||
bool useInternalPlayer;
|
||||
|
||||
ProxyServer proxyServer;
|
||||
|
||||
bool checkForNewReleases;
|
||||
bool disallowContentFromOtherSites;
|
||||
bool enableWebPlugins;
|
||||
bool hideGoldenDictHeader;
|
||||
|
||||
qreal zoomFactor;
|
||||
qreal helpZoomFactor;
|
||||
int wordsZoomLevel;
|
||||
|
||||
unsigned maxStringsInHistory;
|
||||
unsigned storeHistory;
|
||||
bool alwaysExpandOptionalParts;
|
||||
|
||||
unsigned historyStoreInterval;
|
||||
|
||||
bool collapseBigArticles;
|
||||
int articleSizeLimit;
|
||||
|
||||
unsigned short maxDictionaryRefsInContextMenu;
|
||||
#ifndef Q_WS_X11
|
||||
bool trackClipboardChanges;
|
||||
#endif
|
||||
|
||||
QString addonStyle;
|
||||
|
||||
FullTextSearch fts;
|
||||
|
||||
Preferences();
|
||||
};
|
||||
|
||||
/// A MediaWiki network dictionary definition
|
||||
struct MediaWiki
|
||||
{
|
||||
QString id, name, url;
|
||||
bool enabled;
|
||||
QString icon;
|
||||
|
||||
MediaWiki(): enabled( false )
|
||||
{}
|
||||
|
||||
MediaWiki( QString const & id_, QString const & name_, QString const & url_,
|
||||
bool enabled_, QString const & icon_ ):
|
||||
id( id_ ), name( name_ ), url( url_ ), enabled( enabled_ ), icon( icon_ ) {}
|
||||
|
||||
bool operator == ( MediaWiki const & other ) const
|
||||
{ return id == other.id && name == other.name && url == other.url &&
|
||||
enabled == other.enabled && icon == other.icon ; }
|
||||
};
|
||||
|
||||
/// Any website which can be queried though a simple template substitution
|
||||
struct WebSite
|
||||
{
|
||||
QString id, name, url;
|
||||
bool enabled;
|
||||
QString iconFilename;
|
||||
|
||||
WebSite(): enabled( false )
|
||||
{}
|
||||
|
||||
WebSite( QString const & id_, QString const & name_, QString const & url_,
|
||||
bool enabled_, QString const & iconFilename_ ):
|
||||
id( id_ ), name( name_ ), url( url_ ), enabled( enabled_ ), iconFilename( iconFilename_ ) {}
|
||||
|
||||
bool operator == ( WebSite const & other ) const
|
||||
{ return id == other.id && name == other.name && url == other.url &&
|
||||
enabled == other.enabled && iconFilename == other.iconFilename; }
|
||||
};
|
||||
|
||||
/// All the WebSites
|
||||
typedef QVector< WebSite > WebSites;
|
||||
|
||||
/// Any DICT server
|
||||
struct DictServer
|
||||
{
|
||||
QString id, name, url;
|
||||
bool enabled;
|
||||
QString databases;
|
||||
QString strategies;
|
||||
QString iconFilename;
|
||||
|
||||
DictServer(): enabled( false )
|
||||
{}
|
||||
|
||||
DictServer( QString const & id_, QString const & name_, QString const & url_,
|
||||
bool enabled_, QString const & databases_, QString const & strategies_,
|
||||
QString const & iconFilename_ ):
|
||||
id( id_ ), name( name_ ), url( url_ ), enabled( enabled_ ), databases( databases_ ),
|
||||
strategies( strategies_ ), iconFilename( iconFilename_ ) {}
|
||||
|
||||
bool operator == ( DictServer const & other ) const
|
||||
{ return id == other.id && name == other.name && url == other.url
|
||||
&& enabled == other.enabled && databases == other.databases
|
||||
&& strategies == other.strategies
|
||||
&& iconFilename == other.iconFilename; }
|
||||
};
|
||||
|
||||
/// All the DictServers
|
||||
typedef QVector< DictServer > DictServers;
|
||||
|
||||
/// Hunspell configuration
|
||||
struct Hunspell
|
||||
{
|
||||
QString dictionariesPath;
|
||||
|
||||
typedef QVector< QString > Dictionaries;
|
||||
|
||||
Dictionaries enabledDictionaries;
|
||||
|
||||
bool operator == ( Hunspell const & other ) const
|
||||
{ return dictionariesPath == other.dictionariesPath &&
|
||||
enabledDictionaries == other.enabledDictionaries; }
|
||||
|
||||
bool operator != ( Hunspell const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
};
|
||||
|
||||
/// All the MediaWikis
|
||||
typedef QVector< MediaWiki > MediaWikis;
|
||||
|
||||
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
||||
/// Chinese transliteration configuration
|
||||
struct Chinese
|
||||
{
|
||||
bool enable;
|
||||
|
||||
bool enableSCToTWConversion;
|
||||
bool enableSCToHKConversion;
|
||||
bool enableTCToSCConversion;
|
||||
|
||||
Chinese();
|
||||
|
||||
bool operator == ( Chinese const & other ) const
|
||||
{ return enable == other.enable &&
|
||||
enableSCToTWConversion == other.enableSCToTWConversion &&
|
||||
enableSCToHKConversion == other.enableSCToHKConversion &&
|
||||
enableTCToSCConversion == other.enableTCToSCConversion; }
|
||||
|
||||
bool operator != ( Chinese const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
/// Romaji transliteration configuration
|
||||
struct Romaji
|
||||
{
|
||||
bool enable;
|
||||
|
||||
bool enableHepburn;
|
||||
bool enableNihonShiki;
|
||||
bool enableKunreiShiki;
|
||||
bool enableHiragana;
|
||||
bool enableKatakana;
|
||||
|
||||
Romaji();
|
||||
|
||||
bool operator == ( Romaji const & other ) const
|
||||
{ return enable == other.enable &&
|
||||
enableHepburn == other.enableHepburn &&
|
||||
enableNihonShiki == other.enableNihonShiki &&
|
||||
enableKunreiShiki == other.enableKunreiShiki &&
|
||||
enableHiragana == other.enableHiragana &&
|
||||
enableKatakana == other.enableKatakana; }
|
||||
|
||||
bool operator != ( Romaji const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
|
||||
};
|
||||
|
||||
struct Transliteration
|
||||
{
|
||||
bool enableRussianTransliteration;
|
||||
bool enableGermanTransliteration;
|
||||
bool enableGreekTransliteration;
|
||||
bool enableBelarusianTransliteration;
|
||||
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
||||
Chinese chinese;
|
||||
#endif
|
||||
Romaji romaji;
|
||||
|
||||
bool operator == ( Transliteration const & other ) const
|
||||
{ return enableRussianTransliteration == other.enableRussianTransliteration &&
|
||||
enableGermanTransliteration == other.enableGermanTransliteration &&
|
||||
enableGreekTransliteration == other.enableGreekTransliteration &&
|
||||
enableBelarusianTransliteration == other.enableBelarusianTransliteration &&
|
||||
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
||||
chinese == other.chinese &&
|
||||
#endif
|
||||
romaji == other.romaji;
|
||||
}
|
||||
|
||||
bool operator != ( Transliteration const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
|
||||
Transliteration():
|
||||
enableRussianTransliteration( false ),
|
||||
enableGermanTransliteration( false ),
|
||||
enableGreekTransliteration( false ),
|
||||
enableBelarusianTransliteration( false )
|
||||
{}
|
||||
};
|
||||
|
||||
struct Forvo
|
||||
{
|
||||
bool enable;
|
||||
QString apiKey;
|
||||
QString languageCodes;
|
||||
|
||||
Forvo(): enable( false )
|
||||
{}
|
||||
|
||||
bool operator == ( Forvo const & other ) const
|
||||
{ return enable == other.enable &&
|
||||
apiKey == other.apiKey &&
|
||||
languageCodes == other.languageCodes;
|
||||
}
|
||||
|
||||
bool operator != ( Forvo const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
};
|
||||
|
||||
struct Program
|
||||
{
|
||||
bool enabled;
|
||||
enum Type
|
||||
{
|
||||
Audio,
|
||||
PlainText,
|
||||
Html,
|
||||
PrefixMatch,
|
||||
MaxTypeValue
|
||||
} type;
|
||||
QString id, name, commandLine;
|
||||
QString iconFilename;
|
||||
|
||||
Program(): enabled( false )
|
||||
{}
|
||||
|
||||
Program( bool enabled_, Type type_, QString const & id_,
|
||||
QString const & name_, QString const & commandLine_, QString const & iconFilename_ ):
|
||||
enabled( enabled_ ), type( type_ ), id( id_ ), name( name_ ),
|
||||
commandLine( commandLine_ ), iconFilename( iconFilename_ ) {}
|
||||
|
||||
bool operator == ( Program const & other ) const
|
||||
{ return enabled == other.enabled &&
|
||||
type == other.type &&
|
||||
name == other.name &&
|
||||
commandLine == other.commandLine &&
|
||||
iconFilename == other.iconFilename;
|
||||
}
|
||||
|
||||
bool operator != ( Program const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
};
|
||||
|
||||
typedef QVector< Program > Programs;
|
||||
|
||||
struct VoiceEngine
|
||||
{
|
||||
bool enabled;
|
||||
QString id;
|
||||
QString name;
|
||||
QString iconFilename;
|
||||
int volume; // 0-100 allowed
|
||||
int rate; // 0-100 allowed
|
||||
|
||||
VoiceEngine(): enabled( false )
|
||||
, volume( 50 )
|
||||
, rate( 50 )
|
||||
{}
|
||||
VoiceEngine( QString id_, QString name_, int volume_, int rate_ ):
|
||||
enabled( false )
|
||||
, id( id_ )
|
||||
, name( name_ )
|
||||
, volume( volume_ )
|
||||
, rate( rate_ )
|
||||
{}
|
||||
|
||||
bool operator == ( VoiceEngine const & other ) const
|
||||
{
|
||||
return enabled == other.enabled &&
|
||||
id == other.id &&
|
||||
name == other.name &&
|
||||
iconFilename == other.iconFilename &&
|
||||
volume == other.volume &&
|
||||
rate == other.rate;
|
||||
}
|
||||
|
||||
bool operator != ( VoiceEngine const & other ) const
|
||||
{ return ! operator == ( other ); }
|
||||
};
|
||||
|
||||
typedef QVector< VoiceEngine> VoiceEngines;
|
||||
|
||||
struct HeadwordsDialog
|
||||
{
|
||||
int searchMode;
|
||||
bool matchCase;
|
||||
bool autoApply;
|
||||
QString headwordsExportPath;
|
||||
QByteArray headwordsDialogGeometry;
|
||||
|
||||
HeadwordsDialog() :
|
||||
searchMode( 0 ), matchCase( false )
|
||||
, autoApply( false )
|
||||
{}
|
||||
};
|
||||
|
||||
struct Class
|
||||
{
|
||||
Paths paths;
|
||||
SoundDirs soundDirs;
|
||||
Group dictionaryOrder;
|
||||
Group inactiveDictionaries;
|
||||
Groups groups;
|
||||
Preferences preferences;
|
||||
MediaWikis mediawikis;
|
||||
WebSites webSites;
|
||||
DictServers dictServers;
|
||||
Hunspell hunspell;
|
||||
Transliteration transliteration;
|
||||
Forvo forvo;
|
||||
Programs programs;
|
||||
VoiceEngines voiceEngines;
|
||||
|
||||
unsigned lastMainGroupId; // Last used group in main window
|
||||
unsigned lastPopupGroupId; // Last used group in popup window
|
||||
|
||||
QByteArray popupWindowState; // Binary state saved by QMainWindow
|
||||
QByteArray popupWindowGeometry; // Geometry saved by QMainWindow
|
||||
QByteArray dictInfoGeometry; // Geometry of "Dictionary info" window
|
||||
QByteArray inspectorGeometry; // Geometry of WebKit inspector window
|
||||
QByteArray helpWindowGeometry; // Geometry of help window
|
||||
QByteArray helpSplitterState; // Geometry of help splitter
|
||||
|
||||
QString historyExportPath; // Path for export/import history
|
||||
QString resourceSavePath; // Path to save images/audio
|
||||
QString articleSavePath; // Path to save articles
|
||||
|
||||
bool pinPopupWindow; // Last pin status
|
||||
|
||||
QByteArray mainWindowState; // Binary state saved by QMainWindow
|
||||
QByteArray mainWindowGeometry; // Geometry saved by QMainWindow
|
||||
|
||||
MutedDictionaries mutedDictionaries; // Disabled via dictionary bar
|
||||
MutedDictionaries popupMutedDictionaries; // Disabled via dictionary bar in popup
|
||||
|
||||
QDateTime timeForNewReleaseCheck; // Only effective if
|
||||
// preferences.checkForNewReleases is set
|
||||
QString skippedRelease; // Empty by default
|
||||
|
||||
bool showingDictBarNames;
|
||||
|
||||
bool usingSmallIconsInToolbars;
|
||||
|
||||
int maxPictureWidth; // Maximum picture width
|
||||
|
||||
/// Maximum size for the headwords.
|
||||
/// Bigger headwords won't be indexed. For now, only in DSL.
|
||||
unsigned int maxHeadwordSize;
|
||||
|
||||
HeadwordsDialog headwordsDialog;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QRect maximizedMainWindowGeometry;
|
||||
QRect normalMainWindowGeometry;
|
||||
#endif
|
||||
|
||||
QString editDictionaryCommandLine; // Command line to call external editor for dictionary
|
||||
|
||||
Class(): lastMainGroupId( 0 ), lastPopupGroupId( 0 ),
|
||||
pinPopupWindow( false ), showingDictBarNames( false ),
|
||||
usingSmallIconsInToolbars( false ),
|
||||
maxPictureWidth( 0 ), maxHeadwordSize ( 256U )
|
||||
{}
|
||||
Group * getGroup( unsigned id );
|
||||
Group const * getGroup( unsigned id ) const;
|
||||
};
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/// Configuration-specific events. Some parts of the program need to react
|
||||
/// to specific changes in configuration. The object of this class is used
|
||||
/// to emit signals when such events happen -- and the listeners connect to
|
||||
/// them to be notified of them.
|
||||
/// This class is separate from the main Class since QObjects can't be copied.
|
||||
class Events: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// Signals that the value of the mutedDictionaries has changed.
|
||||
/// This emits mutedDictionariesChanged() signal, so the subscribers will
|
||||
/// be notified.
|
||||
void signalMutedDictionariesChanged();
|
||||
|
||||
signals:
|
||||
|
||||
/// THe value of the mutedDictionaries has changed.
|
||||
void mutedDictionariesChanged();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
DEF_EX( exError, "Error with the program's configuration", std::exception )
|
||||
DEF_EX( exCantUseHomeDir, "Can't use home directory to store GoldenDict preferences", exError )
|
||||
DEF_EX( exCantUseIndexDir, "Can't use index directory to store GoldenDict index files", exError )
|
||||
DEF_EX( exCantReadConfigFile, "Can't read the configuration file", exError )
|
||||
DEF_EX( exCantWriteConfigFile, "Can't write the configuration file", exError )
|
||||
DEF_EX( exMalformedConfigFile, "The configuration file is malformed", exError )
|
||||
|
||||
/// Loads the configuration, or creates the default one if none is present
|
||||
Class load() throw( exError );
|
||||
|
||||
/// Saves the configuration
|
||||
void save( Class const & ) throw( exError );
|
||||
|
||||
/// Returns the configuration file name.
|
||||
QString getConfigFileName();
|
||||
|
||||
/// Returns the main configuration directory.
|
||||
QString getConfigDir() throw( exError );
|
||||
|
||||
/// Returns the index directory, where the indices are to be stored.
|
||||
QString getIndexDir() throw( exError );
|
||||
|
||||
/// Returns the filename of a .pid file which should store current pid of
|
||||
/// the process.
|
||||
QString getPidFileName() throw( exError );
|
||||
|
||||
/// Returns the filename of a history file which stores search history.
|
||||
QString getHistoryFileName() throw( exError );
|
||||
|
||||
/// Returns the user .css file name.
|
||||
QString getUserCssFileName() throw( exError );
|
||||
|
||||
/// Returns the user .css file name used for printing only.
|
||||
QString getUserCssPrintFileName() throw( exError );
|
||||
|
||||
/// Returns the user .css file name for the Qt interface customization.
|
||||
QString getUserQtCssFileName() throw( exError );
|
||||
|
||||
/// Returns the program's data dir. Under Linux that would be something like
|
||||
/// /usr/share/apps/goldendict, under Windows C:/Program Files/GoldenDict.
|
||||
QString getProgramDataDir() throw();
|
||||
|
||||
/// Returns the directory storing program localizized files (.qm).
|
||||
QString getLocDir() throw();
|
||||
|
||||
/// Returns the directory storing program help files (.qch).
|
||||
QString getHelpDir() throw();
|
||||
|
||||
#ifdef MAKE_CHINESE_CONVERSION_SUPPORT
|
||||
/// Returns the directory storing OpenCC configuration and dictionary files (.json and .ocd).
|
||||
QString getOpenCCDir() throw();
|
||||
#endif
|
||||
|
||||
/// Returns true if the program is configured as a portable version. In that
|
||||
/// mode, all the settings and indices are kept in the program's directory.
|
||||
bool isPortableVersion() throw();
|
||||
|
||||
/// Returns directory with dictionaries for portable version. It is content/
|
||||
/// in the application's directory.
|
||||
QString getPortableVersionDictionaryDir() throw();
|
||||
|
||||
/// Returns directory with morpgologies for portable version. It is
|
||||
/// content/morphology in the application's directory.
|
||||
QString getPortableVersionMorphoDir() throw();
|
||||
|
||||
/// Returns the add-on styles directory.
|
||||
QString getStylesDir() throw();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo
|
||||
echo To build the program, run qmake, then make.
|
||||
echo
|
||||
echo The following dependency packages are required: libvorbis-dev, zlib1g-dev, libhunspell-dev, x11proto-record-dev, qt4-qmake, libqt4-dev, g++, libxtst-dev, libphonon-dev. They can be named slightly different in different distributions.
|
||||
@@ -0,0 +1,298 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "country.hh"
|
||||
#include "folding.hh"
|
||||
#include <QtCore>
|
||||
#include "wstring_qt.hh"
|
||||
|
||||
namespace Country {
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Database: public QMap< QString, QString >
|
||||
{
|
||||
static Database const & instance()
|
||||
{ static Database db; return db; }
|
||||
|
||||
private:
|
||||
|
||||
Database();
|
||||
|
||||
void addCountry( QString const & country, QString const & code )
|
||||
{
|
||||
(*this)[ gd::toQString( Folding::apply( gd::toWString( country ) ) ) ] = code.toLower();
|
||||
}
|
||||
};
|
||||
|
||||
Database::Database()
|
||||
{
|
||||
addCountry( "Afghanistan", "AF" );
|
||||
addCountry( "Aland Islands", "AX" );
|
||||
addCountry( "Albania", "AL" );
|
||||
addCountry( "Algeria", "DZ" );
|
||||
addCountry( "American Samoa", "AS" );
|
||||
addCountry( "Andorra", "AD" );
|
||||
addCountry( "Angola", "AO" );
|
||||
addCountry( "Anguilla", "AI" );
|
||||
addCountry( "Antarctica", "AQ" );
|
||||
addCountry( "Antigua and Barbuda", "AG" );
|
||||
addCountry( "Argentina", "AR" );
|
||||
addCountry( "Armenia", "AM" );
|
||||
addCountry( "Aruba", "AW" );
|
||||
addCountry( "Australia", "AU" );
|
||||
addCountry( "Austria", "AT" );
|
||||
addCountry( "Azerbaijan", "AZ" );
|
||||
addCountry( "Bahamas", "BS" );
|
||||
addCountry( "Bahrain", "BH" );
|
||||
addCountry( "Bangladesh", "BD" );
|
||||
addCountry( "Barbados", "BB" );
|
||||
addCountry( "Belarus", "BY" );
|
||||
addCountry( "Belgium", "BE" );
|
||||
addCountry( "Belize", "BZ" );
|
||||
addCountry( "Benin", "BJ" );
|
||||
addCountry( "Bermuda", "BM" );
|
||||
addCountry( "Bhutan", "BT" );
|
||||
addCountry( "Bolivia, Plurinational State of", "BO" );
|
||||
addCountry( "Bolivia", "BO" );
|
||||
addCountry( "Bosnia and Herzegovina", "BA" );
|
||||
addCountry( "Botswana", "BW" );
|
||||
addCountry( "Bouvet Island", "BV" );
|
||||
addCountry( "Brazil", "BR" );
|
||||
addCountry( "British Indian Ocean Territory", "IO" );
|
||||
addCountry( "Brunei Darussalam", "BN" );
|
||||
addCountry( "Bulgaria", "BG" );
|
||||
addCountry( "Burkina Faso", "BF" );
|
||||
addCountry( "Burundi", "BI" );
|
||||
addCountry( "Cambodia", "KH" );
|
||||
addCountry( "Cameroon", "CM" );
|
||||
addCountry( "Canada", "CA" );
|
||||
addCountry( "Cape Verde", "CV" );
|
||||
addCountry( "Cayman Islands", "KY" );
|
||||
addCountry( "Central African Republic", "CF" );
|
||||
addCountry( "Chad", "TD" );
|
||||
addCountry( "Chile", "CL" );
|
||||
addCountry( "China", "CN" );
|
||||
addCountry( "Christmas Island", "CX" );
|
||||
addCountry( "Cocos (Keeling) Islands", "CC" );
|
||||
addCountry( "Colombia", "CO" );
|
||||
addCountry( "Comoros", "KM" );
|
||||
addCountry( "Congo", "CG" );
|
||||
addCountry( "Congo, the Democratic Republic of the", "CD" );
|
||||
addCountry( "Cook Islands", "CK" );
|
||||
addCountry( "Costa Rica", "CR" );
|
||||
addCountry( "Cote d'Ivoire", "CI" );
|
||||
addCountry( "Croatia", "HR" );
|
||||
addCountry( "Cuba", "CU" );
|
||||
addCountry( "Cyprus", "CY" );
|
||||
addCountry( "Czech Republic", "CZ" );
|
||||
addCountry( "Denmark", "DK" );
|
||||
addCountry( "Djibouti", "DJ" );
|
||||
addCountry( "Dominica", "DM" );
|
||||
addCountry( "Dominican Republic", "DO" );
|
||||
addCountry( "Ecuador", "EC" );
|
||||
addCountry( "Egypt", "EG" );
|
||||
addCountry( "El Salvador", "SV" );
|
||||
addCountry( "Equatorial Guinea", "GQ" );
|
||||
addCountry( "Eritrea", "ER" );
|
||||
addCountry( "Estonia", "EE" );
|
||||
addCountry( "Ethiopia", "ET" );
|
||||
addCountry( "Falkland Islands (Malvinas)", "FK" );
|
||||
addCountry( "Faroe Islands", "FO" );
|
||||
addCountry( "Fiji", "FJ" );
|
||||
addCountry( "Finland", "FI" );
|
||||
addCountry( "France", "FR" );
|
||||
addCountry( "French Guiana", "GF" );
|
||||
addCountry( "French Polynesia", "PF" );
|
||||
addCountry( "French Southern Territories", "TF" );
|
||||
addCountry( "Gabon", "GA" );
|
||||
addCountry( "Gambia", "GM" );
|
||||
addCountry( "Georgia", "GE" );
|
||||
addCountry( "Germany", "DE" );
|
||||
addCountry( "Ghana", "GH" );
|
||||
addCountry( "Gibraltar", "GI" );
|
||||
addCountry( "Greece", "GR" );
|
||||
addCountry( "Greenland", "GL" );
|
||||
addCountry( "Grenada", "GD" );
|
||||
addCountry( "Guadeloupe", "GP" );
|
||||
addCountry( "Guam", "GU" );
|
||||
addCountry( "Guatemala", "GT" );
|
||||
addCountry( "Guernsey", "GG" );
|
||||
addCountry( "Guinea", "GN" );
|
||||
addCountry( "Guinea-Bissau", "GW" );
|
||||
addCountry( "Guyana", "GY" );
|
||||
addCountry( "Haiti", "HT" );
|
||||
addCountry( "Heard Island and McDonald Islands", "HM" );
|
||||
addCountry( "Holy See (Vatican City State)", "VA" );
|
||||
addCountry( "Honduras", "HN" );
|
||||
addCountry( "Hong Kong", "HK" );
|
||||
addCountry( "Hungary", "HU" );
|
||||
addCountry( "Iceland", "IS" );
|
||||
addCountry( "India", "IN" );
|
||||
addCountry( "Indonesia", "ID" );
|
||||
addCountry( "Iran, Islamic Republic of", "IR" );
|
||||
addCountry( "Iran", "IR" );
|
||||
addCountry( "Iraq", "IQ" );
|
||||
addCountry( "Ireland", "IE" );
|
||||
addCountry( "Isle of Man", "IM" );
|
||||
addCountry( "Israel", "IL" );
|
||||
addCountry( "Italy", "IT" );
|
||||
addCountry( "Jamaica", "JM" );
|
||||
addCountry( "Japan", "JP" );
|
||||
addCountry( "Jersey", "JE" );
|
||||
addCountry( "Jordan", "JO" );
|
||||
addCountry( "Kazakhstan", "KZ" );
|
||||
addCountry( "Kenya", "KE" );
|
||||
addCountry( "Kiribati", "KI" );
|
||||
addCountry( "Korea, Democratic People's Republic of", "KP" );
|
||||
addCountry( "Korea, Republic of", "KR" );
|
||||
addCountry( "Korea", "KR" );
|
||||
addCountry( "Kuwait", "KW" );
|
||||
addCountry( "Kyrgyzstan", "KG" );
|
||||
addCountry( "Lao People's Democratic Republic", "LA" );
|
||||
addCountry( "Latvia", "LV" );
|
||||
addCountry( "Lebanon", "LB" );
|
||||
addCountry( "Lesotho", "LS" );
|
||||
addCountry( "Liberia", "LR" );
|
||||
addCountry( "Libyan Arab Jamahiriya", "LY" );
|
||||
addCountry( "Liechtenstein", "LI" );
|
||||
addCountry( "Lithuania", "LT" );
|
||||
addCountry( "Luxembourg", "LU" );
|
||||
addCountry( "Macao", "MO" );
|
||||
addCountry( "Macedonia, the former Yugoslav Republic of", "MK" );
|
||||
addCountry( "Macedonia", "MK" );
|
||||
addCountry( "Madagascar", "MG" );
|
||||
addCountry( "Malawi", "MW" );
|
||||
addCountry( "Malaysia", "MY" );
|
||||
addCountry( "Maldives", "MV" );
|
||||
addCountry( "Mali", "ML" );
|
||||
addCountry( "Malta", "MT" );
|
||||
addCountry( "Marshall Islands", "MH" );
|
||||
addCountry( "Martinique", "MQ" );
|
||||
addCountry( "Mauritania", "MR" );
|
||||
addCountry( "Mauritius", "MU" );
|
||||
addCountry( "Mayotte", "YT" );
|
||||
addCountry( "Mexico", "MX" );
|
||||
addCountry( "Micronesia, Federated States of", "FM" );
|
||||
addCountry( "Micronesia", "FM" );
|
||||
addCountry( "Moldova, Republic of", "MD" );
|
||||
addCountry( "Moldova", "MD" );
|
||||
addCountry( "Monaco", "MC" );
|
||||
addCountry( "Mongolia", "MN" );
|
||||
addCountry( "Montenegro", "ME" );
|
||||
addCountry( "Montserrat", "MS" );
|
||||
addCountry( "Morocco", "MA" );
|
||||
addCountry( "Mozambique", "MZ" );
|
||||
addCountry( "Myanmar", "MM" );
|
||||
addCountry( "Namibia", "NA" );
|
||||
addCountry( "Nauru", "NR" );
|
||||
addCountry( "Nepal", "NP" );
|
||||
addCountry( "Netherlands", "NL" );
|
||||
addCountry( "Netherlands Antilles", "AN" );
|
||||
addCountry( "New Caledonia", "NC" );
|
||||
addCountry( "New Zealand", "NZ" );
|
||||
addCountry( "Nicaragua", "NI" );
|
||||
addCountry( "Niger", "NE" );
|
||||
addCountry( "Nigeria", "NG" );
|
||||
addCountry( "Niue", "NU" );
|
||||
addCountry( "Norfolk Island", "NF" );
|
||||
addCountry( "Northern Mariana Islands", "MP" );
|
||||
addCountry( "Norway", "NO" );
|
||||
addCountry( "Oman", "OM" );
|
||||
addCountry( "Pakistan", "PK" );
|
||||
addCountry( "Palau", "PW" );
|
||||
addCountry( "Palestinian Territory, Occupied", "PS" );
|
||||
addCountry( "Panama", "PA" );
|
||||
addCountry( "Papua New Guinea", "PG" );
|
||||
addCountry( "Paraguay", "PY" );
|
||||
addCountry( "Peru", "PE" );
|
||||
addCountry( "Philippines", "PH" );
|
||||
addCountry( "Pitcairn", "PN" );
|
||||
addCountry( "Poland", "PL" );
|
||||
addCountry( "Portugal", "PT" );
|
||||
addCountry( "Puerto Rico", "PR" );
|
||||
addCountry( "Qatar", "QA" );
|
||||
addCountry( "Reunion", "RE" );
|
||||
addCountry( "Romania", "RO" );
|
||||
addCountry( "Russian Federation", "RU" );
|
||||
addCountry( "Russia", "RU" );
|
||||
addCountry( "Rwanda", "RW" );
|
||||
addCountry( "Saint Barthélemy", "BL" );
|
||||
addCountry( "Saint Helena, Ascension and Tristan da Cunha", "SH" );
|
||||
addCountry( "Saint Kitts and Nevis", "KN" );
|
||||
addCountry( "Saint Lucia", "LC" );
|
||||
addCountry( "Saint Martin (French part)", "MF" );
|
||||
addCountry( "Saint Pierre and Miquelon", "PM" );
|
||||
addCountry( "Saint Vincent and the Grenadines", "VC" );
|
||||
addCountry( "Samoa", "WS" );
|
||||
addCountry( "San Marino", "SM" );
|
||||
addCountry( "Sao Tome and Principe", "ST" );
|
||||
addCountry( "Saudi Arabia", "SA" );
|
||||
addCountry( "Senegal", "SN" );
|
||||
addCountry( "Serbia", "RS" );
|
||||
addCountry( "Seychelles", "SC" );
|
||||
addCountry( "Sierra Leone", "SL" );
|
||||
addCountry( "Singapore", "SG" );
|
||||
addCountry( "Slovakia", "SK" );
|
||||
addCountry( "Slovenia", "SI" );
|
||||
addCountry( "Solomon Islands", "SB" );
|
||||
addCountry( "Somalia", "SO" );
|
||||
addCountry( "South Africa", "ZA" );
|
||||
addCountry( "South Georgia and the South Sandwich Islands", "GS" );
|
||||
addCountry( "Spain", "ES" );
|
||||
addCountry( "Sri Lanka", "LK" );
|
||||
addCountry( "Sudan", "SD" );
|
||||
addCountry( "Suriname", "SR" );
|
||||
addCountry( "Svalbard and Jan Mayen", "SJ" );
|
||||
addCountry( "Swaziland", "SZ" );
|
||||
addCountry( "Sweden", "SE" );
|
||||
addCountry( "Switzerland", "CH" );
|
||||
addCountry( "Syrian Arab Republic", "SY" );
|
||||
addCountry( "Taiwan, Province of China", "TW" );
|
||||
addCountry( "Tajikistan", "TJ" );
|
||||
addCountry( "Tanzania, United Republic of", "TZ" );
|
||||
addCountry( "Tanzania", "TZ" );
|
||||
addCountry( "Thailand", "TH" );
|
||||
addCountry( "Timor-Leste", "TL" );
|
||||
addCountry( "Togo", "TG" );
|
||||
addCountry( "Tokelau", "TK" );
|
||||
addCountry( "Tonga", "TO" );
|
||||
addCountry( "Trinidad and Tobago", "TT" );
|
||||
addCountry( "Tunisia", "TN" );
|
||||
addCountry( "Turkey", "TR" );
|
||||
addCountry( "Turkmenistan", "TM" );
|
||||
addCountry( "Turks and Caicos Islands", "TC" );
|
||||
addCountry( "Tuvalu", "TV" );
|
||||
addCountry( "Uganda", "UG" );
|
||||
addCountry( "Ukraine", "UA" );
|
||||
addCountry( "United Arab Emirates", "AE" );
|
||||
addCountry( "United Kingdom", "GB" );
|
||||
addCountry( "United States", "US" );
|
||||
addCountry( "United States Minor Outlying Islands", "UM" );
|
||||
addCountry( "Uruguay", "UY" );
|
||||
addCountry( "Uzbekistan", "UZ" );
|
||||
addCountry( "Vanuatu", "VU" );
|
||||
addCountry( "Venezuela, Bolivarian Republic of", "VE" );
|
||||
addCountry( "Venezuela", "VE" );
|
||||
addCountry( "Viet Nam", "VN" );
|
||||
addCountry( "Virgin Islands, British", "VG" );
|
||||
addCountry( "Virgin Islands, U.S.", "VI" );
|
||||
addCountry( "Wallis and Futuna", "WF" );
|
||||
addCountry( "Western Sahara", "EH" );
|
||||
addCountry( "Yemen", "YE" );
|
||||
addCountry( "Zambia", "ZM" );
|
||||
addCountry( "Zimbabwe", "ZW" );
|
||||
}
|
||||
}
|
||||
|
||||
QString englishNametoIso2( QString const & name )
|
||||
{
|
||||
Database::const_iterator i = Database::instance().find( gd::toQString( Folding::apply( gd::toWString( name ) ) ) );
|
||||
|
||||
if ( i == Database::instance().end() )
|
||||
return QString();
|
||||
else
|
||||
return i.value();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef COUNTRY_HH
|
||||
#define COUNTRY_HH
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace Country {
|
||||
|
||||
/// Attempts converting the given country name, in english, to its iso-3166-1
|
||||
/// 2-letter code. If fails, empty string is returned.
|
||||
QString englishNametoIso2( QString const & );
|
||||
|
||||
}
|
||||
|
||||
#endif // COUNTRY_HH
|
||||
@@ -0,0 +1,136 @@
|
||||
#include "decompress.hh"
|
||||
#include "zlib.h"
|
||||
#include "bzlib.h"
|
||||
|
||||
#ifdef MAKE_ZIM_SUPPORT
|
||||
#include "lzma.h"
|
||||
#endif
|
||||
|
||||
#define CHUNK_SIZE 2048
|
||||
|
||||
QByteArray zlibDecompress( const char * bufptr, unsigned length )
|
||||
{
|
||||
z_stream zs;
|
||||
char buf[CHUNK_SIZE];
|
||||
QByteArray str;
|
||||
int res;
|
||||
memset( &zs, 0, sizeof(zs) );
|
||||
zs.next_in = (Bytef *)bufptr;
|
||||
zs.avail_in = length;
|
||||
while( 1 )
|
||||
{
|
||||
res = inflateInit( &zs );
|
||||
if( res != Z_OK )
|
||||
break;
|
||||
while( res != Z_STREAM_END )
|
||||
{
|
||||
zs.next_out = (Bytef *)buf;
|
||||
zs.avail_out = CHUNK_SIZE;
|
||||
res = inflate( &zs, Z_SYNC_FLUSH );
|
||||
str.append( buf, CHUNK_SIZE - zs.avail_out );
|
||||
if( res != Z_OK && res != Z_STREAM_END )
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
inflateEnd( &zs );
|
||||
if( res != Z_STREAM_END )
|
||||
str.clear();
|
||||
return str;
|
||||
}
|
||||
|
||||
string decompressZlib( const char * bufptr, unsigned length )
|
||||
{
|
||||
QByteArray b = zlibDecompress( bufptr, length );
|
||||
return string( b.constData(), b.size() );
|
||||
}
|
||||
|
||||
string decompressBzip2( const char * bufptr, unsigned length )
|
||||
{
|
||||
bz_stream zs;
|
||||
char buf[CHUNK_SIZE];
|
||||
string str;
|
||||
int res;
|
||||
memset( &zs, 0, sizeof(zs) );
|
||||
zs.next_in = (char *)bufptr;
|
||||
zs.avail_in = length;
|
||||
zs.total_in_lo32 = length;
|
||||
while( 1 )
|
||||
{
|
||||
res = BZ2_bzDecompressInit( &zs, 0, 0 );
|
||||
if( res != BZ_OK )
|
||||
break;
|
||||
while( res != BZ_STREAM_END )
|
||||
{
|
||||
zs.next_out = buf;
|
||||
zs.avail_out = CHUNK_SIZE;
|
||||
zs.total_out_lo32 = length;
|
||||
res = BZ2_bzDecompress( &zs );
|
||||
str.append( buf, CHUNK_SIZE - zs.avail_out );
|
||||
if( res != BZ_OK && res != BZ_STREAM_END )
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
BZ2_bzDecompressEnd( &zs );
|
||||
if( res != BZ_STREAM_END )
|
||||
str.clear();
|
||||
return str;
|
||||
}
|
||||
|
||||
#ifdef MAKE_ZIM_SUPPORT
|
||||
|
||||
#define BUFSIZE 0xFFFF
|
||||
|
||||
string decompressLzma2( const char * bufptr, unsigned length,
|
||||
bool raw_decoder )
|
||||
{
|
||||
string str;
|
||||
lzma_ret res;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
lzma_stream strm = LZMA_STREAM_INIT;
|
||||
strm.next_in = reinterpret_cast< const uint8_t * >( bufptr );
|
||||
strm.avail_in = length;
|
||||
|
||||
lzma_options_lzma opt;
|
||||
lzma_filter filters[ 2 ];
|
||||
|
||||
if( raw_decoder )
|
||||
{
|
||||
lzma_lzma_preset(&opt, LZMA_PRESET_DEFAULT);
|
||||
|
||||
filters[ 0 ].id = LZMA_FILTER_LZMA2;
|
||||
filters[ 0 ].options = &opt;
|
||||
filters[ 1 ].id = LZMA_VLI_UNKNOWN;
|
||||
}
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
if( raw_decoder )
|
||||
res = lzma_raw_decoder( &strm, filters );
|
||||
else
|
||||
res = lzma_stream_decoder( &strm, UINT64_MAX, 0 );
|
||||
|
||||
if( res != LZMA_OK )
|
||||
break;
|
||||
|
||||
while ( res != LZMA_STREAM_END )
|
||||
{
|
||||
strm.next_out = reinterpret_cast< uint8_t * >( buf );
|
||||
strm.avail_out = BUFSIZE;
|
||||
res = lzma_code( &strm, LZMA_RUN );
|
||||
str.append( buf, BUFSIZE - strm.avail_out );
|
||||
if( res != LZMA_OK && res != LZMA_STREAM_END )
|
||||
break;
|
||||
}
|
||||
lzma_end( &strm );
|
||||
if( res != LZMA_STREAM_END )
|
||||
str.clear();
|
||||
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef __DECOMPRESS_HH_INCLUDED__
|
||||
#define __DECOMPRESS_HH_INCLUDED__
|
||||
|
||||
#include <QByteArray>
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
QByteArray zlibDecompress( const char * bufptr, unsigned length );
|
||||
|
||||
string decompressZlib( const char * bufptr, unsigned length );
|
||||
|
||||
string decompressBzip2( const char * bufptr, unsigned length );
|
||||
|
||||
#ifdef MAKE_ZIM_SUPPORT
|
||||
|
||||
string decompressLzma2( const char * bufptr, unsigned length,
|
||||
bool raw_decoder = false );
|
||||
|
||||
#endif
|
||||
|
||||
#endif // DECOMPRESS_HH
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <QStyleOptionViewItemV4>
|
||||
|
||||
#include "delegate.hh"
|
||||
|
||||
WordListItemDelegate::WordListItemDelegate( QAbstractItemDelegate * delegate ) :
|
||||
QStyledItemDelegate()
|
||||
{
|
||||
mainDelegate = static_cast< QStyledItemDelegate * >( delegate );
|
||||
}
|
||||
|
||||
void WordListItemDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
|
||||
{
|
||||
QStyleOptionViewItemV4 opt4 = option;
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption( &opt4, index );
|
||||
if( opt4.text.isRightToLeft() )
|
||||
{
|
||||
opt.direction = Qt::RightToLeft;
|
||||
if( opt4.textElideMode != Qt::ElideNone )
|
||||
opt.textElideMode = Qt::ElideLeft;
|
||||
}
|
||||
else
|
||||
{
|
||||
opt.direction = Qt::LeftToRight;
|
||||
if( opt4.textElideMode != Qt::ElideNone )
|
||||
opt.textElideMode = Qt::ElideRight;
|
||||
}
|
||||
mainDelegate->paint( painter, opt, index );
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef __DELEGATE_HH_INCLUDED__
|
||||
#define __DELEGATE_HH_INCLUDED__
|
||||
|
||||
#include <QAbstractItemDelegate>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class WordListItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
WordListItemDelegate( QAbstractItemDelegate * delegate );
|
||||
virtual void paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
|
||||
|
||||
private:
|
||||
QStyledItemDelegate * mainDelegate;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,751 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "dictdfiles.hh"
|
||||
#include "btreeidx.hh"
|
||||
#include "folding.hh"
|
||||
#include "utf8.hh"
|
||||
#include "dictzip.h"
|
||||
#include "htmlescape.hh"
|
||||
#include "fsencoding.hh"
|
||||
#include "langcoder.hh"
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <wctype.h>
|
||||
#include <stdlib.h>
|
||||
#include "gddebug.hh"
|
||||
#include "ftshelpers.hh"
|
||||
#include <QUrl>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <stub_msvc.h>
|
||||
#endif
|
||||
|
||||
namespace DictdFiles {
|
||||
|
||||
using std::map;
|
||||
using std::multimap;
|
||||
using std::pair;
|
||||
using std::set;
|
||||
using std::string;
|
||||
using gd::wstring;
|
||||
using std::vector;
|
||||
using std::list;
|
||||
|
||||
using BtreeIndexing::WordArticleLink;
|
||||
using BtreeIndexing::IndexedWords;
|
||||
using BtreeIndexing::IndexInfo;
|
||||
|
||||
namespace {
|
||||
|
||||
DEF_EX_STR( exCantReadFile, "Can't read file", Dictionary::Ex )
|
||||
DEF_EX( exFailedToReadLineFromIndex, "Failed to read line from index file", Dictionary::Ex )
|
||||
DEF_EX( exMalformedIndexFileLine, "Malformed index file line encountered", Dictionary::Ex )
|
||||
DEF_EX( exInvalidBase64, "Invalid base64 sequence encountered", Dictionary::Ex )
|
||||
DEF_EX_STR( exDictzipError, "DICTZIP error", Dictionary::Ex )
|
||||
|
||||
enum
|
||||
{
|
||||
Signature = 0x58444344, // DCDX on little-endian, XDCD on big-endian
|
||||
CurrentFormatVersion = 5 + BtreeIndexing::FormatVersion + Folding::Version
|
||||
};
|
||||
|
||||
struct IdxHeader
|
||||
{
|
||||
uint32_t signature; // First comes the signature, DCDX
|
||||
uint32_t formatVersion; // File format version (CurrentFormatVersion)
|
||||
uint32_t wordCount; // Total number of words
|
||||
uint32_t articleCount; // Total number of articles
|
||||
uint32_t indexBtreeMaxElements; // Two fields from IndexInfo
|
||||
uint32_t indexRootOffset;
|
||||
uint32_t langFrom; // Source language
|
||||
uint32_t langTo; // Target language
|
||||
}
|
||||
#ifndef _MSC_VER
|
||||
__attribute__((packed))
|
||||
#endif
|
||||
;
|
||||
|
||||
bool indexIsOldOrBad( string const & indexFile )
|
||||
{
|
||||
File::Class idx( indexFile, "rb" );
|
||||
|
||||
IdxHeader header;
|
||||
|
||||
return idx.readRecords( &header, sizeof( header ), 1 ) != 1 ||
|
||||
header.signature != Signature ||
|
||||
header.formatVersion != CurrentFormatVersion;
|
||||
}
|
||||
|
||||
class DictdDictionary: public BtreeIndexing::BtreeDictionary
|
||||
{
|
||||
Mutex idxMutex;
|
||||
File::Class idx, indexFile; // The later is .index file
|
||||
IdxHeader idxHeader;
|
||||
dictData * dz;
|
||||
string dictionaryName;
|
||||
Mutex indexFileMutex, dzMutex;
|
||||
|
||||
public:
|
||||
|
||||
DictdDictionary( string const & id, string const & indexFile,
|
||||
vector< string > const & dictionaryFiles );
|
||||
|
||||
~DictdDictionary();
|
||||
|
||||
virtual string getName() throw()
|
||||
{ return dictionaryName; }
|
||||
|
||||
virtual map< Dictionary::Property, string > getProperties() throw()
|
||||
{ return map< Dictionary::Property, string >(); }
|
||||
|
||||
virtual unsigned long getArticleCount() throw()
|
||||
{ return idxHeader.articleCount; }
|
||||
|
||||
virtual unsigned long getWordCount() throw()
|
||||
{ return idxHeader.wordCount; }
|
||||
|
||||
virtual void loadIcon() throw();
|
||||
|
||||
inline virtual quint32 getLangFrom() const
|
||||
{ return idxHeader.langFrom; }
|
||||
|
||||
inline virtual quint32 getLangTo() const
|
||||
{ return idxHeader.langTo; }
|
||||
|
||||
virtual sptr< Dictionary::DataRequest > getArticle( wstring const &,
|
||||
vector< wstring > const & alts,
|
||||
wstring const & )
|
||||
throw( std::exception );
|
||||
|
||||
virtual QString const& getDescription();
|
||||
|
||||
virtual sptr< Dictionary::DataRequest > getSearchResults( QString const & searchString,
|
||||
int searchMode, bool matchCase,
|
||||
int distanceBetweenWords,
|
||||
int maxResults );
|
||||
void getArticleText( uint32_t articleAddress, QString & headword, QString & text );
|
||||
|
||||
virtual void makeFTSIndex(QAtomicInt & isCancelled, bool firstIteration );
|
||||
|
||||
virtual void setFTSParameters( Config::FullTextSearch const & fts )
|
||||
{
|
||||
can_FTS = fts.enabled
|
||||
&& !fts.disabledTypes.contains( "DICTD", Qt::CaseInsensitive )
|
||||
&& ( fts.maxDictionarySize == 0 || getArticleCount() <= fts.maxDictionarySize );
|
||||
}
|
||||
};
|
||||
|
||||
DictdDictionary::DictdDictionary( string const & id,
|
||||
string const & indexFile,
|
||||
vector< string > const & dictionaryFiles ):
|
||||
BtreeDictionary( id, dictionaryFiles ),
|
||||
idx( indexFile, "rb" ),
|
||||
indexFile( dictionaryFiles[ 0 ], "rb" ),
|
||||
idxHeader( idx.read< IdxHeader >() )
|
||||
{
|
||||
|
||||
// Read the dictionary name
|
||||
idx.seek( sizeof( idxHeader ) );
|
||||
|
||||
vector< char > dName( idx.read< uint32_t >() );
|
||||
idx.read( &dName.front(), dName.size() );
|
||||
dictionaryName = string( &dName.front(), dName.size() );
|
||||
|
||||
// Open the .dict file
|
||||
|
||||
DZ_ERRORS error;
|
||||
dz = dict_data_open( dictionaryFiles[ 1 ].c_str(), &error, 0 );
|
||||
|
||||
if ( !dz )
|
||||
throw exDictzipError( string( dz_error_str( error ) )
|
||||
+ "(" + getDictionaryFilenames()[ 1 ] + ")" );
|
||||
|
||||
// Initialize the index
|
||||
|
||||
openIndex( IndexInfo( idxHeader.indexBtreeMaxElements,
|
||||
idxHeader.indexRootOffset ),
|
||||
idx, idxMutex );
|
||||
|
||||
// Full-text search parameters
|
||||
|
||||
can_FTS = true;
|
||||
|
||||
ftsIdxName = indexFile + "_FTS";
|
||||
|
||||
if( !Dictionary::needToRebuildIndex( dictionaryFiles, ftsIdxName )
|
||||
&& !FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) )
|
||||
FTS_index_completed.ref();
|
||||
}
|
||||
|
||||
DictdDictionary::~DictdDictionary()
|
||||
{
|
||||
if ( dz )
|
||||
dict_data_close( dz );
|
||||
}
|
||||
|
||||
string nameFromFileName( string const & indexFileName )
|
||||
{
|
||||
if ( indexFileName.empty() )
|
||||
return string();
|
||||
|
||||
char const * sep = strrchr( indexFileName.c_str(), FsEncoding::separator() );
|
||||
|
||||
if ( !sep )
|
||||
sep = indexFileName.c_str();
|
||||
|
||||
char const * dot = strrchr( sep, '.' );
|
||||
|
||||
if ( !dot )
|
||||
dot = indexFileName.c_str() + indexFileName.size();
|
||||
|
||||
return Utf8::encode( FsEncoding::decode( string( sep + 1, dot - sep - 1 ) ) );
|
||||
}
|
||||
|
||||
void DictdDictionary::loadIcon() throw()
|
||||
{
|
||||
if ( dictionaryIconLoaded )
|
||||
return;
|
||||
|
||||
QString fileName =
|
||||
QDir::fromNativeSeparators( FsEncoding::decode( getDictionaryFilenames()[ 0 ].c_str() ) );
|
||||
|
||||
// Remove the extension
|
||||
fileName.chop( 5 );
|
||||
|
||||
if( !loadIconFromFile( fileName ) )
|
||||
{
|
||||
// Load failed -- use default icons
|
||||
dictionaryNativeIcon = dictionaryIcon = QIcon(":/icons/icon32_dictd.png");
|
||||
}
|
||||
|
||||
dictionaryIconLoaded = true;
|
||||
}
|
||||
|
||||
uint32_t decodeBase64( string const & str )
|
||||
{
|
||||
static char const digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
uint32_t number = 0;
|
||||
|
||||
for( char const * next = str.c_str(); *next; ++next )
|
||||
{
|
||||
char const * d = strchr( digits, *next );
|
||||
|
||||
if ( !d )
|
||||
throw exInvalidBase64();
|
||||
|
||||
number = number * 64 + ( d - digits );
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
sptr< Dictionary::DataRequest > DictdDictionary::getArticle( wstring const & word,
|
||||
vector< wstring > const & alts,
|
||||
wstring const & )
|
||||
throw( std::exception )
|
||||
{
|
||||
try
|
||||
{
|
||||
vector< WordArticleLink > chain = findArticles( word );
|
||||
|
||||
for( unsigned x = 0; x < alts.size(); ++x )
|
||||
{
|
||||
/// Make an additional query for each alt
|
||||
|
||||
vector< WordArticleLink > altChain = findArticles( alts[ x ] );
|
||||
|
||||
chain.insert( chain.end(), altChain.begin(), altChain.end() );
|
||||
}
|
||||
|
||||
multimap< wstring, string > mainArticles, alternateArticles;
|
||||
|
||||
set< uint32_t > articlesIncluded; // Some synonyms make it that the articles
|
||||
// appear several times. We combat this
|
||||
// by only allowing them to appear once.
|
||||
|
||||
wstring wordCaseFolded = Folding::applySimpleCaseOnly( word );
|
||||
|
||||
char buf[ 16384 ];
|
||||
|
||||
for( unsigned x = 0; x < chain.size(); ++x )
|
||||
{
|
||||
if ( articlesIncluded.find( chain[ x ].articleOffset ) != articlesIncluded.end() )
|
||||
continue; // We already have this article in the body.
|
||||
|
||||
// Now load that article
|
||||
|
||||
{
|
||||
Mutex::Lock _( indexFileMutex );
|
||||
indexFile.seek( chain[ x ].articleOffset );
|
||||
|
||||
if ( !indexFile.gets( buf, sizeof( buf ), true ) )
|
||||
throw exFailedToReadLineFromIndex();
|
||||
}
|
||||
|
||||
char * tab1 = strchr( buf, '\t' );
|
||||
|
||||
if ( !tab1 )
|
||||
throw exMalformedIndexFileLine();
|
||||
|
||||
char * tab2 = strchr( tab1 + 1, '\t' );
|
||||
|
||||
if ( !tab2 )
|
||||
throw exMalformedIndexFileLine();
|
||||
|
||||
// After tab1 should be article offset, after tab2 -- article size
|
||||
|
||||
uint32_t articleOffset = decodeBase64( string( tab1 + 1, tab2 - tab1 - 1 ) );
|
||||
|
||||
char * tab3 = strchr( tab2 + 1, '\t');
|
||||
|
||||
uint32_t articleSize;
|
||||
if ( tab3 )
|
||||
{
|
||||
articleSize = decodeBase64( string( tab2 + 1, tab3 - tab2 - 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
articleSize = decodeBase64( tab2 + 1 );
|
||||
}
|
||||
|
||||
string articleText;
|
||||
|
||||
char * articleBody;
|
||||
{
|
||||
Mutex::Lock _( dzMutex );
|
||||
articleBody = dict_data_read_( dz, articleOffset, articleSize, 0, 0 );
|
||||
}
|
||||
|
||||
if ( !articleBody )
|
||||
{
|
||||
articleText = string( "<div class=\"dictd_article\">DICTZIP error: " )
|
||||
+ dict_error_str( dz ) + "</div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
static QRegExp phonetic( "\\\\([^\\\\]+)\\\\", Qt::CaseInsensitive ); // phonetics: \stuff\ ...
|
||||
static QRegExp refs( "\\{([^\\{\\}]+)\\}", Qt::CaseInsensitive ); // links: {stuff}
|
||||
static QRegExp links( "<a href=\"gdlookup://localhost/([^\"]*)\">", Qt::CaseInsensitive );
|
||||
static QRegExp tags( "<[^>]*>", Qt::CaseInsensitive );
|
||||
|
||||
articleText = string( "<div class=\"dictd_article\"" );
|
||||
if( isToLanguageRTL() )
|
||||
articleText += " dir=\"rtl\"";
|
||||
articleText += ">";
|
||||
|
||||
string convertedText = Html::preformat( articleBody, isToLanguageRTL() );
|
||||
free( articleBody );
|
||||
|
||||
QString articleString = QString::fromUtf8( convertedText.c_str() )
|
||||
.replace(phonetic, "<span class=\"dictd_phonetic\">\\1</span>")
|
||||
.replace(refs, "<a href=\"gdlookup://localhost/\\1\">\\1</a>");
|
||||
convertedText.erase();
|
||||
|
||||
int pos = 0;
|
||||
for( ; ; )
|
||||
{
|
||||
pos = articleString.indexOf( links, pos );
|
||||
if( pos < 0 )
|
||||
break;
|
||||
|
||||
QString link = links.cap( 1 );
|
||||
link.replace( tags, " " );
|
||||
link.replace( " ", " " );
|
||||
articleString.replace( pos + 30, links.cap( 1 ).length(),
|
||||
QString::fromUtf8( QUrl::toPercentEncoding( link.simplified() ) ) );
|
||||
pos += 30;
|
||||
}
|
||||
|
||||
articleString += "</div>";
|
||||
|
||||
articleText += articleString.toUtf8().data();
|
||||
}
|
||||
|
||||
|
||||
// Ok. Now, does it go to main articles, or to alternate ones? We list
|
||||
// main ones first, and alternates after.
|
||||
|
||||
// We do the case-folded comparison here.
|
||||
|
||||
wstring headwordStripped =
|
||||
Folding::applySimpleCaseOnly( Utf8::decode( chain[ x ].word ) );
|
||||
|
||||
multimap< wstring, string > & mapToUse =
|
||||
( wordCaseFolded == headwordStripped ) ?
|
||||
mainArticles : alternateArticles;
|
||||
|
||||
mapToUse.insert( pair< wstring, string >(
|
||||
Folding::applySimpleCaseOnly( Utf8::decode( chain[ x ].word ) ),
|
||||
articleText ) );
|
||||
|
||||
articlesIncluded.insert( chain[ x ].articleOffset );
|
||||
}
|
||||
|
||||
if ( mainArticles.empty() && alternateArticles.empty() )
|
||||
return new Dictionary::DataRequestInstant( false );
|
||||
|
||||
string result;
|
||||
|
||||
multimap< wstring, string >::const_iterator i;
|
||||
|
||||
for( i = mainArticles.begin(); i != mainArticles.end(); ++i )
|
||||
result += i->second;
|
||||
|
||||
for( i = alternateArticles.begin(); i != alternateArticles.end(); ++i )
|
||||
result += i->second;
|
||||
|
||||
sptr< Dictionary::DataRequestInstant > ret =
|
||||
new Dictionary::DataRequestInstant( true );
|
||||
|
||||
ret->getData().resize( result.size() );
|
||||
|
||||
memcpy( &(ret->getData().front()), result.data(), result.size() );
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch( std::exception & e )
|
||||
{
|
||||
return new Dictionary::DataRequestInstant( QString( e.what() ) );
|
||||
}
|
||||
}
|
||||
|
||||
QString const& DictdDictionary::getDescription()
|
||||
{
|
||||
if( !dictionaryDescription.isEmpty() )
|
||||
return dictionaryDescription;
|
||||
|
||||
sptr< Dictionary::DataRequest > req = getArticle( GD_NATIVE_TO_WS( L"00databaseinfo" ),
|
||||
vector< wstring >(), wstring() );
|
||||
|
||||
if( req->dataSize() > 0 )
|
||||
dictionaryDescription = Html::unescape( QString::fromUtf8( req->getFullData().data(), req->getFullData().size() ), true );
|
||||
else
|
||||
dictionaryDescription = "NONE";
|
||||
|
||||
return dictionaryDescription;
|
||||
}
|
||||
|
||||
void DictdDictionary::makeFTSIndex( QAtomicInt & isCancelled, bool firstIteration )
|
||||
{
|
||||
if( !( Dictionary::needToRebuildIndex( getDictionaryFilenames(), ftsIdxName )
|
||||
|| FtsHelpers::ftsIndexIsOldOrBad( ftsIdxName, this ) ) )
|
||||
FTS_index_completed.ref();
|
||||
|
||||
if( haveFTSIndex() )
|
||||
return;
|
||||
|
||||
if( ensureInitDone().size() )
|
||||
return;
|
||||
|
||||
if( firstIteration && getArticleCount() > FTS::MaxDictionarySizeForFastSearch )
|
||||
return;
|
||||
|
||||
gdDebug( "DictD: Building the full-text index for dictionary: %s\n",
|
||||
getName().c_str() );
|
||||
|
||||
try
|
||||
{
|
||||
FtsHelpers::makeFTSIndex( this, isCancelled );
|
||||
FTS_index_completed.ref();
|
||||
}
|
||||
catch( std::exception &ex )
|
||||
{
|
||||
gdWarning( "DictD: Failed building full-text search index for \"%s\", reason: %s\n", getName().c_str(), ex.what() );
|
||||
QFile::remove( FsEncoding::decode( ftsIdxName.c_str() ) );
|
||||
}
|
||||
}
|
||||
|
||||
void DictdDictionary::getArticleText( uint32_t articleAddress, QString & headword, QString & text )
|
||||
{
|
||||
try
|
||||
{
|
||||
char buf[ 16384 ];
|
||||
{
|
||||
Mutex::Lock _( indexFileMutex );
|
||||
indexFile.seek( articleAddress );
|
||||
|
||||
if ( !indexFile.gets( buf, sizeof( buf ), true ) )
|
||||
throw exFailedToReadLineFromIndex();
|
||||
}
|
||||
|
||||
char * tab1 = strchr( buf, '\t' );
|
||||
|
||||
if ( !tab1 )
|
||||
throw exMalformedIndexFileLine();
|
||||
|
||||
headword = QString::fromUtf8( buf, tab1 - buf );
|
||||
|
||||
char * tab2 = strchr( tab1 + 1, '\t' );
|
||||
|
||||
if ( !tab2 )
|
||||
throw exMalformedIndexFileLine();
|
||||
|
||||
// After tab1 should be article offset, after tab2 -- article size
|
||||
|
||||
uint32_t articleOffset = decodeBase64( string( tab1 + 1, tab2 - tab1 - 1 ) );
|
||||
|
||||
char * tab3 = strchr( tab2 + 1, '\t');
|
||||
|
||||
uint32_t articleSize;
|
||||
if ( tab3 )
|
||||
{
|
||||
articleSize = decodeBase64( string( tab2 + 1, tab3 - tab2 - 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
articleSize = decodeBase64( tab2 + 1 );
|
||||
}
|
||||
|
||||
string articleText;
|
||||
|
||||
char * articleBody;
|
||||
{
|
||||
Mutex::Lock _( dzMutex );
|
||||
articleBody = dict_data_read_( dz, articleOffset, articleSize, 0, 0 );
|
||||
}
|
||||
|
||||
if ( !articleBody )
|
||||
{
|
||||
articleText = dict_error_str( dz );
|
||||
}
|
||||
else
|
||||
{
|
||||
static QRegExp phonetic( "\\\\([^\\\\]+)\\\\", Qt::CaseInsensitive ); // phonetics: \stuff\ ...
|
||||
static QRegExp refs( "\\{([^\\{\\}]+)\\}", Qt::CaseInsensitive ); // links: {stuff}
|
||||
|
||||
string convertedText = Html::preformat( articleBody, isToLanguageRTL() );
|
||||
free( articleBody );
|
||||
|
||||
text = QString::fromUtf8( convertedText.data(), convertedText.size() )
|
||||
.replace(phonetic, "<span class=\"dictd_phonetic\">\\1</span>")
|
||||
.replace(refs, "<a href=\"gdlookup://localhost/\\1\">\\1</a>");
|
||||
|
||||
text = Html::unescape( text );
|
||||
}
|
||||
}
|
||||
catch( std::exception &ex )
|
||||
{
|
||||
gdWarning( "DictD: Failed retrieving article from \"%s\", reason: %s\n", getName().c_str(), ex.what() );
|
||||
}
|
||||
}
|
||||
|
||||
sptr< Dictionary::DataRequest > DictdDictionary::getSearchResults( QString const & searchString,
|
||||
int searchMode, bool matchCase,
|
||||
int distanceBetweenWords,
|
||||
int maxResults )
|
||||
{
|
||||
return new FtsHelpers::FTSResultsRequest( *this, searchString,searchMode, matchCase, distanceBetweenWords, maxResults );
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
vector< sptr< Dictionary::Class > > makeDictionaries(
|
||||
vector< string > const & fileNames,
|
||||
string const & indicesDir,
|
||||
Dictionary::Initializing & initializing )
|
||||
throw( std::exception )
|
||||
{
|
||||
vector< sptr< Dictionary::Class > > dictionaries;
|
||||
|
||||
for( vector< string >::const_iterator i = fileNames.begin(); i != fileNames.end();
|
||||
++i )
|
||||
{
|
||||
// Only allow .index suffixes
|
||||
|
||||
if ( i->size() < 6 ||
|
||||
strcasecmp( i->c_str() + ( i->size() - 6 ), ".index" ) != 0 )
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
vector< string > dictFiles( 1, *i );
|
||||
|
||||
// Check if there is an 'abrv' file present
|
||||
string baseName( *i, 0, i->size() - 5 );
|
||||
|
||||
dictFiles.push_back( string() );
|
||||
|
||||
if ( !File::tryPossibleName( baseName + "dict", dictFiles[ 1 ] ) &&
|
||||
!File::tryPossibleName( baseName + "dict.dz", dictFiles[ 1 ] ) )
|
||||
{
|
||||
// No corresponding .dict file, skipping
|
||||
continue;
|
||||
}
|
||||
|
||||
string dictId = Dictionary::makeDictionaryId( dictFiles );
|
||||
|
||||
string indexFile = indicesDir + dictId;
|
||||
|
||||
if ( Dictionary::needToRebuildIndex( dictFiles, indexFile ) ||
|
||||
indexIsOldOrBad( indexFile ) )
|
||||
{
|
||||
// Building the index
|
||||
string dictionaryName = nameFromFileName( dictFiles[ 0 ] );
|
||||
|
||||
gdDebug( "DictD: Building the index for dictionary: %s\n", dictionaryName.c_str() );
|
||||
|
||||
initializing.indexingDictionary( dictionaryName );
|
||||
|
||||
File::Class idx( indexFile, "wb" );
|
||||
|
||||
IdxHeader idxHeader;
|
||||
|
||||
memset( &idxHeader, 0, sizeof( idxHeader ) );
|
||||
|
||||
// We write a dummy header first. At the end of the process the header
|
||||
// will be rewritten with the right values.
|
||||
|
||||
idx.write( idxHeader );
|
||||
|
||||
IndexedWords indexedWords;
|
||||
|
||||
File::Class indexFile( dictFiles[ 0 ], "rb" );
|
||||
|
||||
// Read words from index until none's left.
|
||||
|
||||
char buf[ 16384 ];
|
||||
|
||||
do
|
||||
{
|
||||
uint32_t curOffset = indexFile.tell();
|
||||
|
||||
if ( !indexFile.gets( buf, sizeof( buf ), true ) )
|
||||
break;
|
||||
|
||||
// Check that there are exactly two or three tabs in the record.
|
||||
char * tab1 = strchr( buf, '\t' );
|
||||
if ( tab1 )
|
||||
{
|
||||
char * tab2 = strchr( tab1 + 1, '\t' );
|
||||
if ( tab2 )
|
||||
{
|
||||
char * tab3 = strchr( tab2 + 1, '\t');
|
||||
if ( tab3 )
|
||||
{
|
||||
char * tab4 = strchr( tab3 + 1, '\t');
|
||||
if ( tab4 )
|
||||
{
|
||||
GD_DPRINTF( "Warning: too many tabs present, skipping: %s\n", buf );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle the forth entry, if it exists. From dictfmt man:
|
||||
// When --index-keep-orig option is used fourth column is created
|
||||
// (if necessary) in .index file.
|
||||
indexedWords.addWord( Utf8::decode( string( tab3 + 1, strlen ( tab3 + 1 ) ) ), curOffset );
|
||||
++idxHeader.wordCount;
|
||||
}
|
||||
indexedWords.addWord( Utf8::decode( string( buf, strchr( buf, '\t' ) - buf ) ), curOffset );
|
||||
++idxHeader.wordCount;
|
||||
++idxHeader.articleCount;
|
||||
|
||||
// Check for proper dictionary name
|
||||
if ( !strncmp( buf, "00databaseshort", 15 ) || !strncmp( buf, "00-database-short", 17 ) )
|
||||
{
|
||||
// After tab1 should be article offset, after tab2 -- article size
|
||||
uint32_t articleOffset = decodeBase64( string( tab1 + 1, tab2 - tab1 - 1 ) );
|
||||
uint32_t articleSize = decodeBase64( tab2 + 1 );
|
||||
|
||||
DZ_ERRORS error;
|
||||
dictData * dz = dict_data_open( dictFiles[ 1 ].c_str(), &error, 0 );
|
||||
|
||||
if ( dz )
|
||||
{
|
||||
char * articleBody = dict_data_read_( dz, articleOffset, articleSize, 0, 0 );
|
||||
if ( articleBody )
|
||||
{
|
||||
char * eol = strchr( articleBody, '\n' ); // skip the first line (headword itself)
|
||||
if ( eol )
|
||||
{
|
||||
while( *eol && Utf8::isspace( *eol ) ) ++eol; // skip spaces
|
||||
|
||||
// use only the single line for the dictionary title
|
||||
char * endEol = strchr( eol, '\n' );
|
||||
if ( endEol )
|
||||
*endEol = 0;
|
||||
|
||||
GD_DPRINTF( "DICT NAME: '%s'\n", eol );
|
||||
dictionaryName = eol;
|
||||
}
|
||||
}
|
||||
dict_data_close( dz );
|
||||
}
|
||||
else
|
||||
throw exDictzipError( string( dz_error_str( error ) )
|
||||
+ "(" + dictFiles[ 1 ] + ")" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GD_DPRINTF( "Warning: only a single tab present, skipping: %s\n", buf );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GD_DPRINTF( "Warning: no tabs present, skipping: %s\n", buf );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
} while( !indexFile.eof() );
|
||||
|
||||
|
||||
// Write dictionary name
|
||||
|
||||
idx.write( (uint32_t) dictionaryName.size() );
|
||||
idx.write( dictionaryName.data(), dictionaryName.size() );
|
||||
|
||||
// Build index
|
||||
|
||||
IndexInfo idxInfo = BtreeIndexing::buildIndex( indexedWords, idx );
|
||||
|
||||
idxHeader.indexBtreeMaxElements = idxInfo.btreeMaxElements;
|
||||
idxHeader.indexRootOffset = idxInfo.rootOffset;
|
||||
|
||||
// That concludes it. Update the header.
|
||||
|
||||
idxHeader.signature = Signature;
|
||||
idxHeader.formatVersion = CurrentFormatVersion;
|
||||
|
||||
// read languages
|
||||
QPair<quint32,quint32> langs =
|
||||
LangCoder::findIdsForFilename( QString::fromStdString( dictFiles[ 0 ] ) );
|
||||
|
||||
// if no languages found, try dictionary's name
|
||||
if ( langs.first == 0 || langs.second == 0 )
|
||||
{
|
||||
langs =
|
||||
LangCoder::findIdsForFilename( QString::fromStdString( nameFromFileName( dictFiles[ 0 ] ) ) );
|
||||
}
|
||||
|
||||
idxHeader.langFrom = langs.first;
|
||||
idxHeader.langTo = langs.second;
|
||||
|
||||
idx.rewind();
|
||||
|
||||
idx.write( &idxHeader, sizeof( idxHeader ) );
|
||||
}
|
||||
|
||||
dictionaries.push_back( new DictdDictionary( dictId,
|
||||
indexFile,
|
||||
dictFiles ) );
|
||||
}
|
||||
catch( std::exception & e )
|
||||
{
|
||||
gdWarning( "Dictd dictionary \"%s\" reading failed, error: %s\n",
|
||||
i->c_str(), e.what() );
|
||||
}
|
||||
}
|
||||
|
||||
return dictionaries;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __DICTDFILES_HH_INCLUDED__
|
||||
#define __DICTDFILES_HH_INCLUDED__
|
||||
|
||||
#include "dictionary.hh"
|
||||
|
||||
/// Support for the dictd (.index/dict.dz) files.
|
||||
namespace DictdFiles {
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
vector< sptr< Dictionary::Class > > makeDictionaries(
|
||||
vector< string > const & fileNames,
|
||||
string const & indicesDir,
|
||||
Dictionary::Initializing & )
|
||||
throw( std::exception );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DictGroupWidget</class>
|
||||
<widget class="QWidget" name="DictGroupWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>403</width>
|
||||
<height>333</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="DictListWidget" name="dictionaries"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Group icon:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="groupIcon"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="resources.qrc">:/icons/hotkeys.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Shortcut:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="HotKeyEdit" name="shortcut"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>DictListWidget</class>
|
||||
<extends>QListWidget</extends>
|
||||
<header>groups_widgets.hh</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>HotKeyEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>hotkeyedit.hh</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,342 @@
|
||||
/* This file is (c) 2014 Abs62
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "dictheadwords.hh"
|
||||
#include "gddebug.hh"
|
||||
#include "mainwindow.hh"
|
||||
|
||||
#include <QRegExp>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QTimer>
|
||||
#include <QProgressDialog>
|
||||
|
||||
#define AUTO_APPLY_LIMIT 150000
|
||||
|
||||
DictHeadwords::DictHeadwords( QWidget *parent, Config::Class & cfg_,
|
||||
Dictionary::Class * dict_ ) :
|
||||
QDialog(parent)
|
||||
, cfg( cfg_ )
|
||||
, dict( dict_ )
|
||||
, helpAction( this )
|
||||
{
|
||||
ui.setupUi( this );
|
||||
|
||||
if( cfg.headwordsDialog.headwordsDialogGeometry.size() > 0 )
|
||||
restoreGeometry( cfg.headwordsDialog.headwordsDialogGeometry );
|
||||
|
||||
bool fromMainWindow = parent->objectName() == "MainWindow";
|
||||
|
||||
if( fromMainWindow )
|
||||
setAttribute( Qt::WA_DeleteOnClose, false );
|
||||
|
||||
setWindowFlags( windowFlags() & ~Qt::WindowContextHelpButtonHint );
|
||||
|
||||
ui.searchModeCombo->addItem( tr( "Text" ), QRegExp::FixedString );
|
||||
ui.searchModeCombo->addItem( tr( "Wildcards" ), QRegExp::WildcardUnix );
|
||||
ui.searchModeCombo->addItem( tr( "RegExp" ), QRegExp::RegExp );
|
||||
ui.searchModeCombo->setCurrentIndex( cfg.headwordsDialog.searchMode );
|
||||
|
||||
ui.exportButton->setAutoDefault( false );
|
||||
ui.OKButton->setAutoDefault( false);
|
||||
ui.applyButton->setAutoDefault( true );
|
||||
ui.applyButton->setDefault( true );
|
||||
|
||||
ui.matchCase->setChecked( cfg.headwordsDialog.matchCase );
|
||||
|
||||
model = new QStringListModel( this );
|
||||
model->setStringList( headers );
|
||||
|
||||
proxy = new QSortFilterProxyModel( this );
|
||||
|
||||
proxy->setSourceModel( model );
|
||||
|
||||
proxy->setSortCaseSensitivity( Qt::CaseInsensitive );
|
||||
proxy->setSortLocaleAware( true );
|
||||
proxy->setDynamicSortFilter( false );
|
||||
|
||||
ui.headersListView->setModel( proxy );
|
||||
ui.headersListView->setEditTriggers( QAbstractItemView::NoEditTriggers );
|
||||
|
||||
// very important call, for performance reasons:
|
||||
ui.headersListView->setUniformItemSizes( true );
|
||||
|
||||
delegate = new WordListItemDelegate( ui.headersListView->itemDelegate() );
|
||||
if( delegate )
|
||||
ui.headersListView->setItemDelegate( delegate );
|
||||
|
||||
ui.autoApply->setChecked( cfg.headwordsDialog.autoApply );
|
||||
|
||||
connect( this, SIGNAL( finished( int ) ), this, SLOT( savePos() ) );
|
||||
|
||||
if( !fromMainWindow )
|
||||
{
|
||||
ui.helpButton->hide();
|
||||
connect( this, SIGNAL( closeDialog() ), this, SLOT( accept() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
connect( ui.helpButton, SIGNAL( clicked() ),
|
||||
this, SLOT( helpRequested() ) );
|
||||
|
||||
helpAction.setShortcut( QKeySequence( "F1" ) );
|
||||
helpAction.setShortcutContext( Qt::WidgetWithChildrenShortcut );
|
||||
|
||||
connect( &helpAction, SIGNAL( triggered() ),
|
||||
this, SLOT( helpRequested() ) );
|
||||
|
||||
addAction( &helpAction );
|
||||
}
|
||||
|
||||
connect( ui.OKButton, SIGNAL( clicked( bool ) ), this, SLOT( okButtonClicked() ) );
|
||||
connect( ui.exportButton, SIGNAL( clicked( bool ) ), this, SLOT( exportButtonClicked() ) );
|
||||
connect( ui.applyButton, SIGNAL( clicked( bool ) ), this, SLOT( filterChanged() ) );
|
||||
|
||||
connect( ui.autoApply, SIGNAL( stateChanged( int ) ),
|
||||
this, SLOT( autoApplyStateChanged( int ) ) );
|
||||
|
||||
connect( ui.filterLine, SIGNAL( textChanged( QString ) ),
|
||||
this, SLOT( filterChangedInternal() ) );
|
||||
connect( ui.searchModeCombo, SIGNAL( currentIndexChanged( int ) ),
|
||||
this, SLOT( filterChangedInternal() ) );
|
||||
connect( ui.matchCase, SIGNAL( stateChanged( int ) ),
|
||||
this, SLOT( filterChangedInternal() ) );
|
||||
|
||||
connect( ui.headersListView, SIGNAL( clicked( QModelIndex ) ),
|
||||
this, SLOT( itemClicked( QModelIndex ) ) );
|
||||
|
||||
connect( proxy, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ),
|
||||
this, SLOT( showHeadwordsNumber() ) );
|
||||
|
||||
ui.headersListView->installEventFilter( this );
|
||||
|
||||
setup( dict_ );
|
||||
}
|
||||
|
||||
DictHeadwords::~DictHeadwords()
|
||||
{
|
||||
if( delegate )
|
||||
delete delegate;
|
||||
}
|
||||
|
||||
void DictHeadwords::setup( Dictionary::Class *dict_ )
|
||||
{
|
||||
QApplication::setOverrideCursor( Qt::WaitCursor );
|
||||
|
||||
dict = dict_;
|
||||
|
||||
setWindowTitle( QString::fromUtf8( dict->getName().c_str() ) );
|
||||
|
||||
headers.clear();
|
||||
model->setStringList( headers );
|
||||
|
||||
dict->getHeadwords( headers );
|
||||
model->setStringList( headers );
|
||||
|
||||
proxy->sort( 0 );
|
||||
filterChanged();
|
||||
|
||||
if( headers.size() > AUTO_APPLY_LIMIT )
|
||||
{
|
||||
cfg.headwordsDialog.autoApply = ui.autoApply->isChecked();
|
||||
ui.autoApply->setChecked( false );
|
||||
ui.autoApply->setEnabled( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
ui.autoApply->setEnabled( true );
|
||||
ui.autoApply->setChecked( cfg.headwordsDialog.autoApply );
|
||||
}
|
||||
|
||||
ui.applyButton->setEnabled( !ui.autoApply->isChecked() );
|
||||
|
||||
setWindowIcon( dict->getIcon() );
|
||||
|
||||
dictId = QString( dict->getId().c_str() );
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
void DictHeadwords::savePos()
|
||||
{
|
||||
cfg.headwordsDialog.searchMode = ui.searchModeCombo->currentIndex();
|
||||
cfg.headwordsDialog.matchCase = ui.matchCase->isChecked();
|
||||
|
||||
if( headers.size() <= AUTO_APPLY_LIMIT )
|
||||
cfg.headwordsDialog.autoApply = ui.autoApply->isChecked();
|
||||
|
||||
cfg.headwordsDialog.headwordsDialogGeometry = saveGeometry();
|
||||
}
|
||||
|
||||
bool DictHeadwords::eventFilter( QObject * obj, QEvent * ev )
|
||||
{
|
||||
if( obj == ui.headersListView && ev->type() == QEvent::KeyPress )
|
||||
{
|
||||
QKeyEvent * kev = static_cast< QKeyEvent * >( ev );
|
||||
if( kev->key() == Qt::Key_Return || kev->key() == Qt::Key_Enter )
|
||||
{
|
||||
itemClicked( ui.headersListView->currentIndex() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QDialog::eventFilter( obj, ev );
|
||||
}
|
||||
|
||||
void DictHeadwords::okButtonClicked()
|
||||
{
|
||||
savePos();
|
||||
closeDialog();
|
||||
}
|
||||
|
||||
void DictHeadwords::reject()
|
||||
{
|
||||
savePos();
|
||||
closeDialog();
|
||||
}
|
||||
|
||||
void DictHeadwords::exportButtonClicked()
|
||||
{
|
||||
saveHeadersToFile();
|
||||
}
|
||||
|
||||
void DictHeadwords::filterChangedInternal()
|
||||
{
|
||||
// emit signal in async manner, to avoid UI slowdown
|
||||
if( ui.autoApply->isChecked() )
|
||||
QTimer::singleShot( 100, this, SLOT( filterChanged() ) );
|
||||
}
|
||||
|
||||
void DictHeadwords::filterChanged()
|
||||
{
|
||||
QRegExp::PatternSyntax syntax =
|
||||
QRegExp::PatternSyntax( ui.searchModeCombo->itemData(
|
||||
ui.searchModeCombo->currentIndex()).toInt() );
|
||||
|
||||
Qt::CaseSensitivity caseSensitivity = ui.matchCase->isChecked() ? Qt::CaseSensitive
|
||||
: Qt::CaseInsensitive;
|
||||
|
||||
QRegExp regExp( ui.filterLine->text(), caseSensitivity, syntax );
|
||||
|
||||
QApplication::setOverrideCursor( Qt::WaitCursor );
|
||||
|
||||
proxy->setFilterRegExp( regExp );
|
||||
proxy->sort( 0 );
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
showHeadwordsNumber();
|
||||
}
|
||||
|
||||
void DictHeadwords::itemClicked( const QModelIndex & index )
|
||||
{
|
||||
QVariant value = proxy->data( index, Qt::DisplayRole );
|
||||
if ( value.canConvert< QString >() )
|
||||
{
|
||||
QString headword = value.toString();
|
||||
emit headwordSelected( headword, dictId );
|
||||
}
|
||||
}
|
||||
|
||||
void DictHeadwords::autoApplyStateChanged( int state )
|
||||
{
|
||||
ui.applyButton->setEnabled( state == Qt::Unchecked );
|
||||
}
|
||||
|
||||
void DictHeadwords::showHeadwordsNumber()
|
||||
{
|
||||
ui.headersNumber->setText( tr( "Unique headwords total: %1, filtered: %2" )
|
||||
.arg( QString::number( headers.size() ) )
|
||||
.arg( QString::number( proxy->rowCount() ) ) );
|
||||
}
|
||||
|
||||
void DictHeadwords::saveHeadersToFile()
|
||||
{
|
||||
QString exportPath;
|
||||
if( cfg.headwordsDialog.headwordsExportPath.isEmpty() )
|
||||
exportPath = QDir::homePath();
|
||||
else
|
||||
{
|
||||
exportPath = QDir::fromNativeSeparators( cfg.headwordsDialog.headwordsExportPath );
|
||||
if( !QDir( exportPath ).exists() )
|
||||
exportPath = QDir::homePath();
|
||||
}
|
||||
|
||||
QString fileName = QFileDialog::getSaveFileName( this, tr( "Save headwords to file" ),
|
||||
exportPath,
|
||||
tr( "Text files (*.txt);;All files (*.*)" ) );
|
||||
if( fileName.size() == 0)
|
||||
return;
|
||||
|
||||
cfg.headwordsDialog.headwordsExportPath = QDir::toNativeSeparators(
|
||||
QFileInfo( fileName ).absoluteDir().absolutePath() );
|
||||
QFile file( fileName );
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if ( !file.open( QFile::WriteOnly | QIODevice::Text ) )
|
||||
break;
|
||||
|
||||
int headwordsNumber = proxy->rowCount();
|
||||
|
||||
// Setup progress dialog
|
||||
int n = headwordsNumber;
|
||||
int step = 1;
|
||||
while( n > 1000 )
|
||||
{
|
||||
step *= 10;
|
||||
n /= 10;
|
||||
}
|
||||
|
||||
QProgressDialog progress( tr( "Export headwords..."), tr( "Cancel" ),
|
||||
0, n, this );
|
||||
progress.setWindowModality( Qt::WindowModal );
|
||||
|
||||
// Write UTF-8 BOM
|
||||
QByteArray line;
|
||||
line.append( 0xEF ).append( 0xBB ).append( 0xBF );
|
||||
if ( file.write( line ) != line.size() )
|
||||
break;
|
||||
|
||||
// Write headwords
|
||||
|
||||
int i;
|
||||
for( i = 0; i < headwordsNumber; ++i )
|
||||
{
|
||||
if( i % step == 0 )
|
||||
progress.setValue( i / step );
|
||||
|
||||
if( progress.wasCanceled() )
|
||||
break;
|
||||
|
||||
QVariant value = proxy->data( proxy->index( i, 0 ) );
|
||||
if( !value.canConvert< QString >() )
|
||||
continue;
|
||||
|
||||
line = value.toString().toUtf8();
|
||||
|
||||
line.replace( '\n', ' ' );
|
||||
line.replace( '\r', ' ' );
|
||||
|
||||
line += "\n";
|
||||
|
||||
if ( file.write( line ) != line.size() )
|
||||
break;
|
||||
}
|
||||
|
||||
if( i < headwordsNumber && !progress.wasCanceled() )
|
||||
break;
|
||||
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
|
||||
gdWarning( "Headers export error: %s", file.errorString().toUtf8().data() );
|
||||
file.close();
|
||||
}
|
||||
|
||||
void DictHeadwords::helpRequested()
|
||||
{
|
||||
MainWindow * mainWindow = qobject_cast< MainWindow * >( parentWidget() );
|
||||
if( mainWindow )
|
||||
mainWindow->showGDHelpForID( "Dictionary headwords" );
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef __DICTHEADWORDS_H_INCLUDED__
|
||||
#define __DICTHEADWORDS_H_INCLUDED__
|
||||
|
||||
#include <QDialog>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QStringListModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QAction>
|
||||
|
||||
#include "config.hh"
|
||||
#include "ui_dictheadwords.h"
|
||||
#include "dictionary.hh"
|
||||
#include "delegate.hh"
|
||||
#include "helpwindow.hh"
|
||||
|
||||
class DictHeadwords : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DictHeadwords( QWidget * parent, Config::Class & cfg_,
|
||||
Dictionary::Class * dict_ );
|
||||
virtual ~DictHeadwords();
|
||||
|
||||
void setup( Dictionary::Class * dict_ );
|
||||
|
||||
protected:
|
||||
Config::Class & cfg;
|
||||
Dictionary::Class * dict;
|
||||
QStringList headers;
|
||||
QStringListModel * model;
|
||||
QSortFilterProxyModel * proxy;
|
||||
WordListItemDelegate * delegate;
|
||||
QString dictId;
|
||||
|
||||
QAction helpAction;
|
||||
|
||||
void saveHeadersToFile();
|
||||
bool eventFilter( QObject * obj, QEvent * ev );
|
||||
|
||||
private:
|
||||
Ui::DictHeadwords ui;
|
||||
private slots:
|
||||
void savePos();
|
||||
void filterChangedInternal();
|
||||
void filterChanged();
|
||||
void exportButtonClicked();
|
||||
void okButtonClicked();
|
||||
void itemClicked( const QModelIndex & index );
|
||||
void autoApplyStateChanged( int state );
|
||||
void showHeadwordsNumber();
|
||||
virtual void reject();
|
||||
void helpRequested();
|
||||
|
||||
signals:
|
||||
void headwordSelected( QString const &, QString const & );
|
||||
void closeDialog();
|
||||
};
|
||||
|
||||
#endif // __DICTHEADWORDS_H_INCLUDED__
|
||||
@@ -0,0 +1,151 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DictHeadwords</class>
|
||||
<widget class="QDialog" name="DictHeadwords">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>458</width>
|
||||
<height>550</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="3" column="0">
|
||||
<widget class="QListView" name="headersListView"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Search mode</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="searchModeCombo">
|
||||
<property name="toolTip">
|
||||
<string>This element determines how filter string will be interpreted</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="matchCase">
|
||||
<property name="toolTip">
|
||||
<string>If checked on the symbols case will be take in account when filtering</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Match case</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="exportButton">
|
||||
<property name="toolTip">
|
||||
<string>Exports headwords to file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="helpButton">
|
||||
<property name="text">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="OKButton">
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="applyButton">
|
||||
<property name="toolTip">
|
||||
<string>Press this button to apply filter to headwords list</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="autoApply">
|
||||
<property name="toolTip">
|
||||
<string>If checked any filter changes will we immediately applied to headwords list</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Filter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLineEdit" name="filterLine">
|
||||
<property name="toolTip">
|
||||
<string>Filter string (fixed string, wildcards or regular expression)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="headersNumber">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "dictinfo.hh"
|
||||
#include "langcoder.hh"
|
||||
#include "language.hh"
|
||||
#include "fsencoding.hh"
|
||||
#include <QString>
|
||||
|
||||
DictInfo::DictInfo( Config::Class &cfg_, QWidget *parent ) :
|
||||
QDialog( parent),
|
||||
cfg( cfg_)
|
||||
{
|
||||
ui.setupUi( this );
|
||||
if( cfg.dictInfoGeometry.size() > 0 )
|
||||
restoreGeometry( cfg.dictInfoGeometry );
|
||||
connect( this, SIGNAL( finished( int ) ), this, SLOT( savePos( int ) ) );
|
||||
}
|
||||
|
||||
void DictInfo::showInfo( sptr<Dictionary::Class> dict )
|
||||
{
|
||||
setWindowTitle( QString::fromUtf8( dict->getName().data(), dict->getName().size() ) );
|
||||
|
||||
ui.dictionaryTotalArticles->setText( QString::number( dict->getArticleCount() ) );
|
||||
ui.dictionaryTotalWords->setText( QString::number( dict->getWordCount() ) );
|
||||
ui.dictionaryTranslatesFrom->setText( Language::localizedStringForId( dict->getLangFrom() ) );
|
||||
ui.dictionaryTranslatesTo->setText( Language::localizedStringForId( dict->getLangTo() ) );
|
||||
|
||||
ui.openFolder->setVisible( dict->isLocalDictionary() );
|
||||
ui.editDictionary->setVisible( dict->isLocalDictionary() && !dict->getMainFilename().isEmpty() && !cfg.editDictionaryCommandLine.isEmpty());
|
||||
ui.editDictionary->setToolTip(
|
||||
tr( "Edit the dictionary via command:\n%1" ).arg( cfg.editDictionaryCommandLine ) );
|
||||
|
||||
if( dict->getWordCount() == 0 )
|
||||
ui.headwordsButton->setVisible( false );
|
||||
else
|
||||
ui.buttonsLayout->insertSpacerItem( 0, new QSpacerItem( 40, 20, QSizePolicy::Expanding ) );
|
||||
|
||||
std::vector< std::string > const & filenames = dict->getDictionaryFilenames();
|
||||
|
||||
QString filenamesText;
|
||||
|
||||
for( unsigned x = 0; x < filenames.size(); x++ )
|
||||
{
|
||||
filenamesText += FsEncoding::decode( filenames[ x ].c_str() );
|
||||
filenamesText += '\n';
|
||||
}
|
||||
|
||||
ui.dictionaryFileList->setPlainText( filenamesText );
|
||||
|
||||
QString info = dict->getDescription();
|
||||
|
||||
if( !info.isEmpty() && info.compare( "NONE" ) != 0 )
|
||||
ui.infoLabel->setPlainText( info );
|
||||
else
|
||||
ui.infoLabel->clear();
|
||||
|
||||
setWindowIcon( dict->getIcon() );
|
||||
}
|
||||
|
||||
void DictInfo::savePos( int )
|
||||
{
|
||||
cfg.dictInfoGeometry = saveGeometry();
|
||||
}
|
||||
|
||||
void DictInfo::on_editDictionary_clicked()
|
||||
{
|
||||
done( EDIT_DICTIONARY );
|
||||
}
|
||||
|
||||
void DictInfo::on_openFolder_clicked()
|
||||
{
|
||||
done( OPEN_FOLDER );
|
||||
}
|
||||
|
||||
void DictInfo::on_OKButton_clicked()
|
||||
{
|
||||
done( ACCEPTED );
|
||||
}
|
||||
|
||||
void DictInfo::on_headwordsButton_clicked()
|
||||
{
|
||||
done( SHOW_HEADWORDS );
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef DICTINFO_HH
|
||||
#define DICTINFO_HH
|
||||
|
||||
#include <QDialog>
|
||||
#include "ui_dictinfo.h"
|
||||
#include "dictionary.hh"
|
||||
#include "config.hh"
|
||||
|
||||
class DictInfo: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
enum Actions
|
||||
{
|
||||
REJECTED,
|
||||
ACCEPTED,
|
||||
OPEN_FOLDER,
|
||||
EDIT_DICTIONARY,
|
||||
SHOW_HEADWORDS
|
||||
};
|
||||
|
||||
DictInfo( Config::Class &cfg_, QWidget * parent = 0 );
|
||||
void showInfo( sptr< Dictionary::Class > dict );
|
||||
|
||||
private:
|
||||
Ui::DictInfo ui;
|
||||
Config::Class &cfg;
|
||||
private slots:
|
||||
void savePos( int );
|
||||
void on_editDictionary_clicked();
|
||||
void on_openFolder_clicked();
|
||||
void on_OKButton_clicked();
|
||||
void on_headwordsButton_clicked();
|
||||
};
|
||||
|
||||
#endif // DICTINFO_HH
|
||||
@@ -0,0 +1,315 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DictInfo</class>
|
||||
<widget class="QDialog" name="DictInfo">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Total articles:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="dictionaryTotalArticles">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Translates from:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="dictionaryTranslatesFrom">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Total words:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="dictionaryTotalWords">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Translates to:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLabel" name="dictionaryTranslatesTo">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="openFolder">
|
||||
<property name="text">
|
||||
<string>Open folder</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QPushButton" name="editDictionary">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit dictionary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Files comprising this dictionary:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="dictionaryFileList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>70</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>70</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>224</red>
|
||||
<green>223</green>
|
||||
<blue>223</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>224</red>
|
||||
<green>223</green>
|
||||
<blue>223</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>212</red>
|
||||
<green>208</green>
|
||||
<blue>200</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="undoRedoEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="dictionaryDescriptionLabel">
|
||||
<property name="text">
|
||||
<string>Description:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="infoLabel">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>224</red>
|
||||
<green>223</green>
|
||||
<blue>223</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>224</red>
|
||||
<green>223</green>
|
||||
<blue>223</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>212</red>
|
||||
<green>208</green>
|
||||
<blue>200</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="undoRedoEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonsLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="headwordsButton">
|
||||
<property name="toolTip">
|
||||
<string>Show all unique dictionary headwords</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Headwords</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="OKButton">
|
||||
<property name="text">
|
||||
<string notr="true">OK</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,477 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include "dictionary.hh"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
|
||||
// For needToRebuildIndex(), read below
|
||||
#include <QFileInfo>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "config.hh"
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDateTime>
|
||||
#include "fsencoding.hh"
|
||||
#include "langcoder.hh"
|
||||
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QRegExp>
|
||||
|
||||
#include "qt4x5.hh"
|
||||
|
||||
namespace Dictionary {
|
||||
|
||||
bool Request::isFinished()
|
||||
{
|
||||
return Qt4x5::AtomicInt::loadAcquire( isFinishedFlag );
|
||||
}
|
||||
|
||||
void Request::update()
|
||||
{
|
||||
if ( !Qt4x5::AtomicInt::loadAcquire( isFinishedFlag ) )
|
||||
emit updated();
|
||||
}
|
||||
|
||||
void Request::finish()
|
||||
{
|
||||
if ( !Qt4x5::AtomicInt::loadAcquire( isFinishedFlag ) )
|
||||
{
|
||||
isFinishedFlag.ref();
|
||||
|
||||
emit finished();
|
||||
}
|
||||
}
|
||||
|
||||
void Request::setErrorString( QString const & str )
|
||||
{
|
||||
Mutex::Lock _( errorStringMutex );
|
||||
|
||||
errorString = str;
|
||||
}
|
||||
|
||||
QString Request::getErrorString()
|
||||
{
|
||||
Mutex::Lock _( errorStringMutex );
|
||||
|
||||
return errorString;
|
||||
}
|
||||
|
||||
|
||||
///////// WordSearchRequest
|
||||
|
||||
size_t WordSearchRequest::matchesCount()
|
||||
{
|
||||
Mutex::Lock _( dataMutex );
|
||||
|
||||
return matches.size();
|
||||
}
|
||||
|
||||
WordMatch WordSearchRequest::operator [] ( size_t index ) throw( exIndexOutOfRange )
|
||||
{
|
||||
Mutex::Lock _( dataMutex );
|
||||
|
||||
if ( index >= matches.size() )
|
||||
throw exIndexOutOfRange();
|
||||
|
||||
return matches[ index ];
|
||||
}
|
||||
|
||||
vector< WordMatch > & WordSearchRequest::getAllMatches() throw( exRequestUnfinished )
|
||||
{
|
||||
if ( !isFinished() )
|
||||
throw exRequestUnfinished();
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
void WordSearchRequest::addMatch( WordMatch const & match )
|
||||
{
|
||||
unsigned n;
|
||||
for( n = 0; n < matches.size(); n++ )
|
||||
if( matches[ n ].word.compare( match.word ) == 0 )
|
||||
break;
|
||||
|
||||
if( n >= matches.size() )
|
||||
matches.push_back( match );
|
||||
}
|
||||
|
||||
////////////// DataRequest
|
||||
|
||||
long DataRequest::dataSize()
|
||||
{
|
||||
Mutex::Lock _( dataMutex );
|
||||
|
||||
return hasAnyData ? (long)data.size() : -1;
|
||||
}
|
||||
|
||||
void DataRequest::getDataSlice( size_t offset, size_t size, void * buffer )
|
||||
throw( exSliceOutOfRange )
|
||||
{
|
||||
if ( size == 0 )
|
||||
return;
|
||||
|
||||
Mutex::Lock _( dataMutex );
|
||||
|
||||
if ( offset + size > data.size() || !hasAnyData )
|
||||
throw exSliceOutOfRange();
|
||||
|
||||
memcpy( buffer, &data[ offset ], size );
|
||||
}
|
||||
|
||||
vector< char > & DataRequest::getFullData() throw( exRequestUnfinished )
|
||||
{
|
||||
if ( !isFinished() )
|
||||
throw exRequestUnfinished();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
Class::Class( string const & id_, vector< string > const & dictionaryFiles_ ):
|
||||
id( id_ ), dictionaryFiles( dictionaryFiles_ ), dictionaryIconLoaded( false )
|
||||
, can_FTS( false), FTS_index_completed( false )
|
||||
{
|
||||
}
|
||||
|
||||
void Class::deferredInit()
|
||||
{
|
||||
}
|
||||
|
||||
sptr< WordSearchRequest > Class::stemmedMatch( wstring const & /*str*/,
|
||||
unsigned /*minLength*/,
|
||||
unsigned /*maxSuffixVariation*/,
|
||||
unsigned long /*maxResults*/ )
|
||||
throw( std::exception )
|
||||
{
|
||||
return new WordSearchRequestInstant();
|
||||
}
|
||||
|
||||
sptr< WordSearchRequest > Class::findHeadwordsForSynonym( wstring const & )
|
||||
throw( std::exception )
|
||||
{
|
||||
return new WordSearchRequestInstant();
|
||||
}
|
||||
|
||||
vector< wstring > Class::getAlternateWritings( wstring const & )
|
||||
throw()
|
||||
{
|
||||
return vector< wstring >();
|
||||
}
|
||||
|
||||
sptr< DataRequest > Class::getResource( string const & /*name*/ )
|
||||
throw( std::exception )
|
||||
{
|
||||
return new DataRequestInstant( false );
|
||||
}
|
||||
|
||||
sptr< DataRequest > Class::getSearchResults(const QString &, int, bool, int, int )
|
||||
{
|
||||
return new DataRequestInstant( false );
|
||||
}
|
||||
|
||||
QString const& Class::getDescription()
|
||||
{
|
||||
return dictionaryDescription;
|
||||
}
|
||||
|
||||
QString Class::getMainFilename()
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QIcon const & Class::getIcon() throw()
|
||||
{
|
||||
if( !dictionaryIconLoaded )
|
||||
loadIcon();
|
||||
return dictionaryIcon;
|
||||
}
|
||||
|
||||
QIcon const & Class::getNativeIcon() throw()
|
||||
{
|
||||
if( !dictionaryIconLoaded )
|
||||
loadIcon();
|
||||
return dictionaryNativeIcon;
|
||||
}
|
||||
|
||||
void Class::loadIcon() throw()
|
||||
{
|
||||
dictionaryIconLoaded = true;
|
||||
}
|
||||
|
||||
bool Class::loadIconFromFile( QString const & _filename, bool isFullName )
|
||||
{
|
||||
QFileInfo info;
|
||||
QString fileName( _filename );
|
||||
|
||||
if( isFullName )
|
||||
info = QFileInfo( fileName );
|
||||
else
|
||||
{
|
||||
fileName += "bmp";
|
||||
info = QFileInfo( fileName );
|
||||
if ( !info.isFile() )
|
||||
{
|
||||
fileName.chop( 3 );
|
||||
fileName += "png";
|
||||
info = QFileInfo( fileName );
|
||||
}
|
||||
if ( !info.isFile() )
|
||||
{
|
||||
fileName.chop( 3 );
|
||||
fileName += "jpg";
|
||||
info = QFileInfo( fileName );
|
||||
}
|
||||
if ( !info.isFile() )
|
||||
{
|
||||
fileName.chop( 3 );
|
||||
fileName += "ico";
|
||||
info = QFileInfo( fileName );
|
||||
}
|
||||
}
|
||||
|
||||
if ( info.isFile() )
|
||||
{
|
||||
QImage img( fileName );
|
||||
|
||||
if ( !img.isNull() )
|
||||
{
|
||||
// Load successful
|
||||
|
||||
// Apply the color key
|
||||
|
||||
img.setAlphaChannel( img.createMaskFromColor( QColor( 192, 192, 192 ).rgb(),
|
||||
Qt::MaskOutColor ) );
|
||||
|
||||
dictionaryNativeIcon = QIcon( QPixmap::fromImage( img ) );
|
||||
|
||||
// Transform it to be square
|
||||
int max = img.width() > img.height() ? img.width() : img.height();
|
||||
|
||||
QImage result( max, max, QImage::Format_ARGB32 );
|
||||
result.fill( 0 ); // Black transparent
|
||||
|
||||
QPainter painter( &result );
|
||||
|
||||
painter.drawImage( QPoint( img.width() == max ? 0 : ( max - img.width() ) / 2,
|
||||
img.height() == max ? 0 : ( max - img.height() ) / 2 ),
|
||||
img );
|
||||
|
||||
painter.end();
|
||||
|
||||
dictionaryIcon = QIcon( QPixmap::fromImage( result ) );
|
||||
|
||||
return !dictionaryIcon.isNull();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Class::isolateCSS( QString & css, QString const & wrapperSelector )
|
||||
{
|
||||
if( css.isEmpty() )
|
||||
return;
|
||||
|
||||
int currentPos = 0;
|
||||
QString newCSS;
|
||||
QString prefix( "#gdfrom-" );
|
||||
prefix += QString::fromLatin1( getId().c_str() );
|
||||
if ( !wrapperSelector.isEmpty() )
|
||||
prefix += " " + wrapperSelector;
|
||||
|
||||
// Strip comments
|
||||
css.replace( QRegExp( "\\/\\*[^*]*\\*+([^/][^*]*\\*+)*\\/" ), QString() );
|
||||
|
||||
for( ; ; )
|
||||
{
|
||||
if( currentPos >= css.length() )
|
||||
break;
|
||||
QChar ch = css[ currentPos ];
|
||||
|
||||
if( ch == '@' )
|
||||
{
|
||||
// @ rules
|
||||
|
||||
int n = currentPos;
|
||||
if( css.mid( currentPos, 7 ).compare( "@import", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
// Copy rule as is.
|
||||
n = css.indexOf( ';', currentPos );
|
||||
int n2 = css.indexOf( '{', currentPos );
|
||||
if( n2 > 0 && n > n2 )
|
||||
n = n2 - 1;
|
||||
}
|
||||
else
|
||||
if( css.mid( currentPos, 6 ).compare( "@media", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
// We must to parse it content to isolate it.
|
||||
// Copy all up to '{' and continue parse inside.
|
||||
n = css.indexOf( '{', currentPos );
|
||||
}
|
||||
else
|
||||
if( css.mid( currentPos, 5 ).compare( "@page", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
// Don't copy rule. GD use own page layout.
|
||||
n = css.indexOf( '}', currentPos );
|
||||
if( n < 0 )
|
||||
break;
|
||||
currentPos = n + 1;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy rule as is.
|
||||
n = css.indexOf( '}', currentPos );
|
||||
}
|
||||
|
||||
newCSS.append( css.mid( currentPos, n < 0 ? n : n - currentPos + 1 ) );
|
||||
|
||||
if( n < 0 )
|
||||
break;
|
||||
|
||||
currentPos = n + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ch == '{' )
|
||||
{
|
||||
// Selector declaration block.
|
||||
// We copy it up to '}' as is.
|
||||
|
||||
int n = css.indexOf( '}', currentPos );
|
||||
newCSS.append( css.mid( currentPos, n == -1 ? n : n - currentPos + 1 ) );
|
||||
if( n < 0 )
|
||||
break;
|
||||
currentPos = n + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ch.isLetter() || ch == '.' || ch == '#' || ch == '*' || ch == '\\' )
|
||||
{
|
||||
// This is some selector.
|
||||
// We must to add the isolate prefix to it.
|
||||
|
||||
int n = css.indexOf( QRegExp( "[ \\*\\>\\+,;:\\[\\{\\]]" ), currentPos + 1 );
|
||||
QString s = css.mid( currentPos, n < 0 ? n : n - currentPos );
|
||||
if( n < 0 )
|
||||
{
|
||||
newCSS.append( s );
|
||||
break;
|
||||
}
|
||||
QString trimmed = s.trimmed();
|
||||
if( trimmed.compare( "body", Qt::CaseInsensitive ) == 0
|
||||
|| trimmed.compare( "html", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
newCSS.append( s + " " + prefix + " " );
|
||||
currentPos += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
newCSS.append( prefix + " " );
|
||||
}
|
||||
|
||||
n = css.indexOf( QRegExp( "[,;\\{]" ), currentPos );
|
||||
s = css.mid( currentPos, n < 0 ? n : n - currentPos );
|
||||
newCSS.append( s );
|
||||
if( n < 0 )
|
||||
break;
|
||||
currentPos = n;
|
||||
continue;
|
||||
}
|
||||
|
||||
newCSS.append( ch );
|
||||
++currentPos;
|
||||
}
|
||||
css = newCSS;
|
||||
}
|
||||
|
||||
string makeDictionaryId( vector< string > const & dictionaryFiles ) throw()
|
||||
{
|
||||
std::vector< string > sortedList;
|
||||
|
||||
if ( Config::isPortableVersion() )
|
||||
{
|
||||
// For portable version, we use relative paths
|
||||
sortedList.reserve( dictionaryFiles.size() );
|
||||
|
||||
QDir dictionariesDir( Config::getPortableVersionDictionaryDir() );
|
||||
|
||||
for( unsigned x = 0; x < dictionaryFiles.size(); ++x )
|
||||
{
|
||||
string const & full( dictionaryFiles[ x ] );
|
||||
|
||||
QFileInfo fileInfo( FsEncoding::decode( full.c_str() ) );
|
||||
|
||||
if ( fileInfo.isAbsolute() )
|
||||
sortedList.push_back( FsEncoding::encode( dictionariesDir.relativeFilePath( fileInfo.filePath() ) ) );
|
||||
else
|
||||
{
|
||||
// Well, it's relative. We don't technically support those, but
|
||||
// what the heck
|
||||
sortedList.push_back( full );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
sortedList = dictionaryFiles;
|
||||
|
||||
std::sort( sortedList.begin(), sortedList.end() );
|
||||
|
||||
QCryptographicHash hash( QCryptographicHash::Md5 );
|
||||
|
||||
for( std::vector< string >::const_iterator i = sortedList.begin();
|
||||
i != sortedList.end(); ++i )
|
||||
hash.addData( i->c_str(), i->size() + 1 );
|
||||
|
||||
return hash.result().toHex().data();
|
||||
}
|
||||
|
||||
// While this file is not supposed to have any Qt stuff since it's used by
|
||||
// the dictionary backends, there's no platform-independent way to get hold
|
||||
// of a timestamp of the file, so we use here Qt anyway. It is supposed to
|
||||
// be fixed in the future when it's needed.
|
||||
bool needToRebuildIndex( vector< string > const & dictionaryFiles,
|
||||
string const & indexFile ) throw()
|
||||
{
|
||||
unsigned long lastModified = 0;
|
||||
|
||||
for( std::vector< string >::const_iterator i = dictionaryFiles.begin();
|
||||
i != dictionaryFiles.end(); ++i )
|
||||
{
|
||||
QFileInfo fileInfo( FsEncoding::decode( i->c_str() ) );
|
||||
|
||||
if( fileInfo.isDir() )
|
||||
continue;
|
||||
|
||||
if ( !fileInfo.exists() )
|
||||
return true;
|
||||
|
||||
unsigned long ts = fileInfo.lastModified().toTime_t();
|
||||
|
||||
if ( ts > lastModified )
|
||||
lastModified = ts;
|
||||
}
|
||||
|
||||
QFileInfo fileInfo( FsEncoding::decode( indexFile.c_str() ) );
|
||||
|
||||
if ( !fileInfo.exists() )
|
||||
return true;
|
||||
|
||||
return fileInfo.lastModified().toTime_t() < lastModified;
|
||||
}
|
||||
|
||||
QString generateRandomDictionaryId()
|
||||
{
|
||||
return QString(
|
||||
QCryptographicHash::hash(
|
||||
QDateTime::currentDateTime().toString( "\"Random\"dd.MM.yyyy hh:mm:ss.zzz" ).toUtf8(),
|
||||
QCryptographicHash::Md5 ).toHex() );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __DICTIONARY_HH_INCLUDED__
|
||||
#define __DICTIONARY_HH_INCLUDED__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <QObject>
|
||||
#include <QIcon>
|
||||
#include "sptr.hh"
|
||||
#include "ex.hh"
|
||||
#include "mutex.hh"
|
||||
#include "wstring.hh"
|
||||
#include "langcoder.hh"
|
||||
#include "config.hh"
|
||||
#include "qt4x5.hh"
|
||||
|
||||
/// Abstract dictionary-related stuff
|
||||
namespace Dictionary {
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using gd::wstring;
|
||||
using std::map;
|
||||
|
||||
enum Property
|
||||
{
|
||||
Author,
|
||||
Copyright,
|
||||
Description,
|
||||
Email
|
||||
};
|
||||
|
||||
DEF_EX( Ex, "Dictionary error", std::exception )
|
||||
DEF_EX( exIndexOutOfRange, "The supplied index is out of range", Ex )
|
||||
DEF_EX( exSliceOutOfRange, "The requested data slice is out of range", Ex )
|
||||
DEF_EX( exRequestUnfinished, "The request hasn't yet finished", Ex )
|
||||
|
||||
/// When you request a search to be performed in a dictionary, you get
|
||||
/// this structure in return. It accumulates search results over time.
|
||||
/// The finished() signal is emitted when the search has finished and there's
|
||||
/// no more matches to be expected. Note that before connecting to it, check
|
||||
/// the result of isFinished() -- if it's 'true', the search was instantaneous.
|
||||
/// Destroy the object when you are not interested in results anymore.
|
||||
///
|
||||
/// Creating, destroying and calling member functions of the requests is done
|
||||
/// in the GUI thread, however. Therefore, it is important to make sure those
|
||||
/// operations are fast (this is most important for word searches, where
|
||||
/// new requests are created and old ones deleted immediately upon a user
|
||||
/// changing query).
|
||||
class Request: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// Returns whether the request has been processed in full and finished.
|
||||
/// This means that the data accumulated is final and won't change anymore.
|
||||
bool isFinished();
|
||||
|
||||
/// Either returns an empty string in case there was no error processing
|
||||
/// the request, or otherwise a human-readable string describing the problem.
|
||||
/// Note that an empty result, such as a lack of word or of an article isn't
|
||||
/// an error -- but any kind of failure to connect to, or read the dictionary
|
||||
/// is.
|
||||
QString getErrorString();
|
||||
|
||||
/// Cancels the ongoing request. This may make Request destruct faster some
|
||||
/// time in the future, Use this in preparation to destruct many Requests,
|
||||
/// so that they'd be cancelling in parallel. When the request was fully
|
||||
/// cancelled, it must emit the finished() signal, either as a result of an
|
||||
/// actual finish which has happened just before the cancellation, or solely as
|
||||
/// a result of a request being cancelled (in the latter case, the actual
|
||||
/// request result may be empty or incomplete). That is, finish() must be
|
||||
/// called by a derivative at least once if cancel() was called, either after
|
||||
/// or before it was called.
|
||||
virtual void cancel()=0;
|
||||
|
||||
virtual ~Request()
|
||||
{}
|
||||
|
||||
signals:
|
||||
|
||||
/// This signal is emitted when more data becomes available. Local
|
||||
/// dictionaries typically don't call this, since it is preferred that all
|
||||
/// data would be available from them at once, but network dictionaries
|
||||
/// might call that.
|
||||
void updated();
|
||||
|
||||
/// This signal is emitted when the request has been processed in full and
|
||||
/// finished. That is, it's emitted when isFinished() turns true.
|
||||
void finished();
|
||||
|
||||
protected:
|
||||
|
||||
/// Called by derivatives to signal update().
|
||||
void update();
|
||||
|
||||
/// Called by derivatives to set isFinished() flag and signal finished().
|
||||
void finish();
|
||||
|
||||
/// Sets the error string to be returned by getErrorString().
|
||||
void setErrorString( QString const & );
|
||||
|
||||
private:
|
||||
|
||||
QAtomicInt isFinishedFlag;
|
||||
|
||||
Mutex errorStringMutex;
|
||||
QString errorString;
|
||||
};
|
||||
|
||||
/// This structure represents the word found. In addition to holding the
|
||||
/// word itself, it also holds its weight. It is 0 by default. Negative
|
||||
/// values should be used to store distance from Levenstein-like matching
|
||||
/// algorithms. Positive values are used by morphology matches.
|
||||
struct WordMatch
|
||||
{
|
||||
wstring word;
|
||||
int weight;
|
||||
|
||||
WordMatch(): weight( 0 ) {}
|
||||
WordMatch( wstring const & word_ ): word( word_ ), weight( 0 ){}
|
||||
WordMatch( wstring const & word_, int weight_ ): word( word_ ),
|
||||
weight( weight_ ) {}
|
||||
};
|
||||
|
||||
/// This request type corresponds to all types of word searching operations.
|
||||
class WordSearchRequest: public Request
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
WordSearchRequest(): uncertain( false )
|
||||
{}
|
||||
|
||||
/// Returns the number of matches found. The value can grow over time
|
||||
/// unless isFinished() is true.
|
||||
size_t matchesCount();
|
||||
|
||||
/// Returns the match with the given zero-based index, which should be less
|
||||
/// than matchesCount().
|
||||
WordMatch operator [] ( size_t index ) throw( exIndexOutOfRange );
|
||||
|
||||
/// Returns all the matches found. Since no further locking can or would be
|
||||
/// done, this can only be called after the request has finished.
|
||||
vector< WordMatch > & getAllMatches() throw( exRequestUnfinished );
|
||||
|
||||
/// Returns true if the match was uncertain -- that is, there may be more
|
||||
/// results in the dictionary itself, the dictionary index isn't good enough
|
||||
/// to tell that.
|
||||
bool isUncertain() const
|
||||
{ return uncertain; }
|
||||
|
||||
/// Add match if one is not presented in matches list
|
||||
void addMatch( WordMatch const & match );
|
||||
|
||||
protected:
|
||||
|
||||
// Subclasses should be filling up the 'matches' array, locking the mutex when
|
||||
// whey work with it.
|
||||
Mutex dataMutex;
|
||||
|
||||
vector< WordMatch > matches;
|
||||
bool uncertain;
|
||||
};
|
||||
|
||||
/// This request type corresponds to any kinds of data responses where a
|
||||
/// single large blob of binary data is returned. It currently used of article
|
||||
/// bodies and resources.
|
||||
class DataRequest: public Request
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// Returns the number of bytes read, with a -1 meaning that so far it's
|
||||
/// uncertain whether resource even exists or not, and any non-negative value
|
||||
/// meaning that that amount of bytes is not available.
|
||||
/// If -1 is still being returned after the request has finished, that means
|
||||
/// the resource wasn't found.
|
||||
long dataSize();
|
||||
|
||||
/// Writes "size" bytes starting from "offset" of the data read to the given
|
||||
/// buffer. "size + offset" must be <= than dataSize().
|
||||
void getDataSlice( size_t offset, size_t size, void * buffer )
|
||||
throw( exSliceOutOfRange );
|
||||
|
||||
/// Returns all the data read. Since no further locking can or would be
|
||||
/// done, this can only be called after the request has finished.
|
||||
vector< char > & getFullData() throw( exRequestUnfinished );
|
||||
|
||||
DataRequest(): hasAnyData( false ) {}
|
||||
|
||||
protected:
|
||||
|
||||
// Subclasses should be filling up the 'data' array, locking the mutex when
|
||||
// whey work with it.
|
||||
Mutex dataMutex;
|
||||
|
||||
bool hasAnyData; // With this being false, dataSize() always returns -1
|
||||
vector< char > data;
|
||||
};
|
||||
|
||||
/// A helper class for syncronous word search implementations.
|
||||
class WordSearchRequestInstant: public WordSearchRequest
|
||||
{
|
||||
public:
|
||||
|
||||
WordSearchRequestInstant()
|
||||
{ finish(); }
|
||||
|
||||
virtual void cancel()
|
||||
{}
|
||||
|
||||
vector< WordMatch > & getMatches()
|
||||
{ return matches; }
|
||||
|
||||
void setUncertain( bool value )
|
||||
{ uncertain = value; }
|
||||
};
|
||||
|
||||
/// A helper class for syncronous data read implementations.
|
||||
class DataRequestInstant: public DataRequest
|
||||
{
|
||||
public:
|
||||
|
||||
DataRequestInstant( bool succeeded )
|
||||
{ hasAnyData = succeeded; finish(); }
|
||||
|
||||
DataRequestInstant( QString const & errorString )
|
||||
{ setErrorString( errorString ); finish(); }
|
||||
|
||||
virtual void cancel()
|
||||
{}
|
||||
|
||||
vector< char > & getData()
|
||||
{ return data; }
|
||||
};
|
||||
|
||||
/// Dictionary features. Different dictionaries can possess different features,
|
||||
/// which hint at some of their aspects.
|
||||
enum Feature
|
||||
{
|
||||
/// No features
|
||||
NoFeatures = 0,
|
||||
/// The dictionary is suitable to query when searching for compound expressions.
|
||||
SuitableForCompoundSearching = 1
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS( Features, Feature )
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( Features )
|
||||
|
||||
/// A dictionary. Can be used to query words.
|
||||
class Class
|
||||
{
|
||||
string id;
|
||||
vector< string > dictionaryFiles;
|
||||
|
||||
protected:
|
||||
QString dictionaryDescription;
|
||||
QIcon dictionaryIcon, dictionaryNativeIcon;
|
||||
bool dictionaryIconLoaded;
|
||||
bool can_FTS;
|
||||
QAtomicInt FTS_index_completed;
|
||||
|
||||
// Load user icon if it exist
|
||||
// By default set icon to empty
|
||||
virtual void loadIcon() throw();
|
||||
|
||||
// Load icon from filename directly if isFullName == true
|
||||
// else treat filename as name without extension
|
||||
bool loadIconFromFile( QString const & filename, bool isFullName = false );
|
||||
|
||||
/// Make css content usable only for articles from this dictionary
|
||||
void isolateCSS( QString & css, QString const & wrapperSelector = QString() );
|
||||
|
||||
public:
|
||||
|
||||
/// Creates a dictionary. The id should be made using
|
||||
/// Format::makeDictionaryId(), the dictionaryFiles is the file names the
|
||||
/// dictionary consists of.
|
||||
Class( string const & id, vector< string > const & dictionaryFiles );
|
||||
|
||||
/// Called once after the dictionary is constructed. Usually called for each
|
||||
/// dictionaries once all dictionaries were made. The implementation should
|
||||
/// queue any initialization tasks the dictionary decided to postpone to
|
||||
/// threadpools, network requests etc, so the system could complete them
|
||||
/// in background.
|
||||
/// The default implementation does nothing.
|
||||
virtual void deferredInit();
|
||||
|
||||
/// Returns the dictionary's id.
|
||||
string getId() throw()
|
||||
{ return id; }
|
||||
|
||||
/// Returns the list of file names the dictionary consists of.
|
||||
vector< string > const & getDictionaryFilenames() throw()
|
||||
{ return dictionaryFiles; }
|
||||
|
||||
/// Returns the dictionary's full name, utf8.
|
||||
virtual string getName() throw()=0;
|
||||
|
||||
/// Returns all the available properties, like the author's name, copyright,
|
||||
/// description etc. All strings are in utf8.
|
||||
virtual map< Property, string > getProperties() throw()=0;
|
||||
|
||||
/// Returns the features the dictionary possess. See the Feature enum for
|
||||
/// their list.
|
||||
virtual Features getFeatures() const throw()
|
||||
{ return NoFeatures; }
|
||||
|
||||
/// Returns the number of articles in the dictionary.
|
||||
virtual unsigned long getArticleCount() throw()=0;
|
||||
|
||||
/// Returns the number of words in the dictionary. This can be equal to
|
||||
/// the number of articles, or can be larger if some synonyms are present.
|
||||
virtual unsigned long getWordCount() throw()=0;
|
||||
|
||||
/// Returns the dictionary's icon.
|
||||
virtual QIcon const & getIcon() throw();
|
||||
|
||||
/// Returns the dictionary's native icon. Dsl icons are usually rectangular,
|
||||
/// and are adapted by getIcon() to be square. This function allows getting
|
||||
/// the original icon with no geometry transformations applied.
|
||||
virtual QIcon const & getNativeIcon() throw();
|
||||
|
||||
/// Returns the dictionary's source language.
|
||||
virtual quint32 getLangFrom() const
|
||||
{ return 0; }
|
||||
|
||||
/// Returns the dictionary's target language.
|
||||
virtual quint32 getLangTo() const
|
||||
{ return 0; }
|
||||
|
||||
/// Looks up a given word in the dictionary, aiming for exact matches and
|
||||
/// prefix matches. If it's not possible to locate any prefix matches, no
|
||||
/// prefix results should be added. Not more than maxResults results should
|
||||
/// be stored. The whole operation is supposed to be fast, though some
|
||||
/// dictionaries, the network ones particularly, may of course be slow.
|
||||
virtual sptr< WordSearchRequest > prefixMatch( wstring const &,
|
||||
unsigned long maxResults ) throw( std::exception )=0;
|
||||
|
||||
/// Looks up a given word in the dictionary, aiming to find different forms
|
||||
/// of the given word by allowing suffix variations. This means allowing words
|
||||
/// which can be as short as the input word size minus maxSuffixVariation, or as
|
||||
/// long as the input word size plus maxSuffixVariation, which share at least
|
||||
/// the input word size minus maxSuffixVariation initial symbols.
|
||||
/// Since the goal is to find forms of the words, no matches where a word
|
||||
/// in the middle of a phrase got matched should be returned.
|
||||
/// The default implementation does nothing, returning an empty result.
|
||||
virtual sptr< WordSearchRequest > stemmedMatch( wstring const &,
|
||||
unsigned minLength,
|
||||
unsigned maxSuffixVariation,
|
||||
unsigned long maxResults ) throw( std::exception );
|
||||
|
||||
/// Finds known headwords for the given word, that is, the words for which
|
||||
/// the given word is a synonym. If a dictionary can't perform this operation,
|
||||
/// it should leave the default implementation which always returns an empty
|
||||
/// result.
|
||||
virtual sptr< WordSearchRequest > findHeadwordsForSynonym( wstring const & )
|
||||
throw( std::exception );
|
||||
|
||||
/// For a given word, provides alternate writings of it which are to be looked
|
||||
/// up alongside with it. Transliteration dictionaries implement this. The
|
||||
/// default implementation returns an empty list. Note that this function is
|
||||
/// supposed to be very fast and simple, and the results are thus returned
|
||||
/// syncronously.
|
||||
virtual vector< wstring > getAlternateWritings( wstring const & )
|
||||
throw();
|
||||
|
||||
/// Returns a definition for the given word. The definition should
|
||||
/// be an html fragment (without html/head/body tags) in an utf8 encoding.
|
||||
/// The 'alts' vector could contain a list of words the definitions of which
|
||||
/// should be included in the output as well, being treated as additional
|
||||
/// synonyms for the main word.
|
||||
/// context is a dictionary-specific data, currently only used for the
|
||||
/// 'Websites' feature.
|
||||
virtual sptr< DataRequest > getArticle( wstring const &,
|
||||
vector< wstring > const & alts,
|
||||
wstring const & context = wstring() )
|
||||
throw( std::exception )=0;
|
||||
|
||||
/// Loads contents of a resource named 'name' into the 'data' vector. This is
|
||||
/// usually a picture file referenced in the article or something like that.
|
||||
/// The default implementation always returns the non-existing resource
|
||||
/// response.
|
||||
virtual sptr< DataRequest > getResource( string const & /*name*/ )
|
||||
throw( std::exception );
|
||||
|
||||
/// Returns a results of full-text search of given string similar getArticle().
|
||||
virtual sptr< DataRequest > getSearchResults( QString const & searchString,
|
||||
int searchMode, bool matchCase,
|
||||
int distanceBetweenWords,
|
||||
int maxArticlesPerDictionary );
|
||||
|
||||
// Return dictionary description if presented
|
||||
virtual QString const& getDescription();
|
||||
|
||||
// Return dictionary main file name
|
||||
virtual QString getMainFilename();
|
||||
|
||||
/// Check text direction
|
||||
bool isFromLanguageRTL()
|
||||
{ return LangCoder::isLanguageRTL( getLangFrom() ); }
|
||||
bool isToLanguageRTL()
|
||||
{ return LangCoder::isLanguageRTL( getLangTo() ); }
|
||||
|
||||
/// Return true if dictionary is local dictionary
|
||||
virtual bool isLocalDictionary()
|
||||
{ return false; }
|
||||
|
||||
/// Dictionary can full-text search
|
||||
bool canFTS()
|
||||
{ return can_FTS; }
|
||||
|
||||
/// Dictionary have index for full-text search
|
||||
bool haveFTSIndex()
|
||||
{ return Qt4x5::AtomicInt::loadAcquire( FTS_index_completed ) != 0; }
|
||||
|
||||
/// Make index for full-text search
|
||||
virtual void makeFTSIndex( QAtomicInt &, bool )
|
||||
{}
|
||||
|
||||
/// Set full-text search parameters
|
||||
virtual void setFTSParameters( Config::FullTextSearch const & )
|
||||
{}
|
||||
|
||||
/// Retrieve all dictionary headwords
|
||||
virtual bool getHeadwords( QStringList & )
|
||||
{ return false; }
|
||||
|
||||
virtual ~Class()
|
||||
{}
|
||||
};
|
||||
|
||||
/// Callbacks to be used when the dictionaries are being initialized.
|
||||
class Initializing
|
||||
{
|
||||
public:
|
||||
|
||||
/// Called by the Format instance to notify the caller that the given
|
||||
/// dictionary is being indexed. Since indexing can take some time, this
|
||||
/// is useful to show in some kind of a splash screen.
|
||||
/// The dictionaryName is in utf8.
|
||||
virtual void indexingDictionary( string const & dictionaryName ) throw()=0;
|
||||
|
||||
virtual ~Initializing()
|
||||
{}
|
||||
};
|
||||
|
||||
/// Generates an id based on the set of file names which the dictionary
|
||||
/// consists of. The resulting id is an alphanumeric hex value made by
|
||||
/// hashing the file names. This id should be used to identify dictionary
|
||||
/// and for the index file name, if one is needed.
|
||||
/// This function is supposed to be used by dictionary implementations.
|
||||
string makeDictionaryId( vector< string > const & dictionaryFiles ) throw();
|
||||
|
||||
/// Checks if it is needed to regenerate index file based on its timestamp
|
||||
/// and the timestamps of the dictionary files. If some files are newer than
|
||||
/// the index file, or the index file doesn't exist, returns true. If some
|
||||
/// dictionary files don't exist, returns true, too.
|
||||
/// This function is supposed to be used by dictionary implementations.
|
||||
bool needToRebuildIndex( vector< string > const & dictionaryFiles,
|
||||
string const & indexFile ) throw();
|
||||
|
||||
/// Returns a random dictionary id useful for interactively created
|
||||
/// dictionaries.
|
||||
QString generateRandomDictionaryId();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,400 @@
|
||||
#include "dictionarybar.hh"
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QProcess>
|
||||
#include "gddebug.hh"
|
||||
#include "fsencoding.hh"
|
||||
#include <QDebug>
|
||||
|
||||
using std::vector;
|
||||
|
||||
DictionaryBar::DictionaryBar( QWidget * parent,
|
||||
Config::Events & events, QString const & _editDictionaryCommand, unsigned short const & maxDictionaryRefsInContextMenu_ ):
|
||||
QToolBar( tr( "&Dictionary Bar" ), parent ),
|
||||
mutedDictionaries( 0 ),
|
||||
configEvents( events ),
|
||||
editDictionaryCommand( _editDictionaryCommand ),
|
||||
maxDictionaryRefsInContextMenu(maxDictionaryRefsInContextMenu_),
|
||||
use14x21( false ),
|
||||
timerId( 0 )
|
||||
{
|
||||
setObjectName( "dictionaryBar" );
|
||||
|
||||
maxDictionaryRefsAction = new QAction( QIcon(":/icons/expand_opt.png"), tr( "Extended menu with all dictionaries..." ), this );
|
||||
|
||||
connect( &events, SIGNAL( mutedDictionariesChanged() ),
|
||||
this, SLOT( mutedDictionariesChanged() ) );
|
||||
|
||||
connect( this, SIGNAL(actionTriggered(QAction*)),
|
||||
this, SLOT(actionWasTriggered(QAction*)) );
|
||||
|
||||
installEventFilter( this );
|
||||
}
|
||||
|
||||
static QString elideDictName( QString const & name )
|
||||
{
|
||||
// Some names are way too long -- we insert an ellipsis in the middle of those
|
||||
|
||||
int const maxSize = 33;
|
||||
|
||||
if ( name.size() <= maxSize )
|
||||
return name;
|
||||
|
||||
int const pieceSize = maxSize / 2 - 1;
|
||||
|
||||
return name.left( pieceSize ) + QChar( 0x2026 ) + name.right( pieceSize );
|
||||
}
|
||||
|
||||
void DictionaryBar::setDictionaries( vector< sptr< Dictionary::Class > >
|
||||
const & dictionaries )
|
||||
{
|
||||
setUpdatesEnabled( false );
|
||||
|
||||
allDictionaries.clear();
|
||||
allDictionaries = dictionaries;
|
||||
|
||||
clear();
|
||||
dictActions.clear();
|
||||
|
||||
use14x21 = false;
|
||||
|
||||
for( unsigned x = 0; x < dictionaries.size(); ++x )
|
||||
{
|
||||
QIcon icon = dictionaries[ x ]->getNativeIcon();
|
||||
|
||||
QString dictName = QString::fromUtf8( dictionaries[ x ]->
|
||||
getName().c_str() );
|
||||
|
||||
QAction * action = addAction( icon, elideDictName( dictName ) );
|
||||
|
||||
action->setToolTip( dictName ); // Tooltip need not be shortened
|
||||
|
||||
QString id = QString::fromStdString( dictionaries[ x ]->getId() );
|
||||
|
||||
action->setData( id );
|
||||
|
||||
action->setCheckable( true );
|
||||
|
||||
action->setChecked( mutedDictionaries ? !mutedDictionaries->contains( id ) : true );
|
||||
|
||||
QList< QSize > sizes = icon.availableSizes();
|
||||
|
||||
for( QList< QSize >::iterator i = sizes.begin(); i != sizes.end();
|
||||
++i )
|
||||
if ( i->width() == 14 && i->height() == 21 )
|
||||
use14x21 = true;
|
||||
|
||||
dictActions.append( action );
|
||||
}
|
||||
|
||||
setDictionaryIconSize( 21 );
|
||||
|
||||
setUpdatesEnabled( true );
|
||||
}
|
||||
|
||||
void DictionaryBar::setDictionaryIconSize( int extent )
|
||||
{
|
||||
setIconSize( QSize( use14x21 ? extent*2/3 : extent, extent ) );
|
||||
}
|
||||
|
||||
void DictionaryBar::contextMenuEvent( QContextMenuEvent * event )
|
||||
{
|
||||
showContextMenu( event );
|
||||
}
|
||||
|
||||
void DictionaryBar::showContextMenu( QContextMenuEvent * event, bool extended )
|
||||
{
|
||||
QMenu menu( this );
|
||||
|
||||
QAction * editAction =
|
||||
menu.addAction( QIcon( ":/icons/bookcase.png" ), tr( "Edit this group" ) );
|
||||
|
||||
QAction * infoAction = NULL;
|
||||
QAction * headwordsAction = NULL;
|
||||
QAction * editDictAction = NULL;
|
||||
QAction * openDictFolderAction = NULL;
|
||||
QString dictFilename;
|
||||
|
||||
QAction * dictAction = actionAt( event->x(), event->y() );
|
||||
if( dictAction )
|
||||
{
|
||||
Dictionary::Class *pDict = NULL;
|
||||
QString id = dictAction->data().toString();
|
||||
for( unsigned i = 0; i < allDictionaries.size(); i++ )
|
||||
{
|
||||
if( id.compare( allDictionaries[ i ]->getId().c_str() ) == 0 )
|
||||
{
|
||||
pDict = allDictionaries[ i ].get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( pDict )
|
||||
{
|
||||
infoAction = menu.addAction( tr( "Dictionary info" ) );
|
||||
|
||||
if( pDict->isLocalDictionary() )
|
||||
{
|
||||
if( pDict->getWordCount() > 0 )
|
||||
headwordsAction = menu.addAction( tr( "Dictionary headwords" ) );
|
||||
|
||||
openDictFolderAction = menu.addAction( tr( "Open dictionary folder" ) );
|
||||
|
||||
if( !editDictionaryCommand.isEmpty() )
|
||||
{
|
||||
if( !pDict->getMainFilename().isEmpty() )
|
||||
{
|
||||
dictFilename = pDict->getMainFilename();
|
||||
editDictAction = menu.addAction( tr( "Edit dictionary" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !dictActions.empty() )
|
||||
menu.addSeparator();
|
||||
|
||||
unsigned refsAdded = 0;
|
||||
|
||||
for( QList< QAction * >::iterator i = dictActions.begin();
|
||||
i != dictActions.end(); ++i )
|
||||
{
|
||||
|
||||
// Enough! Or the menu would become too large.
|
||||
if ( refsAdded++ >= maxDictionaryRefsInContextMenu && !extended )
|
||||
{
|
||||
menu.addSeparator();
|
||||
menu.addAction( maxDictionaryRefsAction );
|
||||
break;
|
||||
}
|
||||
|
||||
// We need new action, since the one we have has text elided
|
||||
QAction * action = menu.addAction( (*i)->icon(), (*i)->toolTip() );
|
||||
|
||||
action->setCheckable( true );
|
||||
action->setChecked( (*i)->isChecked() );
|
||||
action->setData( QVariant::fromValue( (void *)*i ) );
|
||||
// Force "icon in menu" on all platforms, for
|
||||
// usability reasons.
|
||||
action->setIconVisibleInMenu( true );
|
||||
}
|
||||
|
||||
connect( this, SIGNAL( closePopupMenu() ), &menu, SLOT( close() ) );
|
||||
|
||||
QAction * result = menu.exec( event->globalPos() );
|
||||
|
||||
if( result && result == infoAction )
|
||||
{
|
||||
QString id = dictAction->data().toString();
|
||||
emit showDictionaryInfo( id );
|
||||
return;
|
||||
}
|
||||
|
||||
if( result && result == headwordsAction )
|
||||
{
|
||||
QString id = dictAction->data().toString();
|
||||
emit showDictionaryHeadwords( id );
|
||||
return;
|
||||
}
|
||||
|
||||
if( result && result == openDictFolderAction )
|
||||
{
|
||||
QString id = dictAction->data().toString();
|
||||
emit openDictionaryFolder( id );
|
||||
return;
|
||||
}
|
||||
|
||||
if( result && result == editDictAction )
|
||||
{
|
||||
QString command( editDictionaryCommand );
|
||||
command.replace( "%GDDICT%", "\"" + dictFilename + "\"" );
|
||||
if( !QProcess::startDetached( command ) )
|
||||
QApplication::beep();
|
||||
}
|
||||
|
||||
if( result && result == maxDictionaryRefsAction )
|
||||
{
|
||||
showContextMenu( event, true );
|
||||
}
|
||||
|
||||
if ( result == editAction )
|
||||
emit editGroupRequested();
|
||||
else
|
||||
if ( result && result->data().value< void * >() )
|
||||
( ( QAction * )( result->data().value< void * >() ) )->trigger();
|
||||
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void DictionaryBar::mutedDictionariesChanged()
|
||||
{
|
||||
//DPRINTF( "Muted dictionaries changed\n" );
|
||||
|
||||
if( !mutedDictionaries )
|
||||
return;
|
||||
|
||||
// Update actions
|
||||
|
||||
setUpdatesEnabled( false );
|
||||
|
||||
for( QList< QAction * >::iterator i = dictActions.begin();
|
||||
i != dictActions.end(); ++i )
|
||||
{
|
||||
bool isUnmuted = !mutedDictionaries->contains( (*i)->data().toString() );
|
||||
|
||||
if ( isUnmuted != (*i)->isChecked() )
|
||||
(*i)->setChecked( isUnmuted );
|
||||
}
|
||||
|
||||
setUpdatesEnabled( true );
|
||||
}
|
||||
|
||||
void DictionaryBar::actionWasTriggered( QAction * action )
|
||||
{
|
||||
if( !mutedDictionaries )
|
||||
return;
|
||||
|
||||
QString id = action->data().toString();
|
||||
|
||||
if ( id.isEmpty() )
|
||||
return; // Some weird action, not our button
|
||||
|
||||
if ( QApplication::keyboardModifiers() &
|
||||
( Qt::ControlModifier | Qt::ShiftModifier ) )
|
||||
{
|
||||
// Solo mode -- either use the dictionary exclusively, or toggle
|
||||
// back all dictionaries if we do that already.
|
||||
|
||||
// Are we solo already?
|
||||
|
||||
bool isSolo = true;
|
||||
|
||||
// For solo, all dictionaries must be unchecked, since we're handling
|
||||
// the result of the dictionary being (un)checked, and in case we were
|
||||
// in solo, now we would end up with no dictionaries being checked at all.
|
||||
for( QList< QAction * >::iterator i = dictActions.begin();
|
||||
i != dictActions.end(); ++i )
|
||||
{
|
||||
if ( (*i)->isChecked() )
|
||||
{
|
||||
isSolo = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isSolo )
|
||||
{
|
||||
// Restore or clear all the dictionaries
|
||||
if ( QApplication::keyboardModifiers() & Qt::ShiftModifier )
|
||||
*mutedDictionaries = storedMutedSet;
|
||||
else
|
||||
{
|
||||
for( QList< QAction * >::iterator i = dictActions.begin();
|
||||
i != dictActions.end(); ++i )
|
||||
mutedDictionaries->remove( (*i)->data().toString() );
|
||||
}
|
||||
storedMutedSet.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save dictionaries state
|
||||
storedMutedSet = *mutedDictionaries;
|
||||
|
||||
// Make dictionary solo
|
||||
for( QList< QAction * >::iterator i = dictActions.begin();
|
||||
i != dictActions.end(); ++i )
|
||||
{
|
||||
QString dictId = (*i)->data().toString();
|
||||
|
||||
if ( dictId == id )
|
||||
mutedDictionaries->remove( dictId );
|
||||
else
|
||||
mutedDictionaries->insert( dictId );
|
||||
}
|
||||
}
|
||||
configEvents.signalMutedDictionariesChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal mode
|
||||
|
||||
storedMutedSet.clear();
|
||||
|
||||
if ( action->isChecked() )
|
||||
{
|
||||
// Unmute the dictionary
|
||||
|
||||
if ( mutedDictionaries->contains( id ) )
|
||||
{
|
||||
mutedDictionaries->remove( id );
|
||||
configEvents.signalMutedDictionariesChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mute the dictionary
|
||||
|
||||
if ( !mutedDictionaries->contains( id ) )
|
||||
{
|
||||
mutedDictionaries->insert( id );
|
||||
configEvents.signalMutedDictionariesChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DictionaryBar::dictsPaneClicked( const QString & id )
|
||||
{
|
||||
for( QList< QAction * >::iterator i = dictActions.begin();
|
||||
i != dictActions.end(); ++i )
|
||||
{
|
||||
QString dictId = (*i)->data().toString();
|
||||
if ( dictId == id )
|
||||
{
|
||||
(*i)->activate( QAction::Trigger );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DictionaryBar::eventFilter( QObject * obj, QEvent * ev )
|
||||
{
|
||||
if(obj == this && !isFloating() )
|
||||
{
|
||||
QPoint pt = parentWidget()->mapFromGlobal( QCursor::pos() );
|
||||
switch( ev->type() )
|
||||
{
|
||||
case QEvent::Leave : if( geometry().contains( pt ) )
|
||||
{
|
||||
if( timerId )
|
||||
killTimer( timerId );
|
||||
timerId = startTimer( 500 );
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::Enter : if( timerId != 0)
|
||||
{
|
||||
killTimer( timerId );
|
||||
timerId = 0;
|
||||
}
|
||||
break;
|
||||
case QEvent::Timer: if( static_cast< QTimerEvent * >( ev )->timerId() == timerId )
|
||||
{
|
||||
if( !geometry().contains( pt ) )
|
||||
{
|
||||
killTimer( timerId );
|
||||
timerId = 0;
|
||||
QEvent event( QEvent::Leave );
|
||||
QApplication::sendEvent( this, &event );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef __DICTIONARYBAR_HH_INCLUDED__
|
||||
#define __DICTIONARYBAR_HH_INCLUDED__
|
||||
|
||||
#include <QToolBar>
|
||||
#include <QSize>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include "dictionary.hh"
|
||||
#include "config.hh"
|
||||
|
||||
/// A bar containing dictionary icons of the currently chosen group.
|
||||
/// Individual dictionaries can be toggled on and off.
|
||||
class DictionaryBar: public QToolBar
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// Constructs an empty dictionary bar
|
||||
DictionaryBar( QWidget * parent,
|
||||
Config::Events &, QString const & _editDictionaryCommand, unsigned short const & maxDictionaryRefsInContextMenu_ );
|
||||
|
||||
/// Sets dictionaries to be displayed in the bar. Their statuses (enabled/
|
||||
/// disabled) are taken from the configuration data.
|
||||
void setDictionaries( std::vector< sptr< Dictionary::Class > > const & );
|
||||
void setMutedDictionaries( Config::MutedDictionaries * mutedDictionaries_ )
|
||||
{ mutedDictionaries = mutedDictionaries_; }
|
||||
Config::MutedDictionaries const * getMutedDictionaries() const
|
||||
{ return mutedDictionaries; }
|
||||
void setDictionaryIconSize( int extent );
|
||||
|
||||
signals:
|
||||
|
||||
/// Signalled when the user decided to edit group the bar currently
|
||||
/// shows.
|
||||
void editGroupRequested();
|
||||
|
||||
/// Signal for show dictionary info command from context menu
|
||||
void showDictionaryInfo( QString const & id );
|
||||
|
||||
/// Signal for show dictionary headwords command from context menu
|
||||
void showDictionaryHeadwords( QString const & id );
|
||||
|
||||
/// Signal for open dictionary folder from context menu
|
||||
void openDictionaryFolder( QString const & id );
|
||||
|
||||
/// Signal to close context menu
|
||||
void closePopupMenu();
|
||||
|
||||
private:
|
||||
|
||||
Config::MutedDictionaries * mutedDictionaries;
|
||||
Config::Events & configEvents;
|
||||
Config::MutedDictionaries storedMutedSet;
|
||||
QString editDictionaryCommand;
|
||||
// how many dictionaries should be shown in the context menu:
|
||||
unsigned short const & maxDictionaryRefsInContextMenu;
|
||||
std::vector< sptr< Dictionary::Class > > allDictionaries;
|
||||
/// All the actions we have added to the toolbar
|
||||
QList< QAction * > dictActions;
|
||||
QAction * maxDictionaryRefsAction;
|
||||
|
||||
bool use14x21;
|
||||
int timerId;
|
||||
|
||||
virtual bool eventFilter( QObject *, QEvent * );
|
||||
|
||||
protected:
|
||||
|
||||
void contextMenuEvent( QContextMenuEvent * event );
|
||||
|
||||
private slots:
|
||||
|
||||
void mutedDictionariesChanged();
|
||||
|
||||
void actionWasTriggered( QAction * );
|
||||
|
||||
void dictsPaneClicked( QString const & );
|
||||
|
||||
void showContextMenu( QContextMenuEvent * event, bool extended = false );
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,875 @@
|
||||
/* This file is (c) 2014 Abs62
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "dictserver.hh"
|
||||
#include "wstring_qt.hh"
|
||||
#include <QUrl>
|
||||
#include <QTcpSocket>
|
||||
#include <QString>
|
||||
#include <list>
|
||||
#include "gddebug.hh"
|
||||
#include "htmlescape.hh"
|
||||
|
||||
namespace DictServer {
|
||||
|
||||
using namespace Dictionary;
|
||||
|
||||
enum {
|
||||
DefaultPort = 2628
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
#define MAX_MATCHES_COUNT 60
|
||||
|
||||
bool readLine( QTcpSocket & socket, QString & line,
|
||||
QString & errorString, QAtomicInt & isCancelled )
|
||||
{
|
||||
line.clear();
|
||||
errorString.clear();
|
||||
if( socket.state() != QTcpSocket::ConnectedState )
|
||||
return false;
|
||||
|
||||
for( ; ; )
|
||||
{
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
return false;
|
||||
|
||||
if( socket.canReadLine() )
|
||||
{
|
||||
QByteArray reply = socket.readLine();
|
||||
line = QString::fromUtf8( reply.data(), reply.size() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( !socket.waitForReadyRead( 2000 ) )
|
||||
{
|
||||
errorString = "Data reading error: socket error " + QString::number( socket.error() )
|
||||
+ ": \"" + socket.errorString() + "\"";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool connectToServer( QTcpSocket & socket, QString const & url,
|
||||
QString & errorString, QAtomicInt & isCancelled )
|
||||
{
|
||||
QUrl serverUrl( url );
|
||||
quint16 port = serverUrl.port( DefaultPort );
|
||||
QString reply;
|
||||
|
||||
for( ; ; )
|
||||
{
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
return false;
|
||||
|
||||
socket.connectToHost( serverUrl.host(), port );
|
||||
|
||||
if( socket.state() != QTcpSocket::ConnectedState )
|
||||
{
|
||||
if( !socket.waitForConnected( 5000 ) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
return false;
|
||||
|
||||
if( !readLine( socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( !reply.isEmpty() && reply.left( 3 ) != "220" )
|
||||
{
|
||||
errorString = "Server refuse connection: " + reply;
|
||||
return false;
|
||||
}
|
||||
|
||||
socket.write( QByteArray( "CLIENT GoldenDict\r\n") );
|
||||
if( !socket.waitForBytesWritten( 1000 ) )
|
||||
break;
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
return false;
|
||||
|
||||
if( !readLine( socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( !serverUrl.userInfo().isEmpty() )
|
||||
{
|
||||
QString authCommand = QString( "AUTH " );
|
||||
|
||||
int pos = serverUrl.userInfo().indexOf( QRegExp( "[:;]" ) );
|
||||
if( pos > 0 )
|
||||
authCommand += serverUrl.userInfo().left( pos )
|
||||
+ " " + serverUrl.userInfo().mid( pos + 1 );
|
||||
else
|
||||
authCommand += serverUrl.userInfo();
|
||||
|
||||
authCommand += "\r\n";
|
||||
|
||||
socket.write( authCommand.toUtf8() );
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
return false;
|
||||
|
||||
if( socket.waitForBytesWritten( 1000 ) )
|
||||
break;
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
return false;
|
||||
|
||||
if( readLine( socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( reply.left( 3 ) != "230" )
|
||||
{
|
||||
errorString = "Authentication error: " + reply;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( !Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
errorString = QString( "Server connection fault, socket error %1: \"%2\"" )
|
||||
.arg( QString::number( socket.error() ) )
|
||||
.arg( socket.errorString() );
|
||||
return false;
|
||||
}
|
||||
|
||||
void disconnectFromServer( QTcpSocket & socket )
|
||||
{
|
||||
if( socket.state() == QTcpSocket::ConnectedState )
|
||||
socket.write( QByteArray( "QUIT\r\n" ) );
|
||||
|
||||
socket.disconnectFromHost();
|
||||
}
|
||||
|
||||
class DictServerDictionary: public Dictionary::Class
|
||||
{
|
||||
string name;
|
||||
QString url, icon;
|
||||
quint32 langId;
|
||||
QString errorString;
|
||||
QTcpSocket socket;
|
||||
QStringList databases;
|
||||
QStringList strategies;
|
||||
QStringList serverDatabases;
|
||||
|
||||
public:
|
||||
|
||||
DictServerDictionary( string const & id, string const & name_,
|
||||
QString const & url_,
|
||||
QString const & database_,
|
||||
QString const & strategies_,
|
||||
QString const & icon_ ):
|
||||
Dictionary::Class( id, vector< string >() ),
|
||||
name( name_ ),
|
||||
url( url_ ),
|
||||
icon( icon_ ),
|
||||
langId( 0 )
|
||||
{
|
||||
int pos = url.indexOf( "://" );
|
||||
if( pos < 0 )
|
||||
url = "dict://" + url;
|
||||
|
||||
databases = database_.split( QRegExp( "[ ,;]" ), QString::SkipEmptyParts );
|
||||
if( databases.isEmpty() )
|
||||
databases.append( "*" );
|
||||
|
||||
strategies = strategies_.split( QRegExp( "[ ,;]" ), QString::SkipEmptyParts );
|
||||
if( strategies.isEmpty() )
|
||||
strategies.append( "prefix" );
|
||||
}
|
||||
|
||||
virtual string getName() throw()
|
||||
{ return name; }
|
||||
|
||||
virtual map< Property, string > getProperties() throw()
|
||||
{ return map< Property, string >(); }
|
||||
|
||||
virtual unsigned long getArticleCount() throw()
|
||||
{ return 0; }
|
||||
|
||||
virtual unsigned long getWordCount() throw()
|
||||
{ return 0; }
|
||||
|
||||
virtual sptr< WordSearchRequest > prefixMatch( wstring const &,
|
||||
unsigned long maxResults ) throw( std::exception );
|
||||
|
||||
virtual sptr< DataRequest > getArticle( wstring const &, vector< wstring > const & alts,
|
||||
wstring const & )
|
||||
throw( std::exception );
|
||||
|
||||
virtual quint32 getLangFrom() const
|
||||
{ return langId; }
|
||||
|
||||
virtual quint32 getLangTo() const
|
||||
{ return langId; }
|
||||
|
||||
virtual QString const & getDescription();
|
||||
protected:
|
||||
|
||||
virtual void loadIcon() throw();
|
||||
|
||||
void getServerDatabases();
|
||||
|
||||
friend class DictServerWordSearchRequest;
|
||||
friend class DictServerArticleRequest;
|
||||
};
|
||||
|
||||
void DictServerDictionary::loadIcon() throw()
|
||||
{
|
||||
if ( dictionaryIconLoaded )
|
||||
return;
|
||||
|
||||
if( !icon.isEmpty() )
|
||||
{
|
||||
QFileInfo fInfo( QDir( Config::getConfigDir() ), icon );
|
||||
if( fInfo.isFile() )
|
||||
loadIconFromFile( fInfo.absoluteFilePath(), true );
|
||||
}
|
||||
if( dictionaryIcon.isNull() )
|
||||
dictionaryIcon = dictionaryNativeIcon = QIcon(":/icons/network.png");
|
||||
dictionaryIconLoaded = true;
|
||||
}
|
||||
|
||||
QString const & DictServerDictionary::getDescription()
|
||||
{
|
||||
if( serverDatabases.isEmpty() )
|
||||
{
|
||||
dictionaryDescription.clear();
|
||||
getServerDatabases();
|
||||
}
|
||||
|
||||
if( dictionaryDescription.isEmpty() )
|
||||
{
|
||||
dictionaryDescription = QCoreApplication::translate( "DictServer", "Url: " ) + url + "\n";
|
||||
dictionaryDescription += QCoreApplication::translate( "DictServer", "Databases: " ) + databases.join( ", " ) + "\n";
|
||||
dictionaryDescription += QCoreApplication::translate( "DictServer", "Search strategies: " ) + strategies.join( ", " );
|
||||
if( !serverDatabases.isEmpty() )
|
||||
{
|
||||
dictionaryDescription += "\n\n";
|
||||
dictionaryDescription += QCoreApplication::translate( "DictServer", "Server databases" )
|
||||
+ " (" + QString::number( serverDatabases.size()) + "):";
|
||||
for( QStringList::const_iterator i = serverDatabases.begin(); i != serverDatabases.end(); ++i )
|
||||
dictionaryDescription += "\n" + *i;
|
||||
}
|
||||
}
|
||||
return dictionaryDescription;
|
||||
}
|
||||
|
||||
void DictServerDictionary::getServerDatabases()
|
||||
{
|
||||
QAtomicInt isCancelled;
|
||||
QTcpSocket * socket = new QTcpSocket;
|
||||
|
||||
if( !socket )
|
||||
return;
|
||||
|
||||
if( connectToServer( *socket, url, errorString, isCancelled ) )
|
||||
{
|
||||
for( ; ; )
|
||||
{
|
||||
QString req = QString( "SHOW DB\r\n" );
|
||||
socket->write( req.toUtf8() );
|
||||
socket->waitForBytesWritten( 1000 );
|
||||
|
||||
QString reply;
|
||||
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
return;
|
||||
|
||||
if( reply.left( 3 ) == "110" )
|
||||
{
|
||||
int countPos = reply.indexOf( ' ', 4 );
|
||||
// Get databases count
|
||||
int count = reply.mid( 4, countPos > 4 ? countPos - 4 : -1 ).toInt();
|
||||
|
||||
// Read databases
|
||||
for( int x = 0; x < count; x++ )
|
||||
{
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( reply[ 0 ] == '.' )
|
||||
break;
|
||||
|
||||
while( reply.endsWith( '\r' ) || reply.endsWith( '\n' ) )
|
||||
reply.chop( 1 );
|
||||
|
||||
if( !reply.isEmpty() )
|
||||
serverDatabases.append( reply );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
gdWarning( "Retrieving databases from \"%s\" fault: %s\n",
|
||||
getName().c_str(), reply.toUtf8().data() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
disconnectFromServer( *socket );
|
||||
}
|
||||
|
||||
if( !errorString.isEmpty() )
|
||||
gdWarning( "Retrieving databases from \"%s\" fault: %s\n",
|
||||
getName().c_str(), errorString.toUtf8().data() );
|
||||
delete socket;
|
||||
}
|
||||
|
||||
class DictServerWordSearchRequest;
|
||||
|
||||
class DictServerWordSearchRequestRunnable: public QRunnable
|
||||
{
|
||||
DictServerWordSearchRequest & r;
|
||||
QSemaphore & hasExited;
|
||||
|
||||
public:
|
||||
|
||||
DictServerWordSearchRequestRunnable( DictServerWordSearchRequest & r_,
|
||||
QSemaphore & hasExited_ ): r( r_ ),
|
||||
hasExited( hasExited_ )
|
||||
{}
|
||||
|
||||
~DictServerWordSearchRequestRunnable()
|
||||
{
|
||||
hasExited.release();
|
||||
}
|
||||
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
class DictServerWordSearchRequest: public Dictionary::WordSearchRequest
|
||||
{
|
||||
QAtomicInt isCancelled;
|
||||
wstring word;
|
||||
QString errorString;
|
||||
QSemaphore hasExited;
|
||||
DictServerDictionary & dict;
|
||||
QTcpSocket * socket;
|
||||
|
||||
public:
|
||||
|
||||
DictServerWordSearchRequest( wstring const & word_,
|
||||
DictServerDictionary & dict_ ) :
|
||||
word( word_ ),
|
||||
dict( dict_ ),
|
||||
socket( 0 )
|
||||
{
|
||||
QThreadPool::globalInstance()->start(
|
||||
new DictServerWordSearchRequestRunnable( *this, hasExited ) );
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
~DictServerWordSearchRequest()
|
||||
{
|
||||
hasExited.acquire();
|
||||
}
|
||||
|
||||
virtual void cancel();
|
||||
|
||||
};
|
||||
|
||||
void DictServerWordSearchRequestRunnable::run()
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
|
||||
void DictServerWordSearchRequest::run()
|
||||
{
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
{
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
socket = new QTcpSocket;
|
||||
|
||||
if( !socket )
|
||||
{
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if( connectToServer( *socket, dict.url, errorString, isCancelled ) )
|
||||
{
|
||||
QStringList matchesList;
|
||||
|
||||
for( int ns = 0; ns < dict.strategies.size(); ns++ )
|
||||
{
|
||||
for( int i = 0; i < dict.databases.size(); i++ )
|
||||
{
|
||||
QString matchReq = QString( "MATCH " )
|
||||
+ dict.databases.at( i )
|
||||
+ " " + dict.strategies.at( ns )
|
||||
+ " \"" + gd::toQString( word )
|
||||
+ "\"\r\n";
|
||||
socket->write( matchReq.toUtf8() );
|
||||
socket->waitForBytesWritten( 1000 );
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
break;
|
||||
|
||||
QString reply;
|
||||
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
break;
|
||||
|
||||
if( reply.left( 3 ) == "250" )
|
||||
{
|
||||
// "OK" reply - matches info will be later
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( reply.left( 3 ) == "552" )
|
||||
{
|
||||
// No matches
|
||||
continue;
|
||||
}
|
||||
|
||||
if( reply[ 0 ] == '5' || reply[ 0 ] == '4' )
|
||||
{
|
||||
// Database error
|
||||
gdWarning( "Find matches in \"%s\", database \"%s\", strategy \"%s\" fault: %s\n",
|
||||
dict.getName().c_str(), dict.databases.at( i ).toUtf8().data(),
|
||||
dict.strategies.at( ns ).toUtf8().data(), reply.toUtf8().data() );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( reply.left( 3 ) == "152" )
|
||||
{
|
||||
// Matches found
|
||||
int countPos = reply.indexOf( ' ', 4 );
|
||||
|
||||
// Get matches count
|
||||
int count = reply.mid( 4, countPos > 4 ? countPos - 4 : -1 ).toInt();
|
||||
|
||||
// Read matches
|
||||
for( int x = 0; x <= count; x++ )
|
||||
{
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
break;
|
||||
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( reply[ 0 ] == '.' )
|
||||
break;
|
||||
|
||||
while( reply.endsWith( '\r' ) || reply.endsWith( '\n' ) )
|
||||
reply.chop( 1 );
|
||||
|
||||
int pos = reply.indexOf( ' ' );
|
||||
if( pos >= 0 )
|
||||
{
|
||||
QString word = reply.mid( pos + 1 );
|
||||
if( word.endsWith( '\"') )
|
||||
word.chop( 1 );
|
||||
if( word[ 0 ] == '\"' )
|
||||
word = word.mid( 1 );
|
||||
|
||||
matchesList.append( word );
|
||||
}
|
||||
}
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) || !errorString.isEmpty() )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) || !errorString.isEmpty() )
|
||||
break;
|
||||
|
||||
matchesList.removeDuplicates();
|
||||
if( matchesList.size() >= MAX_MATCHES_COUNT )
|
||||
break;
|
||||
}
|
||||
|
||||
if( !Qt4x5::AtomicInt::loadAcquire( isCancelled ) && errorString.isEmpty() )
|
||||
{
|
||||
matchesList.removeDuplicates();
|
||||
|
||||
int count = matchesList.size();
|
||||
if( count > MAX_MATCHES_COUNT )
|
||||
count = MAX_MATCHES_COUNT;
|
||||
|
||||
if( count )
|
||||
{
|
||||
Mutex::Lock _( dataMutex );
|
||||
for( int x = 0; x < count; x++ )
|
||||
matches.push_back( gd::toWString( matchesList.at( x ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !errorString.isEmpty() )
|
||||
gdWarning( "Prefix find in \"%s\" fault: %s\n", dict.getName().c_str(),
|
||||
errorString.toUtf8().data() );
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
socket->abort();
|
||||
else
|
||||
disconnectFromServer( *socket );
|
||||
|
||||
delete socket;
|
||||
if( !Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
finish();
|
||||
}
|
||||
|
||||
void DictServerWordSearchRequest::cancel()
|
||||
{
|
||||
isCancelled.ref();
|
||||
|
||||
Mutex::Lock _( dataMutex );
|
||||
finish();
|
||||
}
|
||||
|
||||
class DictServerArticleRequest;
|
||||
|
||||
class DictServerArticleRequestRunnable: public QRunnable
|
||||
{
|
||||
DictServerArticleRequest & r;
|
||||
QSemaphore & hasExited;
|
||||
|
||||
public:
|
||||
|
||||
DictServerArticleRequestRunnable( DictServerArticleRequest & r_,
|
||||
QSemaphore & hasExited_ ): r( r_ ),
|
||||
hasExited( hasExited_ )
|
||||
{}
|
||||
|
||||
~DictServerArticleRequestRunnable()
|
||||
{
|
||||
hasExited.release();
|
||||
}
|
||||
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
class DictServerArticleRequest: public Dictionary::DataRequest
|
||||
{
|
||||
QAtomicInt isCancelled;
|
||||
wstring word;
|
||||
QString errorString;
|
||||
QSemaphore hasExited;
|
||||
DictServerDictionary & dict;
|
||||
QTcpSocket * socket;
|
||||
|
||||
public:
|
||||
|
||||
DictServerArticleRequest( wstring const & word_,
|
||||
DictServerDictionary & dict_ ) :
|
||||
word( word_ ),
|
||||
dict( dict_ ),
|
||||
socket( 0 )
|
||||
{
|
||||
QThreadPool::globalInstance()->start(
|
||||
new DictServerArticleRequestRunnable( *this, hasExited ) );
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
~DictServerArticleRequest()
|
||||
{
|
||||
hasExited.acquire();
|
||||
}
|
||||
|
||||
virtual void cancel();
|
||||
|
||||
};
|
||||
|
||||
void DictServerArticleRequestRunnable::run()
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
|
||||
void DictServerArticleRequest::run()
|
||||
{
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
{
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
socket = new QTcpSocket;
|
||||
|
||||
if( !socket )
|
||||
{
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if( connectToServer( *socket, dict.url, errorString, isCancelled ) )
|
||||
{
|
||||
string articleData;
|
||||
|
||||
for( int i = 0; i < dict.databases.size(); i++ )
|
||||
{
|
||||
QString matchReq = QString( "DEFINE " )
|
||||
+ dict.databases.at( i )
|
||||
+ " \"" + gd::toQString( word ) + "\"\r\n";
|
||||
socket->write( matchReq.toUtf8() );
|
||||
socket->waitForBytesWritten( 1000 );
|
||||
|
||||
QString reply;
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
break;
|
||||
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
break;
|
||||
|
||||
if( reply.left( 3 ) == "250" )
|
||||
{
|
||||
// "OK" reply - matches info will be later
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( reply.left( 3 ) == "552" )
|
||||
{
|
||||
// No matches found
|
||||
continue;
|
||||
}
|
||||
|
||||
if( reply[ 0 ] == '5' || reply[ 0 ] == '4' )
|
||||
{
|
||||
// Database error
|
||||
gdWarning( "Articles request from \"%s\", database \"%s\" fault: %s\n",
|
||||
dict.getName().c_str(), dict.databases.at( i ).toUtf8().data(),
|
||||
reply.toUtf8().data() );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( reply.left( 3 ) == "150" )
|
||||
{
|
||||
// Articles found
|
||||
int countPos = reply.indexOf( ' ', 4 );
|
||||
|
||||
QString articleText;
|
||||
|
||||
// Get articles count
|
||||
int count = reply.mid( 4, countPos > 4 ? countPos - 4 : -1 ).toInt();
|
||||
|
||||
// Read articles
|
||||
for( int x = 0; x < count; x++ )
|
||||
{
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
break;
|
||||
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( reply.left( 3 ) == "250" )
|
||||
break;
|
||||
|
||||
if( reply.left( 3 ) == "151" )
|
||||
{
|
||||
int pos = 4;
|
||||
int endPos;
|
||||
|
||||
// Skip requested word
|
||||
if( reply[ pos ] == '\"' )
|
||||
endPos = reply.indexOf( '\"', pos + 1 ) + 1;
|
||||
else
|
||||
endPos = reply.indexOf( ' ', pos );
|
||||
|
||||
if( endPos < pos )
|
||||
{
|
||||
// It seems mailformed string
|
||||
break;
|
||||
}
|
||||
|
||||
pos = endPos + 1;
|
||||
|
||||
QString dbID, dbName;
|
||||
|
||||
// Retrieve database ID
|
||||
endPos = reply.indexOf( ' ', pos );
|
||||
|
||||
if( endPos < pos )
|
||||
{
|
||||
// It seems mailformed string
|
||||
break;
|
||||
}
|
||||
|
||||
dbID = reply.mid( pos, endPos - pos );
|
||||
|
||||
// Retrieve database ID
|
||||
pos = endPos + 1;
|
||||
endPos = reply.indexOf( ' ', pos );
|
||||
if( reply[ pos ] == '\"' )
|
||||
endPos = reply.indexOf( '\"', pos + 1 ) + 1;
|
||||
else
|
||||
endPos = reply.indexOf( ' ', pos );
|
||||
|
||||
if( endPos < pos )
|
||||
{
|
||||
// It seems mailformed string
|
||||
break;
|
||||
}
|
||||
|
||||
dbName = reply.mid( pos, endPos - pos );
|
||||
if( dbName.endsWith( '\"' ) )
|
||||
dbName.chop( 1 );
|
||||
if( dbName[ 0 ] == '\"' )
|
||||
dbName = dbName.mid( 1 );
|
||||
|
||||
articleData += string( "<div class=\"dictserver_from\">From " )
|
||||
+ dbName.toUtf8().data()
|
||||
+ " [" + dbID.toUtf8().data() + "]:"
|
||||
+ "</div>";
|
||||
|
||||
// Retrieve article text
|
||||
|
||||
articleText.clear();
|
||||
for( ; ; )
|
||||
{
|
||||
if( !readLine( *socket, reply, errorString, isCancelled ) )
|
||||
break;
|
||||
|
||||
if( reply == ".\r\n" )
|
||||
break;
|
||||
|
||||
articleText += reply;
|
||||
}
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) || !errorString.isEmpty() )
|
||||
break;
|
||||
|
||||
QRegExp phonetic( "\\\\([^\\\\]+)\\\\", Qt::CaseInsensitive ); // phonetics: \stuff\ ...
|
||||
QRegExp refs( "\\{([^\\{\\}]+)\\}", Qt::CaseInsensitive ); // links: {stuff}
|
||||
QRegExp links( "<a href=\"gdlookup://localhost/([^\"]*)\">", Qt::CaseInsensitive );
|
||||
QRegExp tags( "<[^>]*>", Qt::CaseInsensitive );
|
||||
|
||||
string articleStr = Html::preformat( articleText.toUtf8().data() );
|
||||
articleText = QString::fromUtf8( articleStr.c_str(), articleStr.size() )
|
||||
.replace(phonetic, "<span class=\"dictd_phonetic\">\\1</span>" )
|
||||
.replace(refs, "<a href=\"gdlookup://localhost/\\1\">\\1</a>" );
|
||||
pos = 0;
|
||||
for( ; ; )
|
||||
{
|
||||
pos = articleText.indexOf( links, pos );
|
||||
if( pos < 0 )
|
||||
break;
|
||||
|
||||
QString link = links.cap( 1 );
|
||||
link.replace( tags, " " );
|
||||
link.replace( " ", " " );
|
||||
articleText.replace( pos + 30, links.cap( 1 ).length(),
|
||||
QString::fromUtf8( QUrl::toPercentEncoding( link.simplified() ) ) );
|
||||
pos += 30;
|
||||
}
|
||||
|
||||
articleData += string( "<div class=\"dictd_article\">" )
|
||||
+ articleText.toUtf8().data()
|
||||
+ "<br></div>";
|
||||
}
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) || !errorString.isEmpty() )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !Qt4x5::AtomicInt::loadAcquire( isCancelled ) && errorString.isEmpty() && !articleData.empty() )
|
||||
{
|
||||
Mutex::Lock _( dataMutex );
|
||||
|
||||
data.resize( articleData.size() );
|
||||
memcpy( &data.front(), articleData.data(), articleData.size() );
|
||||
|
||||
hasAnyData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( !errorString.isEmpty() )
|
||||
gdWarning( "Articles request from \"%s\" fault: %s\n", dict.getName().c_str(),
|
||||
errorString.toUtf8().data() );
|
||||
|
||||
if( Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
socket->abort();
|
||||
else
|
||||
disconnectFromServer( *socket );
|
||||
|
||||
delete socket;
|
||||
if( !Qt4x5::AtomicInt::loadAcquire( isCancelled ) )
|
||||
finish();
|
||||
}
|
||||
|
||||
void DictServerArticleRequest::cancel()
|
||||
{
|
||||
isCancelled.ref();
|
||||
|
||||
Mutex::Lock _( dataMutex );
|
||||
finish();
|
||||
}
|
||||
|
||||
sptr< WordSearchRequest > DictServerDictionary::prefixMatch( wstring const & word,
|
||||
unsigned long maxResults )
|
||||
throw( std::exception )
|
||||
{
|
||||
(void) maxResults;
|
||||
if ( word.size() > 80 )
|
||||
{
|
||||
// Don't make excessively large queries -- they're fruitless anyway
|
||||
|
||||
return new WordSearchRequestInstant();
|
||||
}
|
||||
else
|
||||
return new DictServerWordSearchRequest( word, *this );
|
||||
}
|
||||
|
||||
sptr< DataRequest > DictServerDictionary::getArticle( wstring const & word,
|
||||
vector< wstring > const &,
|
||||
wstring const & )
|
||||
throw( std::exception )
|
||||
{
|
||||
if ( word.size() > 80 )
|
||||
{
|
||||
// Don't make excessively large queries -- they're fruitless anyway
|
||||
|
||||
return new DataRequestInstant( false );
|
||||
}
|
||||
else
|
||||
return new DictServerArticleRequest( word, *this );
|
||||
}
|
||||
|
||||
} // Anonimuos namespace
|
||||
|
||||
vector< sptr< Dictionary::Class > > makeDictionaries( Config::DictServers const & servers )
|
||||
throw( std::exception )
|
||||
{
|
||||
vector< sptr< Dictionary::Class > > result;
|
||||
|
||||
for( int x = 0; x < servers.size(); ++x )
|
||||
{
|
||||
if ( servers[ x ].enabled )
|
||||
result.push_back( new DictServerDictionary( servers[ x ].id.toStdString(),
|
||||
servers[ x ].name.toUtf8().data(),
|
||||
servers[ x ].url,
|
||||
servers[ x ].databases,
|
||||
servers[ x ].strategies,
|
||||
servers[ x ].iconFilename ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace DictServer
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef __DICTSERVER_HH__INCLUDED__
|
||||
#define __DICTSERVER_HH__INCLUDED__
|
||||
|
||||
#include "dictionary.hh"
|
||||
#include "config.hh"
|
||||
|
||||
/// Support for servers supporting DICT protocol.
|
||||
namespace DictServer {
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
vector< sptr< Dictionary::Class > > makeDictionaries(
|
||||
Config::DictServers const & servers )
|
||||
throw( std::exception );
|
||||
|
||||
}
|
||||
|
||||
#endif // __DICTSERVER_HH__INCLUDED__
|
||||
@@ -0,0 +1,24 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __DICTSPANEWIDGET_HH_INCLUDED__
|
||||
#define __DICTSPANEWIDGET_HH_INCLUDED__
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSize>
|
||||
|
||||
/// A widget holding the contents of the 'Dictionaries pane' docklet.
|
||||
class DictsPaneWidget: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
DictsPaneWidget( QWidget * parent = 0 ): QWidget( parent )
|
||||
{}
|
||||
|
||||
virtual QSize sizeHint() const
|
||||
{ return QSize( 204, 204 ); }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,848 @@
|
||||
/* Made up from data.c and other supplementary files of dictd-1.0.11 for the
|
||||
* GoldenDict program.
|
||||
*/
|
||||
|
||||
/* data.c --
|
||||
* Created: Tue Jul 16 12:45:41 1996 by faith@dict.org
|
||||
* Revised: Sat Mar 30 10:46:06 2002 by faith@dict.org
|
||||
* Copyright 1996, 1997, 1998, 2000, 2002 Rickard E. Faith (faith@dict.org)
|
||||
*
|
||||
* 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 the
|
||||
* Free Software Foundation; either version 1, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Suite 500, Boston, MA 02110, USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "dictzip.h"
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ufile.hh"
|
||||
|
||||
#define BUFFERSIZE 10240
|
||||
|
||||
#define OUT_BUFFER_SIZE 0xffffL
|
||||
|
||||
#define IN_BUFFER_SIZE ((unsigned long)((double)(OUT_BUFFER_SIZE - 12) * 0.89))
|
||||
|
||||
/* For gzip-compatible header, as defined in RFC 1952 */
|
||||
|
||||
/* Magic for GZIP (rfc1952) */
|
||||
#define GZ_MAGIC1 0x1f /* First magic byte */
|
||||
#define GZ_MAGIC2 0x8b /* Second magic byte */
|
||||
|
||||
/* FLaGs (bitmapped), from rfc1952 */
|
||||
#define GZ_FTEXT 0x01 /* Set for ASCII text */
|
||||
#define GZ_FHCRC 0x02 /* Header CRC16 */
|
||||
#define GZ_FEXTRA 0x04 /* Optional field (random access index) */
|
||||
#define GZ_FNAME 0x08 /* Original name */
|
||||
#define GZ_COMMENT 0x10 /* Zero-terminated, human-readable comment */
|
||||
#define GZ_MAX 2 /* Maximum compression */
|
||||
#define GZ_FAST 4 /* Fasted compression */
|
||||
|
||||
/* These are from rfc1952 */
|
||||
#define GZ_OS_FAT 0 /* FAT filesystem (MS-DOS, OS/2, NT/Win32) */
|
||||
#define GZ_OS_AMIGA 1 /* Amiga */
|
||||
#define GZ_OS_VMS 2 /* VMS (or OpenVMS) */
|
||||
#define GZ_OS_UNIX 3 /* Unix */
|
||||
#define GZ_OS_VMCMS 4 /* VM/CMS */
|
||||
#define GZ_OS_ATARI 5 /* Atari TOS */
|
||||
#define GZ_OS_HPFS 6 /* HPFS filesystem (OS/2, NT) */
|
||||
#define GZ_OS_MAC 7 /* Macintosh */
|
||||
#define GZ_OS_Z 8 /* Z-System */
|
||||
#define GZ_OS_CPM 9 /* CP/M */
|
||||
#define GZ_OS_TOPS20 10 /* TOPS-20 */
|
||||
#define GZ_OS_NTFS 11 /* NTFS filesystem (NT) */
|
||||
#define GZ_OS_QDOS 12 /* QDOS */
|
||||
#define GZ_OS_ACORN 13 /* Acorn RISCOS */
|
||||
#define GZ_OS_UNKNOWN 255 /* unknown */
|
||||
|
||||
#define GZ_RND_S1 'R' /* First magic for random access format */
|
||||
#define GZ_RND_S2 'A' /* Second magic for random access format */
|
||||
|
||||
#define GZ_ID1 0 /* GZ_MAGIC1 */
|
||||
#define GZ_ID2 1 /* GZ_MAGIC2 */
|
||||
#define GZ_CM 2 /* Compression Method (Z_DEFALTED) */
|
||||
#define GZ_FLG 3 /* FLaGs (see above) */
|
||||
#define GZ_MTIME 4 /* Modification TIME */
|
||||
#define GZ_XFL 8 /* eXtra FLags (GZ_MAX or GZ_FAST) */
|
||||
#define GZ_OS 9 /* Operating System */
|
||||
#define GZ_XLEN 10 /* eXtra LENgth (16bit) */
|
||||
#define GZ_FEXTRA_START 12 /* Start of extra fields */
|
||||
#define GZ_SI1 12 /* Subfield ID1 */
|
||||
#define GZ_SI2 13 /* Subfield ID2 */
|
||||
#define GZ_SUBLEN 14 /* Subfield length (16bit) */
|
||||
#define GZ_VERSION 16 /* Version for subfield format */
|
||||
#define GZ_CHUNKLEN 18 /* Chunk length (16bit) */
|
||||
#define GZ_CHUNKCNT 20 /* Number of chunks (16bit) */
|
||||
#define GZ_RNDDATA 22 /* Random access data (16bit) */
|
||||
|
||||
|
||||
#define DBG_VERBOSE (0<<30|1<< 0) /* Verbose */
|
||||
#define DBG_ZIP (0<<30|1<< 1) /* Zip */
|
||||
#define DBG_UNZIP (0<<30|1<< 2) /* Unzip */
|
||||
#define DBG_SEARCH (0<<30|1<< 3) /* Search */
|
||||
#define DBG_SCAN (0<<30|1<< 4) /* Config file scan */
|
||||
#define DBG_PARSE (0<<30|1<< 5) /* Config file parse */
|
||||
#define DBG_INIT (0<<30|1<< 6) /* Database initialization */
|
||||
#define DBG_PORT (0<<30|1<< 7) /* Log port number for connections */
|
||||
#define DBG_LEV (0<<30|1<< 8) /* Levenshtein matching */
|
||||
#define DBG_AUTH (0<<30|1<< 9) /* Debug authentication */
|
||||
#define DBG_NODETACH (0<<30|1<<10) /* Don't detach as a background proc. */
|
||||
#define DBG_NOFORK (0<<30|1<<11) /* Don't fork (single threaded) */
|
||||
#define DBG_ALT (0<<30|1<<12) /* altcompare() */
|
||||
|
||||
#define LOG_SERVER (0<<30|1<< 0) /* Log server diagnostics */
|
||||
#define LOG_CONNECT (0<<30|1<< 1) /* Log connection information */
|
||||
#define LOG_STATS (0<<30|1<< 2) /* Log termination information */
|
||||
#define LOG_COMMAND (0<<30|1<< 3) /* Log commands */
|
||||
#define LOG_FOUND (0<<30|1<< 4) /* Log words found */
|
||||
#define LOG_NOTFOUND (0<<30|1<< 5) /* Log words not found */
|
||||
#define LOG_CLIENT (0<<30|1<< 6) /* Log client */
|
||||
#define LOG_HOST (0<<30|1<< 7) /* Log remote host name */
|
||||
#define LOG_TIMESTAMP (0<<30|1<< 8) /* Log with timestamps */
|
||||
#define LOG_MIN (0<<30|1<< 9) /* Log a few minimal things */
|
||||
#define LOG_AUTH (0<<30|1<<10) /* Log authentication denials */
|
||||
|
||||
#define DICT_LOG_TERM 0
|
||||
#define DICT_LOG_DEFINE 1
|
||||
#define DICT_LOG_MATCH 2
|
||||
#define DICT_LOG_NOMATCH 3
|
||||
#define DICT_LOG_CLIENT 4
|
||||
#define DICT_LOG_TRACE 5
|
||||
#define DICT_LOG_COMMAND 6
|
||||
#define DICT_LOG_AUTH 7
|
||||
#define DICT_LOG_CONNECT 8
|
||||
|
||||
#define DICT_UNKNOWN 0
|
||||
#define DICT_TEXT 1
|
||||
#define DICT_GZIP 2
|
||||
#define DICT_DZIP 3
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define USE_CACHE 1
|
||||
|
||||
#define dict_data_filter( ... )
|
||||
#define PRINTF( ... )
|
||||
|
||||
#define xmalloc malloc
|
||||
#define xfree free
|
||||
|
||||
static const char * _err_programName = "GoldenDict";
|
||||
|
||||
#define log_error( ... )
|
||||
#define log_error_va( ... )
|
||||
|
||||
static void err_fatal( const char *routine, const char *format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fflush( stdout );
|
||||
if (_err_programName) {
|
||||
if (routine)
|
||||
fprintf( stderr, "%s (%s): ", _err_programName, routine );
|
||||
else
|
||||
fprintf( stderr, "%s: ", _err_programName );
|
||||
} else {
|
||||
if (routine) fprintf( stderr, "%s: ", routine );
|
||||
}
|
||||
|
||||
va_start( ap, format );
|
||||
vfprintf( stderr, format, ap );
|
||||
log_error_va( routine, format, ap );
|
||||
va_end( ap );
|
||||
|
||||
fflush( stderr );
|
||||
fflush( stdout );
|
||||
// exit ( 1 );
|
||||
}
|
||||
|
||||
/* \doc |err_fatal_errno| flushes "stdout", prints a fatal error report on
|
||||
"stderr", prints the system error corresponding to |errno|, flushes
|
||||
"stderr" and "stdout", and calls |exit|. |routine| is the name of the
|
||||
routine in which the error took place. */
|
||||
|
||||
static void err_fatal_errno( const char *routine, const char *format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int errorno = errno;
|
||||
|
||||
fflush( stdout );
|
||||
if (_err_programName) {
|
||||
if (routine)
|
||||
fprintf( stderr, "%s (%s): ", _err_programName, routine );
|
||||
else
|
||||
fprintf( stderr, "%s: ", _err_programName );
|
||||
} else {
|
||||
if (routine) fprintf( stderr, "%s: ", routine );
|
||||
}
|
||||
|
||||
va_start( ap, format );
|
||||
vfprintf( stderr, format, ap );
|
||||
log_error_va( routine, format, ap );
|
||||
va_end( ap );
|
||||
|
||||
#if HAVE_STRERROR
|
||||
fprintf( stderr, "%s: %s\n", routine, strerror( errorno ) );
|
||||
log_error( routine, "%s: %s\n", routine, strerror( errorno ) );
|
||||
#else
|
||||
errno = errorno;
|
||||
perror( routine );
|
||||
log_error( routine, "%s: errno = %d\n", routine, errorno );
|
||||
#endif
|
||||
|
||||
fflush( stderr );
|
||||
fflush( stdout );
|
||||
// exit( 1 );
|
||||
}
|
||||
|
||||
/* \doc |err_internal| flushes "stdout", prints the fatal error message,
|
||||
flushes "stderr" and "stdout", and calls |abort| so that a core dump is
|
||||
generated. */
|
||||
|
||||
static void err_internal( const char *routine, const char *format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fflush( stdout );
|
||||
if (_err_programName) {
|
||||
if (routine)
|
||||
fprintf( stderr, "%s (%s): Internal error\n ",
|
||||
_err_programName, routine );
|
||||
else
|
||||
fprintf( stderr, "%s: Internal error\n ", _err_programName );
|
||||
} else {
|
||||
if (routine) fprintf( stderr, "%s: Internal error\n ", routine );
|
||||
else fprintf( stderr, "Internal error\n " );
|
||||
}
|
||||
|
||||
va_start( ap, format );
|
||||
vfprintf( stderr, format, ap );
|
||||
log_error( routine, format, ap );
|
||||
va_end( ap );
|
||||
|
||||
if (_err_programName)
|
||||
fprintf( stderr, "Aborting %s...\n", _err_programName );
|
||||
else
|
||||
fprintf( stderr, "Aborting...\n" );
|
||||
fflush( stderr );
|
||||
fflush( stdout );
|
||||
// abort();
|
||||
}
|
||||
|
||||
#ifndef __func__
|
||||
# ifdef __FUNCTION__
|
||||
# define __func__ __FUNCTION__
|
||||
# else
|
||||
# define __func__ __FILE__
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static enum DZ_ERRORS dict_read_header( const char *filename,
|
||||
dictData *header,
|
||||
int computeCRC )
|
||||
{
|
||||
FILE *str;
|
||||
int id1, id2, si1, si2;
|
||||
char buffer[BUFFERSIZE];
|
||||
int extraLength, subLength;
|
||||
int i;
|
||||
char *pt;
|
||||
int c;
|
||||
struct stat sb;
|
||||
unsigned long crc = crc32( 0L, Z_NULL, 0 );
|
||||
int count;
|
||||
unsigned long offset;
|
||||
|
||||
if (!(str = gd_fopen( filename, "rb" )))
|
||||
{
|
||||
err_fatal_errno( __func__,
|
||||
"Cannot open data file \"%s\" for read\n", filename );
|
||||
return DZ_ERR_OPENFILE;
|
||||
}
|
||||
|
||||
header->filename = NULL;//str_find( filename );
|
||||
header->headerLength = GZ_XLEN - 1;
|
||||
header->type = DICT_UNKNOWN;
|
||||
|
||||
id1 = getc( str );
|
||||
id2 = getc( str );
|
||||
|
||||
if (id1 != GZ_MAGIC1 || id2 != GZ_MAGIC2) {
|
||||
header->type = DICT_TEXT;
|
||||
fstat( fileno( str ), &sb );
|
||||
header->compressedLength = header->length = sb.st_size;
|
||||
header->origFilename = NULL;//str_find( filename );
|
||||
header->mtime = sb.st_mtime;
|
||||
if (computeCRC) {
|
||||
rewind( str );
|
||||
while (!feof( str )) {
|
||||
if ((count = fread( buffer, 1, BUFFERSIZE, str ))) {
|
||||
crc = crc32( crc, (Bytef *)buffer, count );
|
||||
}
|
||||
}
|
||||
}
|
||||
header->crc = crc;
|
||||
fclose( str );
|
||||
return DZ_NOERROR;
|
||||
}
|
||||
header->type = DICT_GZIP;
|
||||
|
||||
header->method = getc( str );
|
||||
header->flags = getc( str );
|
||||
header->mtime = getc( str ) << 0;
|
||||
header->mtime |= getc( str ) << 8;
|
||||
header->mtime |= getc( str ) << 16;
|
||||
header->mtime |= getc( str ) << 24;
|
||||
header->extraFlags = getc( str );
|
||||
header->os = getc( str );
|
||||
|
||||
if (header->flags & GZ_FEXTRA) {
|
||||
extraLength = getc( str ) << 0;
|
||||
extraLength |= getc( str ) << 8;
|
||||
header->headerLength += extraLength + 2;
|
||||
si1 = getc( str );
|
||||
si2 = getc( str );
|
||||
|
||||
if (si1 == GZ_RND_S1 && si2 == GZ_RND_S2) {
|
||||
subLength = getc( str ) << 0;
|
||||
subLength |= getc( str ) << 8;
|
||||
header->version = getc( str ) << 0;
|
||||
header->version |= getc( str ) << 8;
|
||||
|
||||
if (header->version != 1)
|
||||
{
|
||||
err_internal( __func__,
|
||||
"dzip header version %d not supported\n",
|
||||
header->version );
|
||||
fclose( str );
|
||||
return DZ_ERR_UNSUPPORTED_FORMAT;
|
||||
}
|
||||
|
||||
header->chunkLength = getc( str ) << 0;
|
||||
header->chunkLength |= getc( str ) << 8;
|
||||
header->chunkCount = getc( str ) << 0;
|
||||
header->chunkCount |= getc( str ) << 8;
|
||||
|
||||
if (header->chunkCount <= 0) {
|
||||
fclose( str );
|
||||
return DZ_ERR_INVALID_FORMAT;
|
||||
}
|
||||
header->chunks = xmalloc( sizeof( header->chunks[0] )
|
||||
* header->chunkCount );
|
||||
if( header->chunks == 0 ) {
|
||||
return DZ_ERR_NOMEMORY;
|
||||
}
|
||||
|
||||
for (i = 0; i < header->chunkCount; i++) {
|
||||
header->chunks[i] = getc( str ) << 0;
|
||||
header->chunks[i] |= getc( str ) << 8;
|
||||
}
|
||||
header->type = DICT_DZIP;
|
||||
} else {
|
||||
fseek( str, header->headerLength, SEEK_SET );
|
||||
}
|
||||
}
|
||||
|
||||
if (header->flags & GZ_FNAME) { /* FIXME! Add checking against header len */
|
||||
pt = buffer;
|
||||
while ((c = getc( str )) && c != EOF){
|
||||
*pt++ = c;
|
||||
|
||||
if (pt == buffer + sizeof (buffer)){
|
||||
err_fatal (
|
||||
__func__,
|
||||
"too long FNAME field in dzip file \"%s\"\n", filename);
|
||||
fclose( str );
|
||||
if( header->chunks )
|
||||
free( header->chunks );
|
||||
return DZ_ERR_INVALID_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
*pt = '\0';
|
||||
header->origFilename = NULL;//str_find( buffer );
|
||||
header->headerLength += strlen( buffer ) + 1;
|
||||
} else {
|
||||
header->origFilename = NULL;
|
||||
}
|
||||
|
||||
if (header->flags & GZ_COMMENT) { /* FIXME! Add checking for header len */
|
||||
pt = buffer;
|
||||
while ((c = getc( str )) && c != EOF){
|
||||
*pt++ = c;
|
||||
|
||||
if (pt == buffer + sizeof (buffer)){
|
||||
err_fatal (
|
||||
__func__,
|
||||
"too long COMMENT field in dzip file \"%s\"\n", filename);
|
||||
fclose( str );
|
||||
if( header->chunks )
|
||||
free( header->chunks );
|
||||
return DZ_ERR_INVALID_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
*pt = '\0';
|
||||
header->comment = NULL;//str_find( buffer );
|
||||
header->headerLength += strlen( header->comment ) + 1;
|
||||
} else {
|
||||
header->comment = NULL;
|
||||
}
|
||||
|
||||
if (header->flags & GZ_FHCRC) {
|
||||
getc( str );
|
||||
getc( str );
|
||||
header->headerLength += 2;
|
||||
}
|
||||
|
||||
if (ftell( str ) != header->headerLength + 1)
|
||||
{
|
||||
err_internal( __func__,
|
||||
"File position (%lu) != header length + 1 (%d)\n",
|
||||
ftell( str ), header->headerLength + 1 );
|
||||
fclose( str );
|
||||
if( header->chunks )
|
||||
free( header->chunks );
|
||||
return DZ_ERR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
fseek( str, -8, SEEK_END );
|
||||
header->crc = getc( str ) << 0;
|
||||
header->crc |= getc( str ) << 8;
|
||||
header->crc |= getc( str ) << 16;
|
||||
header->crc |= getc( str ) << 24;
|
||||
header->length = getc( str ) << 0;
|
||||
header->length |= getc( str ) << 8;
|
||||
header->length |= getc( str ) << 16;
|
||||
header->length |= getc( str ) << 24;
|
||||
header->compressedLength = ftell( str );
|
||||
|
||||
/* Compute offsets */
|
||||
header->offsets = xmalloc( sizeof( header->offsets[0] )
|
||||
* header->chunkCount );
|
||||
if( header->offsets == 0 ) {
|
||||
if( header->chunks )
|
||||
free( header->chunks );
|
||||
return DZ_ERR_NOMEMORY;
|
||||
}
|
||||
|
||||
for (offset = header->headerLength + 1, i = 0;
|
||||
i < header->chunkCount;
|
||||
i++)
|
||||
{
|
||||
header->offsets[i] = offset;
|
||||
offset += header->chunks[i];
|
||||
}
|
||||
|
||||
fclose( str );
|
||||
return DZ_NOERROR;
|
||||
}
|
||||
|
||||
dictData *dict_data_open( const char *filename,
|
||||
enum DZ_ERRORS * error,
|
||||
int computeCRC )
|
||||
{
|
||||
dictData *h = NULL;
|
||||
// struct stat sb;
|
||||
int j;
|
||||
|
||||
if (!filename)
|
||||
{
|
||||
*error = DZ_ERR_OPENFILE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h = xmalloc( sizeof( struct dictData ) );
|
||||
if( h == 0 )
|
||||
{
|
||||
*error = DZ_ERR_NOMEMORY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset( h, 0, sizeof( struct dictData ) );
|
||||
#ifdef __WIN32
|
||||
h->fd = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
h->initialized = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
#ifdef __WIN32
|
||||
wchar_t wname[16384];
|
||||
#endif
|
||||
*error = dict_read_header( filename, h, computeCRC );
|
||||
if ( *error != DZ_NOERROR ) {
|
||||
break; /*
|
||||
err_fatal( __func__,
|
||||
"\"%s\" not in text or dzip format\n", filename );*/
|
||||
}
|
||||
|
||||
#ifdef __WIN32
|
||||
if( MultiByteToWideChar( CP_UTF8, 0, filename, -1, wname, 16384 ) == 0 )
|
||||
{
|
||||
*error = DZ_ERR_OPENFILE;
|
||||
break;
|
||||
}
|
||||
|
||||
h->fd = CreateFileW( wname, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
|
||||
OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
|
||||
if( h->fd == INVALID_HANDLE_VALUE )
|
||||
{
|
||||
*error = DZ_ERR_OPENFILE;
|
||||
break;
|
||||
}
|
||||
|
||||
h->size = GetFileSize( h->fd, 0 );
|
||||
#else
|
||||
h->fd = gd_fopen( filename, "rb" );
|
||||
|
||||
if ( !h->fd )
|
||||
{
|
||||
*error = DZ_ERR_OPENFILE;
|
||||
break;
|
||||
/*err_fatal_errno( __func__,
|
||||
"Cannot open data file \"%s\"\n", filename );*/
|
||||
}
|
||||
|
||||
fseek( h->fd, 0, SEEK_END );
|
||||
|
||||
h->size = ftell( h->fd );
|
||||
#endif
|
||||
|
||||
for (j = 0; j < DICT_CACHE_SIZE; j++) {
|
||||
h->cache[j].chunk = -1;
|
||||
h->cache[j].stamp = -1;
|
||||
h->cache[j].inBuffer = NULL;
|
||||
h->cache[j].count = 0;
|
||||
}
|
||||
|
||||
*error = DZ_NOERROR;
|
||||
return h;
|
||||
}
|
||||
dict_data_close( h );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
void dict_data_close( dictData *header )
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!header)
|
||||
return;
|
||||
|
||||
#ifdef __WIN32
|
||||
if ( header->fd != INVALID_HANDLE_VALUE )
|
||||
CloseHandle( header->fd );
|
||||
#else
|
||||
if ( header->fd )
|
||||
fclose( header->fd );
|
||||
#endif
|
||||
|
||||
if (header->chunks) xfree( header->chunks );
|
||||
if (header->offsets) xfree( header->offsets );
|
||||
|
||||
if (header->initialized) {
|
||||
if (inflateEnd( &header->zStream ))
|
||||
err_internal( __func__,
|
||||
"Cannot shut down inflation engine: %s\n",
|
||||
header->zStream.msg );
|
||||
}
|
||||
|
||||
for (i = 0; i < DICT_CACHE_SIZE; ++i){
|
||||
if (header -> cache [i].inBuffer)
|
||||
xfree (header -> cache [i].inBuffer);
|
||||
}
|
||||
|
||||
memset( header, 0, sizeof( struct dictData ) );
|
||||
xfree( header );
|
||||
}
|
||||
|
||||
char *dict_data_read_ (
|
||||
dictData *h, unsigned long start, unsigned long size,
|
||||
const char *preFilter, const char *postFilter )
|
||||
{
|
||||
char * buffer;
|
||||
char * pt;
|
||||
unsigned long end;
|
||||
int count;
|
||||
char *inBuffer;
|
||||
char outBuffer[OUT_BUFFER_SIZE];
|
||||
int firstChunk, lastChunk;
|
||||
int firstOffset, lastOffset;
|
||||
int i, j;
|
||||
int found, target, lastStamp;
|
||||
(void) preFilter;
|
||||
(void) postFilter;
|
||||
|
||||
end = start + size;
|
||||
|
||||
buffer = xmalloc( size + 1 );
|
||||
if( !buffer )
|
||||
{
|
||||
strcpy( h->errorString, dz_error_str( DZ_ERR_NOMEMORY ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( !size )
|
||||
{
|
||||
*buffer = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
PRINTF(DBG_UNZIP,
|
||||
("dict_data_read( %p, %lu, %lu, %s, %s )\n",
|
||||
h, start, size, preFilter, postFilter ));
|
||||
|
||||
assert( h != NULL);
|
||||
switch (h->type) {
|
||||
case DICT_GZIP:
|
||||
/*
|
||||
err_fatal( __func__,
|
||||
"Cannot seek on pure gzip format files.\n"
|
||||
"Use plain text (for performance)"
|
||||
" or dzip format (for space savings).\n" );
|
||||
break;
|
||||
*/
|
||||
strcpy( h->errorString, "Cannot seek on pure gzip format files" );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
case DICT_TEXT:
|
||||
{
|
||||
#ifdef __WIN32
|
||||
DWORD pos = SetFilePointer( h->fd, start, 0, FILE_BEGIN );
|
||||
DWORD readed = 0;
|
||||
if( pos != INVALID_SET_FILE_POINTER || GetLastError() != NO_ERROR )
|
||||
ReadFile( h->fd, buffer, size, &readed, 0 );
|
||||
if( size != readed )
|
||||
#else
|
||||
if ( fseek( h->fd, start, SEEK_SET ) != 0 ||
|
||||
fread( buffer, size, 1, h->fd ) != 1 )
|
||||
#endif
|
||||
{
|
||||
strcpy( h->errorString, dz_error_str( DZ_ERR_READFILE ) );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer[size] = '\0';
|
||||
}
|
||||
break;
|
||||
case DICT_DZIP:
|
||||
if (!h->initialized) {
|
||||
h->zStream.zalloc = NULL;
|
||||
h->zStream.zfree = NULL;
|
||||
h->zStream.opaque = NULL;
|
||||
h->zStream.next_in = 0;
|
||||
h->zStream.avail_in = 0;
|
||||
h->zStream.next_out = NULL;
|
||||
h->zStream.avail_out = 0;
|
||||
if (inflateInit2( &h->zStream, -15 ) != Z_OK)
|
||||
/*
|
||||
err_internal( __func__,
|
||||
"Cannot initialize inflation engine: %s\n",
|
||||
h->zStream.msg );
|
||||
*/
|
||||
{
|
||||
sprintf( h->errorString, "Cannot initialize inflation engine: %s", h->zStream.msg );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
++h->initialized;
|
||||
}
|
||||
firstChunk = start / h->chunkLength;
|
||||
firstOffset = start - firstChunk * h->chunkLength;
|
||||
lastChunk = end / h->chunkLength;
|
||||
lastOffset = end - lastChunk * h->chunkLength;
|
||||
PRINTF(DBG_UNZIP,
|
||||
(" start = %lu, end = %lu\n"
|
||||
"firstChunk = %d, firstOffset = %d,"
|
||||
" lastChunk = %d, lastOffset = %d\n",
|
||||
start, end, firstChunk, firstOffset, lastChunk, lastOffset ));
|
||||
for (pt = buffer, i = firstChunk; i <= lastChunk; i++) {
|
||||
|
||||
/* Access cache */
|
||||
found = 0;
|
||||
target = 0;
|
||||
lastStamp = INT_MAX;
|
||||
for (j = 0; j < DICT_CACHE_SIZE; j++) {
|
||||
#if USE_CACHE
|
||||
if (h->cache[j].chunk == i) {
|
||||
found = 1;
|
||||
target = j;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (h->cache[j].stamp < lastStamp) {
|
||||
lastStamp = h->cache[j].stamp;
|
||||
target = j;
|
||||
}
|
||||
}
|
||||
|
||||
h->cache[target].stamp = ++h->stamp;
|
||||
if( h->stamp < 0 )
|
||||
{
|
||||
h->stamp = 0;
|
||||
for (j = 0; j < DICT_CACHE_SIZE; j++)
|
||||
h->cache[j].stamp = -1;
|
||||
}
|
||||
if (found) {
|
||||
count = h->cache[target].count;
|
||||
inBuffer = h->cache[target].inBuffer;
|
||||
} else {
|
||||
#ifdef __WIN32
|
||||
DWORD pos ;
|
||||
DWORD readed;
|
||||
#endif
|
||||
h->cache[target].chunk = -1;
|
||||
if (!h->cache[target].inBuffer)
|
||||
h->cache[target].inBuffer = xmalloc( h->chunkLength );
|
||||
inBuffer = h->cache[target].inBuffer;
|
||||
if( !inBuffer )
|
||||
{
|
||||
strcpy( h->errorString, dz_error_str( DZ_ERR_NOMEMORY ) );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (h->chunks[i] >= OUT_BUFFER_SIZE ) {
|
||||
/*
|
||||
err_internal( __func__,
|
||||
"h->chunks[%d] = %d >= %ld (OUT_BUFFER_SIZE)\n",
|
||||
i, h->chunks[i], OUT_BUFFER_SIZE );
|
||||
*/
|
||||
sprintf( h->errorString, "h->chunks[%d] = %d >= %ld (OUT_BUFFER_SIZE)\n",
|
||||
i, h->chunks[i], OUT_BUFFER_SIZE );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __WIN32
|
||||
pos = SetFilePointer( h->fd, h->offsets[ i ], 0, FILE_BEGIN );
|
||||
readed = 0;
|
||||
if( pos != INVALID_SET_FILE_POINTER || GetLastError() != NO_ERROR )
|
||||
ReadFile( h->fd, outBuffer, h->chunks[ i ], &readed, 0 );
|
||||
if( h->chunks[ i ] != (int)readed )
|
||||
#else
|
||||
if ( fseek( h->fd, h->offsets[ i ], SEEK_SET ) != 0 ||
|
||||
fread( outBuffer, h->chunks[ i ], 1, h->fd ) != 1 )
|
||||
#endif
|
||||
{
|
||||
strcpy( h->errorString, dz_error_str( DZ_ERR_READFILE ) );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
|
||||
dict_data_filter( outBuffer, &count, OUT_BUFFER_SIZE, preFilter );
|
||||
|
||||
h->zStream.next_in = (Bytef *)outBuffer;
|
||||
h->zStream.avail_in = h->chunks[i];
|
||||
h->zStream.next_out = (Bytef *)inBuffer;
|
||||
h->zStream.avail_out = h->chunkLength;
|
||||
if (inflate( &h->zStream, Z_PARTIAL_FLUSH ) != Z_OK)
|
||||
{
|
||||
// err_fatal( __func__, "inflate: %s\n", h->zStream.msg );
|
||||
sprintf( h->errorString, "inflate: %s\n", h->zStream.msg );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
if (h->zStream.avail_in)
|
||||
/*
|
||||
err_internal( __func__,
|
||||
"inflate did not flush (%d pending, %d avail)\n",
|
||||
h->zStream.avail_in, h->zStream.avail_out );
|
||||
*/
|
||||
{
|
||||
sprintf( h->errorString, "inflate did not flush (%d pending, %d avail)\n",
|
||||
h->zStream.avail_in, h->zStream.avail_out );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = h->chunkLength - h->zStream.avail_out;
|
||||
dict_data_filter( inBuffer, &count, h->chunkLength, postFilter );
|
||||
|
||||
h->cache[target].count = count;
|
||||
h->cache[target].chunk = i;
|
||||
}
|
||||
|
||||
if (i == firstChunk) {
|
||||
if (i == lastChunk) {
|
||||
memcpy( pt, inBuffer + firstOffset, lastOffset-firstOffset);
|
||||
pt += lastOffset - firstOffset;
|
||||
} else {
|
||||
if (count != h->chunkLength )
|
||||
/*
|
||||
err_internal( __func__,
|
||||
"Length = %d instead of %d\n",
|
||||
count, h->chunkLength );
|
||||
*/
|
||||
{
|
||||
sprintf( h->errorString, "Length = %d instead of %d\n",
|
||||
count, h->chunkLength );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
memcpy( pt, inBuffer + firstOffset,
|
||||
h->chunkLength - firstOffset );
|
||||
pt += h->chunkLength - firstOffset;
|
||||
}
|
||||
} else if (i == lastChunk) {
|
||||
memcpy( pt, inBuffer, lastOffset );
|
||||
pt += lastOffset;
|
||||
} else {
|
||||
assert( count == h->chunkLength );
|
||||
memcpy( pt, inBuffer, h->chunkLength );
|
||||
pt += h->chunkLength;
|
||||
}
|
||||
}
|
||||
*pt = '\0';
|
||||
break;
|
||||
case DICT_UNKNOWN:
|
||||
// err_fatal( __func__, "Cannot read unknown file type\n" );
|
||||
strcpy( h->errorString, "Cannot read unknown file type" );
|
||||
xfree( buffer );
|
||||
return 0;
|
||||
}
|
||||
h->errorString[ 0 ] = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *dict_error_str( dictData *data )
|
||||
{
|
||||
return data->errorString;
|
||||
}
|
||||
|
||||
const char * dz_error_str( enum DZ_ERRORS error )
|
||||
{
|
||||
switch( error )
|
||||
{
|
||||
case DZ_NOERROR: return "No error";
|
||||
case DZ_ERR_OPENFILE: return "Open file error";
|
||||
case DZ_ERR_READFILE: return "Read file error";
|
||||
case DZ_ERR_INVALID_FORMAT: return "Invalid file format";
|
||||
case DZ_ERR_UNSUPPORTED_FORMAT: return "Unsupported file format";
|
||||
case DZ_ERR_NOMEMORY: return "Memory allocation error";
|
||||
case DZ_ERR_INTERNAL: return "Internal error";
|
||||
}
|
||||
return "Unknown error";
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/* Made up from data.h and other supplementary files of dictd-1.0.11 for the
|
||||
* GoldenDict program.
|
||||
*/
|
||||
|
||||
/* data.h --
|
||||
* Created: Sat Mar 15 18:04:25 2003 by Aleksey Cheusov <vle@gmx.net>
|
||||
* Copyright 1994-2003 Rickard E. Faith (faith@dict.org)
|
||||
*
|
||||
* 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 the
|
||||
* Free Software Foundation; either version 1, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Suite 500, Boston, MA 02110, USA.
|
||||
*/
|
||||
|
||||
#ifndef _DICTZIP_H_
|
||||
#define _DICTZIP_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#ifdef __WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
/* Excerpts from defs.h */
|
||||
|
||||
#define DICT_CACHE_SIZE 5
|
||||
|
||||
typedef struct dictCache {
|
||||
int chunk;
|
||||
char *inBuffer;
|
||||
int stamp;
|
||||
int count;
|
||||
} dictCache;
|
||||
|
||||
enum DZ_ERRORS {
|
||||
DZ_NOERROR = 0,
|
||||
DZ_ERR_INTERNAL,
|
||||
DZ_ERR_OPENFILE,
|
||||
DZ_ERR_READFILE,
|
||||
DZ_ERR_UNSUPPORTED_FORMAT,
|
||||
DZ_ERR_INVALID_FORMAT,
|
||||
DZ_ERR_NOMEMORY
|
||||
};
|
||||
|
||||
typedef struct dictData {
|
||||
#ifdef __WIN32
|
||||
HANDLE fd; /* file handle */
|
||||
#else
|
||||
FILE * fd; /* file descriptor */
|
||||
#endif
|
||||
|
||||
unsigned long size; /* size of file */
|
||||
|
||||
int type;
|
||||
const char *filename;
|
||||
z_stream zStream;
|
||||
int initialized;
|
||||
|
||||
int headerLength;
|
||||
int method;
|
||||
int flags;
|
||||
time_t mtime;
|
||||
int extraFlags;
|
||||
int os;
|
||||
int version;
|
||||
int chunkLength;
|
||||
int chunkCount;
|
||||
int *chunks;
|
||||
unsigned long *offsets; /* Sum-scan of chunks. */
|
||||
const char *origFilename;
|
||||
const char *comment;
|
||||
unsigned long crc;
|
||||
unsigned long length;
|
||||
unsigned long compressedLength;
|
||||
int stamp;
|
||||
dictCache cache[DICT_CACHE_SIZE];
|
||||
char errorString[512];
|
||||
} dictData;
|
||||
|
||||
|
||||
/* initialize .data file */
|
||||
extern dictData *dict_data_open (
|
||||
const char *filename, enum DZ_ERRORS * error, int computeCRC);
|
||||
/* */
|
||||
extern void dict_data_close (
|
||||
dictData *data);
|
||||
|
||||
extern char *dict_data_read_ (
|
||||
dictData *data,
|
||||
unsigned long start, unsigned long end,
|
||||
const char *preFilter,
|
||||
const char *postFilter );
|
||||
|
||||
extern char *dict_error_str( dictData *data );
|
||||
|
||||
extern const char *dz_error_str( enum DZ_ERRORS error );
|
||||
|
||||
extern int mmap_mode;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* _DICTZIP_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __DSL_HH_INCLUDED__
|
||||
#define __DSL_HH_INCLUDED__
|
||||
|
||||
#include "dictionary.hh"
|
||||
|
||||
/// Support for the ABBYY Lingvo .DSL files.
|
||||
namespace Dsl {
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
vector< sptr< Dictionary::Class > > makeDictionaries(
|
||||
vector< string > const & fileNames,
|
||||
string const & indicesDir,
|
||||
Dictionary::Initializing &,
|
||||
int maxPictureWidth, unsigned int maxHeadwordSize )
|
||||
throw( std::exception );
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,211 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __DSL_DETAILS_HH_INCLUDED__
|
||||
#define __DSL_DETAILS_HH_INCLUDED__
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
#include "dictionary.hh"
|
||||
#include "iconv.hh"
|
||||
|
||||
// Implementation details for Dsl, not part of its interface
|
||||
namespace Dsl {
|
||||
namespace Details {
|
||||
|
||||
using std::string;
|
||||
using gd::wstring;
|
||||
using gd::wchar;
|
||||
using std::list;
|
||||
using std::vector;
|
||||
|
||||
// Those are possible encodings for .dsl files
|
||||
enum DslEncoding
|
||||
{
|
||||
Utf16LE,
|
||||
Utf16BE,
|
||||
Windows1252,
|
||||
Windows1251,
|
||||
Windows1250,
|
||||
Utf8 // This is an extension. Detected solely by the UTF8 BOM.
|
||||
};
|
||||
|
||||
|
||||
/// Parses the DSL language, representing it in its structural DOM form.
|
||||
struct ArticleDom
|
||||
{
|
||||
struct Node: public list< Node >
|
||||
{
|
||||
bool isTag; // true if it is a tag with subnodes, false if it's a leaf text
|
||||
// data.
|
||||
// Those are only used if isTag is true
|
||||
wstring tagName;
|
||||
wstring tagAttrs;
|
||||
wstring text; // This is only used if isTag is false
|
||||
|
||||
class Text {};
|
||||
class Tag {};
|
||||
|
||||
Node( Tag, wstring const & name, wstring const & attrs ): isTag( true ),
|
||||
tagName( name ), tagAttrs( attrs )
|
||||
{}
|
||||
|
||||
Node( Text, wstring const & text_ ): isTag( false ), text( text_ )
|
||||
{}
|
||||
|
||||
/// Concatenates all childen text nodes recursively to form all text
|
||||
/// the node contains stripped of any markup.
|
||||
wstring renderAsText( bool stripTrsTag = false ) const;
|
||||
};
|
||||
|
||||
/// Does the parse at construction. Refer to the 'root' member variable
|
||||
/// afterwards.
|
||||
ArticleDom( wstring const &, string const & dictName = string(),
|
||||
wstring const & headword_ = wstring() );
|
||||
|
||||
/// Root of DOM's tree
|
||||
Node root;
|
||||
|
||||
private:
|
||||
|
||||
void openTag( wstring const & name, wstring const & attr, list< Node * > & stack );
|
||||
|
||||
void closeTag( wstring const & name, list< Node * > & stack,
|
||||
bool warn = true );
|
||||
|
||||
wchar const * stringPos;
|
||||
|
||||
class eot {};
|
||||
|
||||
wchar ch;
|
||||
bool escaped;
|
||||
unsigned transcriptionCount; // >0 = inside a [t] tag
|
||||
|
||||
void nextChar() throw( eot );
|
||||
|
||||
/// Infomation for diagnostic purposes
|
||||
string dictionaryName;
|
||||
wstring headword;
|
||||
};
|
||||
|
||||
/// A adapted version of Iconv which takes Dsl encoding and decodes to wchar.
|
||||
class DslIconv: public Iconv
|
||||
{
|
||||
public:
|
||||
DslIconv( DslEncoding ) throw( Iconv::Ex );
|
||||
void reinit( DslEncoding ) throw( Iconv::Ex );
|
||||
|
||||
/// Returns a name to be passed to iconv for the given dsl encoding.
|
||||
static char const * getEncodingNameFor( DslEncoding );
|
||||
};
|
||||
|
||||
/// Opens the .dsl or .dsl.dz file and allows line-by-line reading. Auto-detects
|
||||
/// the encoding, and reads all headers by itself.
|
||||
class DslScanner
|
||||
{
|
||||
gzFile f;
|
||||
DslEncoding encoding;
|
||||
DslIconv iconv;
|
||||
wstring dictionaryName;
|
||||
wstring langFrom, langTo;
|
||||
char readBuffer[ 65536 ];
|
||||
char * readBufferPtr;
|
||||
size_t readBufferLeft;
|
||||
vector< wchar > wcharBuffer;
|
||||
unsigned linesRead;
|
||||
|
||||
public:
|
||||
|
||||
DEF_EX( Ex, "Dsl scanner exception", Dictionary::Ex )
|
||||
DEF_EX_STR( exCantOpen, "Can't open .dsl file", Ex )
|
||||
DEF_EX( exCantReadDslFile, "Can't read .dsl file", Ex )
|
||||
DEF_EX_STR( exMalformedDslFile, "The .dsl file is malformed:", Ex )
|
||||
DEF_EX( exUnknownCodePage, "The .dsl file specified an unknown code page", Ex )
|
||||
DEF_EX( exEncodingError, "Encoding error", Ex ) // Should never happen really
|
||||
|
||||
DslScanner( string const & fileName ) throw( Ex, Iconv::Ex );
|
||||
~DslScanner() throw();
|
||||
|
||||
/// Returns the detected encoding of this file.
|
||||
DslEncoding getEncoding() const
|
||||
{ return encoding; }
|
||||
|
||||
/// Returns the dictionary's name, as was read from file's headers.
|
||||
wstring const & getDictionaryName() const
|
||||
{ return dictionaryName; }
|
||||
|
||||
/// Returns the dictionary's source language, as was read from file's headers.
|
||||
wstring const & getLangFrom() const
|
||||
{ return langFrom; }
|
||||
|
||||
/// Returns the dictionary's target language, as was read from file's headers.
|
||||
wstring const & getLangTo() const
|
||||
{ return langTo; }
|
||||
|
||||
/// Reads next line from the file. Returns true if reading succeeded --
|
||||
/// the string gets stored in the one passed, along with its physical
|
||||
/// file offset in the file (the uncompressed one if the file is compressed).
|
||||
/// If end of file is reached, false is returned.
|
||||
/// Reading begins from the first line after the headers (ones which start
|
||||
/// with #).
|
||||
bool readNextLine( wstring &, size_t & offset ) throw( Ex, Iconv::Ex );
|
||||
|
||||
/// Similar readNextLine but strip all DSL comments {{...}}
|
||||
bool readNextLineWithoutComments( wstring &, size_t & offset ) throw( Ex, Iconv::Ex );
|
||||
|
||||
/// Returns the number of lines read so far from the file.
|
||||
unsigned getLinesRead() const
|
||||
{ return linesRead; }
|
||||
|
||||
/// Converts the given number of characters to the number of bytes they
|
||||
/// would occupy in the file, knowing its encoding. It's possible to know
|
||||
/// that because no multibyte encodings are supported in .dsls.
|
||||
inline size_t distanceToBytes( size_t ) const;
|
||||
};
|
||||
|
||||
/// This function either removes parts of string enclosed in braces, or leaves
|
||||
/// them intact. The braces themselves are removed always, though.
|
||||
void processUnsortedParts( wstring & str, bool strip );
|
||||
|
||||
/// Expands optional parts of a headword (ones marked with parentheses),
|
||||
/// producing all possible combinations where they are present or absent.
|
||||
void expandOptionalParts( wstring & str, list< wstring > * result,
|
||||
size_t x = 0, bool inside_recurse = false );
|
||||
|
||||
/// Expands all unescaped tildes, inserting tildeReplacement text instead of
|
||||
/// them.
|
||||
void expandTildes( wstring & str, wstring const & tildeReplacement );
|
||||
|
||||
/// Unescapes any escaped chars. Be sure to handle all their special meanings
|
||||
/// before unescaping them.
|
||||
void unescapeDsl( wstring & str );
|
||||
|
||||
/// Normalizes the headword. Currently turns any sequences of consecutive spaces
|
||||
/// into a single space.
|
||||
void normalizeHeadword( wstring & );
|
||||
|
||||
/// Strip DSL {{...}} comments
|
||||
void stripComments( wstring &, bool & );
|
||||
|
||||
inline size_t DslScanner::distanceToBytes( size_t x ) const
|
||||
{
|
||||
switch( encoding )
|
||||
{
|
||||
case Utf16LE:
|
||||
case Utf16BE:
|
||||
return x*2;
|
||||
default:
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the given language name taken from Dsl header (i.e. getLangFrom(),
|
||||
/// getLangTo()) to its proper language id.
|
||||
quint32 dslLanguageToId( wstring const & name );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,293 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#include "editdictionaries.hh"
|
||||
#include "loaddictionaries.hh"
|
||||
#include "dictinfo.hh"
|
||||
#include "mainwindow.hh"
|
||||
#include <QMessageBox>
|
||||
|
||||
using std::vector;
|
||||
|
||||
EditDictionaries::EditDictionaries( QWidget * parent, Config::Class & cfg_,
|
||||
vector< sptr< Dictionary::Class > > & dictionaries_,
|
||||
Instances::Groups & groupInstances_,
|
||||
QNetworkAccessManager & dictNetMgr_ ):
|
||||
QDialog( parent ), cfg( cfg_ ), dictionaries( dictionaries_ ),
|
||||
groupInstances( groupInstances_ ),
|
||||
dictNetMgr( dictNetMgr_ ),
|
||||
origCfg( cfg ),
|
||||
sources( this, cfg ),
|
||||
orderAndProps( new OrderAndProps( this, cfg.dictionaryOrder, cfg.inactiveDictionaries,
|
||||
dictionaries ) ),
|
||||
groups( new Groups( this, dictionaries, cfg.groups, orderAndProps->getCurrentDictionaryOrder() ) ),
|
||||
dictionariesChanged( false ),
|
||||
groupsChanged( false ),
|
||||
lastCurrentTab( 0 )
|
||||
, helpWindow( 0 )
|
||||
, helpAction( this )
|
||||
{
|
||||
// Some groups may have contained links to non-existnent dictionaries. We
|
||||
// would like to preserve them if no edits were done. To that end, we save
|
||||
// the initial group readings so that if no edits were really done, we won't
|
||||
// be changing groups.
|
||||
origCfg.groups = groups->getGroups();
|
||||
origCfg.dictionaryOrder = orderAndProps->getCurrentDictionaryOrder();
|
||||
origCfg.inactiveDictionaries = orderAndProps->getCurrentInactiveDictionaries();
|
||||
|
||||
ui.setupUi( this );
|
||||
|
||||
setWindowIcon( QIcon(":/icons/book.png") );
|
||||
|
||||
ui.tabs->clear();
|
||||
|
||||
ui.tabs->addTab( &sources, QIcon(":/icons/reload.png"), tr( "&Sources" ) );
|
||||
ui.tabs->addTab( orderAndProps.get(), QIcon(":/icons/book.png"), tr( "&Dictionaries" ) );
|
||||
ui.tabs->addTab( groups.get(), QIcon(":/icons/bookcase.png"), tr( "&Groups" ) );
|
||||
|
||||
connect( ui.buttons, SIGNAL( clicked( QAbstractButton * ) ),
|
||||
this, SLOT( buttonBoxClicked( QAbstractButton * ) ) );
|
||||
|
||||
connect( &sources, SIGNAL( rescan() ), this, SLOT( rescanSources() ) );
|
||||
|
||||
connect( groups.get(), SIGNAL( showDictionaryInfo( QString const & ) ),
|
||||
this, SIGNAL( showDictionaryInfo( QString const & ) ) );
|
||||
|
||||
connect( orderAndProps.get(), SIGNAL( showDictionaryHeadwords( QString const & ) ),
|
||||
this, SIGNAL( showDictionaryHeadwords( QString const & ) ) );
|
||||
|
||||
connect( ui.buttons, SIGNAL( helpRequested() ),
|
||||
this, SLOT( helpRequested() ) );
|
||||
|
||||
helpAction.setShortcut( QKeySequence( "F1" ) );
|
||||
helpAction.setShortcutContext( Qt::WidgetWithChildrenShortcut );
|
||||
|
||||
connect( &helpAction, SIGNAL( triggered() ),
|
||||
this, SLOT( helpRequested() ) );
|
||||
|
||||
addAction( &helpAction );
|
||||
|
||||
}
|
||||
|
||||
void EditDictionaries::editGroup( unsigned id )
|
||||
{
|
||||
if ( id == Instances::Group::AllGroupId )
|
||||
ui.tabs->setCurrentIndex( 1 );
|
||||
else
|
||||
{
|
||||
ui.tabs->setCurrentIndex( 2 );
|
||||
groups->editGroup( id );
|
||||
}
|
||||
}
|
||||
|
||||
void EditDictionaries::save()
|
||||
{
|
||||
Config::Groups newGroups = groups->getGroups();
|
||||
Config::Group newOrder = orderAndProps->getCurrentDictionaryOrder();
|
||||
Config::Group newInactive = orderAndProps->getCurrentInactiveDictionaries();
|
||||
|
||||
if ( isSourcesChanged() )
|
||||
acceptChangedSources( false );
|
||||
|
||||
if ( origCfg.groups != newGroups || origCfg.dictionaryOrder != newOrder ||
|
||||
origCfg.inactiveDictionaries != newInactive )
|
||||
{
|
||||
groupsChanged = true;
|
||||
cfg.groups = newGroups;
|
||||
cfg.dictionaryOrder = newOrder;
|
||||
cfg.inactiveDictionaries = newInactive;
|
||||
}
|
||||
}
|
||||
|
||||
void EditDictionaries::accept()
|
||||
{
|
||||
save();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void EditDictionaries::on_tabs_currentChanged( int index )
|
||||
{
|
||||
if ( index == -1 || !isVisible() )
|
||||
return; // Sent upon the construction/destruction
|
||||
|
||||
if ( !lastCurrentTab && index )
|
||||
{
|
||||
// We're switching away from the Sources tab -- if its contents were
|
||||
// changed, we need to either apply or reject now.
|
||||
|
||||
if ( isSourcesChanged() )
|
||||
{
|
||||
ui.tabs->setCurrentIndex( 0 );
|
||||
|
||||
QMessageBox question( QMessageBox::Question, tr( "Sources changed" ),
|
||||
tr( "Some sources were changed. Would you like to accept the changes?" ),
|
||||
QMessageBox::NoButton, this );
|
||||
|
||||
QPushButton * accept = question.addButton( tr( "Accept" ), QMessageBox::AcceptRole );
|
||||
|
||||
question.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
|
||||
|
||||
question.exec();
|
||||
|
||||
if ( question.clickedButton() == accept )
|
||||
{
|
||||
acceptChangedSources( true );
|
||||
|
||||
lastCurrentTab = index;
|
||||
ui.tabs->setCurrentIndex( index );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prevent tab from switching
|
||||
lastCurrentTab = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if ( lastCurrentTab == 1 && index != 1 )
|
||||
{
|
||||
// When switching from the dictionary order, we need to propagate any
|
||||
// changes to the groups.
|
||||
groups->updateDictionaryOrder( orderAndProps->getCurrentDictionaryOrder() );
|
||||
}
|
||||
|
||||
lastCurrentTab = index;
|
||||
}
|
||||
|
||||
void EditDictionaries::rescanSources()
|
||||
{
|
||||
acceptChangedSources( true );
|
||||
}
|
||||
|
||||
void EditDictionaries::buttonBoxClicked( QAbstractButton * button )
|
||||
{
|
||||
if (ui.buttons->buttonRole(button) == QDialogButtonBox::ApplyRole) {
|
||||
if ( isSourcesChanged() ) {
|
||||
acceptChangedSources( true );
|
||||
}
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
bool EditDictionaries::isSourcesChanged() const
|
||||
{
|
||||
return sources.getPaths() != cfg.paths ||
|
||||
sources.getSoundDirs() != cfg.soundDirs ||
|
||||
sources.getHunspell() != cfg.hunspell ||
|
||||
sources.getTransliteration() != cfg.transliteration ||
|
||||
sources.getForvo() != cfg.forvo ||
|
||||
sources.getMediaWikis() != cfg.mediawikis ||
|
||||
sources.getWebSites() != cfg.webSites ||
|
||||
sources.getDictServers() != cfg.dictServers ||
|
||||
sources.getPrograms() != cfg.programs ||
|
||||
sources.getVoiceEngines() != cfg.voiceEngines;
|
||||
}
|
||||
|
||||
void EditDictionaries::acceptChangedSources( bool rebuildGroups )
|
||||
{
|
||||
dictionariesChanged = true;
|
||||
|
||||
Config::Groups savedGroups = groups->getGroups();
|
||||
Config::Group savedOrder = orderAndProps->getCurrentDictionaryOrder();
|
||||
Config::Group savedInactive = orderAndProps->getCurrentInactiveDictionaries();
|
||||
|
||||
cfg.paths = sources.getPaths();
|
||||
cfg.soundDirs = sources.getSoundDirs();
|
||||
cfg.hunspell = sources.getHunspell();
|
||||
cfg.transliteration = sources.getTransliteration();
|
||||
cfg.forvo = sources.getForvo();
|
||||
cfg.mediawikis = sources.getMediaWikis();
|
||||
cfg.webSites = sources.getWebSites();
|
||||
cfg.dictServers = sources.getDictServers();
|
||||
cfg.programs = sources.getPrograms();
|
||||
cfg.voiceEngines = sources.getVoiceEngines();
|
||||
|
||||
groupInstances.clear(); // Those hold pointers to dictionaries, we need to
|
||||
// free them.
|
||||
|
||||
ui.tabs->setUpdatesEnabled( false );
|
||||
ui.tabs->removeTab( 1 );
|
||||
ui.tabs->removeTab( 1 );
|
||||
groups.reset();
|
||||
orderAndProps.reset();
|
||||
|
||||
loadDictionaries( this, true, cfg, dictionaries, dictNetMgr );
|
||||
|
||||
// If no changes to groups were made, update the original data
|
||||
bool noGroupEdits = ( origCfg.groups == savedGroups );
|
||||
|
||||
if ( noGroupEdits )
|
||||
savedGroups = cfg.groups;
|
||||
|
||||
Instances::updateNames( savedGroups, dictionaries );
|
||||
|
||||
bool noOrderEdits = ( origCfg.dictionaryOrder == savedOrder );
|
||||
|
||||
if ( noOrderEdits )
|
||||
savedOrder = cfg.dictionaryOrder;
|
||||
|
||||
Instances::updateNames( savedOrder, dictionaries );
|
||||
|
||||
bool noInactiveEdits = ( origCfg.inactiveDictionaries == savedInactive );
|
||||
|
||||
if ( noInactiveEdits )
|
||||
savedInactive = cfg.inactiveDictionaries;
|
||||
|
||||
Instances::updateNames( savedInactive, dictionaries );
|
||||
|
||||
if ( rebuildGroups )
|
||||
{
|
||||
orderAndProps = new OrderAndProps( this, savedOrder, savedInactive, dictionaries );
|
||||
ui.tabs->insertTab( 1, orderAndProps.get(), QIcon(":/icons/book.png"), tr( "&Dictionaries" ) );
|
||||
|
||||
groups = new Groups( this, dictionaries, savedGroups, orderAndProps->getCurrentDictionaryOrder() );
|
||||
ui.tabs->insertTab( 2, groups.get(), QIcon(":/icons/bookcase.png"), tr( "&Groups" ) );
|
||||
|
||||
ui.tabs->setUpdatesEnabled( true );
|
||||
|
||||
if ( noGroupEdits )
|
||||
origCfg.groups = groups->getGroups();
|
||||
|
||||
if ( noOrderEdits )
|
||||
origCfg.dictionaryOrder = orderAndProps->getCurrentDictionaryOrder();
|
||||
|
||||
if ( noInactiveEdits )
|
||||
origCfg.inactiveDictionaries = orderAndProps->getCurrentInactiveDictionaries();
|
||||
}
|
||||
}
|
||||
|
||||
void EditDictionaries::helpRequested()
|
||||
{
|
||||
if( !helpWindow )
|
||||
{
|
||||
MainWindow * mainWindow = qobject_cast< MainWindow * >( parentWidget() );
|
||||
if( mainWindow )
|
||||
mainWindow->closeGDHelp();
|
||||
|
||||
helpWindow = new Help::HelpWindow( this, cfg );
|
||||
|
||||
if( helpWindow )
|
||||
{
|
||||
helpWindow->setWindowFlags( Qt::Window );
|
||||
|
||||
connect( helpWindow, SIGNAL( needClose() ),
|
||||
this, SLOT( closeHelp() ) );
|
||||
helpWindow->showHelpFor( "Manage dictionaries" );
|
||||
helpWindow->show();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !helpWindow->isVisible() )
|
||||
helpWindow->show();
|
||||
|
||||
helpWindow->activateWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void EditDictionaries::closeHelp()
|
||||
{
|
||||
if( helpWindow )
|
||||
helpWindow->hide();
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
|
||||
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
|
||||
|
||||
#ifndef __EDITDICTIONARIES_HH_INCLUDED__
|
||||
#define __EDITDICTIONARIES_HH_INCLUDED__
|
||||
|
||||
#include "dictionary.hh"
|
||||
#include "config.hh"
|
||||
#include "ui_editdictionaries.h"
|
||||
#include "sources.hh"
|
||||
#include "orderandprops.hh"
|
||||
#include "groups.hh"
|
||||
#include "instances.hh"
|
||||
#include "helpwindow.hh"
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QAction>
|
||||
|
||||
class EditDictionaries: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
EditDictionaries( QWidget * parent, Config::Class & cfg,
|
||||
std::vector< sptr< Dictionary::Class > > & dictionaries,
|
||||
Instances::Groups & groupInstances, // We only clear those on rescan
|
||||
QNetworkAccessManager & dictNetMgr );
|
||||
|
||||
~EditDictionaries()
|
||||
{ if( helpWindow ) delete helpWindow; }
|
||||
|
||||
/// Instructs the dialog to position itself on editing the given group.
|
||||
void editGroup( unsigned id );
|
||||
|
||||
/// Returns true if any changes to the 'dictionaries' vector passed were done.
|
||||
bool areDictionariesChanged() const
|
||||
{ return dictionariesChanged; }
|
||||
|
||||
/// Returns true if groups were changed.
|
||||
bool areGroupsChanged() const
|
||||
{ return groupsChanged; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void accept();
|
||||
|
||||
private slots:
|
||||
|
||||
void on_tabs_currentChanged( int index );
|
||||
|
||||
void buttonBoxClicked( QAbstractButton * button );
|
||||
|
||||
void rescanSources();
|
||||
|
||||
void helpRequested();
|
||||
void closeHelp();
|
||||
|
||||
signals:
|
||||
|
||||
void showDictionaryInfo( QString const & dictId );
|
||||
|
||||
void showDictionaryHeadwords( QString const & dictId );
|
||||
|
||||
private:
|
||||
|
||||
bool isSourcesChanged() const;
|
||||
|
||||
void acceptChangedSources( bool rebuildGroups );
|
||||
|
||||
void save();
|
||||
|
||||
private:
|
||||
|
||||
Config::Class & cfg;
|
||||
std::vector< sptr< Dictionary::Class > > & dictionaries;
|
||||
Instances::Groups & groupInstances;
|
||||
QNetworkAccessManager & dictNetMgr;
|
||||
|
||||
// Backed up to decide later if something was changed or not
|
||||
Config::Class origCfg;
|
||||
|
||||
Ui::EditDictionaries ui;
|
||||
Sources sources;
|
||||
sptr< OrderAndProps > orderAndProps;
|
||||
sptr< Groups > groups;
|
||||
|
||||
bool dictionariesChanged;
|
||||
bool groupsChanged;
|
||||
|
||||
int lastCurrentTab;
|
||||
|
||||
Help::HelpWindow * helpWindow;
|
||||
QAction helpAction;
|
||||
};
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user