File libspelling-0.4.9.obscpio of Package libspelling
07070100000000000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001B00000000libspelling-0.4.9/.foundry07070100000001000081A400000000000000000000000168A626C200000012000000000000000000000000000000000000002600000000libspelling-0.4.9/.foundry/.gitignoretmp/
user/
cache/
07070100000002000081A400000000000000000000000168A626C200000167000000000000000000000000000000000000002100000000libspelling-0.4.9/.gitlab-ci.ymlinclude:
- project: "GNOME/citemplates"
file: "templates/default-rules.yml"
- component: "gitlab.gnome.org/GNOME/citemplates/gnomeos-basic-ci@master"
- component: "gitlab.gnome.org/GNOME/citemplates/basic-deploy-docs@master"
- component: "gitlab.gnome.org/GNOME/citemplates/release-service@master"
inputs:
dist-job-name: "build-gnomeos"
07070100000003000081A400000000000000000000000168A626C2000067A2000000000000000000000000000000000000001A00000000libspelling-0.4.9/COPYING GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
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 this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
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
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey 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 library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
07070100000004000081A400000000000000000000000168A626C2000012E6000000000000000000000000000000000000001700000000libspelling-0.4.9/NEWSlibspelling 0.4.9
=================
This is a bug-fix release for GNOME 48 and newer
* Improve discovery of no-spell-check tags
* Fix various leaks in the testsuite which CI now tests for
* Improve life-cycle tracking of text-region from SpellingEngine
* CI now runs ASAN/LSAN/UBSAN and others on every commit
* Translation updates
libspelling 0.4.8
=================
This is a bug-fix release for GNOME 48 and newer
* Improve initial dictionary selection which is particularly
useful for languages which don't typically use spaces to
separate word boundaries.
* Build system fixes for building without enchant
* Translation updates
libspelling 0.4.7
=================
This is a bug-fix release for GNOME 47 and newer
* Fix a potential assertion failure in B+Tree item removal
* Translation updates
libspelling 0.4.6
=================
This is a bug-fix release for GNOME 47
* Improve detection of asynchronous fragment cancellation to break out
of long-running spellchecking.
* Fix Rust example and documentation links.
libspelling 0.4.5
=================
This is a bug-fix release for GNOME 47
* Various robustness fixes for weak ref tracking
* Various GObject Introspection fixes
* A new build option to install a static library (libspelling-1.a)
* Fix a minor leak of extra_word_chars for a language dictionary
* Fix ICU success check
* Translation updates
libspelling 0.4.4
=================
This is a bug-fix release for GNOME 47
* Adds a few dozen translations for menu items
libspelling 0.4.3
=================
This is a bug-fix release for GNOME 47
* Fix build to disable assertions in release builds
* Fix an off-by-one in assertion
* Add i18n for translations of menu entries
* Ignore empty strings when building job list
libspelling 0.4.2
=================
This is a bug-fix release for GNOME 47
* Limit the number of corrections to 10 in enchant backend to help with
dictionary configurations which produce many results.
* Hide add/ignore menu items when disabled.
* Don't show word corrections when spellcheck is disabled.
libspelling 0.4.1
=================
This is a bug-fix release for GNOME 47
* Fixes an important issue tracking weak pointers from a GtkTextBuffer.
* Fixes an important issue where we could get invalid text iters from
GtkTextBuffer::delete-range() after the deletion occurred when other
signal handlers are in play.
libspelling 0.4.0
=================
This is a stable release for GNOME 47.0
* Protect the SpellingEngine from systems with misconfigured dictionaries.
* Protect the SpellingTextBufferAdapter from NULL language codes.
* Protect the SpellingJob from uncooperative break chars which could
result in zero length runs.
* Fix some incorrect licenses.
libspelling 0.3.1
=================
This is an unstable release for GNOME 47.rc.
* Immediately clear tags for invalidated regions without round-tripping
to the checker thread.
* Bump soname for ABI changes in 0.3
* Fix licenses to be LGPLv2.1+
* Add sysprof profiler marks for performance profiling
* Update example so people know to copy the button-press work
to update the menu immediately.
* Fix pkg-config fields
* Fix libspelling usage from subproject
* Make introspection building optional
* Documentation improvements
libspelling 0.3.0
=================
This is an unstable release for GNOME 47.beta.
The highlight for this release is a new threaded spellchecking
engine. It performs text analysis and dictionary lookups in
bulk off the UI thread. Care is taken to catch collisions in the
face of user editing while spellchecking operations are in flight.
I expect a significant reduction in initial spellchecking time
after opening a document. Opening `gtktextbuffer.c` in Text Editor
was more than 8x faster.
Currently, libspelling relies on GTK main until 4.15.5 is released
so do keep this in mind if you are a distributor.
Some API has changed, though that is unlikely to affect any known
applications using libspelling.
* Many new unit tests are part of the testsuite
* SpellingLanguage was renamed to SpellingDictionary
* SpellingLanguageInfo was renamed to SpellingLanguage
* Various helpers were added to SpellingTextBufferAdapter so
that they can update spelling menus. Applications that update
the cursor position on clicks before showing menus may want to
force the menu updating before `menu.popup`.
* SpellingTextBufferAdapter now uses the new GtkTextBufferCommitNotify
API in GTK 4.15.5 and newer
libspelling 0.2.1
=================
* Fix licenses to all be LGPLv2.1+
* Fix handling of cursor-moved signal
libspelling 0.2.0
=================
This is the first stable release of libspelling, for GNOME 45
While I don't promise that we won't break ABI before 1.0, we do aim
to try for that.
07070100000005000081A400000000000000000000000168A626C20000089B000000000000000000000000000000000000001C00000000libspelling-0.4.9/README.md# libspelling
A spellcheck library for GTK 4.
This library is heavily based upon GNOME Text Editor and GNOME Builder's
spellcheck implementation. However, it is licensed LGPLv2.1+.
## Documentation
[Our documentation](https://gnome.pages.gitlab.gnome.org/libspelling/libspelling-1/) is updated on every commit.
## Installing Dictionaries
Currently, libspelling wraps `enchant-2` only.
That means to get additional dictionaries you need to follow the same directions as enchant.
Generally, that means installing packages like `apsell-en` or `hunspell-fr` and so forth.
Enchant should pick those up and use them the next time a libspelling-based application is run.
## Example
### In C
```c
SpellingChecker *checker = spelling_checker_get_default ();
g_autoptr(SpellingTextBufferAdapter) adapter = spelling_text_buffer_adapter_new (source_buffer, checker);
GMenuModel *extra_menu = spelling_text_buffer_adapter_get_menu_model (adapter);
gtk_text_view_set_extra_menu (GTK_TEXT_VIEW (source_view), extra_menu);
gtk_widget_insert_action_group (GTK_WIDGET (source_view), "spelling", G_ACTION_GROUP (adapter));
spelling_text_buffer_adapter_set_enabled (adapter, TRUE);
```
### In Python
```python
from gi.repository import Spelling
checker = Spelling.Checker.get_default()
adapter = Spelling.TextBufferAdapter.new(buffer, checker)
extra_menu = adapter.get_menu_model()
view.set_extra_menu(extra_menu)
view.insert_action_group('spelling', adapter)
adapter.set_enabled(True)
```
### In JavaScript
```js
const Spelling = imports.gi.Spelling;
let checker = Spelling.Checker.get_default()
let adapter = Spelling.TextBufferAdapter.new(buffer, checker)
let extra_menu = adapter.get_menu_model()
view.set_extra_menu(extra_menu)
view.insert_action_group('spelling', adapter)
adapter.set_enabled(true)
```
### In Rust
Add the [bindings dependency](https://crates.io/crates/libspelling) to your Cargo.toml
```rust
let checker = libspelling::Checker::default();
let adapter = libspelling::TextBufferAdapter::new(&buffer, &checker);
let extra_menu = adapter.menu_model();
view.set_extra_menu(Some(&extra_menu));
view.insert_action_group("spelling", Some(&adapter));
adapter.set_enabled(true);
```
07070100000006000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001700000000libspelling-0.4.9/data07070100000007000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001D00000000libspelling-0.4.9/data/icons07070100000008000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000002200000000libspelling-0.4.9/data/icons/apps07070100000009000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000002B00000000libspelling-0.4.9/data/icons/apps/symbolic0707010000000A000081A400000000000000000000000168A626C20000058B000000000000000000000000000000000000004400000000libspelling-0.4.9/data/icons/apps/symbolic/libspelling-symbolic.svg<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256"><style>.a{fill:#f00}.b{fill:#fff}.c{fill:none;stroke:#fff;stroke-width:16}</style><filter id="f0"><feFlood flood-color="#ffffff" flood-opacity="1" /><feBlend mode="normal" in2="SourceGraphic"/><feComposite in2="SourceAlpha" operator="in" /></filter><g filter="url(#f0)"><path class="a" d="m98 152.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z"/><path class="a" d="m120 152.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z"/><path class="a" d="m142 152.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z"/></g><path class="b" d="m101 137l13.2-33.3h6.5l13.2 33.3h-6.1l-3.7-8.7h-13.6l-3.7 8.7zm11.5-14.1h9.4l-4.7-12.4zm33.4-5.5q2.1 0 3.7 0.4 1.6 0.3 2.6 1 0.9 0.6 1.5 1.5 0.7 1 0.9 1.9 0.2 1 0.2 2.2v12.6h-3.6l-0.5-1.2q-2.6 1.6-6 1.6-3.6 0-5.8-1.7-2.3-1.7-2.3-4 0-1.4 0.8-2.5 0.7-1.1 1.9-1.7 1.2-0.7 3-1.1 1.8-0.4 3.6-0.5 1.8-0.2 4.1-0.2v-0.7q0-0.7-0.2-1.2-0.1-0.5-0.5-1-0.4-0.6-1.5-0.9-0.9-0.3-2.5-0.3-2.2 0-5.8 0.9l-0.8 0.2-1-3.8 0.7-0.3q3.6-1.2 7.5-1.2zm4.1 14.7v-2.8h-0.5q-3.9 0-5.9 0.5-1.9 0.5-1.9 1.6 0 0.7 1.2 1.3 1.2 0.6 2.9 0.6 2.4 0 4.2-1.2z"/><path class="c" d="m64 90c0-9.9 8.1-18 18-18h92c9.9 0 18 8.1 18 18v76c0 9.9-8.1 18-18 18h-92c-9.9 0-18-8.1-18-18z"/></svg>0707010000000B000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001700000000libspelling-0.4.9/docs0707010000000C000081A400000000000000000000000168A626C20000063F000000000000000000000000000000000000002800000000libspelling-0.4.9/docs/Spelling.toml.in[library]
version = "@version@"
browse_url = "https://gitlab.gnome.org/GNOME/libspelling/"
repository_url = "https://gitlab.gnome.org/GNOME/libspelling.git"
website_url = "https://gitlab.gnome.org/GNOME/libspelling/"
docs_url = "https://gnome.pages.gitlab.gnome.org/libspelling/libspelling-1/"
authors = "Christian Hergert"
license = "LGPL-2.1-or-later"
description = "A GNOME library for spellchecking"
dependencies = ["Gio-2.0", "Gtk-4.0", "GtkSource-5"]
devhelp = true
search_index = true
logo_url = "libspelling.svg"
[dependencies."GObject-2.0"]
name = "GObject"
description = "The base type system library"
docs_url = "https://docs.gtk.org/gobject/"
[dependencies."GLib-2.0"]
name = "GLib"
description = "The base type system library"
docs_url = "https://docs.gtk.org/glib/"
[dependencies."Gio-2.0"]
name = "GIO"
description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
docs_url = "https://docs.gtk.org/gio/"
[dependencies."Gtk-4.0"]
name = "GTK"
description = "The GTK toolkit"
docs_url = "https://docs.gtk.org/gtk4/"
[dependencies."GtkSource-5"]
name = "GtkSourceView"
description = "The GTK source editor widget"
docs_url = "https://gnome.pages.gitlab.gnome.org/gtksourceview/gtksourceview5/"
[theme]
name = "basic"
show_index_summary = true
show_class_hierarchy = true
[source-location]
base_url = "https://gitlab.gnome.org/GNOME/libspelling/-/blob/main/"
[extra]
# The same order will be used when generating the index
content_files = [
"overview.md",
]
content_images = [
"libspelling.svg",
]
urlmap_file = "urlmap.js"
0707010000000D000081A400000000000000000000000168A626C20000084E000000000000000000000000000000000000002700000000libspelling-0.4.9/docs/libspelling.svg<?xml version="1.0" encoding="UTF-8"?>
<svg width="256" height="256" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="Gradient" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" style="stop-color:#FF7800;stop-opacity:1" />
<stop offset="100%" style="stop-color:#E01B24;stop-opacity:1" />
</linearGradient>
<filter id="alpha-to-white">
<feColorMatrix in="SourceGraphic" type="matrix"
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
</filter>
<g id="child-svg"><svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 144 144" width="144" height="144"><style>.a{fill:none;stroke:#fff;stroke-width:16}.b{fill:#fff}</style><path fill-rule="evenodd" class="a" d="m8 34c0-9.9 8.1-18 18-18h92c9.9 0 18 8.1 18 18v76c0 9.9-8.1 18-18 18h-92c-9.9 0-18-8.1-18-18z" /><path class="b" d="m42 96.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z" /><path class="b" d="m64 96.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z" /><path class="b" d="m86 96.5c0-1.9 1.6-3.5 3.5-3.5h9c1.9 0 3.5 1.6 3.5 3.5 0 1.9-1.6 3.5-3.5 3.5h-9c-1.9 0-3.5-1.6-3.5-3.5z" /><path fill-rule="evenodd" class="b" d="m45 81l13.2-33.3h6.5l13.2 33.3h-6.1l-3.7-8.7h-13.6l-3.7 8.7zm11.5-14.1h9.4l-4.7-12.4zm33.4-5.5q2.1 0 3.7 0.4 1.6 0.3 2.6 1 0.9 0.6 1.5 1.5 0.7 1 0.9 1.9 0.2 1 0.2 2.2v12.6h-3.6l-0.5-1.2q-2.6 1.6-6 1.6-3.6 0-5.8-1.7-2.3-1.7-2.3-4 0-1.4 0.8-2.5 0.7-1.1 1.9-1.7 1.2-0.7 3-1.1 1.8-0.4 3.6-0.5 1.8-0.2 4.1-0.2v-0.7q0-0.7-0.2-1.2-0.1-0.5-0.5-1-0.4-0.6-1.5-0.9-0.9-0.3-2.5-0.3-2.2 0-5.8 0.9l-0.8 0.2-1-3.8 0.7-0.3q3.6-1.2 7.5-1.2zm4.1 14.7v-2.8h-0.5q-3.9 0-5.9 0.5-1.9 0.5-1.9 1.6 0 0.7 1.2 1.3 1.2 0.6 2.9 0.6 2.4 0 4.2-1.2z" /></svg></g>
</defs>
<rect
width="256"
height="256"
fill="url(#Gradient)"
ry="0"
x="0"
y="0" />
<use xlink:href="#child-svg" filter="url(#alpha-to-white)"
transform="matrix(0.8888888888888888,0,0,0.8888888888888888,64,64)" />
</svg>
0707010000000E000081A400000000000000000000000168A626C200000389000000000000000000000000000000000000002300000000libspelling-0.4.9/docs/meson.buildtoml_conf = configuration_data()
toml_conf.set('version', meson.project_version())
gidocgen = find_program('gi-docgen')
gidocgen_common_args = [
'--quiet',
'--no-namespace-dir',
]
if get_option('werror')
gidocgen_common_args += ['--fatal-warnings']
endif
docs_dir = get_option('datadir') / 'doc'
source_toml = configure_file(
input: 'Spelling.toml.in',
output: 'Spelling.toml',
configuration: toml_conf,
install: true,
install_dir: docs_dir / 'libspelling-@0@'.format(api_version),
)
custom_target('spelling-doc',
input: [ source_toml, libspelling_gir[0] ],
output: 'libspelling-@0@'.format(api_version),
command: [
gidocgen,
'generate',
gidocgen_common_args,
'--config=@INPUT0@',
'--output-dir=@OUTPUT@',
'--content-dir=@0@'.format(meson.current_source_dir()),
'@INPUT1@',
],
build_by_default: true,
install: true,
install_dir: docs_dir,
)
0707010000000F000081A400000000000000000000000168A626C200000173000000000000000000000000000000000000002300000000libspelling-0.4.9/docs/overview.mdTitle: Overview
# Overview
Spelling is a [GNOME](https://www.gnome.org/) library that provides spellchecking
for `GtkTextView` widgets.
## pkg-config name
To build a program that uses Spelling, you can use the following command to get
the cflags and libraries necessary to compile and link.
```sh
gcc hello.c `pkg-config --cflags --libs libspelling-1` -o hello
```
07070100000010000081A400000000000000000000000168A626C20000016E000000000000000000000000000000000000002100000000libspelling-0.4.9/docs/urlmap.js
// A map between namespaces and base URLs for their online documentation
baseURLs = [
[ 'GLib', 'https://docs.gtk.org/glib/' ],
[ 'GObject', 'https://docs.gtk.org/gobject/' ],
[ 'Gio', 'https://docs.gtk.org/gio/' ],
[ 'Gtk', 'https://docs.gtk.org/gtk4/' ],
[ 'GtkSource', 'https://gnome.pages.gitlab.gnome.org/gtksourceview/gtksourceview5/'],
]
07070100000011000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001600000000libspelling-0.4.9/lib07070100000012000081A400000000000000000000000168A626C2000099ED000000000000000000000000000000000000002600000000libspelling-0.4.9/lib/cjhtextregion.c/* cjhtextregion.c
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This file 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 Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "cjhtextregionprivate.h"
#include "cjhtextregionbtree.h"
/**
* SECTION:cjhtextregion
* @Title: CjhTextRegion
* @Short_description: track regions of text with a hybrid B+Tree and piecetable
*
* This data-structure is a hybrid between a PieceTable and a B+Tree, which I've
* decided to call a Piece+Tree. It allows for very fast tracking of regions of
* text (in a single dimension, meaning no sub-regions).
*
* This is very useful for tracking where work still needs to be done in a text
* buffer such as for spelling mistakes, syntax highlighting, error checking, or
* multi-device synchronization.
*
* See_also: https://blogs.gnome.org/chergert/2021/03/26/bplustree_augmented_piecetable/
*/
#ifndef G_DISABLE_ASSERT
# define DEBUG_VALIDATE(a,b) G_STMT_START { if (a) cjh_text_region_node_validate(a,b); } G_STMT_END
#else
# define DEBUG_VALIDATE(a,b) G_STMT_START { } G_STMT_END
#endif
static inline void
cjh_text_region_invalid_cache (CjhTextRegion *region)
{
region->cached_result = NULL;
region->cached_result_offset = 0;
}
G_GNUC_UNUSED static void
cjh_text_region_node_validate (CjhTextRegionNode *node,
CjhTextRegionNode *parent)
{
gsize length = 0;
gsize length_in_parent = 0;
g_assert (node != NULL);
g_assert (UNTAG (node->tagged_parent) == parent);
g_assert (cjh_text_region_node_is_leaf (node) ||
UNTAG (node->tagged_parent) == node->tagged_parent);
g_assert (!parent || !cjh_text_region_node_is_leaf (parent));
g_assert (!parent || !SORTED_ARRAY_IS_EMPTY (&parent->branch.children));
if (parent != NULL)
{
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
if (child->node == node)
{
length_in_parent = child->length;
goto found;
}
});
g_assert_not_reached ();
}
found:
if (parent != NULL)
g_assert_cmpint (length_in_parent, ==, cjh_text_region_node_length (node));
for (CjhTextRegionNode *iter = parent;
iter != NULL;
iter = cjh_text_region_node_get_parent (iter))
g_assert_false (cjh_text_region_node_is_leaf (iter));
if (cjh_text_region_node_is_leaf (node))
{
SORTED_ARRAY_FOREACH (&node->leaf.runs, CjhTextRegionRun, run, {
g_assert_cmpint (run->length, >, 0);
length += run->length;
});
if (node->leaf.prev != NULL)
g_assert_true (cjh_text_region_node_is_leaf (node->leaf.prev));
if (node->leaf.next != NULL)
g_assert_true (cjh_text_region_node_is_leaf (node->leaf.next));
}
else
{
SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, {
CjhTextRegionChild *next = SORTED_ARRAY_FOREACH_PEEK (&node->branch.children);
g_assert_nonnull (child->node);
g_assert_cmpint (child->length, >, 0);
g_assert_cmpint (child->length, ==, cjh_text_region_node_length (child->node));
g_assert_true (cjh_text_region_node_get_parent (child->node) == node);
length += child->length;
if (next != NULL && next->node)
{
g_assert_cmpint (cjh_text_region_node_is_leaf (child->node), ==,
cjh_text_region_node_is_leaf (next->node));
if (cjh_text_region_node_is_leaf (child->node))
{
g_assert_true (child->node->leaf.next == next->node);
g_assert_true (child->node == next->node->leaf.prev);
}
else
{
g_assert_true (child->node->branch.next == next->node);
g_assert_true (child->node == next->node->branch.prev);
}
}
});
}
if (parent != NULL)
g_assert_cmpint (length_in_parent, ==, length);
}
static void
cjh_text_region_split (CjhTextRegion *region,
gsize offset,
const CjhTextRegionRun *run,
CjhTextRegionRun *left,
CjhTextRegionRun *right)
{
if (region->split_func != NULL)
region->split_func (offset, run, left, right);
}
static CjhTextRegionNode *
cjh_text_region_node_new (CjhTextRegionNode *parent,
gboolean is_leaf)
{
CjhTextRegionNode *node;
g_assert (UNTAG (parent) == parent);
node = g_new0 (CjhTextRegionNode, 1);
node->tagged_parent = TAG (parent, is_leaf);
if (is_leaf)
{
SORTED_ARRAY_INIT (&node->leaf.runs);
node->leaf.prev = NULL;
node->leaf.next = NULL;
}
else
{
SORTED_ARRAY_INIT (&node->branch.children);
}
g_assert (cjh_text_region_node_get_parent (node) == parent);
return node;
}
static void
cjh_text_region_subtract_from_parents (CjhTextRegion *region,
CjhTextRegionNode *node,
gsize length)
{
CjhTextRegionNode *parent = cjh_text_region_node_get_parent (node);
if (parent == NULL || length == 0)
return;
cjh_text_region_invalid_cache (region);
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
if (child->node == node)
{
g_assert (length <= child->length);
child->length -= length;
cjh_text_region_subtract_from_parents (region, parent, length);
return;
}
});
g_assert_not_reached ();
}
static void
cjh_text_region_add_to_parents (CjhTextRegion *region,
CjhTextRegionNode *node,
gsize length)
{
CjhTextRegionNode *parent = cjh_text_region_node_get_parent (node);
if (parent == NULL || length == 0)
return;
cjh_text_region_invalid_cache (region);
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
if (child->node == node)
{
child->length += length;
cjh_text_region_add_to_parents (region, parent, length);
return;
}
});
g_assert_not_reached ();
}
static inline gboolean
cjh_text_region_node_is_root (CjhTextRegionNode *node)
{
return node != NULL && cjh_text_region_node_get_parent (node) == NULL;
}
static CjhTextRegionNode *
cjh_text_region_node_search_recurse (CjhTextRegionNode *node,
gsize offset,
gsize *offset_within_node)
{
CjhTextRegionChild *last_child = NULL;
g_assert (node != NULL);
g_assert (offset_within_node != NULL);
/* If we reached a leaf, that is all we need to do */
if (cjh_text_region_node_is_leaf (node))
{
*offset_within_node = offset;
return node;
}
g_assert (!cjh_text_region_node_is_leaf (node));
g_assert (!SORTED_ARRAY_IS_EMPTY (&node->branch.children));
g_assert (offset <= cjh_text_region_node_length (node));
SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, {
g_assert (child->length > 0);
g_assert (child->node != NULL);
if (offset < child->length)
return cjh_text_region_node_search_recurse (child->node, offset, offset_within_node);
offset -= child->length;
last_child = child;
});
/* We're right-most, so it belongs at the end. Add back the length we removed
* while trying to resolve within the parent branch.
*/
g_assert (last_child != NULL);
g_assert (node->branch.next == NULL);
return cjh_text_region_node_search_recurse (last_child->node,
offset + last_child->length,
offset_within_node);
}
static CjhTextRegionNode *
cjh_text_region_search (CjhTextRegion *region,
gsize offset,
gsize *offset_within_node)
{
CjhTextRegionNode *result;
*offset_within_node = 0;
g_assert (region->cached_result == NULL ||
cjh_text_region_node_is_leaf (region->cached_result));
/* Try to reuse cached node to avoid traversal since in most cases
* an insert will be followed by another insert.
*/
if (region->cached_result != NULL && offset >= region->cached_result_offset)
{
gsize calc_offset = region->cached_result_offset + cjh_text_region_node_length (region->cached_result);
if (offset < calc_offset ||
(offset == calc_offset && region->cached_result->leaf.next == NULL))
{
*offset_within_node = offset - region->cached_result_offset;
return region->cached_result;
}
}
if (offset == 0)
result = _cjh_text_region_get_first_leaf (region);
else
result = cjh_text_region_node_search_recurse (®ion->root, offset, offset_within_node);
/* Now save it for cached reuse */
if (result != NULL)
{
region->cached_result = result;
region->cached_result_offset = offset - *offset_within_node;
}
return result;
}
static void
cjh_text_region_root_split (CjhTextRegion *region,
CjhTextRegionNode *root)
{
CjhTextRegionNode *left;
CjhTextRegionNode *right;
CjhTextRegionChild new_child;
g_assert (region != NULL);
g_assert (!cjh_text_region_node_is_leaf (root));
g_assert (cjh_text_region_node_is_root (root));
g_assert (!SORTED_ARRAY_IS_EMPTY (&root->branch.children));
left = cjh_text_region_node_new (root, FALSE);
right = cjh_text_region_node_new (root, FALSE);
left->branch.next = right;
right->branch.prev = left;
SORTED_ARRAY_SPLIT2 (&root->branch.children, &left->branch.children, &right->branch.children);
SORTED_ARRAY_FOREACH (&left->branch.children, CjhTextRegionChild, child, {
cjh_text_region_node_set_parent (child->node, left);
});
SORTED_ARRAY_FOREACH (&right->branch.children, CjhTextRegionChild, child, {
cjh_text_region_node_set_parent (child->node, right);
});
g_assert (SORTED_ARRAY_IS_EMPTY (&root->branch.children));
new_child.node = right;
new_child.length = cjh_text_region_node_length (right);
SORTED_ARRAY_PUSH_HEAD (&root->branch.children, new_child);
new_child.node = left;
new_child.length = cjh_text_region_node_length (left);
SORTED_ARRAY_PUSH_HEAD (&root->branch.children, new_child);
g_assert (SORTED_ARRAY_LENGTH (&root->branch.children) == 2);
DEBUG_VALIDATE (root, NULL);
DEBUG_VALIDATE (left, root);
DEBUG_VALIDATE (right, root);
}
static CjhTextRegionNode *
cjh_text_region_branch_split (CjhTextRegion *region,
CjhTextRegionNode *left)
{
G_GNUC_UNUSED gsize old_length;
CjhTextRegionNode *parent;
CjhTextRegionNode *right;
gsize right_length = 0;
gsize left_length = 0;
guint i = 0;
g_assert (region != NULL);
g_assert (left != NULL);
g_assert (!cjh_text_region_node_is_leaf (left));
g_assert (!cjh_text_region_node_is_root (left));
old_length = cjh_text_region_node_length (left);
/*
* This operation should not change the height of the tree. Only
* splitting the root node can change the height of the tree. So
* here we add a new right node, and update the parent to point to
* it right after our node.
*
* Since no new items are added, lengths do not change and we do
* not need to update lengths up the hierarchy except for our two
* effected nodes (and their direct parent).
*/
parent = cjh_text_region_node_get_parent (left);
/* Create a new node to split half the items into */
right = cjh_text_region_node_new (parent, FALSE);
/* Insert node into branches linked list */
right->branch.next = left->branch.next;
right->branch.prev = left;
if (right->branch.next != NULL)
right->branch.next->branch.prev = right;
left->branch.next = right;
SORTED_ARRAY_SPLIT (&left->branch.children, &right->branch.children);
SORTED_ARRAY_FOREACH (&right->branch.children, CjhTextRegionChild, child, {
cjh_text_region_node_set_parent (child->node, right);
});
#ifndef G_DISABLE_ASSERT
SORTED_ARRAY_FOREACH (&left->branch.children, CjhTextRegionChild, child, {
g_assert (cjh_text_region_node_get_parent (child->node) == left);
});
#endif
right_length = cjh_text_region_node_length (right);
left_length = cjh_text_region_node_length (left);
g_assert (right_length + left_length == old_length);
g_assert (SORTED_ARRAY_LENGTH (&parent->branch.children) < SORTED_ARRAY_CAPACITY (&parent->branch.children));
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
i++;
if (child->node == left)
{
CjhTextRegionChild right_child;
right_child.node = right;
right_child.length = right_length;
child->length = left_length;
SORTED_ARRAY_INSERT_VAL (&parent->branch.children, i, right_child);
DEBUG_VALIDATE (left, parent);
DEBUG_VALIDATE (right, parent);
DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent));
return right;
}
});
g_assert_not_reached ();
}
static CjhTextRegionNode *
cjh_text_region_leaf_split (CjhTextRegion *region,
CjhTextRegionNode *left)
{
G_GNUC_UNUSED gsize length;
CjhTextRegionNode *parent;
CjhTextRegionNode *right;
gsize right_length;
guint i;
g_assert (region != NULL);
g_assert (left != NULL);
g_assert (cjh_text_region_node_is_leaf (left));
parent = cjh_text_region_node_get_parent (left);
g_assert (parent != left);
g_assert (!cjh_text_region_node_is_leaf (parent));
g_assert (!SORTED_ARRAY_IS_EMPTY (&parent->branch.children));
g_assert (!SORTED_ARRAY_IS_FULL (&parent->branch.children));
length = cjh_text_region_node_length (left);
g_assert (length > 0);
DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent));
DEBUG_VALIDATE (left, parent);
right = cjh_text_region_node_new (parent, TRUE);
SORTED_ARRAY_SPLIT (&left->leaf.runs, &right->leaf.runs);
right_length = cjh_text_region_node_length (right);
g_assert (length == right_length + cjh_text_region_node_length (left));
g_assert (cjh_text_region_node_is_leaf (left));
g_assert (cjh_text_region_node_is_leaf (right));
i = 0;
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
G_GNUC_UNUSED const CjhTextRegionChild *next = SORTED_ARRAY_FOREACH_PEEK (&parent->branch.children);
++i;
g_assert (cjh_text_region_node_is_leaf (child->node));
g_assert (next == NULL || cjh_text_region_node_is_leaf (next->node));
if (child->node == left)
{
CjhTextRegionChild right_child;
g_assert (child->length >= right_length);
g_assert (next == NULL || left->leaf.next == next->node);
if (left->leaf.next != NULL)
left->leaf.next->leaf.prev = right;
right->leaf.prev = left;
right->leaf.next = left->leaf.next;
left->leaf.next = right;
right_child.node = right;
right_child.length = right_length;
child->length -= right_length;
g_assert (child->length > 0);
g_assert (right_child.length > 0);
SORTED_ARRAY_INSERT_VAL (&parent->branch.children, i, right_child);
g_assert (right != NULL);
g_assert (cjh_text_region_node_is_leaf (right));
g_assert (right->leaf.prev == left);
g_assert (left->leaf.next == right);
DEBUG_VALIDATE (left, parent);
DEBUG_VALIDATE (right, parent);
DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent));
return right;
}
});
g_assert_not_reached ();
}
static inline gboolean
cjh_text_region_node_needs_split (CjhTextRegionNode *node)
{
/*
* We want to split the tree node if there is not enough space to
* split a single entry into two AND add a new entry. That means we
* need two empty slots before we ever perform an insert.
*/
if (!cjh_text_region_node_is_leaf (node))
return SORTED_ARRAY_LENGTH (&node->branch.children) >= (SORTED_ARRAY_CAPACITY (&node->branch.children) - 2);
else
return SORTED_ARRAY_LENGTH (&node->leaf.runs) >= (SORTED_ARRAY_CAPACITY (&node->leaf.runs) - 2);
}
static inline CjhTextRegionNode *
cjh_text_region_node_split (CjhTextRegion *region,
CjhTextRegionNode *node)
{
CjhTextRegionNode *parent;
g_assert (node != NULL);
cjh_text_region_invalid_cache (region);
parent = cjh_text_region_node_get_parent (node);
if (parent != NULL &&
cjh_text_region_node_needs_split (parent))
cjh_text_region_node_split (region, parent);
if (!cjh_text_region_node_is_leaf (node))
{
if (cjh_text_region_node_is_root (node))
{
cjh_text_region_root_split (region, node);
return ®ion->root;
}
return cjh_text_region_branch_split (region, node);
}
else
{
return cjh_text_region_leaf_split (region, node);
}
}
CjhTextRegion *
_cjh_text_region_new (CjhTextRegionJoinFunc join_func,
CjhTextRegionSplitFunc split_func)
{
CjhTextRegion *self;
CjhTextRegionNode *leaf;
CjhTextRegionChild child;
self = g_new0 (CjhTextRegion, 1);
self->length = 0;
self->join_func = join_func;
self->split_func = split_func;
/* The B+Tree has a root node (a branch) and a single leaf
* as a child to simplify how we do splits/rotations/etc.
*/
leaf = cjh_text_region_node_new (&self->root, TRUE);
child.node = leaf;
child.length = 0;
SORTED_ARRAY_INIT (&self->root.branch.children);
SORTED_ARRAY_PUSH_HEAD (&self->root.branch.children, child);
return self;
}
static void
cjh_text_region_node_free (CjhTextRegionNode *node)
{
if (node == NULL)
return;
if (!cjh_text_region_node_is_leaf (node))
{
SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, {
cjh_text_region_node_free (child->node);
});
}
g_free (node);
}
void
_cjh_text_region_free (CjhTextRegion *region)
{
if (region == NULL)
return;
g_assert (cjh_text_region_node_is_root (®ion->root));
g_assert (!SORTED_ARRAY_IS_EMPTY (®ion->root.branch.children));
SORTED_ARRAY_FOREACH (®ion->root.branch.children, CjhTextRegionChild, child, {
cjh_text_region_node_free (child->node);
});
g_free (region);
}
static inline gboolean
join_run (CjhTextRegion *region,
gsize offset,
const CjhTextRegionRun *left,
const CjhTextRegionRun *right,
CjhTextRegionRun *joined)
{
gboolean join;
g_assert (region != NULL);
g_assert (left != NULL);
g_assert (right != NULL);
g_assert (joined != NULL);
if (region->join_func != NULL)
join = region->join_func (offset, left, right);
else
join = FALSE;
if (join)
{
joined->length = left->length + right->length;
joined->data = left->data;
return TRUE;
}
return FALSE;
}
void
_cjh_text_region_insert (CjhTextRegion *region,
gsize offset,
gsize length,
gpointer data)
{
CjhTextRegionRun to_insert = { length, data };
CjhTextRegionNode *target;
CjhTextRegionNode *node;
CjhTextRegionNode *parent;
gsize offset_within_node = offset;
guint i;
g_assert (region != NULL);
g_assert (offset <= region->length);
if (length == 0)
return;
target = cjh_text_region_search (region, offset, &offset_within_node);
g_assert (cjh_text_region_node_is_leaf (target));
g_assert (offset_within_node <= cjh_text_region_node_length (target));
/* We should only hit this if we have an empty tree. */
if G_UNLIKELY (SORTED_ARRAY_IS_EMPTY (&target->leaf.runs))
{
g_assert (offset == 0);
SORTED_ARRAY_PUSH_HEAD (&target->leaf.runs, to_insert);
g_assert (cjh_text_region_node_length (target) == length);
goto inserted;
}
/* Split up to region->root if necessary */
if (cjh_text_region_node_needs_split (target))
{
DEBUG_VALIDATE (target, cjh_text_region_node_get_parent (target));
/* Split the target into two and then re-locate our position as
* we might need to be in another node.
*
* TODO: Potentially optimization here to look at prev/next to
* locate which we need. Complicated though since we don't
* have real offsets.
*/
cjh_text_region_node_split (region, target);
target = cjh_text_region_search (region, offset, &offset_within_node);
g_assert (cjh_text_region_node_is_leaf (target));
g_assert (offset_within_node <= cjh_text_region_node_length (target));
DEBUG_VALIDATE (target, cjh_text_region_node_get_parent (target));
}
i = 0;
SORTED_ARRAY_FOREACH (&target->leaf.runs, CjhTextRegionRun, run, {
/*
* If this insert request would happen immediately after this run,
* we want to see if we can chain it to this run or the beginning
* of the next run.
*
* Note: We coudld also follow the the B+tree style linked-leaf to
* the next leaf and compare against it's first item. But that is
* out of scope for this prototype.
*/
if (offset_within_node == 0)
{
if (!join_run (region, offset, &to_insert, run, run))
SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i, to_insert);
goto inserted;
}
else if (offset_within_node == run->length)
{
CjhTextRegionRun *next = SORTED_ARRAY_FOREACH_PEEK (&target->leaf.runs);
/* Try to chain to the end of this run or the beginning of the next */
if (!join_run (region, offset, run, &to_insert, run) &&
(next == NULL || !join_run (region, offset, &to_insert, next, next)))
SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i + 1, to_insert);
goto inserted;
}
else if (offset_within_node < run->length)
{
CjhTextRegionRun left;
CjhTextRegionRun right;
left.length = offset_within_node;
left.data = run->data;
right.length = run->length - offset_within_node;
right.data = run->data;
cjh_text_region_split (region, offset - offset_within_node, run, &left, &right);
*run = left;
if (!join_run (region, offset, &to_insert, &right, &to_insert))
SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i + 1, right);
if (!join_run (region, offset - offset_within_node, run, &to_insert, run))
SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i + 1, to_insert);
goto inserted;
}
offset_within_node -= run->length;
i++;
});
g_assert_not_reached ();
inserted:
g_assert (target != NULL);
/*
* Now update each of the parent nodes in the tree so that they have
* an apprporiate length along with the child pointer. This allows them
* to calculate offsets while walking the tree (without derefrencing the
* child node) at the cost of us walking back up the tree.
*/
for (parent = cjh_text_region_node_get_parent (target), node = target;
parent != NULL;
node = parent, parent = cjh_text_region_node_get_parent (node))
{
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
if (child->node == node)
{
child->length += length;
goto found_in_parent;
}
});
g_assert_not_reached ();
found_in_parent:
DEBUG_VALIDATE (node, parent);
continue;
}
region->length += length;
g_assert (region->length == cjh_text_region_node_length (®ion->root));
}
void
_cjh_text_region_replace (CjhTextRegion *region,
gsize offset,
gsize length,
gpointer data)
{
g_assert (region != NULL);
if (length == 0)
return;
/* TODO: This could be optimized to avoid possible splits
* by merging adjoining runs.
*/
_cjh_text_region_remove (region, offset, length);
_cjh_text_region_insert (region, offset, length, data);
g_assert (region->length == cjh_text_region_node_length (®ion->root));
}
guint
_cjh_text_region_get_length (CjhTextRegion *region)
{
g_assert (region != NULL);
return region->length;
}
static void
cjh_text_region_branch_compact (CjhTextRegion *region,
CjhTextRegionNode *node)
{
CjhTextRegionNode *parent;
CjhTextRegionNode *left;
CjhTextRegionNode *right;
CjhTextRegionNode *target;
gsize added = 0;
gsize length;
g_assert (region != NULL);
g_assert (node != NULL);
g_assert (!cjh_text_region_node_is_leaf (node));
SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, {
if (child->node == NULL)
{
g_assert (child->length == 0);
SORTED_ARRAY_FOREACH_REMOVE (&node->branch.children);
}
});
if (cjh_text_region_node_is_root (node))
return;
parent = cjh_text_region_node_get_parent (node);
g_assert (parent != NULL);
g_assert (!cjh_text_region_node_is_leaf (parent));
/* Reparent child in our stead if we can remove this node */
if (SORTED_ARRAY_LENGTH (&node->branch.children) == 1 &&
SORTED_ARRAY_LENGTH (&parent->branch.children) == 1)
{
CjhTextRegionChild *descendant = &SORTED_ARRAY_PEEK_HEAD (&node->branch.children);
g_assert (parent->branch.prev == NULL);
g_assert (parent->branch.next == NULL);
g_assert (node->branch.prev == NULL);
g_assert (node->branch.next == NULL);
g_assert (descendant->node != NULL);
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
if (child->node == node)
{
child->node = descendant->node;
cjh_text_region_node_set_parent (child->node, parent);
descendant->node = NULL;
descendant->length = 0;
goto compact_parent;
}
});
g_assert_not_reached ();
}
if (node->branch.prev == NULL && node->branch.next == NULL)
return;
if (SORTED_ARRAY_LENGTH (&node->branch.children) >= CJH_TEXT_REGION_MIN_BRANCHES)
return;
length = cjh_text_region_node_length (node);
cjh_text_region_subtract_from_parents (region, node, length);
/* Remove this node, we'll reparent the children with edges */
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
if (child->node == node)
{
SORTED_ARRAY_FOREACH_REMOVE (&parent->branch.children);
goto found;
}
});
g_assert_not_reached ();
found:
left = node->branch.prev;
right = node->branch.next;
if (left != NULL)
left->branch.next = right;
if (right != NULL)
right->branch.prev = left;
if (left == NULL ||
(right != NULL &&
SORTED_ARRAY_LENGTH (&left->branch.children) > SORTED_ARRAY_LENGTH (&right->branch.children)))
{
target = right;
g_assert (target->branch.prev == left);
SORTED_ARRAY_FOREACH_REVERSE (&node->branch.children, CjhTextRegionChild, child, {
if (SORTED_ARRAY_LENGTH (&target->branch.children) >= CJH_TEXT_REGION_MAX_BRANCHES-1)
{
cjh_text_region_add_to_parents (region, target, added);
added = 0;
cjh_text_region_branch_split (region, target);
g_assert (target->branch.prev == left);
}
cjh_text_region_node_set_parent (child->node, target);
added += child->length;
SORTED_ARRAY_PUSH_HEAD (&target->branch.children, *child);
child->node = NULL;
child->length = 0;
});
cjh_text_region_add_to_parents (region, target, added);
}
else
{
target = left;
g_assert (target->branch.next == right);
SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, {
if (SORTED_ARRAY_LENGTH (&target->branch.children) >= CJH_TEXT_REGION_MAX_BRANCHES-1)
{
cjh_text_region_add_to_parents (region, target, added);
added = 0;
target = cjh_text_region_branch_split (region, target);
}
cjh_text_region_node_set_parent (child->node, target);
added += child->length;
SORTED_ARRAY_PUSH_TAIL (&target->branch.children, *child);
child->node = NULL;
child->length = 0;
});
cjh_text_region_add_to_parents (region, target, added);
}
DEBUG_VALIDATE (left, cjh_text_region_node_get_parent (left));
DEBUG_VALIDATE (right, cjh_text_region_node_get_parent (right));
DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent));
compact_parent:
if (parent != NULL)
cjh_text_region_branch_compact (region, parent);
cjh_text_region_node_free (node);
}
static void
cjh_text_region_leaf_compact (CjhTextRegion *region,
CjhTextRegionNode *node)
{
CjhTextRegionNode *parent;
CjhTextRegionNode *target;
CjhTextRegionNode *left;
CjhTextRegionNode *right;
gsize added = 0;
g_assert (region != NULL);
g_assert (node != NULL);
g_assert (cjh_text_region_node_is_leaf (node));
g_assert (SORTED_ARRAY_LENGTH (&node->leaf.runs) < CJH_TEXT_REGION_MIN_RUNS);
/* Short-circuit if we are the only node */
if (node->leaf.prev == NULL && node->leaf.next == NULL)
return;
parent = cjh_text_region_node_get_parent (node);
left = node->leaf.prev;
right = node->leaf.next;
g_assert (parent != NULL);
g_assert (!cjh_text_region_node_is_leaf (parent));
g_assert (left == NULL || cjh_text_region_node_is_leaf (left));
g_assert (right == NULL || cjh_text_region_node_is_leaf (right));
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
if (child->node == node)
{
cjh_text_region_subtract_from_parents (region, node, child->length);
g_assert (child->length == 0);
SORTED_ARRAY_FOREACH_REMOVE (&parent->branch.children);
goto found;
}
});
g_assert_not_reached ();
found:
if (left != NULL)
left->leaf.next = right;
if (right != NULL)
right->leaf.prev = left;
node->leaf.next = NULL;
node->leaf.prev = NULL;
if (left == NULL ||
(right != NULL &&
SORTED_ARRAY_LENGTH (&left->leaf.runs) > SORTED_ARRAY_LENGTH (&right->leaf.runs)))
{
target = right;
g_assert (target->leaf.prev == left);
SORTED_ARRAY_FOREACH_REVERSE (&node->leaf.runs, CjhTextRegionRun, run, {
if (SORTED_ARRAY_LENGTH (&target->leaf.runs) >= CJH_TEXT_REGION_MAX_RUNS-1)
{
cjh_text_region_add_to_parents (region, target, added);
added = 0;
cjh_text_region_node_split (region, target);
g_assert (target->leaf.prev == left);
}
added += run->length;
SORTED_ARRAY_PUSH_HEAD (&target->leaf.runs, *run);
});
cjh_text_region_add_to_parents (region, target, added);
}
else
{
target = left;
g_assert (target->leaf.next == right);
SORTED_ARRAY_FOREACH (&node->leaf.runs, CjhTextRegionRun, run, {
if (SORTED_ARRAY_LENGTH (&target->leaf.runs) >= CJH_TEXT_REGION_MAX_RUNS-1)
{
cjh_text_region_add_to_parents (region, target, added);
added = 0;
target = cjh_text_region_node_split (region, target);
left = target;
}
added += run->length;
SORTED_ARRAY_PUSH_TAIL (&target->leaf.runs, *run);
});
cjh_text_region_add_to_parents (region, target, added);
}
DEBUG_VALIDATE (left, cjh_text_region_node_get_parent (left));
DEBUG_VALIDATE (right, cjh_text_region_node_get_parent (right));
DEBUG_VALIDATE (parent, cjh_text_region_node_get_parent (parent));
cjh_text_region_branch_compact (region, parent);
cjh_text_region_node_free (node);
}
void
_cjh_text_region_remove (CjhTextRegion *region,
gsize offset,
gsize length)
{
CjhTextRegionNode *target;
gsize offset_within_node;
gsize to_remove = length;
gsize calc_offset;
guint i;
g_assert (region != NULL);
g_assert (length <= region->length);
g_assert (offset < region->length);
g_assert (length <= region->length - offset);
if (length == 0)
return;
target = cjh_text_region_search (region, offset, &offset_within_node);
g_assert (target != NULL);
g_assert (cjh_text_region_node_is_leaf (target));
g_assert (SORTED_ARRAY_LENGTH (&target->leaf.runs) > 0);
g_assert (offset >= offset_within_node);
calc_offset = offset - offset_within_node;
i = 0;
SORTED_ARRAY_FOREACH (&target->leaf.runs, CjhTextRegionRun, run, {
++i;
g_assert (to_remove > 0);
if (offset_within_node >= run->length)
{
offset_within_node -= run->length;
calc_offset += run->length;
}
else if (offset_within_node > 0 && to_remove >= run->length - offset_within_node)
{
CjhTextRegionRun left;
CjhTextRegionRun right;
left.length = offset_within_node;
left.data = run->data;
right.length = run->length - left.length;
right.data = run->data;
cjh_text_region_split (region, calc_offset, run, &left, &right);
to_remove -= right.length;
calc_offset += left.length;
offset_within_node = 0;
*run = left;
if (to_remove == 0)
break;
}
else if (offset_within_node > 0 && to_remove < run->length - offset_within_node)
{
CjhTextRegionRun saved;
CjhTextRegionRun left;
CjhTextRegionRun right;
CjhTextRegionRun right2;
CjhTextRegionRun center;
left.length = offset_within_node;
left.data = run->data;
right.length = run->length - left.length;
right.data = run->data;
cjh_text_region_split (region, calc_offset, run, &left, &right);
center.length = to_remove;
center.data = run->data;
right2.length = run->length - offset_within_node - to_remove;
right2.data = run->data;
cjh_text_region_split (region, calc_offset + left.length, &right, ¢er, &right2);
saved = *run;
*run = left;
if (!join_run (region, calc_offset, run, &right2, run))
{
if (!SORTED_ARRAY_IS_FULL (&target->leaf.runs))
{
/* If there is space in our sorted array for the additional
* split we have here, then go ahead and do that since it
* avoids re-entering the btree.
*/
SORTED_ARRAY_INSERT_VAL (&target->leaf.runs, i, right2);
}
else
{
/* Degenerate case in that our leaf is full. We could
* potentially peek at the next and push to the head there, but
* I'm avoiding that because it would amortize out with the
* split in place anyway.
*/
*run = saved;
cjh_text_region_node_split (region, target);
_cjh_text_region_remove (region, offset, length);
break;
}
}
offset_within_node = 0;
to_remove = 0;
break;
}
else if (offset_within_node == 0 && to_remove < run->length)
{
CjhTextRegionRun left;
CjhTextRegionRun right;
left.length = to_remove;
left.data = run->data;
right.length = run->length - to_remove;
right.data = run->data;
cjh_text_region_split (region, calc_offset, run, &left, &right);
to_remove = 0;
offset_within_node = 0;
*run = right;
break;
}
else if (offset_within_node == 0 && to_remove >= run->length)
{
to_remove -= run->length;
SORTED_ARRAY_FOREACH_REMOVE (&target->leaf.runs);
if (to_remove == 0)
break;
}
else
{
g_assert_not_reached ();
}
g_assert (to_remove > 0);
});
region->length -= length - to_remove;
cjh_text_region_subtract_from_parents (region, target, length - to_remove);
if (SORTED_ARRAY_LENGTH (&target->leaf.runs) < CJH_TEXT_REGION_MIN_RUNS)
cjh_text_region_leaf_compact (region, target);
g_assert (region->length == cjh_text_region_node_length (®ion->root));
if (to_remove > 0)
_cjh_text_region_remove (region, offset, to_remove);
}
void
_cjh_text_region_foreach (CjhTextRegion *region,
CjhTextRegionForeachFunc func,
gpointer user_data)
{
CjhTextRegionNode *leaf;
gsize offset = 0;
g_return_if_fail (region != NULL);
g_return_if_fail (func != NULL);
for (leaf = _cjh_text_region_get_first_leaf (region);
leaf != NULL;
leaf = leaf->leaf.next)
{
g_assert (leaf->leaf.next == NULL || leaf->leaf.next->leaf.prev == leaf);
SORTED_ARRAY_FOREACH (&leaf->leaf.runs, CjhTextRegionRun, run, {
if (func (offset, run, user_data))
return;
offset += run->length;
});
}
}
void
_cjh_text_region_foreach_in_range (CjhTextRegion *region,
gsize begin,
gsize end,
CjhTextRegionForeachFunc func,
gpointer user_data)
{
CjhTextRegionNode *leaf;
gsize position;
gsize offset_within_node = 0;
g_return_if_fail (region != NULL);
g_return_if_fail (func != NULL);
g_return_if_fail (begin <= region->length);
g_return_if_fail (end <= region->length);
g_return_if_fail (begin <= end);
if (begin == end || begin == region->length)
return;
if (begin == 0)
leaf = _cjh_text_region_get_first_leaf (region);
else
leaf = cjh_text_region_search (region, begin, &offset_within_node);
g_assert (offset_within_node < cjh_text_region_node_length (leaf));
position = begin - offset_within_node;
while (position < end)
{
SORTED_ARRAY_FOREACH (&leaf->leaf.runs, CjhTextRegionRun, run, {
if (offset_within_node >= run->length)
{
offset_within_node -= run->length;
}
else
{
offset_within_node = 0;
if (func (position, run, user_data))
return;
}
position += run->length;
if (position >= end)
break;
});
leaf = leaf->leaf.next;
}
}
07070100000013000081A400000000000000000000000168A626C200007BEF000000000000000000000000000000000000002B00000000libspelling-0.4.9/lib/cjhtextregionbtree.h/* cjhtextregionbtree.h
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This file 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 Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include "cjhtextregionprivate.h"
G_BEGIN_DECLS
/* The following set of macros are used to create a queue similar to a
* double-ended linked list but using integers as indexes for items within the
* queue. Doing so allows for inserting or removing items from a b+tree node
* without having to memmove() data to maintain sorting orders.
*/
#define VAL_QUEUE_INVALID(Node) ((glib_typeof((Node)->head))-1)
#define VAL_QUEUE_LENGTH(Node) ((Node)->length)
#define VAL_QUEUE_EMPTY(Node) ((Node)->head == VAL_QUEUE_INVALID(Node))
#define VAL_QUEUE_PEEK_HEAD(Node) ((Node)->head)
#define VAL_QUEUE_PEEK_TAIL(Node) ((Node)->tail)
#define VAL_QUEUE_IS_VALID(Node, ID) ((ID) != VAL_QUEUE_INVALID(Node))
#define VAL_QUEUE_NODE(Type, N_Items) \
struct { \
Type length; \
Type head; \
Type tail; \
struct { \
Type prev; \
Type next; \
} items[N_Items]; \
}
#define VAL_QUEUE_INIT(Node) \
G_STMT_START { \
(Node)->length = 0; \
(Node)->head = VAL_QUEUE_INVALID(Node); \
(Node)->tail = VAL_QUEUE_INVALID(Node); \
for (guint _i = 0; _i < G_N_ELEMENTS ((Node)->items); _i++) \
{ \
(Node)->items[_i].next = VAL_QUEUE_INVALID(Node); \
(Node)->items[_i].prev = VAL_QUEUE_INVALID(Node); \
} \
} G_STMT_END
#ifndef G_DISABLE_ASSERT
# define _VAL_QUEUE_VALIDATE(Node) \
G_STMT_START { \
glib_typeof((Node)->head) count = 0; \
\
if ((Node)->tail != VAL_QUEUE_INVALID(Node)) \
g_assert_cmpint((Node)->items[(Node)->tail].next, ==, VAL_QUEUE_INVALID(Node)); \
if ((Node)->head != VAL_QUEUE_INVALID(Node)) \
g_assert_cmpint((Node)->items[(Node)->head].prev , ==, VAL_QUEUE_INVALID(Node)); \
\
for (glib_typeof((Node)->head) _viter = (Node)->head; \
VAL_QUEUE_IS_VALID(Node, _viter); \
_viter = (Node)->items[_viter].next) \
{ \
count++; \
} \
\
g_assert_cmpint(count, ==, (Node)->length); \
} G_STMT_END
#else
# define _VAL_QUEUE_VALIDATE(Node) G_STMT_START { } G_STMT_END
#endif
#define VAL_QUEUE_PUSH_HEAD(Node, ID) \
G_STMT_START { \
(Node)->items[ID].prev = VAL_QUEUE_INVALID(Node); \
(Node)->items[ID].next = (Node)->head; \
if (VAL_QUEUE_IS_VALID(Node, (Node)->head)) \
(Node)->items[(Node)->head].prev = ID; \
(Node)->head = ID; \
if (!VAL_QUEUE_IS_VALID(Node, (Node)->tail)) \
(Node)->tail = ID; \
(Node)->length++; \
_VAL_QUEUE_VALIDATE(Node); \
} G_STMT_END
#define VAL_QUEUE_PUSH_TAIL(Node, ID) \
G_STMT_START { \
(Node)->items[ID].prev = (Node)->tail; \
(Node)->items[ID].next = VAL_QUEUE_INVALID(Node); \
if (VAL_QUEUE_IS_VALID (Node, (Node)->tail)) \
(Node)->items[(Node)->tail].next = ID; \
(Node)->tail = ID; \
if (!VAL_QUEUE_IS_VALID(Node, (Node)->head)) \
(Node)->head = ID; \
(Node)->length++; \
_VAL_QUEUE_VALIDATE(Node); \
} G_STMT_END
#define VAL_QUEUE_INSERT(Node, Nth, Val) \
G_STMT_START { \
g_assert_cmpint (VAL_QUEUE_LENGTH(Node),<,G_N_ELEMENTS((Node)->items)); \
\
if ((Nth) == 0) \
{ \
VAL_QUEUE_PUSH_HEAD(Node, Val); \
} \
else if ((Nth) == (Node)->length) \
{ \
VAL_QUEUE_PUSH_TAIL(Node, Val); \
} \
else \
{ \
glib_typeof((Node)->head) ID; \
glib_typeof((Node)->head) _nth; \
\
g_assert_cmpint (VAL_QUEUE_LENGTH(Node), >, 0); \
g_assert (VAL_QUEUE_IS_VALID(Node, (Node)->head)); \
g_assert (VAL_QUEUE_IS_VALID(Node, (Node)->tail)); \
\
for (ID = (Node)->head, _nth = 0; \
_nth < (Nth) && VAL_QUEUE_IS_VALID(Node, ID); \
ID = (Node)->items[ID].next, ++_nth) \
{ /* Do Nothing */ } \
\
g_assert (VAL_QUEUE_IS_VALID(Node, ID)); \
g_assert (VAL_QUEUE_IS_VALID(Node, (Node)->items[ID].prev)); \
\
(Node)->items[Val].prev = (Node)->items[ID].prev; \
(Node)->items[Val].next = ID; \
(Node)->items[(Node)->items[ID].prev].next = Val; \
(Node)->items[ID].prev = Val; \
\
(Node)->length++; \
\
_VAL_QUEUE_VALIDATE(Node); \
} \
} G_STMT_END
#define VAL_QUEUE_POP_HEAD(Node,_pos) VAL_QUEUE_POP_NTH((Node), 0, _pos)
#define VAL_QUEUE_POP_TAIL(Node,_pos) VAL_QUEUE_POP_NTH((Node), (Node)->length - 1, _pos)
#define VAL_QUEUE_POP_AT(Node, _pos) \
G_STMT_START { \
g_assert (_pos != VAL_QUEUE_INVALID(Node)); \
g_assert (_pos < G_N_ELEMENTS ((Node)->items)); \
\
if ((Node)->items[_pos].prev != VAL_QUEUE_INVALID(Node)) \
(Node)->items[(Node)->items[_pos].prev].next = (Node)->items[_pos].next; \
if ((Node)->items[_pos].next != VAL_QUEUE_INVALID(Node)) \
(Node)->items[(Node)->items[_pos].next].prev = (Node)->items[_pos].prev; \
if ((Node)->head == _pos) \
(Node)->head = (Node)->items[_pos].next; \
if ((Node)->tail == _pos) \
(Node)->tail = (Node)->items[_pos].prev; \
\
(Node)->items[_pos].prev = VAL_QUEUE_INVALID((Node)); \
(Node)->items[_pos].next = VAL_QUEUE_INVALID((Node)); \
\
(Node)->length--; \
\
_VAL_QUEUE_VALIDATE(Node); \
} G_STMT_END
#define VAL_QUEUE_POP_NTH(Node, Nth, _pos) \
G_STMT_START { \
_pos = VAL_QUEUE_INVALID(Node); \
\
if (Nth == 0) \
_pos = (Node)->head; \
else if (Nth >= (((Node)->length) - 1)) \
_pos = (Node)->tail; \
else \
VAL_QUEUE_NTH (Node, Nth, _pos); \
\
if (_pos != VAL_QUEUE_INVALID(Node)) \
VAL_QUEUE_POP_AT (Node, _pos); \
} G_STMT_END
#define VAL_QUEUE_NTH(Node, Nth, _iter) \
G_STMT_START { \
glib_typeof((Node)->head) _nth; \
if (Nth == 0) \
_iter = (Node)->head; \
else if (Nth >= (((Node)->length) - 1)) \
_iter = (Node)->tail; \
else \
{ \
for (_iter = (Node)->head, _nth = 0; \
_nth < (Nth); \
_iter = (Node)->items[_iter].next, ++_nth) \
{ \
/* Do Nothing */ \
g_assert (_iter != VAL_QUEUE_INVALID(Node)); \
} \
} \
} G_STMT_END
#define _VAL_QUEUE_MOVE(Node, Old, New) \
G_STMT_START { \
(Node)->items[New] = (Node)->items[Old]; \
if ((Node)->items[New].prev != VAL_QUEUE_INVALID(Node)) \
(Node)->items[(Node)->items[New].prev].next = New; \
if ((Node)->items[New].next != VAL_QUEUE_INVALID(Node)) \
(Node)->items[(Node)->items[New].next].prev = New; \
if ((Node)->head == Old) \
(Node)->head = New; \
if ((Node)->tail == Old) \
(Node)->tail = New; \
} G_STMT_END
/*
* SORTED_ARRAY_FIELD:
* @TYPE: The type of the structure used by elements in the array
* @N_ITEMS: The maximum number of items in the array
*
* This creates a new inline structure that can be embedded within
* other super-structures.
*
* @N_ITEMS must be <= 254 or this macro will fail.
*/
#define SORTED_ARRAY_FIELD(TYPE,N_ITEMS) \
struct { \
TYPE items[N_ITEMS]; \
VAL_QUEUE_NODE(guint8, N_ITEMS) q; \
}
/*
* SORTED_ARRAY_INIT:
* @FIELD: A pointer to a SortedArray
*
* This will initialize a node that has been previously registered
* using %SORTED_ARRAY_FIELD(). You must call this macro before
* using the SortedArray structure.
*/
#define SORTED_ARRAY_INIT(FIELD) \
G_STMT_START { \
G_STATIC_ASSERT (G_N_ELEMENTS((FIELD)->items) < 255); \
VAL_QUEUE_INIT(&(FIELD)->q); \
} G_STMT_END
/*
* SORTED_ARRAY_LENGTH:
* @FIELD: A pointer to the SortedArray field.
*
* This macro will evaluate to the number of items inserted into
* the SortedArray.
*/
#define SORTED_ARRAY_LENGTH(FIELD) (VAL_QUEUE_LENGTH(&(FIELD)->q))
/*
* SORTED_ARRAY_CAPACITY:
* @FIELD: A pointer to the SortedArray field.
*
* This macro will evaluate to the number of elements in the SortedArray.
* This is dependent on how the SortedArray was instantiated using
* the %SORTED_ARRAY_FIELD() macro.
*/
#define SORTED_ARRAY_CAPACITY(FIELD) (G_N_ELEMENTS((FIELD)->items))
/*
* SORTED_ARRAY_IS_FULL:
* @FIELD: A pointer to the SortedArray field.
*
* This macro will evaluate to 1 if the SortedArray is at capacity.
* Otherwise, the macro will evaluate to 0.
*/
#define SORTED_ARRAY_IS_FULL(FIELD) (SORTED_ARRAY_LENGTH(FIELD) == SORTED_ARRAY_CAPACITY(FIELD))
/*
* SORTED_ARRAY_IS_EMPTY:
* @FIELD: A SortedArray field
*
* This macro will evaluate to 1 if the SortedArray contains zero children.
*/
#define SORTED_ARRAY_IS_EMPTY(FIELD) (SORTED_ARRAY_LENGTH(FIELD) == 0)
/*
* SORTED_ARRAY_INSERT_VAL:
* @FIELD: A pointer to a SortedArray field.
* @POSITION: the logical position at which to insert
* @ELEMENT: The element to insert
*
* This will insert a new item into the array. It is invalid API use
* to call this function while the SortedArray is at capacity. Check
* SORTED_ARRAY_IS_FULL() before using this function to be certain.
*/
#define SORTED_ARRAY_INSERT_VAL(FIELD,POSITION,ELEMENT) \
G_STMT_START { \
guint8 _pos; \
\
g_assert (POSITION <= SORTED_ARRAY_LENGTH(FIELD)); \
\
_pos = VAL_QUEUE_LENGTH(&(FIELD)->q); \
g_assert (_pos != VAL_QUEUE_INVALID(&(FIELD)->q)); \
(FIELD)->items[_pos] = ELEMENT; \
VAL_QUEUE_INSERT(&(FIELD)->q, POSITION, _pos); \
} G_STMT_END
#define SORTED_ARRAY_REMOVE_INDEX(FIELD,POSITION,_ele) \
G_STMT_START { \
guint8 _pos; \
guint8 _len; \
\
VAL_QUEUE_POP_NTH(&(FIELD)->q, POSITION, _pos); \
if (_pos == VAL_QUEUE_INVALID(&(FIELD)->q)) \
{ \
g_assert_not_reached (); \
break; \
} \
\
_ele = (FIELD)->items[_pos]; \
_len = VAL_QUEUE_LENGTH(&(FIELD)->q); \
\
/* We must preserve our invariant of having no empty gaps \
* in the array so that se can place new items always at the \
* end (to avoid scanning for an empty spot). \
* Therefore we move our tail item into the removed slot and \
* adjust the iqueue positions (which are all O(1). \
*/ \
\
if (_pos < _len) \
{ \
(FIELD)->items[_pos] = (FIELD)->items[_len]; \
_VAL_QUEUE_MOVE(&(FIELD)->q, _len, _pos); \
} \
} G_STMT_END
/* SORTED_ARRAY_FOREACH_REMOVE:
*
* This a form of SORTED_ARRAY_REMOVE_INDEX but to be used when you
* are within a SORTED_ARRAY_FOREACH() to avoid extra scanning.
*/
#define SORTED_ARRAY_FOREACH_REMOVE(FIELD) \
G_STMT_START { \
guint8 _pos = _current; \
guint8 _len = VAL_QUEUE_LENGTH(&(FIELD)->q); \
\
g_assert (_len > 0); \
g_assert (_pos < _len); \
VAL_QUEUE_POP_AT(&(FIELD)->q, _pos); \
g_assert (VAL_QUEUE_LENGTH(&(FIELD)->q) == _len-1); \
_len--; \
\
/* We must preserve our invariant of having no empty gaps \
* in the array so that se can place new items always at the \
* end (to avoid scanning for an empty spot). \
* Therefore we move our tail item into the removed slot and \
* adjust the iqueue positions (which are all O(1). \
*/ \
\
if (_pos < _len) \
{ \
(FIELD)->items[_pos] = (FIELD)->items[_len]; \
_VAL_QUEUE_MOVE(&(FIELD)->q, _len, _pos); \
\
/* We might need to change the iter if next position moved */ \
if (_aiter == _len) \
_aiter = _pos; \
} \
\
} G_STMT_END
/*
* SORTED_ARRAY_FOREACH:
* @FIELD: A pointer to a SortedArray
* @Element: The type of the elements in @FIELD
* @Name: the name for a pointer of type @Element
* @LABlock: a {} tyle block to execute for each item. You may use
* "break" to exit the foreach.
*
* Calls @Block for every element stored in @FIELD. A pointer to
* each element will be provided as a variable named @Name.
*/
#define SORTED_ARRAY_FOREACH(FIELD, Element, Name, LABlock) \
G_STMT_START { \
for (glib_typeof((FIELD)->q.head) _aiter = (FIELD)->q.head; \
_aiter != VAL_QUEUE_INVALID(&(FIELD)->q); \
/* Do Nothing */) \
{ \
G_GNUC_UNUSED glib_typeof((FIELD)->q.head) _current = _aiter; \
Element * Name = &(FIELD)->items[_aiter]; \
_aiter = (FIELD)->q.items[_aiter].next; \
LABlock \
} \
} G_STMT_END
#define SORTED_ARRAY_FOREACH_REVERSE(FIELD, Element, Name, LABlock) \
G_STMT_START { \
for (glib_typeof((FIELD)->q.head) _aiter = (FIELD)->q.tail; \
_aiter != VAL_QUEUE_INVALID(&(FIELD)->q); \
/* Do Nothing */) \
{ \
G_GNUC_UNUSED glib_typeof((FIELD)->q.head) _current = _aiter; \
Element * Name = &(FIELD)->items[_aiter]; \
_aiter = (FIELD)->q.items[_aiter].prev; \
LABlock \
} \
} G_STMT_END
#define SORTED_ARRAY_FOREACH_PEEK(FIELD) \
(((FIELD)->q.items[_current].next != VAL_QUEUE_INVALID(&(FIELD)->q)) \
? &(FIELD)->items[(FIELD)->q.items[_current].next] : NULL)
#define SORTED_ARRAY_SPLIT(FIELD, SPLIT) \
G_STMT_START { \
guint8 _mid; \
\
SORTED_ARRAY_INIT(SPLIT); \
\
_mid = SORTED_ARRAY_LENGTH(FIELD) / 2; \
\
for (guint8 _z = 0; _z < _mid; _z++) \
{ \
glib_typeof((FIELD)->items[0]) ele; \
SORTED_ARRAY_POP_TAIL(FIELD, ele); \
SORTED_ARRAY_PUSH_HEAD(SPLIT, ele); \
} \
} G_STMT_END
#define SORTED_ARRAY_SPLIT2(FIELD, LEFT, RIGHT) \
G_STMT_START { \
guint8 mid; \
\
SORTED_ARRAY_INIT(LEFT); \
SORTED_ARRAY_INIT(RIGHT); \
\
mid = SORTED_ARRAY_LENGTH(FIELD) / 2; \
\
for (guint8 i = 0; i < mid; i++) \
{ \
glib_typeof((FIELD)->items[0]) ele; \
SORTED_ARRAY_POP_TAIL(FIELD, ele); \
SORTED_ARRAY_PUSH_HEAD(RIGHT, ele); \
} \
\
while (!SORTED_ARRAY_IS_EMPTY(FIELD)) \
{ \
glib_typeof((FIELD)->items[0]) ele; \
SORTED_ARRAY_POP_TAIL(FIELD, ele); \
SORTED_ARRAY_PUSH_HEAD(LEFT, ele); \
} \
} G_STMT_END
#define SORTED_ARRAY_PEEK_HEAD(FIELD) ((FIELD)->items[VAL_QUEUE_PEEK_HEAD(&(FIELD)->q)])
#define SORTED_ARRAY_POP_HEAD(FIELD,_ele) SORTED_ARRAY_REMOVE_INDEX(FIELD, 0, _ele)
#define SORTED_ARRAY_POP_TAIL(FIELD,_ele) SORTED_ARRAY_REMOVE_INDEX(FIELD, SORTED_ARRAY_LENGTH(FIELD)-1, _ele)
#define SORTED_ARRAY_PUSH_HEAD(FIELD, ele) \
G_STMT_START { \
guint8 _pos = VAL_QUEUE_LENGTH(&(FIELD)->q); \
g_assert_cmpint (_pos, <, G_N_ELEMENTS ((FIELD)->items)); \
(FIELD)->items[_pos] = ele; \
VAL_QUEUE_PUSH_HEAD(&(FIELD)->q, _pos); \
} G_STMT_END
#define SORTED_ARRAY_PUSH_TAIL(FIELD, ele) \
G_STMT_START { \
guint8 _pos = VAL_QUEUE_LENGTH(&(FIELD)->q); \
g_assert_cmpint (_pos, <, G_N_ELEMENTS ((FIELD)->items)); \
(FIELD)->items[_pos] = ele; \
VAL_QUEUE_PUSH_TAIL(&(FIELD)->q, _pos); \
} G_STMT_END
#define CJH_TEXT_REGION_MAX_BRANCHES 26
#define CJH_TEXT_REGION_MIN_BRANCHES (CJH_TEXT_REGION_MAX_BRANCHES/3)
#define CJH_TEXT_REGION_MAX_RUNS 26
#define CJH_TEXT_REGION_MIN_RUNS (CJH_TEXT_REGION_MAX_RUNS/3)
typedef union _CjhTextRegionNode CjhTextRegionNode;
typedef struct _CjhTextRegionBranch CjhTextRegionBranch;
typedef struct _CjhTextRegionLeaf CjhTextRegionLeaf;
typedef struct _CjhTextRegionChild CjhTextRegionChild;
struct _CjhTextRegionChild
{
CjhTextRegionNode *node;
gsize length;
};
struct _CjhTextRegionBranch
{
CjhTextRegionNode *tagged_parent;
CjhTextRegionNode *prev;
CjhTextRegionNode *next;
SORTED_ARRAY_FIELD (CjhTextRegionChild, CJH_TEXT_REGION_MAX_BRANCHES) children;
};
struct _CjhTextRegionLeaf
{
CjhTextRegionNode *tagged_parent;
CjhTextRegionNode *prev;
CjhTextRegionNode *next;
SORTED_ARRAY_FIELD (CjhTextRegionRun, CJH_TEXT_REGION_MAX_RUNS) runs;
};
union _CjhTextRegionNode
{
/* pointer to the parent, low bit 0x1 means leaf node */
CjhTextRegionNode *tagged_parent;
struct _CjhTextRegionLeaf leaf;
struct _CjhTextRegionBranch branch;
};
struct _CjhTextRegion
{
CjhTextRegionNode root;
CjhTextRegionJoinFunc join_func;
CjhTextRegionSplitFunc split_func;
gsize length;
CjhTextRegionNode *cached_result;
gsize cached_result_offset;
};
#define TAG(ptr,val) GSIZE_TO_POINTER(GPOINTER_TO_SIZE(ptr)|(gsize)val)
#define UNTAG(ptr) GSIZE_TO_POINTER(GPOINTER_TO_SIZE(ptr) & ~(gsize)1)
static inline CjhTextRegionNode *
cjh_text_region_node_get_parent (CjhTextRegionNode *node)
{
if (node == NULL)
return NULL;
return UNTAG (node->tagged_parent);
}
static inline gboolean
cjh_text_region_node_is_leaf (CjhTextRegionNode *node)
{
CjhTextRegionNode *parent = cjh_text_region_node_get_parent (node);
return parent != NULL && node->tagged_parent != parent;
}
static inline void
cjh_text_region_node_set_parent (CjhTextRegionNode *node,
CjhTextRegionNode *parent)
{
node->tagged_parent = TAG (parent, cjh_text_region_node_is_leaf (node));
}
static inline gsize
cjh_text_region_node_length (CjhTextRegionNode *node)
{
gsize length = 0;
g_assert (node != NULL);
if (cjh_text_region_node_is_leaf (node))
{
SORTED_ARRAY_FOREACH (&node->leaf.runs, CjhTextRegionRun, run, {
length += run->length;
});
}
else
{
SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, {
length += child->length;
});
}
return length;
}
static inline CjhTextRegionNode *
_cjh_text_region_get_first_leaf (CjhTextRegion *self)
{
for (CjhTextRegionNode *iter = &self->root;
iter;
iter = SORTED_ARRAY_PEEK_HEAD (&iter->branch.children).node)
{
if (cjh_text_region_node_is_leaf (iter))
return iter;
}
g_assert_not_reached ();
}
G_END_DECLS
07070100000014000081A400000000000000000000000168A626C20000191D000000000000000000000000000000000000002D00000000libspelling-0.4.9/lib/cjhtextregionprivate.h/* cjhtextregionprivate.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This file 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 Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
typedef struct _CjhTextRegion CjhTextRegion;
typedef struct _CjhTextRegionRun
{
gsize length;
gpointer data;
} CjhTextRegionRun;
/*
* CjhTextRegionForeachFunc:
* @offset: the offset in characters within the text region
* @run: the run of text and data pointer
* @user_data: user data supplied
*
* Function callback to iterate through runs within a text region.
*
* Returns: %FALSE to continue iteration, otherwise %TRUE to stop.
*/
typedef gboolean (*CjhTextRegionForeachFunc) (gsize offset,
const CjhTextRegionRun *run,
gpointer user_data);
/*
* CjhTextRegionJoinFunc:
*
* This callback is used to determine if two runs can be joined together.
* This is useful when you have similar data pointers between two runs
* and seeing them as one run is irrelevant to the code using the
* text region.
*
* The default calllback for joining will return %FALSE so that no joins
* may occur.
*
* Returns: %TRUE if the runs can be joined; otherwise %FALSE
*/
typedef gboolean (*CjhTextRegionJoinFunc) (gsize offset,
const CjhTextRegionRun *left,
const CjhTextRegionRun *right);
/*
* CjhTextRegionSplitFunc:
*
* This function is responsible for splitting a run into two runs.
* This can happen a delete happens in the middle of a run.
*
* By default, @left will contain the run prior to the delete, and
* @right will contain the run after the delete.
*
* You can use the run lengths to determine where the delete was made
* using @offset which is an absolute offset from the beginning of the
* region.
*
* If you would like to keep a single run after the deletion, then
* set @right to contain a length of zero and add it's previous
* length to @left.
*
* All the length in @left and @right must be accounted for.
*
* This function is useful when using CjhTextRegion as a piecetable
* where you want to adjust the data pointer to point at a new
* section of an original or change buffer.
*/
typedef void (*CjhTextRegionSplitFunc) (gsize offset,
const CjhTextRegionRun *run,
CjhTextRegionRun *left,
CjhTextRegionRun *right);
CjhTextRegion *_cjh_text_region_new (CjhTextRegionJoinFunc join_func,
CjhTextRegionSplitFunc split_func);
void _cjh_text_region_insert (CjhTextRegion *region,
gsize offset,
gsize length,
gpointer data);
void _cjh_text_region_replace (CjhTextRegion *region,
gsize offset,
gsize length,
gpointer data);
void _cjh_text_region_remove (CjhTextRegion *region,
gsize offset,
gsize length);
guint _cjh_text_region_get_length (CjhTextRegion *region);
void _cjh_text_region_foreach (CjhTextRegion *region,
CjhTextRegionForeachFunc func,
gpointer user_data);
void _cjh_text_region_foreach_in_range (CjhTextRegion *region,
gsize begin,
gsize end,
CjhTextRegionForeachFunc func,
gpointer user_data);
void _cjh_text_region_free (CjhTextRegion *region);
static inline gboolean
_cjh_text_region_get_run_at_offset_cb (gsize offset,
const CjhTextRegionRun *run,
gpointer user_data)
{
struct {
const CjhTextRegionRun *run;
gsize real_offset;
} *state = user_data;
state->run = run;
state->real_offset = offset;
return TRUE;
}
static inline const CjhTextRegionRun *
_cjh_text_region_get_run_at_offset (CjhTextRegion *region,
gsize offset,
gsize *real_offset)
{
struct {
const CjhTextRegionRun *run;
gsize real_offset;
} state = { NULL, 0 };
gsize lo = offset;
gsize hi = MIN (offset + 1, _cjh_text_region_get_length (region));
if (offset == _cjh_text_region_get_length (region))
{
*real_offset = offset;
return NULL;
}
_cjh_text_region_foreach_in_range (region, lo, hi,
_cjh_text_region_get_run_at_offset_cb,
&state);
*real_offset = state.real_offset;
return state.run;
}
G_END_DECLS
07070100000015000081A400000000000000000000000168A626C200004310000000000000000000000000000000000000002900000000libspelling-0.4.9/lib/egg-action-group.h/* egg-action-group.h
*
* Copyright 2017-2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
#define EGG_DEFINE_ACTION_GROUP(Type, prefix, ...) \
struct _##Type##ActionEntry { \
const gchar *name; \
void (*activate) (Type *self, GVariant *param); \
const gchar *parameter_type; \
const gchar *state; \
void (*change_state) (Type *self, GVariant *state); \
} prefix##_actions[] = __VA_ARGS__; \
\
typedef struct { \
GVariant *state; \
GVariant *state_hint; \
guint enabled : 1; \
} Type##ActionInfo; \
\
static gboolean \
_##prefix##_has_action (GActionGroup *group, \
const gchar *name) \
{ \
for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \
{ \
if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \
return TRUE; \
} \
return FALSE; \
} \
\
static gchar ** \
_##prefix##_list_actions (GActionGroup *group) \
{ \
GPtrArray *ar = g_ptr_array_new (); \
\
for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \
g_ptr_array_add (ar, g_strdup (prefix##_actions[i].name)); \
g_ptr_array_add (ar, NULL); \
\
return (gchar **)g_ptr_array_free (ar, FALSE); \
} \
\
static void \
_##prefix##_action_info_free (gpointer data) \
{ \
Type##ActionInfo *info = data; \
g_clear_pointer (&info->state, g_variant_unref); \
g_clear_pointer (&info->state_hint, g_variant_unref); \
g_slice_free (Type##ActionInfo, info); \
} \
\
static Type##ActionInfo * \
_##prefix##_get_action_info (GActionGroup *group, \
const gchar *name) \
{ \
g_autofree gchar *fullname = g_strdup_printf ("ACTION-INFO:%s", name); \
Type##ActionInfo *info = g_object_get_data (G_OBJECT (group), fullname); \
if (info == NULL) \
{ \
info = g_slice_new0 (Type##ActionInfo); \
info->enabled = TRUE; \
for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \
{ \
if (g_strcmp0 (prefix##_actions[i].name, name) == 0) \
{ \
if (prefix##_actions[i].state != NULL) \
info->state = g_variant_parse ( \
NULL, prefix##_actions[i].state, NULL, NULL, NULL); \
break; \
} \
} \
g_object_set_data_full (G_OBJECT (group), fullname, info, \
_##prefix##_action_info_free); \
} \
return info; \
} \
\
G_GNUC_UNUSED static inline GVariant * \
prefix##_get_action_state (Type *self, \
const gchar *name) \
{ \
Type##ActionInfo *info = _##prefix##_get_action_info (G_ACTION_GROUP (self), \
name); \
return info->state; \
} \
\
G_GNUC_UNUSED static inline void \
prefix##_set_action_state (Type *self, \
const gchar *name, \
GVariant *state) \
{ \
Type##ActionInfo *info = _##prefix##_get_action_info (G_ACTION_GROUP (self), \
name); \
if (state != info->state) \
{ \
g_clear_pointer (&info->state, g_variant_unref); \
info->state = state ? g_variant_ref_sink (state) : NULL; \
g_action_group_action_state_changed (G_ACTION_GROUP (self), name, state); \
} \
} \
\
G_GNUC_UNUSED static inline void \
prefix##_set_action_enabled (Type *self, \
const gchar *name, \
gboolean enabled) \
{ \
Type##ActionInfo *info = _##prefix##_get_action_info (G_ACTION_GROUP (self), \
name); \
if (enabled != info->enabled) \
{ \
info->enabled = !!enabled; \
g_action_group_action_enabled_changed (G_ACTION_GROUP (self), \
name, enabled); \
} \
} \
\
static void \
_##prefix##_change_action_state (GActionGroup *group, \
const gchar *name, \
GVariant *state) \
{ \
for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \
{ \
if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \
{ \
if (prefix##_actions[i].change_state) \
prefix##_actions[i].change_state ((Type*)group, state); \
return; \
} \
} \
} \
\
static void \
_##prefix##_activate_action (GActionGroup *group, \
const gchar *name, \
GVariant *param) \
{ \
for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \
{ \
if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \
{ \
if (prefix##_actions[i].activate) \
prefix##_actions[i].activate ((Type*)group, param); \
return; \
} \
} \
} \
\
static gboolean \
_##prefix##_query_action (GActionGroup *group, \
const gchar *name, \
gboolean *enabled, \
const GVariantType **parameter_type, \
const GVariantType **state_type, \
GVariant **state_hint, \
GVariant **state) \
{ \
if (enabled) *enabled = FALSE; \
if (parameter_type) *parameter_type = NULL ; \
if (state_type) *state_type = NULL ; \
if (state_hint) *state_hint = NULL ; \
if (state) *state = NULL ; \
for (guint i = 0; i < G_N_ELEMENTS(prefix##_actions); i++) \
{ \
if (g_strcmp0 (name, prefix##_actions[i].name) == 0) \
{ \
Type##ActionInfo *info = _##prefix##_get_action_info(group, name); \
if (prefix##_actions[i].change_state && state_type) \
*state_type = prefix##_actions[i].parameter_type ? \
G_VARIANT_TYPE(prefix##_actions[i].parameter_type) : \
NULL; \
else if (prefix##_actions[i].activate && parameter_type) \
*parameter_type = prefix##_actions[i].parameter_type ? \
G_VARIANT_TYPE(prefix##_actions[i].parameter_type) :\
NULL; \
if (state_hint) \
*state_hint = info->state_hint != NULL ? \
g_variant_ref (info->state_hint) : NULL; \
if (state) \
*state = info->state != NULL ? \
g_variant_ref (info->state) : NULL; \
if (enabled) \
*enabled = info->enabled; \
return TRUE; \
} \
} \
return FALSE; \
} \
\
static void \
prefix##_init_action_group (GActionGroupInterface *iface) \
{ \
iface->has_action = _##prefix##_has_action; \
iface->list_actions = _##prefix##_list_actions; \
iface->change_action_state = _##prefix##_change_action_state; \
iface->activate_action = _##prefix##_activate_action; \
iface->query_action = _##prefix##_query_action; \
}
G_END_DECLS
07070100000016000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001E00000000libspelling-0.4.9/lib/enchant07070100000017000081A400000000000000000000000168A626C2000000EC000000000000000000000000000000000000002A00000000libspelling-0.4.9/lib/enchant/meson.buildlibenchant_dep = dependency('enchant-2')
libicu_dep = dependency('icu-uc')
libspelling_deps += [libenchant_dep, libicu_dep]
libspelling_private_sources += files([
'spelling-enchant-dictionary.c',
'spelling-enchant-provider.c',
])
07070100000018000081A400000000000000000000000168A626C20000281E000000000000000000000000000000000000003C00000000libspelling-0.4.9/lib/enchant/spelling-enchant-dictionary.c/* spelling-enchant-dictionary.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <pango/pango.h>
#include <enchant.h>
#include "spelling-enchant-dictionary.h"
#define MAX_RESULTS 10
struct _SpellingEnchantDictionary
{
SpellingDictionary parent_instance;
PangoLanguage *language;
EnchantDict *native;
char *extra_word_chars;
};
G_DEFINE_FINAL_TYPE (SpellingEnchantDictionary, spelling_enchant_dictionary, SPELLING_TYPE_DICTIONARY)
enum {
PROP_0,
PROP_NATIVE,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
/**
* spelling_enchant_dictionary_new:
*
* Create a new `SpellingEnchantDictionary`.
*
* Returns: (transfer full): a newly created `SpellingEnchantDictionary`
*/
SpellingDictionary *
spelling_enchant_dictionary_new (const char *code,
gpointer native)
{
return g_object_new (SPELLING_TYPE_ENCHANT_DICTIONARY,
"code", code,
"native", native,
NULL);
}
static inline gboolean
word_is_number (const char *word,
gsize word_len)
{
g_assert (word_len > 0);
for (gsize i = 0; i < word_len; i++)
{
if (word[i] < '0' || word[i] > '9')
return FALSE;
}
return TRUE;
}
static gboolean
spelling_enchant_dictionary_contains_word (SpellingDictionary *dictionary,
const char *word,
gssize word_len)
{
SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary;
g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self));
g_assert (word != NULL);
g_assert (word_len >= 0);
if (word_is_number (word, word_len))
return TRUE;
return enchant_dict_check (self->native, word, word_len) == 0;
}
static char **
strv_copy_n (const char * const *strv,
gsize n)
{
char **copy = g_new (char *, n + 1);
for (gsize i = 0; i < n; i++)
copy[i] = g_strdup (strv[i]);
copy[n] = NULL;
return copy;
}
static char **
spelling_enchant_dictionary_list_corrections (SpellingDictionary *dictionary,
const char *word,
gssize word_len)
{
SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary;
size_t count = 0;
char **tmp;
char **ret = NULL;
g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self));
g_assert (word != NULL);
g_assert (word_len > 0);
if ((tmp = enchant_dict_suggest (self->native, word, word_len, &count)) && count > 0)
{
if (g_strv_length (tmp) <= MAX_RESULTS)
ret = g_strdupv (tmp);
else
ret = strv_copy_n ((const char * const *)tmp, MAX_RESULTS);
enchant_dict_free_string_list (self->native, tmp);
}
return g_steal_pointer (&ret);
}
static char **
spelling_enchant_dictionary_split (SpellingEnchantDictionary *self,
const char *words)
{
PangoLogAttr *attrs;
GArray *ar;
gsize n_chars;
g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self));
if (words == NULL || self->language == NULL)
return NULL;
/* We don't care about splitting obnoxious stuff */
if ((n_chars = g_utf8_strlen (words, -1)) > 1024)
return NULL;
attrs = g_newa (PangoLogAttr, n_chars + 1);
pango_get_log_attrs (words, -1, -1, self->language, attrs, n_chars + 1);
ar = g_array_new (TRUE, FALSE, sizeof (char*));
for (gsize i = 0; i < n_chars + 1; i++)
{
if (attrs[i].is_word_start)
{
for (gsize j = i + 1; j < n_chars + 1; j++)
{
if (attrs[j].is_word_end)
{
char *substr = g_utf8_substring (words, i, j);
g_array_append_val (ar, substr);
i = j;
break;
}
}
}
}
return (char **)(gpointer)g_array_free (ar, FALSE);
}
static void
spelling_enchant_dictionary_add_all_to_session (SpellingEnchantDictionary *self,
const char * const *words)
{
g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self));
if (words == NULL || words[0] == NULL)
return;
for (guint i = 0; words[i]; i++)
enchant_dict_add_to_session (self->native, words[i], -1);
}
static void
spelling_enchant_dictionary_add_word (SpellingDictionary *dictionary,
const char *word)
{
SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary;
g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self));
g_assert (word != NULL);
enchant_dict_add (self->native, word, -1);
}
static void
spelling_enchant_dictionary_ignore_word (SpellingDictionary *dictionary,
const char *word)
{
SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary;
g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self));
g_assert (word != NULL);
enchant_dict_add_to_session (self->native, word, -1);
}
static const char *
spelling_enchant_dictionary_get_extra_word_chars (SpellingDictionary *dictionary)
{
SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)dictionary;
g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self));
return self->extra_word_chars;
}
static void
spelling_enchant_dictionary_constructed (GObject *object)
{
SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)object;
g_auto(GStrv) split = NULL;
const char *extra_word_chars;
const char *code;
g_assert (SPELLING_IS_ENCHANT_DICTIONARY (self));
G_OBJECT_CLASS (spelling_enchant_dictionary_parent_class)->constructed (object);
code = spelling_dictionary_get_code (SPELLING_DICTIONARY (self));
self->language = pango_language_from_string (code);
if ((split = spelling_enchant_dictionary_split (self, g_get_real_name ())))
spelling_enchant_dictionary_add_all_to_session (self, (const char * const *)split);
if ((extra_word_chars = enchant_dict_get_extra_word_characters (self->native)))
{
const char *end_pos = NULL;
/* Sometimes we get invalid UTF-8 from enchant, so handle that directly.
* In particular, the data seems corrupted from Fedora.
*/
if (g_utf8_validate (extra_word_chars, -1, &end_pos))
self->extra_word_chars = g_strdup (extra_word_chars);
else
self->extra_word_chars = g_strndup (extra_word_chars, end_pos - extra_word_chars);
}
}
static void
spelling_enchant_dictionary_finalize (GObject *object)
{
SpellingEnchantDictionary *self = (SpellingEnchantDictionary *)object;
/* Owned by provider */
self->native = NULL;
/* Global, no need to free */
self->language = NULL;
g_clear_pointer (&self->extra_word_chars, g_free);
G_OBJECT_CLASS (spelling_enchant_dictionary_parent_class)->finalize (object);
}
static void
spelling_enchant_dictionary_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpellingEnchantDictionary *self = SPELLING_ENCHANT_DICTIONARY (object);
switch (prop_id)
{
case PROP_NATIVE:
g_value_set_pointer (value, self->native);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_enchant_dictionary_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpellingEnchantDictionary *self = SPELLING_ENCHANT_DICTIONARY (object);
switch (prop_id)
{
case PROP_NATIVE:
self->native = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_enchant_dictionary_class_init (SpellingEnchantDictionaryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SpellingDictionaryClass *dictionary_class = SPELLING_DICTIONARY_CLASS (klass);
object_class->constructed = spelling_enchant_dictionary_constructed;
object_class->finalize = spelling_enchant_dictionary_finalize;
object_class->get_property = spelling_enchant_dictionary_get_property;
object_class->set_property = spelling_enchant_dictionary_set_property;
dictionary_class->contains_word = spelling_enchant_dictionary_contains_word;
dictionary_class->list_corrections = spelling_enchant_dictionary_list_corrections;
dictionary_class->add_word = spelling_enchant_dictionary_add_word;
dictionary_class->ignore_word = spelling_enchant_dictionary_ignore_word;
dictionary_class->get_extra_word_chars = spelling_enchant_dictionary_get_extra_word_chars;
properties[PROP_NATIVE] =
g_param_spec_pointer ("native",
"Native",
"The native enchant dictionary",
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
spelling_enchant_dictionary_init (SpellingEnchantDictionary *self)
{
}
gpointer
spelling_enchant_dictionary_get_native (SpellingEnchantDictionary *self)
{
g_return_val_if_fail (SPELLING_IS_ENCHANT_DICTIONARY (self), NULL);
return self->native;
}
07070100000019000081A400000000000000000000000168A626C200000576000000000000000000000000000000000000003C00000000libspelling-0.4.9/lib/enchant/spelling-enchant-dictionary.h/* spelling-enchant-dictionary.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include "spelling-dictionary-internal.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_ENCHANT_DICTIONARY (spelling_enchant_dictionary_get_type())
G_DECLARE_FINAL_TYPE (SpellingEnchantDictionary, spelling_enchant_dictionary, SPELLING, ENCHANT_DICTIONARY, SpellingDictionary)
SpellingDictionary *spelling_enchant_dictionary_new (const char *code,
gpointer native);
gpointer spelling_enchant_dictionary_get_native (SpellingEnchantDictionary *self);
G_END_DECLS
0707010000001A000081A400000000000000000000000168A626C2000015A9000000000000000000000000000000000000003A00000000libspelling-0.4.9/lib/enchant/spelling-enchant-provider.c/* spelling-enchant-provider.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <enchant.h>
#include <locale.h>
#include <unicode/uloc.h>
#include <gio/gio.h>
#include "spelling-language-private.h"
#include "spelling-enchant-dictionary.h"
#include "spelling-enchant-provider.h"
struct _SpellingEnchantProvider
{
SpellingProvider parent_instance;
};
G_DEFINE_FINAL_TYPE (SpellingEnchantProvider, spelling_enchant_provider, SPELLING_TYPE_PROVIDER)
static GHashTable *dictionaries;
static EnchantBroker *
get_broker (void)
{
static EnchantBroker *broker;
if (broker == NULL)
broker = enchant_broker_init ();
return broker;
}
static char *
_icu_uchar_to_char (const UChar *input,
gsize max_input_len)
{
GString *str;
g_assert (input != NULL);
g_assert (max_input_len > 0);
if (input[0] == 0)
return NULL;
str = g_string_new (NULL);
for (gsize i = 0; i < max_input_len; i++)
{
if (input[i] == 0)
break;
g_string_append_unichar (str, input[i]);
}
return g_string_free (str, FALSE);
}
static char *
get_display_name (const char *code)
{
const char * const *names = g_get_language_names ();
for (guint i = 0; names[i]; i++)
{
UChar ret[256];
UErrorCode status = U_ZERO_ERROR;
uloc_getDisplayName (code, names[i], ret, G_N_ELEMENTS (ret), &status);
if (U_SUCCESS (status))
return _icu_uchar_to_char (ret, G_N_ELEMENTS (ret));
}
return NULL;
}
static char *
get_display_language (const char *code)
{
const char * const *names = g_get_language_names ();
for (guint i = 0; names[i]; i++)
{
UChar ret[256];
UErrorCode status = U_ZERO_ERROR;
uloc_getDisplayLanguage (code, names[i], ret, G_N_ELEMENTS (ret), &status);
if (U_SUCCESS (status))
return _icu_uchar_to_char (ret, G_N_ELEMENTS (ret));
}
return NULL;
}
/**
* spelling_enchant_provider_new:
*
* Create a new `SpellingEnchantProvider`.
*
* Returns: (transfer full): a newly created `SpellingEnchantProvider`
*/
SpellingProvider *
spelling_enchant_provider_new (void)
{
return g_object_new (SPELLING_TYPE_ENCHANT_PROVIDER,
"display-name", "Enchant",
NULL);
}
static gboolean
spelling_enchant_provider_supports_language (SpellingProvider *provider,
const char *language)
{
g_assert (SPELLING_IS_ENCHANT_PROVIDER (provider));
g_assert (language != NULL);
return enchant_broker_dict_exists (get_broker (), language);
}
static void
list_languages_cb (const char * const lang_tag,
const char * const provider_name,
const char * const provider_desc,
const char * const provider_file,
gpointer user_data)
{
GListStore *store = user_data;
char *name = get_display_name (lang_tag);
char *group = get_display_language (lang_tag);
if (name != NULL)
{
g_autoptr(SpellingLanguage) language = spelling_language_new (name, lang_tag, group);
g_list_store_append (store, language);
}
g_free (name);
g_free (group);
}
static GListModel *
spelling_enchant_provider_list_languages (SpellingProvider *provider)
{
EnchantBroker *broker = get_broker ();
GListStore *store = g_list_store_new (SPELLING_TYPE_LANGUAGE);
enchant_broker_list_dicts (broker, list_languages_cb, store);
return G_LIST_MODEL (store);
}
static SpellingDictionary *
spelling_enchant_provider_load_dictionary (SpellingProvider *provider,
const char *language)
{
SpellingDictionary *ret;
g_assert (SPELLING_IS_ENCHANT_PROVIDER (provider));
g_assert (language != NULL);
if (dictionaries == NULL)
dictionaries = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
if (!(ret = g_hash_table_lookup (dictionaries, language)))
{
EnchantDict *dict = enchant_broker_request_dict (get_broker (), language);
if (dict == NULL)
return NULL;
ret = spelling_enchant_dictionary_new (language, dict);
g_hash_table_insert (dictionaries, (char *)g_intern_string (language), ret);
}
return ret ? g_object_ref (ret) : NULL;
}
static void
spelling_enchant_provider_class_init (SpellingEnchantProviderClass *klass)
{
SpellingProviderClass *spell_provider_class = SPELLING_PROVIDER_CLASS (klass);
spell_provider_class->supports_language = spelling_enchant_provider_supports_language;
spell_provider_class->list_languages = spelling_enchant_provider_list_languages;
spell_provider_class->load_dictionary= spelling_enchant_provider_load_dictionary;
}
static void
spelling_enchant_provider_init (SpellingEnchantProvider *self)
{
}
0707010000001B000081A400000000000000000000000168A626C200000482000000000000000000000000000000000000003A00000000libspelling-0.4.9/lib/enchant/spelling-enchant-provider.h/* spelling-enchant-provider.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include "spelling-provider-internal.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_ENCHANT_PROVIDER (spelling_enchant_provider_get_type())
G_DECLARE_FINAL_TYPE (SpellingEnchantProvider, spelling_enchant_provider, SPELLING, ENCHANT_PROVIDER, SpellingProvider)
SpellingProvider *spelling_enchant_provider_new (void);
G_END_DECLS
0707010000001C000081A400000000000000000000000168A626C2000015E5000000000000000000000000000000000000002500000000libspelling-0.4.9/lib/gconstructor.h/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __G_CONSTRUCTOR_H__
#define __G_CONSTRUCTOR_H__
/*
If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and
destructors, in a usable way, including e.g. on library unload. If not you're on
your own.
Some compilers need #pragma to handle this, which does not work with macros,
so the way you need to use this is (for constructors):
#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor)
#endif
G_DEFINE_CONSTRUCTOR(my_constructor)
static void my_constructor(void) {
...
}
*/
#ifndef __GTK_DOC_IGNORE__
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
#define G_HAS_CONSTRUCTORS 1
#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void);
#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void);
#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
/* Visual studio 2008 and later has _Pragma */
/*
* Only try to include gslist.h if not already included via glib.h,
* so that items using gconstructor.h outside of GLib (such as
* GResources) continue to build properly.
*/
#ifndef __G_LIB_H__
#include "gslist.h"
#endif
#include <stdlib.h>
#define G_HAS_CONSTRUCTORS 1
/* We do some weird things to avoid the constructors being optimized
* away on VS2015 if WholeProgramOptimization is enabled. First we
* make a reference to the array from the wrapper to make sure its
* references. Then we use a pragma to make sure the wrapper function
* symbol is always included at the link stage. Also, the symbols
* need to be extern (but not dllexport), even though they are not
* really used from another object file.
*/
/* We need to account for differences between the mangling of symbols
* for x86 and x64/ARM/ARM64 programs, as symbols on x86 are prefixed
* with an underscore but symbols on x64/ARM/ARM64 are not.
*/
#ifdef _M_IX86
#define G_MSVC_SYMBOL_PREFIX "_"
#else
#define G_MSVC_SYMBOL_PREFIX ""
#endif
#define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX)
#define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX)
#define G_MSVC_CTOR(_func,_sym_prefix) \
static void _func(void); \
extern int (* _array ## _func)(void); \
int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \
__pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \
__pragma(section(".CRT$XCU",read)) \
__declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper;
#define G_MSVC_DTOR(_func,_sym_prefix) \
static void _func(void); \
extern int (* _array ## _func)(void); \
int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \
__pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \
__pragma(section(".CRT$XCU",read)) \
__declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor;
#elif defined (_MSC_VER)
#define G_HAS_CONSTRUCTORS 1
/* Pre Visual studio 2008 must use #pragma section */
#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
section(".CRT$XCU",read)
#define G_DEFINE_CONSTRUCTOR(_func) \
static void _func(void); \
static int _func ## _wrapper(void) { _func(); return 0; } \
__declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper;
#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
section(".CRT$XCU",read)
#define G_DEFINE_DESTRUCTOR(_func) \
static void _func(void); \
static int _func ## _constructor(void) { atexit (_func); return 0; } \
__declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor;
#elif defined(__SUNPRO_C)
/* This is not tested, but i believe it should work, based on:
* http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c
*/
#define G_HAS_CONSTRUCTORS 1
#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
init(_func)
#define G_DEFINE_CONSTRUCTOR(_func) \
static void _func(void);
#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
fini(_func)
#define G_DEFINE_DESTRUCTOR(_func) \
static void _func(void);
#else
/* constructors not supported for this compiler */
#endif
#endif /* __GTK_DOC_IGNORE__ */
#endif /* __G_CONSTRUCTOR_H__ */
0707010000001D000081A400000000000000000000000168A626C200001F8D000000000000000000000000000000000000002500000000libspelling-0.4.9/lib/gdkarrayimpl.c/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include <glib.h>
G_BEGIN_DECLS
#ifndef GDK_ARRAY_TYPE_NAME
#define GDK_ARRAY_TYPE_NAME GdkArray
#endif
#ifndef GDK_ARRAY_NAME
#define GDK_ARRAY_NAME gdk_array
#endif
#ifndef GDK_ARRAY_ELEMENT_TYPE
#define GDK_ARRAY_ELEMENT_TYPE gpointer
#endif
#ifdef GDK_ARRAY_PREALLOC
#if GDK_ARRAY_PREALLOC == 0
#undef GDK_ARRAY_PREALLOC
#endif
#endif
#ifdef GDK_ARRAY_NULL_TERMINATED
#define GDK_ARRAY_REAL_SIZE(_size) ((_size) + 1)
#define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof (_T_) - 1)
#else
#define GDK_ARRAY_REAL_SIZE(_size) (_size)
#define GDK_ARRAY_MAX_SIZE (G_MAXSIZE / sizeof (_T_))
#endif
/* make this readable */
#define _T_ GDK_ARRAY_ELEMENT_TYPE
#define GdkArray GDK_ARRAY_TYPE_NAME
#define gdk_array_paste_more(GDK_ARRAY_NAME, func_name) GDK_ARRAY_NAME ## _ ## func_name
#define gdk_array_paste(GDK_ARRAY_NAME, func_name) gdk_array_paste_more (GDK_ARRAY_NAME, func_name)
#define gdk_array(func_name) gdk_array_paste (GDK_ARRAY_NAME, func_name)
typedef struct GdkArray GdkArray;
struct GdkArray
{
_T_ *start;
_T_ *end;
_T_ *end_allocation;
#ifdef GDK_ARRAY_PREALLOC
_T_ preallocated[GDK_ARRAY_REAL_SIZE(GDK_ARRAY_PREALLOC)];
#endif
};
/* no G_GNUC_UNUSED here, if you don't use an array type, remove it. */
static inline void
gdk_array(init) (GdkArray *self)
{
#ifdef GDK_ARRAY_PREALLOC
self->start = self->preallocated;
self->end = self->start;
self->end_allocation = self->start + GDK_ARRAY_PREALLOC;
#ifdef GDK_ARRAY_NULL_TERMINATED
*self->start = *(_T_[1]) { 0 };
#endif
#else
self->start = NULL;
self->end = NULL;
self->end_allocation = NULL;
#endif
}
G_GNUC_UNUSED static inline gsize
gdk_array(get_capacity) (const GdkArray *self)
{
return self->end_allocation - self->start;
}
G_GNUC_UNUSED static inline gsize
gdk_array(get_size) (const GdkArray *self)
{
return self->end - self->start;
}
static inline void
gdk_array(free_elements) (_T_ *start,
_T_ *end)
{
#ifdef GDK_ARRAY_FREE_FUNC
_T_ *e;
for (e = start; e < end; e++)
#ifdef GDK_ARRAY_BY_VALUE
GDK_ARRAY_FREE_FUNC (e);
#else
GDK_ARRAY_FREE_FUNC (*e);
#endif
#endif
}
/* no G_GNUC_UNUSED here */
static inline void
gdk_array(clear) (GdkArray *self)
{
gdk_array(free_elements) (self->start, self->end);
#ifdef GDK_ARRAY_PREALLOC
if (self->start != self->preallocated)
#endif
g_free (self->start);
gdk_array(init) (self);
}
/*
* gdk_array_steal:
* @self: the array
*
* Steals all data in the array and clears the array.
*
* If you need to know the size of the data, you should query it
* beforehand.
*
* Returns: The array's data
**/
G_GNUC_UNUSED static inline _T_ *
gdk_array(steal) (GdkArray *self)
{
_T_ *result;
#ifdef GDK_ARRAY_PREALLOC
if (self->start == self->preallocated)
{
gsize size = GDK_ARRAY_REAL_SIZE (gdk_array(get_size) (self));
result = g_new (_T_, size);
memcpy (result, self->preallocated, sizeof (_T_) * size);
}
else
#endif
result = self->start;
gdk_array(init) (self);
return result;
}
G_GNUC_UNUSED static inline _T_ *
gdk_array(get_data) (const GdkArray *self)
{
return self->start;
}
G_GNUC_UNUSED static inline _T_ *
gdk_array(index) (const GdkArray *self,
gsize pos)
{
return self->start + pos;
}
G_GNUC_UNUSED static inline gboolean
gdk_array(is_empty) (const GdkArray *self)
{
return self->end == self->start;
}
G_GNUC_UNUSED static inline void
gdk_array(reserve) (GdkArray *self,
gsize n)
{
gsize new_capacity, size, capacity;
if (G_UNLIKELY (n > GDK_ARRAY_MAX_SIZE))
g_error ("requesting array size of %zu, but maximum size is %zu", n, GDK_ARRAY_MAX_SIZE);
capacity = gdk_array(get_capacity) (self);
if (n <= capacity)
return;
size = gdk_array(get_size) (self);
/* capacity * 2 can overflow, that's why we MAX() */
new_capacity = MAX (GDK_ARRAY_REAL_SIZE (n), capacity * 2);
#ifdef GDK_ARRAY_PREALLOC
if (self->start == self->preallocated)
{
self->start = g_new (_T_, new_capacity);
memcpy (self->start, self->preallocated, sizeof (_T_) * GDK_ARRAY_REAL_SIZE (size));
}
else
#endif
#ifdef GDK_ARRAY_NULL_TERMINATED
if (self->start == NULL)
{
self->start = g_new (_T_, new_capacity);
*self->start = *(_T_[1]) { 0 };
}
else
#endif
self->start = g_renew (_T_, self->start, new_capacity);
self->end = self->start + size;
self->end_allocation = self->start + new_capacity;
#ifdef GDK_ARRAY_NULL_TERMINATED
self->end_allocation--;
#endif
}
G_GNUC_UNUSED static inline void
gdk_array(splice) (GdkArray *self,
gsize pos,
gsize removed,
gboolean stolen,
#ifdef GDK_ARRAY_BY_VALUE
const _T_ *additions,
#else
_T_ *additions,
#endif
gsize added)
{
gsize size;
gsize remaining;
size = gdk_array(get_size) (self);
g_assert (pos + removed <= size);
remaining = size - pos - removed;
if (!stolen)
gdk_array(free_elements) (gdk_array(index) (self, pos),
gdk_array(index) (self, pos + removed));
gdk_array(reserve) (self, size - removed + added);
if (GDK_ARRAY_REAL_SIZE (remaining) && removed != added)
memmove (gdk_array(index) (self, pos + added),
gdk_array(index) (self, pos + removed),
GDK_ARRAY_REAL_SIZE (remaining) * sizeof (_T_));
if (added)
{
if (additions)
memcpy (gdk_array(index) (self, pos),
additions,
added * sizeof (_T_));
#ifndef GDK_ARRAY_NO_MEMSET
else
memset (gdk_array(index) (self, pos), 0, added * sizeof (_T_));
#endif
}
/* might overflow, but does the right thing */
self->end += added - removed;
}
G_GNUC_UNUSED static void
gdk_array(set_size) (GdkArray *self,
gsize new_size)
{
gsize old_size = gdk_array(get_size) (self);
if (new_size > old_size)
gdk_array(splice) (self, old_size, 0, FALSE, NULL, new_size - old_size);
else
gdk_array(splice) (self, new_size, old_size - new_size, FALSE, NULL, 0);
}
G_GNUC_UNUSED static void
gdk_array(append) (GdkArray *self,
#ifdef GDK_ARRAY_BY_VALUE
_T_ *value)
#else
_T_ value)
#endif
{
gdk_array(splice) (self,
gdk_array(get_size) (self),
0,
FALSE,
#ifdef GDK_ARRAY_BY_VALUE
value,
#else
&value,
#endif
1);
}
#ifdef GDK_ARRAY_BY_VALUE
G_GNUC_UNUSED static _T_ *
gdk_array(get) (const GdkArray *self,
gsize pos)
{
return gdk_array(index) (self, pos);
}
#else
G_GNUC_UNUSED static _T_
gdk_array(get) (const GdkArray *self,
gsize pos)
{
return *gdk_array(index) (self, pos);
}
#endif
#ifndef GDK_ARRAY_NO_UNDEF
#undef _T_
#undef GdkArray
#undef gdk_array_paste_more
#undef gdk_array_paste
#undef gdk_array
#undef GDK_ARRAY_REAL_SIZE
#undef GDK_ARRAY_MAX_SIZE
#undef GDK_ARRAY_BY_VALUE
#undef GDK_ARRAY_ELEMENT_TYPE
#undef GDK_ARRAY_FREE_FUNC
#undef GDK_ARRAY_NAME
#undef GDK_ARRAY_NULL_TERMINATED
#undef GDK_ARRAY_PREALLOC
#undef GDK_ARRAY_TYPE_NAME
#undef GDK_ARRAY_NO_MEMSET
#endif
G_END_DECLS
0707010000001E000081A400000000000000000000000168A626C2000004F5000000000000000000000000000000000000002400000000libspelling-0.4.9/lib/libspelling.h/* libspelling.h
*
* Copyright 2023 Christian Hergert
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define LIBSPELLING_INSIDE
# include "spelling-checker.h"
# include "spelling-dictionary.h"
# include "spelling-init.h"
# include "spelling-language.h"
# include "spelling-provider.h"
# include "spelling-text-buffer-adapter.h"
# include "spelling-types.h"
# include "spelling-version.h"
# include "spelling-version-macros.h"
#undef LIBSPELLING_INSIDE
G_END_DECLS
0707010000001F000081A400000000000000000000000168A626C200000FDB000000000000000000000000000000000000002200000000libspelling-0.4.9/lib/meson.buildapi_version = '1'
so_version = '2'
libspelling_private_sources = [
'cjhtextregion.c',
'spelling-cursor.c',
'spelling-empty-provider.c',
'spelling-engine.c',
'spelling-job.c',
'spelling-menu.c',
]
libspelling_public_sources = [
'spelling-init.c',
'spelling-checker.c',
'spelling-dictionary.c',
'spelling-language.c',
'spelling-provider.c',
'spelling-text-buffer-adapter.c',
]
libspelling_public_headers = [
'libspelling.h',
'spelling-checker.h',
'spelling-dictionary.h',
'spelling-init.h',
'spelling-language.h',
'spelling-provider.h',
'spelling-text-buffer-adapter.h',
'spelling-types.h',
'spelling-version-macros.h',
]
libspelling_deps = [
libgio_dep,
libgtk_dep,
gtksource_dep,
]
if profiler_enabled
libspelling_deps += [libsysprof_capture_dep]
endif
if get_option('enchant').enabled()
subdir('enchant')
endif
libspelling_sources = libspelling_private_sources + libspelling_public_sources
version_split = meson.project_version().split('.')
version_conf = configuration_data()
version_conf.set('VERSION', meson.project_version())
version_conf.set('MAJOR_VERSION', version_split[0])
version_conf.set('MINOR_VERSION', version_split[1])
version_conf.set('MICRO_VERSION', version_split[2])
configure_file(
input: 'spelling-version.h.in',
output: 'spelling-version.h',
configuration: version_conf,
install: true,
install_dir: join_paths(get_option('includedir'), 'libspelling-' + api_version)
)
libspelling_static = static_library( 'spelling-' + api_version, libspelling_sources,
dependencies: libspelling_deps,
gnu_symbol_visibility: 'hidden',
c_args: ['-DLIBSPELLING_COMPILATION'],
include_directories: [include_directories('..'), include_directories('.')],
install: get_option('install-static'),
)
libspelling_static_dep = declare_dependency(
link_whole: libspelling_static,
dependencies: libspelling_deps,
include_directories: [include_directories('.')],
)
libspelling = shared_library('spelling-' + api_version,
dependencies: [libspelling_static_dep],
gnu_symbol_visibility: 'hidden',
soversion: so_version,
version: '@0@.0.0'.format(so_version),
darwin_versions: '@0@.0'.format(so_version),
install: true,
c_args: ['-DLIBSPELLING_COMPILATION'] + release_args,
)
libspelling_dep = declare_dependency(
link_with: libspelling,
dependencies: libspelling_deps,
include_directories: [include_directories('.')],
)
meson.override_dependency('libspelling-' + api_version, libspelling_dep)
install_headers(libspelling_public_headers, subdir: 'libspelling-' + api_version)
pkg = import('pkgconfig')
pkg.generate(
description: 'A spellcheck library for GTK',
libraries: libspelling,
name: 'libspelling-' + api_version,
filebase: 'libspelling-' + api_version,
version: meson.project_version(),
subdirs: 'libspelling-' + api_version,
requires: ['gio-2.0', 'gtk4', 'gtksourceview-5'],
install_dir: join_paths(get_option('libdir'), 'pkgconfig')
)
if get_option('introspection').enabled()
gnome = import('gnome')
libspelling_gir = gnome.generate_gir(libspelling,
nsversion: api_version,
sources: [libspelling_public_sources, libspelling_public_headers],
namespace: 'Spelling',
symbol_prefix: 'spelling',
identifier_prefix: 'Spelling',
includes: ['Gio-2.0', 'Gtk-4.0', 'GtkSource-5'],
export_packages: 'libspelling-' + api_version,
install: true,
header: 'libspelling.h',
extra_args: '-DLIBSPELLING_COMPILATION',
)
if get_option('vapi')
spelling_vapi = gnome.generate_vapi('libspelling-' + api_version,
sources: libspelling_gir.get(0),
install: true,
install_dir: join_paths(datadir, 'vala', 'vapi'),
packages: ['gio-2.0', 'gtk4', 'gtksourceview-5'],
)
endif
endif
07070100000020000081A400000000000000000000000168A626C200000430000000000000000000000000000000000000003100000000libspelling-0.4.9/lib/spelling-checker-private.h/* spelling-checker-private.h
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <pango/pango.h>
#include "spelling-checker.h"
G_BEGIN_DECLS
PangoLanguage *_spelling_checker_get_pango_language (SpellingChecker *self);
SpellingDictionary *_spelling_checker_get_dictionary (SpellingChecker *self);
G_END_DECLS
07070100000021000081A400000000000000000000000168A626C2000029AA000000000000000000000000000000000000002900000000libspelling-0.4.9/lib/spelling-checker.c/*
* spelling-checker.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <string.h>
#include "spelling-checker-private.h"
#include "spelling-dictionary.h"
#include "spelling-provider.h"
/**
* SpellingChecker:
*
* `SpellingChecker` is the core class of libspelling. It provides high-level
* APIs for spellchecking.
*/
struct _SpellingChecker
{
GObject parent_instance;
SpellingProvider *provider;
SpellingDictionary *dictionary;
PangoLanguage *language;
};
G_DEFINE_FINAL_TYPE (SpellingChecker, spelling_checker, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_LANGUAGE,
PROP_PROVIDER,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
/**
* spelling_checker_new:
* @provider: (nullable): the `SpellingProvider` to use, or %NULL for the default one
* @language: (nullable): the language to use, or %NULL for the default one
*
* Create a new `SpellingChecker`.
*
* Returns: (transfer full): a newly created `SpellingChecker`
*/
SpellingChecker *
spelling_checker_new (SpellingProvider *provider,
const char *language)
{
g_return_val_if_fail (!provider || SPELLING_IS_PROVIDER (provider), NULL);
if (provider == NULL)
provider = spelling_provider_get_default ();
if (language == NULL)
language = spelling_provider_get_default_code (provider);
return g_object_new (SPELLING_TYPE_CHECKER,
"provider", provider,
"language", language,
NULL);
}
static void
spelling_checker_constructed (GObject *object)
{
SpellingChecker *self = (SpellingChecker *)object;
g_assert (SPELLING_IS_CHECKER (self));
G_OBJECT_CLASS (spelling_checker_parent_class)->constructed (object);
if (self->provider == NULL)
self->provider = spelling_provider_get_default ();
}
static void
spelling_checker_finalize (GObject *object)
{
SpellingChecker *self = (SpellingChecker *)object;
g_clear_object (&self->provider);
g_clear_object (&self->dictionary);
G_OBJECT_CLASS (spelling_checker_parent_class)->finalize (object);
}
static void
spelling_checker_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpellingChecker *self = SPELLING_CHECKER (object);
switch (prop_id)
{
case PROP_PROVIDER:
g_value_set_object (value, spelling_checker_get_provider (self));
break;
case PROP_LANGUAGE:
g_value_set_string (value, spelling_checker_get_language (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_checker_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpellingChecker *self = SPELLING_CHECKER (object);
switch (prop_id)
{
case PROP_PROVIDER:
self->provider = g_value_dup_object (value);
break;
case PROP_LANGUAGE:
spelling_checker_set_language (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_checker_class_init (SpellingCheckerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = spelling_checker_constructed;
object_class->finalize = spelling_checker_finalize;
object_class->get_property = spelling_checker_get_property;
object_class->set_property = spelling_checker_set_property;
/**
* SpellingChecker:language:
*
* The "language" to use when checking words with the configured
* `SpellingProvider`. For example, `en_US`.
*/
properties[PROP_LANGUAGE] =
g_param_spec_string ("language", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
/**
* SpellingChecker:provider:
*
* The "provider" property contains the provider that is providing
* information to the spell checker.
*
* Currently, only Enchant is supported, and requires using the
* `SpellingEnchantProvider`. Setting this to %NULL will get
* the default provider.
*/
properties[PROP_PROVIDER] =
g_param_spec_object ("provider", NULL, NULL,
SPELLING_TYPE_PROVIDER,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
spelling_checker_init (SpellingChecker *self)
{
}
/**
* spelling_checker_get_language:
*
* Gets the language being used by the spell checker.
*
* Returns: (nullable): a string describing the current language.
*/
const char *
spelling_checker_get_language (SpellingChecker *self)
{
g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL);
return self->dictionary ? spelling_dictionary_get_code (self->dictionary) : NULL;
}
/**
* spelling_checker_set_language:
* @self: a `SpellingChecker`
* @language: the language to use
*
* Sets the language code to use when communicating with the provider,
* such as `en_US`.
*/
void
spelling_checker_set_language (SpellingChecker *self,
const char *language)
{
g_return_if_fail (SPELLING_IS_CHECKER (self));
if (g_strcmp0 (language, spelling_checker_get_language (self)) != 0)
{
self->language = pango_language_from_string (language);
g_clear_object (&self->dictionary);
self->dictionary = spelling_provider_load_dictionary (self->provider, language);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]);
}
}
/**
* spelling_checker_get_provider:
*
* Gets the spell provider used by the spell checker.
*
* Currently, only Enchant-2 is supported.
*
* Returns: (transfer none) (not nullable): a `SpellingProvider`
*/
SpellingProvider *
spelling_checker_get_provider (SpellingChecker *self)
{
g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL);
return self->provider;
}
/**
* spelling_checker_check_word:
* @self: a `SpellingChecker`
* @word: a word to be checked
* @word_len: length of the word, in bytes
*
* Checks if the active dictionary contains @word.
*
* Returns: %TRUE if the dictionary contains the word
*/
gboolean
spelling_checker_check_word (SpellingChecker *self,
const char *word,
gssize word_len)
{
g_return_val_if_fail (SPELLING_IS_CHECKER (self), FALSE);
if (word == NULL || word_len == 0)
return FALSE;
if (self->dictionary == NULL)
return TRUE;
if (word_len < 0)
word_len = strlen (word);
return spelling_dictionary_contains_word (self->dictionary, word, word_len);
}
/**
* spelling_checker_list_corrections:
* @self: a `SpellingChecker`
* @word: a word to be checked
*
* Retrieves a list of possible corrections for @word.
*
* Returns: (nullable) (transfer full) (array zero-terminated=1) (type utf8):
* A list of possible corrections, or %NULL.
*/
char **
spelling_checker_list_corrections (SpellingChecker *self,
const char *word)
{
g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL);
g_return_val_if_fail (word != NULL, NULL);
if (self->dictionary == NULL)
return NULL;
return spelling_dictionary_list_corrections (self->dictionary, word, -1);
}
/**
* spelling_checker_add_word:
* @self: a `SpellingChecker`
* @word: a word to be added
*
* Adds @word to the active dictionary.
*/
void
spelling_checker_add_word (SpellingChecker *self,
const char *word)
{
g_return_if_fail (SPELLING_IS_CHECKER (self));
g_return_if_fail (word != NULL);
if (self->dictionary != NULL)
spelling_dictionary_add_word (self->dictionary, word);
}
/**
* spelling_checker_ignore_word:
* @self: a `SpellingChecker`
* @word: a word to be ignored
*
* Requests the active dictionary to ignore @word.
*/
void
spelling_checker_ignore_word (SpellingChecker *self,
const char *word)
{
g_return_if_fail (SPELLING_IS_CHECKER (self));
g_return_if_fail (word != NULL);
if (self->dictionary != NULL)
spelling_dictionary_ignore_word (self->dictionary, word);
}
/**
* spelling_checker_get_extra_word_chars:
* @self: a `SpellingChecker`
*
* Gets the extra word characters of the active dictionary.
*
* Returns: (transfer none): extra word characters
*/
const char *
spelling_checker_get_extra_word_chars (SpellingChecker *self)
{
g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL);
if (self->dictionary != NULL)
return spelling_dictionary_get_extra_word_chars (self->dictionary);
return "";
}
/**
* spelling_checker_get_default:
*
* Gets a default `SpellingChecker` using the default provider and language.
*
* Returns: (transfer none): a `SpellingChecker`
*/
SpellingChecker *
spelling_checker_get_default (void)
{
static SpellingChecker *instance;
if (instance == NULL)
{
SpellingProvider *provider = spelling_provider_get_default ();
const char *code = spelling_provider_get_default_code (provider);
instance = spelling_checker_new (provider, code);
g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance);
}
return instance;
}
PangoLanguage *
_spelling_checker_get_pango_language (SpellingChecker *self)
{
g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL);
if (self->language == NULL)
return pango_language_get_default ();
return self->language;
}
SpellingDictionary *
_spelling_checker_get_dictionary (SpellingChecker *self)
{
g_return_val_if_fail (SPELLING_IS_CHECKER (self), NULL);
return self->dictionary;
}
07070100000022000081A400000000000000000000000168A626C200000B68000000000000000000000000000000000000002900000000libspelling-0.4.9/lib/spelling-checker.h/*
* spelling-checker.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION)
# error "Only <libspelling.h> can be included directly."
#endif
#include <glib-object.h>
#include "spelling-types.h"
#include "spelling-version-macros.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_CHECKER (spelling_checker_get_type())
SPELLING_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SpellingChecker, spelling_checker, SPELLING, CHECKER, GObject)
SPELLING_AVAILABLE_IN_ALL
SpellingChecker *spelling_checker_get_default (void);
SPELLING_AVAILABLE_IN_ALL
SpellingChecker *spelling_checker_new (SpellingProvider *provider,
const char *language);
SPELLING_AVAILABLE_IN_ALL
SpellingProvider *spelling_checker_get_provider (SpellingChecker *self);
SPELLING_AVAILABLE_IN_ALL
const char *spelling_checker_get_language (SpellingChecker *self);
SPELLING_AVAILABLE_IN_ALL
void spelling_checker_set_language (SpellingChecker *self,
const char *language);
SPELLING_AVAILABLE_IN_ALL
gboolean spelling_checker_check_word (SpellingChecker *self,
const char *word,
gssize word_len);
SPELLING_AVAILABLE_IN_ALL
char **spelling_checker_list_corrections (SpellingChecker *self,
const char *word);
SPELLING_AVAILABLE_IN_ALL
void spelling_checker_add_word (SpellingChecker *self,
const char *word);
SPELLING_AVAILABLE_IN_ALL
void spelling_checker_ignore_word (SpellingChecker *self,
const char *word);
SPELLING_AVAILABLE_IN_ALL
const char *spelling_checker_get_extra_word_chars (SpellingChecker *self);
G_END_DECLS
07070100000023000081A400000000000000000000000168A626C2000004D5000000000000000000000000000000000000003000000000libspelling-0.4.9/lib/spelling-compat-private.h/* spelling-compat-private.h
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
#if !GLIB_CHECK_VERSION(2,76,0)
static inline gboolean
g_set_str (char **str_pointer,
const char *new_str)
{
char *copy;
if (*str_pointer == new_str ||
(*str_pointer && new_str && strcmp (*str_pointer, new_str) == 0))
return FALSE;
copy = g_strdup (new_str);
g_free (*str_pointer);
*str_pointer = copy;
return TRUE;
}
#endif
G_END_DECLS
07070100000024000081A400000000000000000000000168A626C2000007C6000000000000000000000000000000000000003000000000libspelling-0.4.9/lib/spelling-cursor-private.h/* spelling-cursor-private.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
typedef struct _SpellingCursor SpellingCursor;
typedef struct _CjhTextRegion CjhTextRegion;
SpellingCursor *spelling_cursor_new (GtkTextBuffer *buffer,
CjhTextRegion *region,
GtkTextTag *no_spell_check_tag,
const char *extra_word_chars);
void spelling_cursor_free (SpellingCursor *cursor);
gboolean spelling_cursor_next (SpellingCursor *cursor,
GtkTextIter *word_begin,
GtkTextIter *word_end);
gboolean spelling_iter_forward_word_end (GtkTextIter *iter,
const char *extra_word_chars);
gboolean spelling_iter_backward_word_start (GtkTextIter *iter,
const char *extra_word_chars);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpellingCursor, spelling_cursor_free)
G_END_DECLS
07070100000025000081A400000000000000000000000168A626C200002572000000000000000000000000000000000000002800000000libspelling-0.4.9/lib/spelling-cursor.c/*
* spelling-cursor.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "cjhtextregionprivate.h"
#include "spelling-cursor-private.h"
#define RUN_UNCHECKED NULL
#if 1
# define RETURN(r) G_STMT_START { return (r); } G_STMT_END
#else
# define RETURN(r) G_STMT_START { typeof(r) _r = (r); g_debug (" EXIT: %s(): %s", G_STRFUNC, G_STRLOC); return (_r); } G_STMT_END
#endif
typedef struct
{
CjhTextRegion *region;
GtkTextBuffer *buffer;
gssize pos;
} RegionIter;
typedef struct
{
GtkTextBuffer *buffer;
GtkTextTag *tag;
GtkTextIter pos;
} TagIter;
typedef struct
{
GtkTextBuffer *buffer;
GtkTextIter word_begin;
GtkTextIter word_end;
} WordIter;
struct _SpellingCursor
{
RegionIter region;
TagIter tag;
WordIter word;
const char *extra_word_chars;
};
static void
region_iter_init (RegionIter *self,
GtkTextBuffer *buffer,
CjhTextRegion *region)
{
self->region = region;
self->buffer = buffer;
self->pos = -1;
}
static gboolean
region_iter_next_cb (gsize position,
const CjhTextRegionRun *run,
gpointer user_data)
{
if (run->data == RUN_UNCHECKED)
{
gsize *pos = user_data;
*pos = position;
RETURN (TRUE);
}
RETURN (FALSE);
}
static gboolean
region_iter_next (RegionIter *self,
GtkTextIter *iter)
{
gsize pos, new_pos;
if (self->pos >= (gssize)_cjh_text_region_get_length (self->region))
{
gtk_text_buffer_get_end_iter (self->buffer, iter);
RETURN (FALSE);
}
if (self->pos < 0)
pos = 0;
else
pos = self->pos;
_cjh_text_region_foreach_in_range (self->region,
pos,
_cjh_text_region_get_length (self->region),
region_iter_next_cb,
&new_pos);
pos = MAX (pos, new_pos);
gtk_text_buffer_get_iter_at_offset (self->buffer, iter, pos);
self->pos = pos;
RETURN (TRUE);
}
static void
region_iter_seek (RegionIter *self,
const GtkTextIter *iter)
{
/* Move to position past the word */
self->pos = gtk_text_iter_get_offset (iter) + 1;
}
static void
tag_iter_init (TagIter *self,
GtkTextBuffer *buffer,
GtkTextTag *tag)
{
self->buffer = buffer;
self->tag = tag;
gtk_text_buffer_get_start_iter (buffer, &self->pos);
}
static gboolean
tag_iter_next (TagIter *self,
GtkTextIter *pos)
{
if (self->tag && gtk_text_iter_has_tag (&self->pos, self->tag))
{
/* Should always succeed because we are within the tag */
gtk_text_iter_forward_to_tag_toggle (&self->pos, self->tag);
}
*pos = self->pos;
RETURN (TRUE);
}
static void
tag_iter_seek (TagIter *self,
const GtkTextIter *iter)
{
self->pos = *iter;
}
static inline gboolean
utf8_contains_unichar (const char *utf8,
gunichar ch)
{
if (ch == 0)
RETURN (FALSE);
for (const char *c = utf8; *c; c = g_utf8_next_char (c))
{
if (ch == g_utf8_get_char (c))
RETURN (TRUE);
}
RETURN (FALSE);
}
static inline gboolean
is_word_char (const GtkTextIter *iter,
const char *extra_word_chars)
{
if (gtk_text_iter_starts_word (iter))
RETURN (TRUE);
if (gtk_text_iter_inside_word (iter))
RETURN (TRUE);
if (extra_word_chars != NULL)
{
if (utf8_contains_unichar (extra_word_chars, gtk_text_iter_get_char (iter)))
RETURN (TRUE);
}
RETURN (FALSE);
}
gboolean
spelling_iter_forward_word_end (GtkTextIter *iter,
const char *extra_word_chars)
{
GtkTextIter orig = *iter;
GtkTextIter peek;
if (!gtk_text_iter_forward_word_end (iter))
{
/* We might get here if we moved but are at the end of the buffer.
* If the previous character ends a word, that is fine too.
*/
if (gtk_text_iter_is_end (iter) &&
!gtk_text_iter_equal (iter, &orig))
RETURN (gtk_text_iter_ends_word (iter));
RETURN (FALSE);
}
if (!is_word_char (iter, extra_word_chars))
RETURN (TRUE);
peek = *iter;
while (gtk_text_iter_forward_char (&peek) &&
is_word_char (&peek, extra_word_chars)) { /* Do nothing */ }
*iter = peek;
RETURN (TRUE);
}
gboolean
spelling_iter_backward_word_start (GtkTextIter *iter,
const char *extra_word_chars)
{
GtkTextIter peek;
if (gtk_text_iter_is_start (iter))
RETURN (FALSE);
if (!gtk_text_iter_backward_word_start (iter))
RETURN (gtk_text_iter_starts_word (iter));
peek = *iter;
while (is_word_char (&peek, extra_word_chars) &&
gtk_text_iter_backward_char (&peek)) { /* Do Nothing */ }
if (!gtk_text_iter_is_start (&peek) || !is_word_char (&peek, extra_word_chars))
{
if (!gtk_text_iter_equal (&peek, iter))
gtk_text_iter_forward_char (&peek);
}
*iter = peek;
RETURN (TRUE);
}
static void
word_iter_init (WordIter *self,
GtkTextBuffer *buffer)
{
self->buffer = buffer;
gtk_text_buffer_get_start_iter (buffer, &self->word_begin);
self->word_end = self->word_begin;
}
static gboolean
word_iter_next (WordIter *self,
GtkTextIter *word_begin,
GtkTextIter *word_end,
const char *extra_word_chars)
{
if (!spelling_iter_forward_word_end (&self->word_end, extra_word_chars))
{
*word_begin = self->word_end;
*word_end = self->word_end;
RETURN (FALSE);
}
self->word_begin = self->word_end;
if (!spelling_iter_backward_word_start (&self->word_begin, extra_word_chars))
{
*word_begin = self->word_end;
*word_end = self->word_end;
RETURN (FALSE);
}
*word_begin = self->word_begin;
*word_end = self->word_end;
RETURN (TRUE);
}
static void
word_iter_seek (WordIter *self,
const GtkTextIter *iter)
{
self->word_begin = *iter;
self->word_end = *iter;
}
SpellingCursor *
spelling_cursor_new (GtkTextBuffer *buffer,
CjhTextRegion *region,
GtkTextTag *no_spell_check_tag,
const char *extra_word_chars)
{
SpellingCursor *self;
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
g_return_val_if_fail (region != NULL, NULL);
g_return_val_if_fail (!no_spell_check_tag || GTK_IS_TEXT_TAG (no_spell_check_tag), NULL);
self = g_rc_box_new0 (SpellingCursor);
region_iter_init (&self->region, buffer, region);
tag_iter_init (&self->tag, buffer, no_spell_check_tag);
word_iter_init (&self->word, buffer);
self->extra_word_chars = extra_word_chars ? g_intern_string (extra_word_chars) : "";
return self;
}
void
spelling_cursor_free (SpellingCursor *self)
{
g_rc_box_release (self);
}
static gboolean
contains_tag (const GtkTextIter *word_begin,
const GtkTextIter *word_end,
GtkTextTag *tag)
{
GtkTextIter toggle_iter;
if (tag == NULL)
RETURN (FALSE);
if (gtk_text_iter_has_tag (word_begin, tag))
RETURN (TRUE);
toggle_iter = *word_begin;
if (!gtk_text_iter_forward_to_tag_toggle (&toggle_iter, tag))
RETURN (FALSE);
RETURN (gtk_text_iter_compare (word_end, &toggle_iter) > 0);
}
gboolean
spelling_cursor_next (SpellingCursor *self,
GtkTextIter *word_begin,
GtkTextIter *word_end)
{
/* Try to advance skipping any checked region in the buffer */
if (!region_iter_next (&self->region, word_end))
{
*word_begin = *word_end;
RETURN (FALSE);
}
/* Pass that position to the next iter, so it can skip
* past anything that is already checked. Then try to move
* forward so that we can skip past regions in the text
* buffer that are to be ignored by spellcheck.
*/
tag_iter_seek (&self->tag, word_end);
if (!tag_iter_next (&self->tag, word_end))
{
*word_begin = *word_end;
RETURN (FALSE);
}
/* Now pass that information to the word iter, so that it can
* jump forward to the next word starting from our tag/region
* positions.
*/
word_iter_seek (&self->word, word_end);
if (!word_iter_next (&self->word, word_begin, word_end, self->extra_word_chars))
RETURN (FALSE);
/* Now pass our new position to the region so that it will
* skip past the word when advancing.
*/
region_iter_seek (&self->region, word_end);
/* If this word contains the no-spell-check tag, then try
* again to skip past even more content.
*/
if (contains_tag (word_begin, word_end, self->tag.tag))
RETURN (spelling_cursor_next (self, word_begin, word_end));
RETURN (TRUE);
}
07070100000026000081A400000000000000000000000168A626C2000009D5000000000000000000000000000000000000003500000000libspelling-0.4.9/lib/spelling-dictionary-internal.h/* spelling-dictionary-internal.h
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gtk/gtk.h>
#include "spelling-dictionary.h"
G_BEGIN_DECLS
#define SPELLING_DICTIONARY_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SPELLING_TYPE_DICTIONARY, SpellingDictionaryClass)
typedef struct _SpellingBoundary
{
guint offset;
guint length;
guint byte_offset;
guint byte_length;
} SpellingBoundary;
struct _SpellingDictionary
{
GObject parent_instance;
const char *code;
GMutex mutex;
};
struct _SpellingDictionaryClass
{
GObjectClass parent_class;
void (*lock) (SpellingDictionary *self);
void (*unlock) (SpellingDictionary *self);
gboolean (*contains_word) (SpellingDictionary *self,
const char *word,
gssize word_len);
char **(*list_corrections) (SpellingDictionary *self,
const char *word,
gssize word_len);
void (*add_word) (SpellingDictionary *self,
const char *word);
void (*ignore_word) (SpellingDictionary *self,
const char *word);
const char *(*get_extra_word_chars) (SpellingDictionary *self);
};
GtkBitset *_spelling_dictionary_check_words (SpellingDictionary *self,
const char *text,
const SpellingBoundary *positions,
guint n_positions);
G_END_DECLS
07070100000027000081A400000000000000000000000168A626C200002262000000000000000000000000000000000000002C00000000libspelling-0.4.9/lib/spelling-dictionary.c/* spelling-dictionary.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <string.h>
#include "spelling-dictionary-internal.h"
/**
* SpellingDictionary:
*
* Abstract base class for spellchecking dictionaries.
*/
G_DEFINE_ABSTRACT_TYPE (SpellingDictionary, spelling_dictionary, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_CODE,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static void
spelling_dictionary_real_lock (SpellingDictionary *self)
{
g_mutex_lock (&self->mutex);
}
static void
spelling_dictionary_real_unlock (SpellingDictionary *self)
{
g_mutex_unlock (&self->mutex);
}
static inline void
spelling_dictionary_lock (SpellingDictionary *self)
{
SPELLING_DICTIONARY_GET_CLASS (self)->lock (self);
}
static inline void
spelling_dictionary_unlock (SpellingDictionary *self)
{
SPELLING_DICTIONARY_GET_CLASS (self)->unlock (self);
}
static void
spelling_dictionary_finalize (GObject *object)
{
SpellingDictionary *self = (SpellingDictionary *)object;
self->code = NULL;
g_mutex_clear (&self->mutex);
G_OBJECT_CLASS (spelling_dictionary_parent_class)->finalize (object);
}
static void
spelling_dictionary_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpellingDictionary *self = SPELLING_DICTIONARY (object);
switch (prop_id)
{
case PROP_CODE:
g_value_set_string (value, spelling_dictionary_get_code (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_dictionary_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpellingDictionary *self = SPELLING_DICTIONARY (object);
switch (prop_id)
{
case PROP_CODE:
self->code = g_intern_string (g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_dictionary_class_init (SpellingDictionaryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = spelling_dictionary_finalize;
object_class->get_property = spelling_dictionary_get_property;
object_class->set_property = spelling_dictionary_set_property;
klass->lock = spelling_dictionary_real_lock;
klass->unlock = spelling_dictionary_real_unlock;
/**
* SpellingDictionary:code:
*
* The language code, for example `en_US`.
*/
properties[PROP_CODE] =
g_param_spec_string ("code", NULL, NULL,
NULL,
(G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
spelling_dictionary_init (SpellingDictionary *self)
{
g_mutex_init (&self->mutex);
}
/**
* spelling_dictionary_get_code:
* @self: a `SpellingDictionary`
*
* Gets the language code of the dictionary, or %NULL if undefined.
*
* Returns: (transfer none) (nullable): the language code of the dictionary
*/
const char *
spelling_dictionary_get_code (SpellingDictionary *self)
{
g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), NULL);
return self->code;
}
/**
* spelling_dictionary_contains_word:
* @self: a `SpellingDictionary`
* @word: a word to be checked
* @word_len: length of the word, in bytes
*
* Checks if the dictionary contains @word.
*
* Returns: %TRUE if the dictionary contains the word
*/
gboolean
spelling_dictionary_contains_word (SpellingDictionary *self,
const char *word,
gssize word_len)
{
gboolean ret;
g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), FALSE);
g_return_val_if_fail (word != NULL, FALSE);
if (word_len < 0)
word_len = strlen (word);
spelling_dictionary_lock (self);
ret = SPELLING_DICTIONARY_GET_CLASS (self)->contains_word (self, word, word_len);
spelling_dictionary_unlock (self);
return ret;
}
/**
* spelling_dictionary_list_corrections:
* @self: a `SpellingDictionary`
* @word: a word to be checked
* @word_len: the length of @word, or -1 if @word is zero-terminated
*
* Retrieves a list of possible corrections for @word.
*
* Returns: (nullable) (transfer full) (array zero-terminated=1) (type utf8):
* A list of possible corrections, or %NULL.
*/
char **
spelling_dictionary_list_corrections (SpellingDictionary *self,
const char *word,
gssize word_len)
{
char **ret;
g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), NULL);
g_return_val_if_fail (word != NULL, NULL);
g_return_val_if_fail (word != NULL || word_len == 0, NULL);
if (word_len < 0)
word_len = strlen (word);
if (word_len == 0)
return NULL;
spelling_dictionary_lock (self);
ret = SPELLING_DICTIONARY_GET_CLASS (self)->list_corrections (self, word, word_len);
spelling_dictionary_unlock (self);
return ret;
}
/**
* spelling_dictionary_add_word:
* @self: a `SpellingDictionary`
* @word: a word to be added
*
* Adds @word to the dictionary.
*/
void
spelling_dictionary_add_word (SpellingDictionary *self,
const char *word)
{
g_return_if_fail (SPELLING_IS_DICTIONARY (self));
g_return_if_fail (word != NULL);
if (SPELLING_DICTIONARY_GET_CLASS (self)->add_word)
{
spelling_dictionary_lock (self);
SPELLING_DICTIONARY_GET_CLASS (self)->add_word (self, word);
spelling_dictionary_unlock (self);
}
}
/**
* spelling_dictionary_ignore_word:
* @self: a `SpellingDictionary`
* @word: a word to be ignored
*
* Requests the dictionary to ignore @word.
*/
void
spelling_dictionary_ignore_word (SpellingDictionary *self,
const char *word)
{
g_return_if_fail (SPELLING_IS_DICTIONARY (self));
g_return_if_fail (word != NULL);
if (SPELLING_DICTIONARY_GET_CLASS (self)->ignore_word)
{
spelling_dictionary_lock (self);
SPELLING_DICTIONARY_GET_CLASS (self)->ignore_word (self, word);
spelling_dictionary_unlock (self);
}
}
/**
* spelling_dictionary_get_extra_word_chars:
* @self: a `SpellingDictionary`
*
* Gets the extra word characters of the dictionary.
*
* Returns: (transfer none): extra word characters
*/
const char *
spelling_dictionary_get_extra_word_chars (SpellingDictionary *self)
{
const char *ret = "";
g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), NULL);
if (SPELLING_DICTIONARY_GET_CLASS (self)->get_extra_word_chars)
{
spelling_dictionary_lock (self);
ret = SPELLING_DICTIONARY_GET_CLASS (self)->get_extra_word_chars (self);
spelling_dictionary_unlock (self);
}
return ret;
}
GtkBitset *
_spelling_dictionary_check_words (SpellingDictionary *self,
const char *text,
const SpellingBoundary *positions,
guint n_positions)
{
gboolean (*contains_word) (SpellingDictionary *, const char *, gssize);
GtkBitset *bitset;
g_return_val_if_fail (SPELLING_IS_DICTIONARY (self), NULL);
g_return_val_if_fail (text != NULL, NULL);
bitset = gtk_bitset_new_empty ();
if (n_positions == 0)
return bitset;
contains_word = SPELLING_DICTIONARY_GET_CLASS (self)->contains_word;
spelling_dictionary_lock (self);
for (guint i = 0; i < n_positions; i++)
{
const char *word = &text[positions[i].byte_offset];
guint wordlen = positions[i].byte_length;
if (!(*contains_word) (self, word, wordlen))
gtk_bitset_add (bitset, i);
}
spelling_dictionary_unlock (self);
return bitset;
}
07070100000028000081A400000000000000000000000168A626C200000B7E000000000000000000000000000000000000002C00000000libspelling-0.4.9/lib/spelling-dictionary.h/*
* spelling-dictionary.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION)
# error "Only <libspelling.h> can be included directly."
#endif
#include <glib-object.h>
#include "spelling-types.h"
#include "spelling-version-macros.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_DICTIONARY (spelling_dictionary_get_type())
#define SPELLING_IS_DICTIONARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, SPELLING_TYPE_DICTIONARY))
#define SPELLING_DICTIONARY(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, SPELLING_TYPE_DICTIONARY, SpellingDictionary))
#define SPELLING_DICTIONARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, SPELLING_TYPE_DICTIONARY, SpellingDictionaryClass))
typedef struct _SpellingDictionary SpellingDictionary;
typedef struct _SpellingDictionaryClass SpellingDictionaryClass;
SPELLING_AVAILABLE_IN_ALL
GType spelling_dictionary_get_type (void) G_GNUC_CONST;
SPELLING_AVAILABLE_IN_ALL
const char *spelling_dictionary_get_code (SpellingDictionary *self);
SPELLING_AVAILABLE_IN_ALL
gboolean spelling_dictionary_contains_word (SpellingDictionary *self,
const char *word,
gssize word_len);
SPELLING_AVAILABLE_IN_ALL
char **spelling_dictionary_list_corrections (SpellingDictionary *self,
const char *word,
gssize word_len);
SPELLING_AVAILABLE_IN_ALL
void spelling_dictionary_add_word (SpellingDictionary *self,
const char *word);
SPELLING_AVAILABLE_IN_ALL
void spelling_dictionary_ignore_word (SpellingDictionary *self,
const char *word);
SPELLING_AVAILABLE_IN_ALL
const char *spelling_dictionary_get_extra_word_chars (SpellingDictionary *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpellingDictionary, g_object_unref)
G_END_DECLS
07070100000029000081A400000000000000000000000168A626C20000047D000000000000000000000000000000000000003800000000libspelling-0.4.9/lib/spelling-empty-provider-private.h/* spelling-empty-provider-private.h
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include "spelling-provider-internal.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_EMPTY_PROVIDER (spelling_empty_provider_get_type())
G_DECLARE_FINAL_TYPE (SpellingEmptyProvider, spelling_empty_provider, SPELLING, EMPTY_SPELL_PROVIDER, SpellingProvider)
SpellingProvider *spelling_empty_provider_new (void);
G_END_DECLS
0707010000002A000081A400000000000000000000000168A626C2000007FC000000000000000000000000000000000000003000000000libspelling-0.4.9/lib/spelling-empty-provider.c/* spelling-empty-provider.c
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "spelling-empty-provider-private.h"
#include "spelling-language.h"
struct _SpellingEmptyProvider
{
SpellingProvider parent_instance;
};
G_DEFINE_FINAL_TYPE (SpellingEmptyProvider, spelling_empty_provider, SPELLING_TYPE_PROVIDER)
SpellingProvider *
spelling_empty_provider_new (void)
{
return g_object_new (SPELLING_TYPE_EMPTY_PROVIDER, NULL);
}
static GListModel *
empty_list_languages (SpellingProvider *provider)
{
return G_LIST_MODEL (g_list_store_new (SPELLING_TYPE_LANGUAGE));
}
static SpellingDictionary *
empty_load_dictionary (SpellingProvider *provider,
const char *language)
{
return NULL;
}
static gboolean
empty_supports_language (SpellingProvider *provider,
const char *language)
{
return FALSE;
}
static void
spelling_empty_provider_class_init (SpellingEmptyProviderClass *klass)
{
SpellingProviderClass *provider_class = SPELLING_PROVIDER_CLASS (klass);
provider_class->list_languages = empty_list_languages;
provider_class->load_dictionary = empty_load_dictionary;
provider_class->supports_language = empty_supports_language;
}
static void
spelling_empty_provider_init (SpellingEmptyProvider *self)
{
}
0707010000002B000081A400000000000000000000000168A626C200000FD9000000000000000000000000000000000000003000000000libspelling-0.4.9/lib/spelling-engine-private.h/* spelling-engine-private.h
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gtk/gtk.h>
#include "spelling-dictionary-internal.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_ENGINE (spelling_engine_get_type())
typedef struct _SpellingAdapter
{
gboolean (*check_enabled) (gpointer instance);
guint (*get_cursor) (gpointer instance);
char *(*copy_text) (gpointer instance,
guint position,
guint length);
void (*apply_tag) (gpointer instance,
guint position,
guint length);
void (*clear_tag) (gpointer instance,
guint position,
guint length);
gboolean (*backward_word_start) (gpointer instance,
guint *position);
gboolean (*forward_word_end) (gpointer instance,
guint *position);
void (*intersect_spellcheck_region) (gpointer instance,
GtkBitset *region);
PangoLanguage *(*get_language) (gpointer instance);
SpellingDictionary *(*get_dictionary) (gpointer instance);
} SpellingAdapter;
G_DECLARE_FINAL_TYPE (SpellingEngine, spelling_engine, SPELLING, ENGINE, GObject)
SpellingEngine *spelling_engine_new (const SpellingAdapter *adapter,
GObject *instance);
void spelling_engine_before_insert_text (SpellingEngine *self,
guint position,
guint length);
void spelling_engine_after_insert_text (SpellingEngine *self,
guint position,
guint length);
void spelling_engine_before_delete_range (SpellingEngine *self,
guint position,
guint length);
void spelling_engine_after_delete_range (SpellingEngine *self,
guint position);
void spelling_engine_iteration (SpellingEngine *self);
void spelling_engine_invalidate (SpellingEngine *self,
guint position,
guint length);
void spelling_engine_invalidate_all (SpellingEngine *self);
G_END_DECLS
0707010000002C000081A400000000000000000000000168A626C2000040F7000000000000000000000000000000000000002800000000libspelling-0.4.9/lib/spelling-engine.c/* spelling-engine.c
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <gtksourceview/gtksource.h>
#include "cjhtextregionprivate.h"
#include "spelling-engine-private.h"
#include "spelling-job-private.h"
#define TAG_NEEDS_CHECK GUINT_TO_POINTER(1)
#define TAG_CHECKED GUINT_TO_POINTER(0)
#define INVALIDATE_DELAY_MSECS 100
#define WATERMARK_PER_JOB 1000
struct _SpellingEngine
{
GObject parent_instance;
CjhTextRegion *region;
GWeakRef instance_wr;
SpellingJob *active;
SpellingAdapter adapter;
guint queued_update_handler;
};
typedef struct
{
SpellingEngine *self;
GObject *instance;
GtkBitset *bitset;
GtkBitset *all;
guint size;
} CollectRanges;
G_DEFINE_FINAL_TYPE (SpellingEngine, spelling_engine, G_TYPE_OBJECT)
static void spelling_engine_queue_update (SpellingEngine *self,
guint delay_msec);
static gboolean
spelling_engine_check_enabled (SpellingEngine *self)
{
g_autoptr(GObject) instance = g_weak_ref_get (&self->instance_wr);
if (instance != NULL)
return self->adapter.check_enabled (instance);
return FALSE;
}
static gboolean
has_unchecked_regions_cb (gsize offset,
const CjhTextRegionRun *run,
gpointer user_data)
{
gboolean *ret = user_data;
*ret |= run->data == TAG_NEEDS_CHECK;
return *ret;
}
static gboolean
spelling_engine_has_unchecked_regions (SpellingEngine *self)
{
gboolean ret = FALSE;
_cjh_text_region_foreach (self->region, has_unchecked_regions_cb, &ret);
return ret;
}
static gboolean
spelling_engine_extend_range (SpellingEngine *self,
guint *begin,
guint *end)
{
g_autoptr(GObject) instance = NULL;
g_assert (SPELLING_IS_ENGINE (self));
g_assert (begin != NULL);
g_assert (end != NULL);
if ((instance = g_weak_ref_get (&self->instance_wr)))
{
guint tmp;
tmp = *begin;
if (self->adapter.backward_word_start (instance, &tmp))
*begin = tmp;
tmp = *end;
if (self->adapter.forward_word_end (instance, &tmp))
*end = tmp;
}
return *begin != *end;
}
static void
spelling_engine_add_fragment (SpellingEngine *self,
GObject *instance,
SpellingJob *job,
GtkBitset *bitset,
guint begin,
guint end)
{
g_autoptr(GBytes) bytes = NULL;
char *text;
g_assert (SPELLING_IS_ENGINE (self));
g_assert (bitset != NULL);
g_assert (end >= begin);
text = self->adapter.copy_text (instance, begin, end - begin + 1);
bytes = g_bytes_new_take (text, strlen (text));
spelling_job_add_fragment (job, bytes, begin, end - begin + 1);
}
static void
spelling_engine_add_fragments (SpellingEngine *self,
GObject *instance,
SpellingJob *job,
GtkBitset *bitset)
{
GtkBitsetIter iter;
guint pos;
g_assert (SPELLING_IS_ENGINE (self));
g_assert (SPELLING_IS_JOB (job));
g_assert (bitset != NULL);
if (gtk_bitset_iter_init_first (&iter, bitset, &pos))
{
guint begin = pos;
guint end = pos;
while (gtk_bitset_iter_next (&iter, &pos))
{
if (pos != end + 1)
{
spelling_engine_add_fragment (self, instance, job, bitset, begin, end);
begin = end = pos;
}
else
{
end = pos;
}
}
spelling_engine_add_fragment (self, instance, job, bitset, begin, end);
}
}
static gsize
spelling_engine_add_range (SpellingEngine *self,
GObject *instance,
guint begin,
guint end,
GtkBitset *all,
GtkBitset *bitset)
{
gsize ret;
g_assert (SPELLING_IS_ENGINE (self));
g_assert (self->active != NULL);
g_assert (SPELLING_IS_JOB (self->active));
g_assert (begin <= end);
g_assert (all != NULL);
g_assert (bitset != NULL);
/* Track this range in "all" as we'll need to clear the areas
* that have "no-spell-check" in our textregion too. We can
* figure that out by subtracting bitset from all.
*/
gtk_bitset_add_range (all, begin, end - begin);
/* Track what the adapter thinks should be in this run */
gtk_bitset_add_range (bitset, begin, end - begin);
self->adapter.intersect_spellcheck_region (instance, bitset);
/* And now subtract that from the all to cover the gaps */
gtk_bitset_subtract (all, bitset);
/* Add fragments for the sub-regions we need to check */
spelling_engine_add_fragments (self, instance, self->active, bitset);
/* Track the size so we can bail after sufficent data to check */
ret = gtk_bitset_get_size (bitset);
/* Reset bitset for next run */
gtk_bitset_remove_all (bitset);
return ret;
}
static gboolean
collect_ranges (gsize offset,
const CjhTextRegionRun *run,
gpointer user_data)
{
CollectRanges *collect = user_data;
guint begin;
guint end;
if (run->data != TAG_NEEDS_CHECK)
return FALSE;
begin = offset;
end = offset + run->length;
spelling_engine_extend_range (collect->self, &begin, &end);
collect->size += spelling_engine_add_range (collect->self,
collect->instance,
begin, end,
collect->all,
collect->bitset);
return collect->size >= WATERMARK_PER_JOB;
}
static void
spelling_engine_job_finished (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SpellingJob *job = (SpellingJob *)object;
g_autoptr(GObject) instance = NULL;
g_autoptr(SpellingEngine) self = user_data;
g_autofree SpellingBoundary *fragments = NULL;
g_autofree SpellingMistake *mistakes = NULL;
guint n_fragments = 0;
guint n_mistakes = 0;
g_assert (SPELLING_IS_JOB (job));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (SPELLING_IS_ENGINE (self));
g_clear_object (&self->active);
if (!(instance = g_weak_ref_get (&self->instance_wr)))
return;
if (!self->adapter.check_enabled (instance))
return;
spelling_job_run_finish (job, result, &fragments, &n_fragments, &mistakes, &n_mistakes);
for (guint f = 0; f < n_fragments; f++)
{
self->adapter.clear_tag (instance, fragments[f].offset, fragments[f].length);
_cjh_text_region_replace (self->region,
fragments[f].offset, fragments[f].length,
TAG_CHECKED);
}
for (guint m = 0; m < n_mistakes; m++)
self->adapter.apply_tag (instance, mistakes[m].offset, mistakes[m].length);
/* Check immediately if there is more */
if (spelling_engine_has_unchecked_regions (self))
spelling_engine_queue_update (self, 0);
}
static void
spelling_engine_clear_runs (SpellingEngine *self,
GtkBitset *bitset)
{
GtkBitsetIter iter;
guint pos;
g_assert (SPELLING_IS_ENGINE (self));
g_assert (bitset != NULL);
if (gtk_bitset_iter_init_first (&iter, bitset, &pos))
{
guint begin = pos;
guint end = pos;
while (gtk_bitset_iter_next (&iter, &pos))
{
if (pos == end + 1)
{
end++;
continue;
}
_cjh_text_region_replace (self->region, begin, end - begin + 1, TAG_CHECKED);
begin = pos;
end = pos;
}
_cjh_text_region_replace (self->region, begin, end - begin + 1, TAG_CHECKED);
}
}
static gboolean
spelling_engine_tick (gpointer data)
{
SpellingEngine *self = data;
g_autoptr(GtkBitset) bitset = NULL;
g_autoptr(GtkBitset) all = NULL;
g_autoptr(GObject) instance = NULL;
const CjhTextRegionRun *run;
SpellingDictionary *dictionary;
PangoLanguage *language;
CollectRanges collect;
gsize real_offset;
guint cursor;
g_assert (SPELLING_IS_ENGINE (self));
g_assert (self->active == NULL);
/* Be safe against weak-pointer lost or bad dictionary installations */
if (!(instance = g_weak_ref_get (&self->instance_wr)) ||
!(dictionary = self->adapter.get_dictionary (instance)) ||
!(language = self->adapter.get_language (instance)))
{
g_clear_handle_id (&self->queued_update_handler, g_source_remove);
return G_SOURCE_REMOVE;
}
self->active = spelling_job_new (dictionary, language);
bitset = gtk_bitset_new_empty ();
all = gtk_bitset_new_empty ();
/* Always check the cursor location so that spellcheck feels snappy */
cursor = self->adapter.get_cursor (instance);
run = _cjh_text_region_get_run_at_offset (self->region, cursor, &real_offset);
if (run == NULL || run->data == TAG_NEEDS_CHECK)
{
guint begin = cursor;
guint end = cursor;
if (spelling_engine_extend_range (self, &begin, &end))
spelling_engine_add_range (self, instance, begin, end, all, bitset);
}
collect.self = self;
collect.bitset = bitset;
collect.all = all;
collect.size = 0;
collect.instance = instance;
_cjh_text_region_foreach (self->region, collect_ranges, &collect);
/* We need to clear everything from our textregion that is still
* in @all as those are gaps in what should be checked, such as
* no-spell-check regions.
*/
spelling_engine_clear_runs (self, all);
spelling_job_run (self->active,
spelling_engine_job_finished,
g_object_ref (self));
g_clear_handle_id (&self->queued_update_handler, g_source_remove);
return G_SOURCE_REMOVE;
}
static void
spelling_engine_queue_update (SpellingEngine *self,
guint delay_msec)
{
g_assert (SPELLING_IS_ENGINE (self));
if (self->active != NULL)
return;
if (!spelling_engine_check_enabled (self))
return;
if (self->queued_update_handler == 0)
self->queued_update_handler = g_timeout_add_full (G_PRIORITY_LOW,
delay_msec,
spelling_engine_tick,
self,
NULL);
}
static gboolean
spelling_engine_join_range (gsize offset,
const CjhTextRegionRun *left,
const CjhTextRegionRun *right)
{
return left->data == right->data;
}
static void
spelling_engine_split_range (gsize offset,
const CjhTextRegionRun *run,
CjhTextRegionRun *left,
CjhTextRegionRun *right)
{
/* We could potentially scan forward/back here from the offset
* where the split occurs to mark the sub-regions as needing
* to be checked. However we do that at another layer currently
* so no need to do it here.
*
* It's also a bit difficult since you can't split the edge
* runs into more than two runs and that would be necessary
* if they were more than a couple words.
*/
}
static void
spelling_engine_dispose (GObject *object)
{
SpellingEngine *self = (SpellingEngine *)object;
g_clear_object (&self->active);
g_clear_handle_id (&self->queued_update_handler, g_source_remove);
g_weak_ref_set (&self->instance_wr, NULL);
G_OBJECT_CLASS (spelling_engine_parent_class)->dispose (object);
}
static void
spelling_engine_finalize (GObject *object)
{
SpellingEngine *self = (SpellingEngine *)object;
g_weak_ref_clear (&self->instance_wr);
g_clear_pointer (&self->region, _cjh_text_region_free);
G_OBJECT_CLASS (spelling_engine_parent_class)->finalize (object);
}
static void
spelling_engine_class_init (SpellingEngineClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = spelling_engine_dispose;
object_class->finalize = spelling_engine_finalize;
}
static void
spelling_engine_init (SpellingEngine *self)
{
g_weak_ref_init (&self->instance_wr, NULL);
self->region = _cjh_text_region_new (spelling_engine_join_range,
spelling_engine_split_range);
}
SpellingEngine *
spelling_engine_new (const SpellingAdapter *adapter,
GObject *instance)
{
SpellingEngine *self;
g_return_val_if_fail (adapter != NULL, NULL);
g_return_val_if_fail (G_IS_OBJECT (instance), NULL);
self = g_object_new (SPELLING_TYPE_ENGINE, NULL);
g_weak_ref_set (&self->instance_wr, instance);
self->adapter = *adapter;
return self;
}
void
spelling_engine_before_insert_text (SpellingEngine *self,
guint position,
guint length)
{
g_return_if_fail (SPELLING_IS_ENGINE (self));
if (length == 0)
return;
if (self->active)
spelling_job_notify_insert (self->active, position, length);
_cjh_text_region_insert (self->region, position, length, TAG_NEEDS_CHECK);
}
void
spelling_engine_after_insert_text (SpellingEngine *self,
guint position,
guint length)
{
g_return_if_fail (SPELLING_IS_ENGINE (self));
if (length == 0)
return;
spelling_engine_invalidate (self, position, length);
}
void
spelling_engine_before_delete_range (SpellingEngine *self,
guint position,
guint length)
{
g_return_if_fail (SPELLING_IS_ENGINE (self));
if (length == 0)
return;
if (self->active)
spelling_job_notify_delete (self->active, position, length);
_cjh_text_region_remove (self->region, position, length);
}
void
spelling_engine_after_delete_range (SpellingEngine *self,
guint position)
{
g_return_if_fail (SPELLING_IS_ENGINE (self));
spelling_engine_invalidate (self, position, 0);
}
void
spelling_engine_iteration (SpellingEngine *self)
{
g_return_if_fail (SPELLING_IS_ENGINE (self));
if (self->active == NULL)
spelling_engine_tick (self);
}
void
spelling_engine_invalidate_all (SpellingEngine *self)
{
g_autoptr(GObject) instance = NULL;
guint length;
g_return_if_fail (SPELLING_IS_ENGINE (self));
g_clear_object (&self->active);
g_clear_handle_id (&self->queued_update_handler, g_source_remove);
length = _cjh_text_region_get_length (self->region);
if (length > 0)
{
_cjh_text_region_replace (self->region, 0, length, TAG_NEEDS_CHECK);
if ((instance = g_weak_ref_get (&self->instance_wr)))
self->adapter.clear_tag (instance, 0, length);
}
spelling_engine_queue_update (self, 0);
}
void
spelling_engine_invalidate (SpellingEngine *self,
guint position,
guint length)
{
g_autoptr(GObject) instance = NULL;
g_assert (SPELLING_IS_ENGINE (self));
if (self->active)
spelling_job_invalidate (self->active, position, length);
_cjh_text_region_replace (self->region, position, length, TAG_NEEDS_CHECK);
if ((instance = g_weak_ref_get (&self->instance_wr)))
self->adapter.clear_tag (instance, position, length);
spelling_engine_queue_update (self, 0);
}
0707010000002D000081A400000000000000000000000168A626C200000782000000000000000000000000000000000000002600000000libspelling-0.4.9/lib/spelling-init.c/* libspelling.c
*
* Copyright 2023 Christian Hergert
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <glib/gi18n.h>
#include "spelling-checker.h"
#include "spelling-dictionary.h"
#include "spelling-init.h"
#include "spelling-language.h"
#include "spelling-provider.h"
#include "spelling-text-buffer-adapter.h"
#include "gconstructor.h"
G_DEFINE_CONSTRUCTOR (_spelling_init)
static void
_spelling_init (void)
{
static gsize initialized;
if (g_once_init_enter (&initialized))
{
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
g_type_ensure (SPELLING_TYPE_CHECKER);
g_type_ensure (SPELLING_TYPE_DICTIONARY);
g_type_ensure (SPELLING_TYPE_LANGUAGE);
g_type_ensure (SPELLING_TYPE_PROVIDER);
g_type_ensure (SPELLING_TYPE_TEXT_BUFFER_ADAPTER);
g_once_init_leave (&initialized, TRUE);
}
}
/**
* spelling_init:
*
* Call this function before using any other libspelling functions in your
* applications. It will initialize everything needed to operate the library.
*/
void
spelling_init (void)
{
_spelling_init ();
}
0707010000002E000081A400000000000000000000000168A626C20000042A000000000000000000000000000000000000002600000000libspelling-0.4.9/lib/spelling-init.h/* spelling-init.h
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION)
# error "Only <libspelling.h> can be included directly."
#endif
#include "spelling-version-macros.h"
G_BEGIN_DECLS
SPELLING_AVAILABLE_IN_ALL
void spelling_init (void);
G_END_DECLS
0707010000002F000081A400000000000000000000000168A626C200000D56000000000000000000000000000000000000002D00000000libspelling-0.4.9/lib/spelling-job-private.h/* spelling-job-private.h
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gio/gio.h>
#include "spelling-dictionary-internal.h"
G_BEGIN_DECLS
typedef struct _SpellingMistake
{
guint offset;
guint length;
} SpellingMistake;
#define SPELLING_TYPE_JOB (spelling_job_get_type())
G_DECLARE_FINAL_TYPE (SpellingJob, spelling_job, SPELLING, JOB, GObject)
SpellingJob *spelling_job_new (SpellingDictionary *dictionary,
PangoLanguage *language);
void spelling_job_discard (SpellingJob *self);
void spelling_job_run (SpellingJob *self,
GAsyncReadyCallback callback,
gpointer user_data);
void spelling_job_run_finish (SpellingJob *self,
GAsyncResult *result,
SpellingBoundary **fragments,
guint *n_fragments,
SpellingMistake **mistakes,
guint *n_mistakes);
void spelling_job_run_sync (SpellingJob *self,
SpellingBoundary **fragments,
guint *n_fragments,
SpellingMistake **mistakes,
guint *n_mistakes);
void spelling_job_add_fragment (SpellingJob *self,
GBytes *bytes,
guint position,
guint length);
void spelling_job_notify_delete (SpellingJob *self,
guint position,
guint length);
void spelling_job_notify_insert (SpellingJob *self,
guint position,
guint length);
void spelling_job_invalidate (SpellingJob *self,
guint position,
guint length);
G_END_DECLS
07070100000030000081A400000000000000000000000168A626C200004A20000000000000000000000000000000000000002500000000libspelling-0.4.9/lib/spelling-job.c/* spelling-job.c
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <pango/pango.h>
#include "spelling-dictionary-internal.h"
#include "spelling-job-private.h"
#include "spelling-trace.h"
#define GDK_ARRAY_NAME spelling_boundaries
#define GDK_ARRAY_TYPE_NAME SpellingBoundaries
#define GDK_ARRAY_ELEMENT_TYPE SpellingBoundary
#define GDK_ARRAY_BY_VALUE 1
#define GDK_ARRAY_PREALLOC 8
#define GDK_ARRAY_NO_MEMSET
#include "gdkarrayimpl.c"
typedef struct _SpellingFragment
{
GBytes *bytes;
guint position;
guint length;
gboolean must_discard;
} SpellingFragment;
typedef struct _SpellingMistakes
{
const SpellingFragment *fragment;
GArray *boundaries;
} SpellingMistakes;
struct _SpellingJob
{
GObject parent_instance;
SpellingDictionary *dictionary;
PangoLanguage *language;
char *extra_word_chars;
GArray *fragments;
guint frozen : 1;
};
enum {
PROP_0,
PROP_DICTIONARY,
PROP_LANGUAGE,
PROP_EXTRA_WORD_CHARS,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SpellingJob, spelling_job, G_TYPE_OBJECT)
static GParamSpec *properties[N_PROPS];
static void
clear_fragment (gpointer data)
{
SpellingFragment *fragment = data;
g_clear_pointer (&fragment->bytes, g_bytes_unref);
}
static void
spelling_job_dispose (GObject *object)
{
SpellingJob *self = (SpellingJob *)object;
g_clear_object (&self->dictionary);
g_clear_pointer (&self->fragments, g_array_unref);
g_clear_pointer (&self->extra_word_chars, g_free);
self->language = NULL;
G_OBJECT_CLASS (spelling_job_parent_class)->dispose (object);
}
static void
spelling_job_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpellingJob *self = SPELLING_JOB (object);
switch (prop_id)
{
case PROP_DICTIONARY:
g_value_set_object (value, self->dictionary);
break;
case PROP_EXTRA_WORD_CHARS:
g_value_set_string (value, self->extra_word_chars);
break;
case PROP_LANGUAGE:
g_value_set_pointer (value, self->language);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_job_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpellingJob *self = SPELLING_JOB (object);
switch (prop_id)
{
case PROP_DICTIONARY:
self->dictionary = g_value_dup_object (value);
break;
case PROP_EXTRA_WORD_CHARS:
self->extra_word_chars = g_value_dup_string (value);
break;
case PROP_LANGUAGE:
self->language = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_job_class_init (SpellingJobClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = spelling_job_dispose;
object_class->get_property = spelling_job_get_property;
object_class->set_property = spelling_job_set_property;
properties[PROP_DICTIONARY] =
g_param_spec_object ("dictionary", NULL, NULL,
SPELLING_TYPE_DICTIONARY,
(G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
properties[PROP_EXTRA_WORD_CHARS] =
g_param_spec_string ("extra-word-chars", NULL, NULL,
NULL,
(G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
properties[PROP_LANGUAGE] =
g_param_spec_pointer ("language", NULL, NULL,
(G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
spelling_job_init (SpellingJob *self)
{
self->fragments = g_array_new (FALSE, FALSE, sizeof (SpellingFragment));
g_array_set_clear_func (self->fragments, clear_fragment);
}
SpellingJob *
spelling_job_new (SpellingDictionary *dictionary,
PangoLanguage *language)
{
const char *extra_word_chars;
g_return_val_if_fail (SPELLING_IS_DICTIONARY (dictionary), NULL);
g_return_val_if_fail (language != NULL, NULL);
extra_word_chars = spelling_dictionary_get_extra_word_chars (dictionary);
return g_object_new (SPELLING_TYPE_JOB,
"dictionary", dictionary,
"extra-word-chars", extra_word_chars,
"language", language,
NULL);
}
static inline gboolean
is_extra_word_char (const char *extra_word_chars,
gunichar ch)
{
if (extra_word_chars == NULL)
return FALSE;
for (const char *c = extra_word_chars; *c; c = g_utf8_next_char (c))
{
if (ch == g_utf8_get_char (c))
return TRUE;
}
return FALSE;
}
static gboolean
find_word_start (const char **textptr,
gsize *iptr,
const PangoLogAttr *attrs,
gsize attrslen,
const char *extra_word_chars)
{
while (*iptr < attrslen)
{
if (attrs[*iptr].is_word_start)
return TRUE;
if (!attrs[*iptr].is_white)
{
gunichar ch = g_utf8_get_char (*textptr);
if (is_extra_word_char (extra_word_chars, ch))
return TRUE;
}
*textptr = g_utf8_next_char (*textptr);
(*iptr)++;
}
return FALSE;
}
static gboolean
find_word_end (const char **textptr,
gsize *iptr,
const PangoLogAttr *attrs,
gsize attrslen,
const char *extra_word_chars)
{
while (*iptr < attrslen)
{
if (attrs[*iptr].is_word_end)
{
gboolean skipped = FALSE;
/* We're at a word boundary, but we might have an extra word
* char here. If so, skip past the word char.
*/
while (*iptr < attrslen &&
!attrs[*iptr].is_white &&
is_extra_word_char (extra_word_chars, g_utf8_get_char (*textptr)))
{
skipped = TRUE;
*textptr = g_utf8_next_char (*textptr);
(*iptr)++;
}
/* If we landed on a word start we must continue as it might
* be something like `words's` where `'` is the extra word char
* but `s` is not in extra_word_chars.
*/
if (skipped &&
*iptr < attrslen &&
attrs[*iptr].is_word_start)
(void)find_word_end (textptr, iptr, attrs, attrslen, extra_word_chars);
return TRUE;
}
*textptr = g_utf8_next_char (*textptr);
(*iptr)++;
}
return FALSE;
}
static void
clear_mistakes (gpointer data)
{
SpellingMistakes *mistakes = data;
mistakes->fragment = NULL;
g_clear_pointer (&mistakes->boundaries, g_array_unref);
}
static void
spelling_job_check (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SpellingJob *self = source_object;
SpellingBoundaries boundaries;
g_autoptr(GArray) result = NULL;
g_assert (G_IS_TASK (task));
g_assert (SPELLING_IS_JOB (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
spelling_boundaries_init (&boundaries);
result = g_array_new (FALSE, FALSE, sizeof (SpellingMistakes));
g_array_set_clear_func (result, clear_mistakes);
SPELLING_PROFILER_LOG ("Checking %u fragments", self->fragments->len);
for (guint f = 0; f < self->fragments->len; f++)
{
const SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, f);
G_GNUC_UNUSED g_autofree char *message = NULL;
g_autoptr(GtkBitset) bitset = NULL;
g_autofree PangoLogAttr *attrs = NULL;
SpellingMistakes mistakes;
const char *text;
const char *p;
GtkBitsetIter iter;
gsize textlen;
gsize attrslen;
gsize i;
guint pos;
SPELLING_PROFILER_BEGIN_MARK;
spelling_boundaries_clear (&boundaries);
mistakes.fragment = fragment;
mistakes.boundaries = NULL;
text = g_bytes_get_data (fragment->bytes, &textlen);
attrslen = g_utf8_strlen (text, textlen) + 1;
attrs = g_new0 (PangoLogAttr, attrslen);
g_assert (textlen <= G_MAXINT);
g_assert (attrslen <= G_MAXINT);
pango_get_log_attrs (text, (int)textlen, -1, self->language, attrs, attrslen);
p = text;
i = 0;
for (gsize count = 0; TRUE; count++)
{
SpellingBoundary boundary;
const char *before = p;
/* Occasionally check to break out of large runs */
if ((count & 0xFF) == 0 && g_atomic_int_get (&fragment->must_discard))
break;
/* Find next word start */
if (!find_word_start (&p, &i, attrs, attrslen-1, self->extra_word_chars))
break;
boundary.byte_offset = p - text;
boundary.offset = i;
/* Ensure we've moved at least one character as find_word_end() may stop
* on the current character it is on.
*/
if (p == before)
{
p = g_utf8_next_char (p);
i++;
}
if (!find_word_end (&p, &i, attrs, attrslen-1, self->extra_word_chars))
break;
boundary.length = i - boundary.offset;
boundary.byte_length = p - text - boundary.byte_offset;
if (boundary.byte_length > 0)
spelling_boundaries_append (&boundaries, &boundary);
}
if (g_atomic_int_get (&fragment->must_discard))
continue;
bitset = _spelling_dictionary_check_words (self->dictionary,
text,
spelling_boundaries_index (&boundaries, 0),
spelling_boundaries_get_size (&boundaries));
if (gtk_bitset_iter_init_first (&iter, bitset, &pos))
{
mistakes.boundaries = g_array_new (FALSE, FALSE, sizeof (SpellingBoundary));
do
{
const SpellingBoundary *b = spelling_boundaries_index (&boundaries, pos);
g_array_append_vals (mistakes.boundaries, b, 1);
}
while (gtk_bitset_iter_next (&iter, &pos));
g_array_append_val (result, mistakes);
}
if G_UNLIKELY (SPELLING_PROFILER_ACTIVE)
message = g_strdup_printf ("%u chars, %u bytes, %u mistakes",
(guint)attrslen,
(guint)textlen,
mistakes.boundaries ? mistakes.boundaries->len : 0);
SPELLING_PROFILER_END_MARK ("Check", message);
}
spelling_boundaries_clear (&boundaries);
g_task_return_pointer (task,
g_steal_pointer (&result),
(GDestroyNotify)g_array_unref);
}
void
spelling_job_run (SpellingJob *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SPELLING_IS_JOB (self));
self->frozen = TRUE;
task = g_task_new (self, NULL, callback, user_data);
g_task_set_source_tag (task, spelling_job_run);
g_task_run_in_thread (task, spelling_job_check);
}
void
spelling_job_run_finish (SpellingJob *self,
GAsyncResult *result,
SpellingBoundary **fragments,
guint *n_fragments,
SpellingMistake **mistakes,
guint *n_mistakes)
{
g_autoptr(GArray) ar = NULL;
g_return_if_fail (SPELLING_IS_JOB (self));
g_return_if_fail (G_IS_TASK (result));
g_return_if_fail (n_fragments != NULL || fragments == NULL);
g_return_if_fail (mistakes != NULL);
g_return_if_fail (n_mistakes != NULL);
*n_mistakes = 0;
*mistakes = NULL;
if (n_fragments != NULL)
*n_fragments = 0;
if (fragments != NULL)
*fragments = NULL;
if (n_fragments != NULL)
{
for (guint i = 0; i < self->fragments->len; i++)
{
const SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i);
if (fragment->must_discard)
continue;
(*n_fragments)++;
}
}
if (fragments != NULL)
{
guint pos = 0;
*fragments = g_new0 (SpellingBoundary, *n_fragments);
for (guint i = 0; i < self->fragments->len; i++)
{
const SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i);
if (fragment->must_discard)
continue;
(*fragments)[pos].offset = fragment->position;
(*fragments)[pos].length = fragment->length;
pos++;
}
}
ar = g_task_propagate_pointer (G_TASK (result), NULL);
if (ar != NULL)
{
guint pos = 0;
for (guint i = 0; i < ar->len; i++)
{
const SpellingMistakes *m = &g_array_index (ar, SpellingMistakes, i);
if (m->fragment->must_discard)
continue;
*n_mistakes += m->boundaries->len;
}
if (*n_mistakes == 0)
return;
*mistakes = g_new0 (SpellingMistake, *n_mistakes);
for (guint i = 0; i < ar->len; i++)
{
const SpellingMistakes *m = &g_array_index (ar, SpellingMistakes, i);
if (m->fragment->must_discard)
continue;
for (guint j = 0; j < m->boundaries->len; j++)
{
const SpellingBoundary *boundary = &g_array_index (m->boundaries, SpellingBoundary, j);
(*mistakes)[pos].offset = m->fragment->position + boundary->offset;
(*mistakes)[pos].length = boundary->length;
pos++;
}
}
g_assert (pos == *n_mistakes);
}
}
void
spelling_job_run_sync (SpellingJob *self,
SpellingBoundary **fragments,
guint *n_fragments,
SpellingMistake **mistakes,
guint *n_mistakes)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SPELLING_IS_JOB (self));
g_return_if_fail (n_fragments != NULL || fragments == NULL);
g_return_if_fail (mistakes != NULL);
g_return_if_fail (n_mistakes != NULL);
self->frozen = TRUE;
task = g_task_new (self, NULL, NULL, NULL);
g_task_set_source_tag (task, spelling_job_run);
spelling_job_check (task, self, NULL, NULL);
spelling_job_run_finish (self, G_ASYNC_RESULT (task), fragments, n_fragments, mistakes, n_mistakes);
}
void
spelling_job_add_fragment (SpellingJob *self,
GBytes *bytes,
guint position,
guint length)
{
SpellingFragment fragment = {0};
g_return_if_fail (SPELLING_IS_JOB (self));
g_return_if_fail (bytes != NULL);
g_return_if_fail (self->frozen == FALSE);
fragment.bytes = g_bytes_ref (bytes);
fragment.position = position;
fragment.length = length;
fragment.must_discard = FALSE;
g_array_append_val (self->fragments, fragment);
}
void
spelling_job_notify_insert (SpellingJob *self,
guint position,
guint length)
{
g_return_if_fail (SPELLING_IS_JOB (self));
for (guint i = 0; i < self->fragments->len; i++)
{
SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i);
if (fragment->must_discard)
continue;
/* Inserts after w/ at least 1 position after fragment */
if (position > fragment->position + fragment->length)
continue;
/* Inserts before w/ at least 1 position before fragment */
if (position < fragment->position)
{
fragment->position += length;
continue;
}
g_atomic_int_set (&fragment->must_discard, TRUE);
}
}
void
spelling_job_notify_delete (SpellingJob *self,
guint position,
guint length)
{
g_return_if_fail (SPELLING_IS_JOB (self));
for (guint i = 0; i < self->fragments->len; i++)
{
SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i);
if (fragment->must_discard)
continue;
/* Deletes after w/ at least 1 position after fragment */
if (position > fragment->position + fragment->length)
continue;
/* If we had the ability to look back at text to see if a boundary
* character was before the cursor here, we could potentially avoid
* bailing. But that is more effort than it's worth when we can just
* recheck things.
*/
/* Deletes before w/ at least 1 position before fragment */
if (position + length < fragment->position)
{
fragment->position -= length;
continue;
}
g_atomic_int_set (&fragment->must_discard, TRUE);
}
}
void
spelling_job_invalidate (SpellingJob *self,
guint position,
guint length)
{
g_return_if_fail (SPELLING_IS_JOB (self));
for (guint i = 0; i < self->fragments->len; i++)
{
SpellingFragment *fragment = &g_array_index (self->fragments, SpellingFragment, i);
if (fragment->must_discard)
continue;
if (position > fragment->position + fragment->length)
continue;
if (position + length < fragment->position)
continue;
g_atomic_int_set (&fragment->must_discard, TRUE);
}
}
07070100000031000081A400000000000000000000000168A626C200000431000000000000000000000000000000000000003200000000libspelling-0.4.9/lib/spelling-language-private.h/*
* spelling-language-private.h
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include "spelling-language.h"
G_BEGIN_DECLS
SpellingLanguage *spelling_language_new (const char *name,
const char *code,
const char *group);
G_END_DECLS
07070100000032000081A400000000000000000000000168A626C20000165D000000000000000000000000000000000000002A00000000libspelling-0.4.9/lib/spelling-language.c/*
* spelling-language.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "spelling-language-private.h"
/**
* SpellingLanguage:
*
* Represents a spellchecking language.
*/
struct _SpellingLanguage
{
GObject parent_instance;
char *name;
char *code;
char *group;
};
G_DEFINE_FINAL_TYPE (SpellingLanguage, spelling_language, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_CODE,
PROP_GROUP,
PROP_NAME,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
/**
* spelling_language_new:
*
* Create a new `SpellingLanguage`.
*
* Returns: (transfer full): a newly created `SpellingLanguage`
*/
SpellingLanguage *
spelling_language_new (const char *name,
const char *code,
const char *group)
{
return g_object_new (SPELLING_TYPE_LANGUAGE,
"name", name,
"code", code,
"group", group,
NULL);
}
static void
spelling_language_finalize (GObject *object)
{
SpellingLanguage *self = (SpellingLanguage *)object;
g_clear_pointer (&self->name, g_free);
g_clear_pointer (&self->code, g_free);
g_clear_pointer (&self->group, g_free);
G_OBJECT_CLASS (spelling_language_parent_class)->finalize (object);
}
static void
spelling_language_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpellingLanguage *self = SPELLING_LANGUAGE (object);
switch (prop_id)
{
case PROP_NAME:
g_value_set_string (value, spelling_language_get_name (self));
break;
case PROP_CODE:
g_value_set_string (value, spelling_language_get_code (self));
break;
case PROP_GROUP:
g_value_set_string (value, spelling_language_get_group (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_language_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpellingLanguage *self = SPELLING_LANGUAGE (object);
switch (prop_id)
{
case PROP_NAME:
self->name = g_value_dup_string (value);
break;
case PROP_CODE:
self->code = g_value_dup_string (value);
break;
case PROP_GROUP:
self->group = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_language_class_init (SpellingLanguageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = spelling_language_finalize;
object_class->get_property = spelling_language_get_property;
object_class->set_property = spelling_language_set_property;
/**
* SpellingLanguage:name:
*
* The name of the language.
*/
properties[PROP_NAME] =
g_param_spec_string ("name", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
/**
* SpellingLanguage:code:
*
* The language code.
*/
properties[PROP_CODE] =
g_param_spec_string ("code", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
/**
* SpellingLanguage:group:
*
* A group for sorting, usually the country name.
*/
properties[PROP_GROUP] =
g_param_spec_string ("group", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
spelling_language_init (SpellingLanguage *self)
{
}
/**
* spelling_language_get_name:
* @self: a `SpellingLanguage`
*
* Gets the name of the language, or %NULL if undefined.
*
* Returns: (transfer none) (nullable): the name of the language
*/
const char *
spelling_language_get_name (SpellingLanguage *self)
{
g_return_val_if_fail (SPELLING_IS_LANGUAGE (self), NULL);
return self->name;
}
/**
* spelling_language_get_code:
* @self: a `SpellingLanguage`
*
* Gets the code of the language, or %NULL if undefined.
*
* Returns: (transfer none) (nullable): the code of the language
*/
const char *
spelling_language_get_code (SpellingLanguage *self)
{
g_return_val_if_fail (SPELLING_IS_LANGUAGE (self), NULL);
return self->code;
}
/**
* spelling_language_get_group:
* @self: a `SpellingLanguage`
*
* Gets the group of the language, or %NULL if undefined.
*
* Returns: (transfer none) (nullable): the group of the language
*/
const char *
spelling_language_get_group (SpellingLanguage *self)
{
g_return_val_if_fail (SPELLING_IS_LANGUAGE (self), NULL);
return self->group;
}
07070100000033000081A400000000000000000000000168A626C2000005E8000000000000000000000000000000000000002A00000000libspelling-0.4.9/lib/spelling-language.h/*
* spelling-language.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION)
# error "Only <libspelling.h> can be included directly."
#endif
#include <glib-object.h>
#include "spelling-version-macros.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_LANGUAGE (spelling_language_get_type())
SPELLING_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SpellingLanguage, spelling_language, SPELLING, LANGUAGE, GObject)
SPELLING_AVAILABLE_IN_ALL
const char *spelling_language_get_group (SpellingLanguage *self);
SPELLING_AVAILABLE_IN_ALL
const char *spelling_language_get_name (SpellingLanguage *self);
SPELLING_AVAILABLE_IN_ALL
const char *spelling_language_get_code (SpellingLanguage *self);
G_END_DECLS
07070100000034000081A400000000000000000000000168A626C200000478000000000000000000000000000000000000002E00000000libspelling-0.4.9/lib/spelling-menu-private.h/*
* spelling-menu-private.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
GMenuModel *spelling_menu_new (void);
void spelling_menu_set_corrections (GMenuModel *menu,
const char *word,
const char * const *words);
G_END_DECLS
07070100000035000081A400000000000000000000000168A626C200002743000000000000000000000000000000000000002600000000libspelling-0.4.9/lib/spelling-menu.c/*
* spelling-menu.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <glib/gi18n-lib.h>
#include "spelling-compat-private.h"
#include "spelling-language.h"
#include "spelling-menu-private.h"
#include "spelling-provider.h"
#define MAX_CORRECTIONS 5
#define SPELLING_CORRECTIONS_MENU "SPELLING_CORRECTIONS_MENU"
#define SPELLING_TYPE_CORRECTIONS (spelling_corrections_get_type())
G_DECLARE_FINAL_TYPE (SpellingCorrections, spelling_corrections, SPELLING, CORRECTIONS, GMenuModel)
struct _SpellingCorrections
{
GMenuModel parent_instance;
char *word;
char **corrections;
};
G_DEFINE_FINAL_TYPE (SpellingCorrections, spelling_corrections, G_TYPE_MENU_MODEL)
static int
spelling_corrections_get_n_items (GMenuModel *model)
{
SpellingCorrections *self = SPELLING_CORRECTIONS (model);
return self->corrections ? g_strv_length (self->corrections) : 0;
}
static gboolean
spelling_corrections_is_mutable (GMenuModel *model)
{
return TRUE;
}
static GMenuModel *
spelling_corrections_get_item_link (GMenuModel *model,
int position,
const char *link)
{
return NULL;
}
static void
spelling_corrections_get_item_links (GMenuModel *model,
int position,
GHashTable **links)
{
*links = NULL;
}
static void
spelling_corrections_get_item_attributes (GMenuModel *model,
int position,
GHashTable **attributes)
{
SpellingCorrections *self = SPELLING_CORRECTIONS (model);
const char *correction;
GHashTable *ht;
g_assert (G_IS_MENU_MODEL (model));
g_assert (attributes != NULL);
*attributes = NULL;
if (position < 0 ||
self->corrections == NULL ||
position >= g_strv_length (self->corrections))
return;
correction = self->corrections[position];
g_assert (correction != NULL);
ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
g_hash_table_insert (ht, g_strdup (G_MENU_ATTRIBUTE_ACTION), g_variant_ref_sink (g_variant_new_string ("spelling.correct")));
g_hash_table_insert (ht, g_strdup (G_MENU_ATTRIBUTE_TARGET), g_variant_ref_sink (g_variant_new_string (correction)));
g_hash_table_insert (ht, g_strdup (G_MENU_ATTRIBUTE_LABEL), g_variant_ref_sink (g_variant_new_string (correction)));
*attributes = ht;
}
static void
spelling_menu_dispose (GObject *object)
{
SpellingCorrections *self = (SpellingCorrections *)object;
g_clear_pointer (&self->word, g_free);
g_clear_pointer (&self->corrections, g_strfreev);
G_OBJECT_CLASS (spelling_corrections_parent_class)->dispose (object);
}
static void
spelling_corrections_class_init (SpellingCorrectionsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GMenuModelClass *menu_model_class = G_MENU_MODEL_CLASS (klass);
object_class->dispose = spelling_menu_dispose;
menu_model_class->get_n_items = spelling_corrections_get_n_items;
menu_model_class->is_mutable = spelling_corrections_is_mutable;
menu_model_class->get_item_link = spelling_corrections_get_item_link;
menu_model_class->get_item_links = spelling_corrections_get_item_links;
menu_model_class->get_item_attributes = spelling_corrections_get_item_attributes;
}
static void
spelling_corrections_init (SpellingCorrections *self)
{
}
static void
spelling_corrections_set (SpellingCorrections *self,
const char *word,
const char * const *corrections)
{
char **copy;
guint removed = 0;
guint added = 0;
g_assert (SPELLING_IS_CORRECTIONS (self));
g_set_str (&self->word, word);
if (self->corrections == NULL && corrections == NULL)
return;
if (corrections != NULL &&
self->corrections != NULL &&
g_strv_equal (corrections, (const char * const *)self->corrections))
return;
if (self->corrections != NULL)
removed = g_strv_length (self->corrections);
if (corrections != NULL)
added = g_strv_length ((char **)corrections);
copy = g_strdupv ((char **)corrections);
g_strfreev (self->corrections);
self->corrections = copy;
g_menu_model_items_changed (G_MENU_MODEL (self), 0, removed, added);
}
static GMenuModel *
spelling_corrections_new (void)
{
return g_object_new (SPELLING_TYPE_CORRECTIONS, NULL);
}
static int
count_groups (GListModel *model)
{
g_autoptr(GHashTable) groups = g_hash_table_new (g_str_hash, g_str_equal);
guint n_items;
g_assert (G_IS_LIST_MODEL (model));
n_items = g_list_model_get_n_items (model);
for (guint i = 0; i < n_items; i++)
{
g_autoptr(SpellingLanguage) language = g_list_model_get_item (model, i);
const char *group = spelling_language_get_group (language);
if (group != NULL && group[0] != 0 && !g_hash_table_contains (groups, group))
g_hash_table_insert (groups, (char *)group, NULL);
}
return g_hash_table_size (groups);
}
static void
populate_languages (GMenu *menu)
{
SpellingProvider *provider = spelling_provider_get_default ();
g_autoptr(GListModel) languages = spelling_provider_list_languages (provider);
g_autoptr(GHashTable) groups = NULL;
guint n_items;
if (languages == NULL)
return;
groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
n_items = g_list_model_get_n_items (languages);
/* First setup our groups. We do that up front so we can avoid
* checking below, but also so we can hoist a single group up
* into the parent menu if necessary.
*/
if (count_groups (languages) > 1)
{
for (guint i = 0; i < n_items; i++)
{
g_autoptr(SpellingLanguage) language = g_list_model_get_item (languages, i);
const char *group = spelling_language_get_group (language);
GMenu *group_menu;
if (group == NULL || group[0] == 0)
continue;
if (!g_hash_table_contains (groups, group))
{
group_menu = g_menu_new ();
g_menu_append_submenu (menu, group, G_MENU_MODEL (group_menu));
g_hash_table_insert (groups,
g_strdup (group),
g_steal_pointer (&group_menu));
}
}
}
for (guint i = 0; i < n_items; i++)
{
g_autoptr(SpellingLanguage) language = g_list_model_get_item (languages, i);
const char *name = spelling_language_get_name (language);
const char *group = spelling_language_get_group (language);
const char *code = spelling_language_get_code (language);
g_autoptr(GMenuItem) item = NULL;
GMenu *group_menu;
if (group == NULL || !(group_menu = g_hash_table_lookup (groups, group)))
group_menu = menu;
g_assert (G_IS_MENU (group_menu));
item = g_menu_item_new (name, NULL);
g_menu_item_set_action_and_target (item, "spelling.language", "s", code);
g_menu_append_item (group_menu, item);
}
}
/**
* spelling_menu_new:
*
* Creates a new menu which can be updated with spelling options.
*
* Returns: (transfer full): a `GMenuModel`
*/
GMenuModel *
spelling_menu_new (void)
{
static GMenu *languages_menu;
static GMenuItem *languages_item;
g_autoptr(GMenu) menu = g_menu_new ();
g_autoptr(GMenuModel) corrections_menu = spelling_corrections_new ();
g_autoptr(GMenuItem) add_item = g_menu_item_new (_("Add to Dictionary"), "spelling.add");
g_autoptr(GMenuItem) ignore_item = g_menu_item_new (_("Ignore"), "spelling.ignore");
g_autoptr(GMenuItem) check_item = g_menu_item_new (_("Check Spelling"), "spelling.enabled");
if (languages_menu == NULL)
{
languages_menu = g_menu_new ();
populate_languages (languages_menu);
}
if (languages_item == NULL)
languages_item = g_menu_item_new_submenu (_("Languages"), G_MENU_MODEL (languages_menu));
g_menu_item_set_attribute (add_item, "hidden-when", "s", "action-disabled");
g_menu_item_set_attribute (ignore_item, "hidden-when", "s", "action-disabled");
g_menu_item_set_attribute (check_item, "role", "s", "check");
g_menu_item_set_attribute (languages_item, "submenu-action", "s", "spellcheck.enabled");
g_menu_append_section (menu, NULL, G_MENU_MODEL (corrections_menu));
g_menu_append_item (menu, add_item);
g_menu_append_item (menu, ignore_item);
g_menu_append_item (menu, check_item);
g_menu_append_item (menu, languages_item);
g_object_set_data_full (G_OBJECT (menu),
SPELLING_CORRECTIONS_MENU,
g_object_ref (corrections_menu),
g_object_unref);
return G_MENU_MODEL (g_steal_pointer (&menu));
}
void
spelling_menu_set_corrections (GMenuModel *menu,
const char *word,
const char * const *words)
{
SpellingCorrections *corrections_menu;
g_return_if_fail (G_IS_MENU_MODEL (menu));
if ((corrections_menu = g_object_get_data (G_OBJECT (menu), SPELLING_CORRECTIONS_MENU)))
{
g_assert (SPELLING_IS_CORRECTIONS (corrections_menu));
spelling_corrections_set (corrections_menu, word, words);
}
}
07070100000036000081A400000000000000000000000168A626C200000631000000000000000000000000000000000000003300000000libspelling-0.4.9/lib/spelling-provider-internal.h/*
* spelling-provider-internal.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include "spelling-provider.h"
G_BEGIN_DECLS
#define SPELLING_PROVIDER_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SPELLING_TYPE_PROVIDER, SpellingProviderClass)
struct _SpellingProvider
{
GObject parent_instance;
char *display_name;
};
struct _SpellingProviderClass
{
GObjectClass parent_class;
GListModel *(*list_languages) (SpellingProvider *self);
gboolean (*supports_language) (SpellingProvider *self,
const char *language);
SpellingDictionary *(*load_dictionary) (SpellingProvider *self,
const char *language);
const char *(*get_default_code) (SpellingProvider *self);
};
G_END_DECLS
07070100000037000081A400000000000000000000000168A626C200001EF9000000000000000000000000000000000000002A00000000libspelling-0.4.9/lib/spelling-provider.c/*
* spelling-provider.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "spelling-dictionary.h"
#include "spelling-empty-provider-private.h"
#include "spelling-provider-internal.h"
#ifdef HAVE_ENCHANT
# include "enchant/spelling-enchant-provider.h"
#endif
/**
* SpellingProvider:
*
* Abstract base class for spellchecking providers.
*/
G_DEFINE_ABSTRACT_TYPE (SpellingProvider, spelling_provider, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_DISPLAY_NAME,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void
spelling_provider_finalize (GObject *object)
{
SpellingProvider *self = (SpellingProvider *)object;
g_clear_pointer (&self->display_name, g_free);
G_OBJECT_CLASS (spelling_provider_parent_class)->finalize (object);
}
static void
spelling_provider_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpellingProvider *self = SPELLING_PROVIDER (object);
switch (prop_id)
{
case PROP_DISPLAY_NAME:
g_value_set_string (value, spelling_provider_get_display_name (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_provider_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpellingProvider *self = SPELLING_PROVIDER (object);
switch (prop_id)
{
case PROP_DISPLAY_NAME:
self->display_name = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_provider_class_init (SpellingProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = spelling_provider_finalize;
object_class->get_property = spelling_provider_get_property;
object_class->set_property = spelling_provider_set_property;
/**
* SpellingProvider:display-name:
*
* The display name.
*/
properties [PROP_DISPLAY_NAME] =
g_param_spec_string ("display-name",
"Display Name",
"Display Name",
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
spelling_provider_init (SpellingProvider *self)
{
}
/**
* spelling_provider_get_display_name:
* @self: a `SpellingProvider`
*
* Gets the display name of the provider, or %NULL if undefined.
*
* Returns: (transfer none) (nullable): the display name of the provider
*/
const char *
spelling_provider_get_display_name (SpellingProvider *self)
{
g_return_val_if_fail (SPELLING_IS_PROVIDER (self), NULL);
return self->display_name;
}
/**
* spelling_provider_get_default:
*
* Gets the default spell provider.
*
* Returns: (transfer none): a `SpellingProvider`
*/
SpellingProvider *
spelling_provider_get_default (void)
{
static SpellingProvider *instance;
if (instance == NULL)
{
#if HAVE_ENCHANT
instance = spelling_enchant_provider_new ();
#endif
if (instance == NULL)
instance = spelling_empty_provider_new ();
g_set_weak_pointer (&instance, instance);
}
return instance;
}
/**
* spelling_provider_supports_language:
* @self: a `SpellingProvider`
* @language: the language such as `en_US`.
*
* Checks of @language is supported by the provider.
*
* Returns: %TRUE if @language is supported, otherwise %FALSE
*/
gboolean
spelling_provider_supports_language (SpellingProvider *self,
const char *language)
{
g_return_val_if_fail (SPELLING_IS_PROVIDER (self), FALSE);
g_return_val_if_fail (language != NULL, FALSE);
return SPELLING_PROVIDER_GET_CLASS (self)->supports_language (self, language);
}
/**
* spelling_provider_list_languages:
* @self: a `SpellingProvider`
*
* Gets a `GListModel` of languages supported by the provider.
*
* Returns: (transfer full): a `GListModel` of `SpellingLanguage`
*/
GListModel *
spelling_provider_list_languages (SpellingProvider *self)
{
GListModel *ret;
g_return_val_if_fail (SPELLING_IS_PROVIDER (self), NULL);
ret = SPELLING_PROVIDER_GET_CLASS (self)->list_languages (self);
g_return_val_if_fail (!ret || G_IS_LIST_MODEL (ret), NULL);
return ret;
}
/**
* spelling_provider_load_dictionary:
* @self: a `SpellingProvider`
* @language: the language to load such as `en_US`.
*
* Gets a `SpellingDictionary` for the requested language, or %NULL
* if the language is not supported.
*
* Returns: (transfer full) (nullable): a `SpellingDictionary` or %NULL
*/
SpellingDictionary *
spelling_provider_load_dictionary (SpellingProvider *self,
const char *language)
{
SpellingDictionary *ret;
g_return_val_if_fail (SPELLING_IS_PROVIDER (self), NULL);
g_return_val_if_fail (language != NULL, NULL);
ret = SPELLING_PROVIDER_GET_CLASS (self)->load_dictionary (self, language);
g_return_val_if_fail (!ret || SPELLING_IS_DICTIONARY (ret), NULL);
return ret;
}
/**
* spelling_provider_get_default_code:
* @self: a `SpellingProvider`
*
* Gets the default language code for the detected system locales, or %NULL
* if the provider doesn't support any of them.
*
* Returns: (transfer none) (nullable): the default language code
*/
const char *
spelling_provider_get_default_code (SpellingProvider *self)
{
const char * const *langs;
const char *ret;
g_return_val_if_fail (SPELLING_IS_PROVIDER (self), NULL);
if (SPELLING_PROVIDER_GET_CLASS (self)->get_default_code &&
(ret = SPELLING_PROVIDER_GET_CLASS (self)->get_default_code (self)))
return ret;
langs = g_get_language_names ();
if (langs != NULL)
{
for (guint i = 0; langs[i]; i++)
{
/* Skip past things like "thing.utf8" since we'll
* prefer to just have "thing" as it ensures we're
* more likely to get code matches elsewhere. Also
* ignore "C" at this point (we'll try that later).
*/
if (strchr (langs[i], '.') || g_str_equal (langs[i], "C"))
continue;
if (spelling_provider_supports_language (self, langs[i]))
return langs[i];
}
/* Since nothing matches the currently language set,
* try to take the first match. Languages like zh_CN
* are unlikely to have a spelling dictionary and we
* don't want to enforce en_US type boundaries on them
* as the experience would be abysmal.
*/
for (guint i = 0; langs[i]; i++)
{
if (strchr (langs[i], '.') || g_str_equal (langs[i], "C"))
continue;
return langs[i];
}
}
if (spelling_provider_supports_language (self, "en_US"))
return "en_US";
if (spelling_provider_supports_language (self, "C"))
return "C";
return NULL;
}
07070100000038000081A400000000000000000000000168A626C200000A06000000000000000000000000000000000000002A00000000libspelling-0.4.9/lib/spelling-provider.h/*
* spelling-provider.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION)
# error "Only <libspelling.h> can be included directly."
#endif
#include <gio/gio.h>
#include "spelling-types.h"
#include "spelling-version-macros.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_PROVIDER (spelling_provider_get_type())
#define SPELLING_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, SPELLING_TYPE_PROVIDER))
#define SPELLING_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, SPELLING_TYPE_PROVIDER, SpellingProvider))
#define SPELLING_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, SPELLING_TYPE_PROVIDER, SpellingProviderClass))
typedef struct _SpellingProvider SpellingProvider;
typedef struct _SpellingProviderClass SpellingProviderClass;
SPELLING_AVAILABLE_IN_ALL
GType spelling_provider_get_type (void) G_GNUC_CONST;
SPELLING_AVAILABLE_IN_ALL
SpellingProvider *spelling_provider_get_default (void);
SPELLING_AVAILABLE_IN_ALL
const char *spelling_provider_get_default_code (SpellingProvider *self);
SPELLING_AVAILABLE_IN_ALL
const char *spelling_provider_get_display_name (SpellingProvider *self);
SPELLING_AVAILABLE_IN_ALL
gboolean spelling_provider_supports_language (SpellingProvider *self,
const char *language);
SPELLING_AVAILABLE_IN_ALL
GListModel *spelling_provider_list_languages (SpellingProvider *self);
SPELLING_AVAILABLE_IN_ALL
SpellingDictionary *spelling_provider_load_dictionary (SpellingProvider *self,
const char *language);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SpellingProvider, g_object_unref)
G_END_DECLS
07070100000039000081A400000000000000000000000168A626C20000A06F000000000000000000000000000000000000003500000000libspelling-0.4.9/lib/spelling-text-buffer-adapter.c/*
* spelling-text-buffer-adapter.c
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "egg-action-group.h"
#include "spelling-compat-private.h"
#include "spelling-checker-private.h"
#include "spelling-cursor-private.h"
#include "spelling-engine-private.h"
#include "spelling-menu-private.h"
#include "spelling-text-buffer-adapter.h"
#define NO_SPELL_CHECK_TAG "gtksourceview:context-classes:no-spell-check"
/**
* SpellingTextBufferAdapter:
*
* `SpellingTextBufferAdapter` implements helpers to easily add spellchecking
* capabilities to a `GtkSourceBuffer`.
*/
#define INVALIDATE_DELAY_MSECS 100
#define MAX_WORD_CHARS 100
struct _SpellingTextBufferAdapter
{
GObject parent_instance;
SpellingEngine *engine;
GSignalGroup *buffer_signals;
GWeakRef buffer_wr;
SpellingChecker *checker;
GtkTextTag *no_spell_check_tag;
GMenuModel *menu;
GMenu *top_menu;
char *word_under_cursor;
/* Borrowed pointers */
GtkTextMark *insert_mark;
GtkTextTag *tag;
guint commit_handler;
guint cursor_position;
guint incoming_cursor_position;
guint queued_cursor_moved;
guint enabled : 1;
};
static void spelling_add_action (SpellingTextBufferAdapter *self,
GVariant *param);
static void spelling_ignore_action (SpellingTextBufferAdapter *self,
GVariant *param);
static void spelling_enabled_action (SpellingTextBufferAdapter *self,
GVariant *param);
static void spelling_correct_action (SpellingTextBufferAdapter *self,
GVariant *param);
static void spelling_language_action (SpellingTextBufferAdapter *self,
GVariant *param);
EGG_DEFINE_ACTION_GROUP (SpellingTextBufferAdapter, spelling_text_buffer_adapter, {
{ "add", spelling_add_action },
{ "correct", spelling_correct_action, "s" },
{ "enabled", spelling_enabled_action, NULL, "false" },
{ "ignore", spelling_ignore_action },
{ "language", spelling_language_action, "s", "''" },
})
G_DEFINE_FINAL_TYPE_WITH_CODE (SpellingTextBufferAdapter, spelling_text_buffer_adapter, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, spelling_text_buffer_adapter_init_action_group))
enum {
PROP_0,
PROP_BUFFER,
PROP_CHECKER,
PROP_ENABLED,
PROP_LANGUAGE,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static void
spelling_text_buffer_adapter_commit_notify (GtkTextBuffer *buffer,
GtkTextBufferNotifyFlags flags,
guint position,
guint length,
gpointer user_data)
{
SpellingTextBufferAdapter *self = user_data;
g_assert (GTK_IS_TEXT_BUFFER (buffer));
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
if (flags == GTK_TEXT_BUFFER_NOTIFY_BEFORE_INSERT)
spelling_engine_before_insert_text (self->engine, position, length);
else if (flags == GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT)
spelling_engine_after_insert_text (self->engine, position, length);
else if (flags == GTK_TEXT_BUFFER_NOTIFY_BEFORE_DELETE)
spelling_engine_before_delete_range (self->engine, position, length);
else if (flags == GTK_TEXT_BUFFER_NOTIFY_AFTER_DELETE)
spelling_engine_after_delete_range (self->engine, position);
}
static gboolean
spelling_text_buffer_adapter_check_enabled (gpointer instance)
{
SpellingTextBufferAdapter *self = instance;
g_autoptr(GtkTextBuffer) buffer = NULL;
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return FALSE;
if (gtk_source_buffer_get_loading (GTK_SOURCE_BUFFER (buffer)))
return FALSE;
return self->enabled;
}
static guint
spelling_text_buffer_adapter_get_cursor (gpointer instance)
{
SpellingTextBufferAdapter *self = instance;
g_autoptr(GtkTextBuffer) buffer = NULL;
GtkTextIter iter;
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return 0;
gtk_text_buffer_get_iter_at_mark (buffer, &iter, self->insert_mark);
return gtk_text_iter_get_offset (&iter);
}
static char *
spelling_text_buffer_adapter_copy_text (gpointer instance,
guint position,
guint length)
{
SpellingTextBufferAdapter *self = instance;
g_autoptr(GtkTextBuffer) buffer = NULL;
GtkTextIter begin;
GtkTextIter end;
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
{
g_warn_if_reached ();
return g_new0 (char, length + 1);
}
gtk_text_buffer_get_iter_at_offset (buffer, &begin, position);
gtk_text_buffer_get_iter_at_offset (buffer, &end, position + length);
return gtk_text_iter_get_slice (&begin, &end);
}
static void
spelling_text_buffer_adapter_apply_tag (gpointer instance,
guint position,
guint length)
{
SpellingTextBufferAdapter *self = instance;
g_autoptr(GtkTextBuffer) buffer = NULL;
GtkTextIter begin;
GtkTextIter end;
if (self->tag == NULL)
return;
/* If the position overlaps our cursor position, ignore it. We don't
* want to show that to the user while they are typing and will
* instead deal with it when the cursor leaves the word.
*/
if (position <= self->cursor_position &&
position + length >= self->cursor_position)
return;
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return;
gtk_text_buffer_get_iter_at_offset (buffer, &begin, position);
gtk_text_buffer_get_iter_at_offset (buffer, &end, position + length);
gtk_text_buffer_apply_tag (buffer, self->tag, &begin, &end);
}
static void
spelling_text_buffer_adapter_clear_tag (gpointer instance,
guint position,
guint length)
{
SpellingTextBufferAdapter *self = instance;
g_autoptr(GtkTextBuffer) buffer = NULL;
GtkTextIter begin;
GtkTextIter end;
if (self->tag == NULL)
return;
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return;
gtk_text_buffer_get_iter_at_offset (buffer, &begin, position);
gtk_text_buffer_get_iter_at_offset (buffer, &end, position + length);
gtk_text_buffer_remove_tag (buffer, self->tag, &begin, &end);
}
static gboolean
spelling_text_buffer_adapter_backward_word_start (gpointer instance,
guint *position)
{
SpellingTextBufferAdapter *self = instance;
g_autoptr(GtkTextBuffer) buffer = NULL;
const char *extra_word_chars = NULL;
GtkTextIter iter;
guint prev = *position;
if (self->checker != NULL)
extra_word_chars = spelling_checker_get_extra_word_chars (self->checker);
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return FALSE;
gtk_text_buffer_get_iter_at_offset (buffer, &iter, *position);
spelling_iter_backward_word_start (&iter, extra_word_chars);
*position = gtk_text_iter_get_offset (&iter);
return prev != *position;
}
static gboolean
spelling_text_buffer_adapter_forward_word_end (gpointer instance,
guint *position)
{
SpellingTextBufferAdapter *self = instance;
g_autoptr(GtkTextBuffer) buffer = NULL;
const char *extra_word_chars = NULL;
GtkTextIter iter;
guint prev = *position;
if (self->checker != NULL)
extra_word_chars = spelling_checker_get_extra_word_chars (self->checker);
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return FALSE;
gtk_text_buffer_get_iter_at_offset (buffer, &iter, *position);
spelling_iter_forward_word_end (&iter, extra_word_chars);
*position = gtk_text_iter_get_offset (&iter);
return prev != *position;
}
static PangoLanguage *
spelling_text_buffer_adapter_get_pango_language (gpointer instance)
{
SpellingTextBufferAdapter *self = instance;
return _spelling_checker_get_pango_language (self->checker);
}
static SpellingDictionary *
spelling_text_buffer_adapter_get_dictionary (gpointer instance)
{
SpellingTextBufferAdapter *self = instance;
return _spelling_checker_get_dictionary (self->checker);
}
static void
spelling_text_buffer_adapter_intersect_spellcheck_region (gpointer instance,
GtkBitset *region)
{
SpellingTextBufferAdapter *self = instance;
g_autoptr(GtkTextBuffer) buffer = NULL;
GtkTextIter begin;
GtkTextIter end;
GtkTextIter iter;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
if (self->no_spell_check_tag == NULL || gtk_bitset_is_empty (region))
return;
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return;
gtk_text_buffer_get_iter_at_offset (buffer,
&begin,
gtk_bitset_get_minimum (region));
gtk_text_buffer_get_iter_at_offset (buffer,
&end,
gtk_bitset_get_maximum (region));
if (gtk_text_iter_has_tag (&begin, self->no_spell_check_tag))
{
if (!gtk_text_iter_starts_tag (&begin, self->no_spell_check_tag))
gtk_text_iter_backward_to_tag_toggle (&begin, self->no_spell_check_tag);
}
else
{
gtk_text_iter_forward_to_tag_toggle (&begin, self->no_spell_check_tag);
}
/* At this point we either have a no-spell-check tag or the
* @begin iter will be at the end of the file and we can be
* certain it will be >= 0.
*/
while (gtk_text_iter_compare (&begin, &end) < 0)
{
iter = begin;
gtk_text_iter_forward_to_tag_toggle (&iter, self->no_spell_check_tag);
g_assert (gtk_text_iter_compare (&begin, &iter) < 0);
g_assert (gtk_text_iter_has_tag (&begin, self->no_spell_check_tag));
g_assert (!gtk_text_iter_has_tag (&iter, self->no_spell_check_tag));
#if 0
g_print ("%u:%u to %u:%u: NO SPELL CHECK (%u -> %u)\n",
gtk_text_iter_get_line (&begin) + 1,
gtk_text_iter_get_line_offset (&begin) + 1,
gtk_text_iter_get_line (&iter) + 1,
gtk_text_iter_get_line_offset (&iter) + 1,
gtk_text_iter_get_offset (&begin),
gtk_text_iter_get_offset (&iter));
#endif
gtk_bitset_remove_range_closed (region,
gtk_text_iter_get_offset (&begin),
gtk_text_iter_get_offset (&iter) - 1);
begin = iter;
gtk_text_iter_forward_to_tag_toggle (&begin, self->no_spell_check_tag);
}
}
static const SpellingAdapter adapter_funcs = {
.check_enabled = spelling_text_buffer_adapter_check_enabled,
.get_cursor = spelling_text_buffer_adapter_get_cursor,
.copy_text = spelling_text_buffer_adapter_copy_text,
.apply_tag = spelling_text_buffer_adapter_apply_tag,
.clear_tag = spelling_text_buffer_adapter_clear_tag,
.backward_word_start = spelling_text_buffer_adapter_backward_word_start,
.forward_word_end = spelling_text_buffer_adapter_forward_word_end,
.get_language = spelling_text_buffer_adapter_get_pango_language,
.get_dictionary = spelling_text_buffer_adapter_get_dictionary,
.intersect_spellcheck_region = spelling_text_buffer_adapter_intersect_spellcheck_region,
};
static inline gboolean
forward_word_end (SpellingTextBufferAdapter *self,
GtkTextIter *iter)
{
const char *extra_word_chars = NULL;
if (self->checker != NULL)
extra_word_chars = spelling_checker_get_extra_word_chars (self->checker);
return spelling_iter_forward_word_end (iter, extra_word_chars);
}
static inline gboolean
backward_word_start (SpellingTextBufferAdapter *self,
GtkTextIter *iter)
{
const char *extra_word_chars = NULL;
if (self->checker != NULL)
extra_word_chars = spelling_checker_get_extra_word_chars (self->checker);
return spelling_iter_backward_word_start (iter, extra_word_chars);
}
static gboolean
get_word_at_position (SpellingTextBufferAdapter *self,
guint position,
GtkTextIter *begin,
GtkTextIter *end)
{
g_autoptr(GtkTextBuffer) buffer = NULL;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return FALSE;
gtk_text_buffer_get_iter_at_offset (buffer, begin, position);
*end = *begin;
if (gtk_text_iter_ends_word (end))
{
backward_word_start (self, begin);
return TRUE;
}
if (!gtk_text_iter_starts_word (begin))
{
if (!gtk_text_iter_inside_word (begin))
return FALSE;
backward_word_start (self, begin);
}
if (!gtk_text_iter_ends_word (end))
forward_word_end (self, end);
return TRUE;
}
/**
* spelling_text_buffer_adapter_new:
* @buffer: (not nullable): a `GtkSourceBuffer`
* @checker: a `SpellingChecker`
*
* Create a new `SpellingTextBufferAdapter`.
*
* Returns: (transfer full): a newly created `SpellingTextBufferAdapter`
*/
SpellingTextBufferAdapter *
spelling_text_buffer_adapter_new (GtkSourceBuffer *buffer,
SpellingChecker *checker)
{
g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
g_return_val_if_fail (!checker || SPELLING_IS_CHECKER (checker), NULL);
return g_object_new (SPELLING_TYPE_TEXT_BUFFER_ADAPTER,
"buffer", buffer,
"checker", checker,
NULL);
}
/**
* spelling_text_buffer_adapter_invalidate_all:
* @self: a `SpellingTextBufferAdapter`
*
* Invalidate the spelling engine, to force parsing again.
*
* Invalidation is automatically done on [property@GtkSource.Buffer:loading]
* change.
*/
void
spelling_text_buffer_adapter_invalidate_all (SpellingTextBufferAdapter *self)
{
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
spelling_engine_invalidate_all (self->engine);
}
static void
on_tag_added_cb (SpellingTextBufferAdapter *self,
GtkTextTag *tag,
GtkTextTagTable *tag_table)
{
g_autofree char *name = NULL;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (GTK_IS_TEXT_TAG (tag));
g_assert (GTK_IS_TEXT_TAG_TABLE (tag_table));
g_object_get (tag,
"name", &name,
NULL);
if (name && strcmp (name, NO_SPELL_CHECK_TAG) == 0)
{
g_set_object (&self->no_spell_check_tag, tag);
spelling_text_buffer_adapter_invalidate_all (self);
}
}
static void
on_tag_removed_cb (SpellingTextBufferAdapter *self,
GtkTextTag *tag,
GtkTextTagTable *tag_table)
{
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (GTK_IS_TEXT_TAG (tag));
g_assert (GTK_IS_TEXT_TAG_TABLE (tag_table));
if (tag == self->no_spell_check_tag)
{
g_clear_object (&self->no_spell_check_tag);
spelling_text_buffer_adapter_invalidate_all (self);
}
}
static void
invalidate_tag_region_cb (SpellingTextBufferAdapter *self,
GtkTextTag *tag,
GtkTextIter *begin,
GtkTextIter *end,
GtkTextBuffer *buffer)
{
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (GTK_IS_TEXT_TAG (tag));
g_assert (GTK_IS_TEXT_BUFFER (buffer));
if (tag == self->no_spell_check_tag)
{
gtk_text_iter_order (begin, end);
spelling_engine_invalidate (self->engine,
gtk_text_iter_get_offset (begin),
gtk_text_iter_get_offset (end) - gtk_text_iter_get_offset (begin));
}
}
static void
apply_error_style_cb (GtkSourceBuffer *buffer,
GParamSpec *pspec,
GtkTextTag *tag)
{
GtkSourceStyleScheme *scheme;
GtkSourceStyle *style;
static GdkRGBA error_color;
g_assert (GTK_SOURCE_IS_BUFFER (buffer));
g_assert (GTK_IS_TEXT_TAG (tag));
if G_UNLIKELY (error_color.alpha == .0)
gdk_rgba_parse (&error_color, "#e01b24");
g_object_set (tag,
"underline", PANGO_UNDERLINE_ERROR_LINE,
"underline-rgba", &error_color,
"background-set", FALSE,
"foreground-set", FALSE,
"weight-set", FALSE,
"variant-set", FALSE,
"style-set", FALSE,
"indent-set", FALSE,
"size-set", FALSE,
NULL);
if ((scheme = gtk_source_buffer_get_style_scheme (buffer)))
{
if ((style = gtk_source_style_scheme_get_style (scheme, "def:misspelled-word")))
gtk_source_style_apply (style, tag);
}
}
static void
spelling_text_buffer_adapter_set_buffer (SpellingTextBufferAdapter *self,
GtkSourceBuffer *buffer)
{
GtkTextIter begin, end;
GtkTextTagTable *tag_table;
GtkTextTag *tag;
guint offset;
guint length;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (GTK_SOURCE_IS_BUFFER (buffer));
g_weak_ref_set (&self->buffer_wr, buffer);
self->insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
self->commit_handler =
gtk_text_buffer_add_commit_notify (GTK_TEXT_BUFFER (buffer),
(GTK_TEXT_BUFFER_NOTIFY_BEFORE_INSERT |
GTK_TEXT_BUFFER_NOTIFY_AFTER_INSERT |
GTK_TEXT_BUFFER_NOTIFY_BEFORE_DELETE |
GTK_TEXT_BUFFER_NOTIFY_AFTER_DELETE),
spelling_text_buffer_adapter_commit_notify,
self, NULL);
g_signal_group_set_target (self->buffer_signals, buffer);
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
offset = gtk_text_iter_get_offset (&begin);
length = gtk_text_iter_get_offset (&end) - offset;
if (length > 0)
{
spelling_engine_before_insert_text (self->engine, offset, length);
spelling_engine_after_insert_text (self->engine, offset, length);
}
self->tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer), NULL,
"underline", PANGO_UNDERLINE_ERROR,
NULL);
g_signal_connect_object (buffer,
"notify::style-scheme",
G_CALLBACK (apply_error_style_cb),
self->tag,
0);
apply_error_style_cb (GTK_SOURCE_BUFFER (buffer), NULL, self->tag);
/* Track tag changes from the tag table and extract "no-spell-check"
* tag from GtkSourceView so that we can avoid words with that tag.
*/
tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer));
g_signal_connect_object (tag_table,
"tag-added",
G_CALLBACK (on_tag_added_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (tag_table,
"tag-removed",
G_CALLBACK (on_tag_removed_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (buffer,
"apply-tag",
G_CALLBACK (invalidate_tag_region_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (buffer,
"remove-tag",
G_CALLBACK (invalidate_tag_region_cb),
self,
G_CONNECT_SWAPPED);
if ((tag = gtk_text_tag_table_lookup (tag_table, NO_SPELL_CHECK_TAG)))
on_tag_added_cb (self, tag, tag_table);
}
static void
remember_word_under_cursor (SpellingTextBufferAdapter *self)
{
g_autofree char *word = NULL;
g_autoptr(GtkTextBuffer) buffer = NULL;
g_auto(GStrv) corrections = NULL;
GtkTextMark *insert;
GtkTextIter iter, begin, end;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_clear_pointer (&self->word_under_cursor, g_free);
if (self->checker == NULL)
goto cleanup;
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
goto cleanup;
insert = gtk_text_buffer_get_insert (buffer);
gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
if (get_word_at_position (self, gtk_text_iter_get_offset (&iter), &begin, &end))
{
word = gtk_text_iter_get_slice (&begin, &end);
if (spelling_checker_check_word (self->checker, word, -1))
g_clear_pointer (&word, g_free);
else
corrections = spelling_checker_list_corrections (self->checker, word);
}
cleanup:
g_set_str (&self->word_under_cursor, word);
spelling_text_buffer_adapter_set_action_enabled (self, "add", !!word);
spelling_text_buffer_adapter_set_action_enabled (self, "ignore", !!word);
if (self->menu)
spelling_menu_set_corrections (self->menu, word, (const char * const *)corrections);
}
/**
* spelling_text_buffer_adapter_set_enabled:
* @self: a `SpellingTextBufferAdapter`
* @enabled: whether the spellcheck is enabled
*
* If %TRUE spellcheck is enabled.
*/
void
spelling_text_buffer_adapter_set_enabled (SpellingTextBufferAdapter *self,
gboolean enabled)
{
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
enabled = !!enabled;
if (enabled != self->enabled)
{
self->enabled = enabled;
spelling_text_buffer_adapter_set_action_state (self,
"enabled",
g_variant_new_boolean (enabled));
if (!enabled)
{
spelling_text_buffer_adapter_set_action_enabled (self, "add", FALSE);
spelling_text_buffer_adapter_set_action_enabled (self, "ignore", FALSE);
if (self->menu)
spelling_menu_set_corrections (self->menu, NULL, NULL);
}
else
{
remember_word_under_cursor (self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLED]);
spelling_engine_invalidate_all (self->engine);
}
}
static gboolean
spelling_text_buffer_adapter_cursor_moved_cb (gpointer data)
{
SpellingTextBufferAdapter *self = data;
g_autoptr(GtkTextBuffer) buffer = NULL;
GtkTextIter begin, end;
gboolean enabled;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
self->queued_cursor_moved = 0;
/* Protect against weak-pointer lost */
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return G_SOURCE_REMOVE;
enabled = spelling_text_buffer_adapter_get_enabled (self);
/* Invalidate the old position */
if (enabled && get_word_at_position (self, self->cursor_position, &begin, &end))
spelling_engine_invalidate (self->engine,
gtk_text_iter_get_offset (&begin),
gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&begin));
self->cursor_position = self->incoming_cursor_position;
/* Invalidate word at new position */
if (enabled && get_word_at_position (self, self->cursor_position, &begin, &end))
spelling_engine_invalidate (self->engine,
gtk_text_iter_get_offset (&begin),
gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&begin));
remember_word_under_cursor (self);
return G_SOURCE_REMOVE;
}
static void
spelling_text_buffer_adapter_cursor_moved (SpellingTextBufferAdapter *self,
GtkSourceBuffer *buffer)
{
GtkTextIter iter;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (GTK_SOURCE_IS_BUFFER (buffer));
gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter, self->insert_mark);
self->incoming_cursor_position = gtk_text_iter_get_offset (&iter);
g_clear_handle_id (&self->queued_cursor_moved, g_source_remove);
if (!spelling_text_buffer_adapter_check_enabled (self))
return;
self->queued_cursor_moved = g_timeout_add_full (G_PRIORITY_LOW,
INVALIDATE_DELAY_MSECS,
spelling_text_buffer_adapter_cursor_moved_cb,
g_object_ref (self),
g_object_unref);
}
static void
spelling_text_buffer_adapter_notify_loading_cb (SpellingTextBufferAdapter *self,
GParamSpec *pspec,
GtkSourceBuffer *buffer)
{
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (GTK_SOURCE_IS_BUFFER (buffer));
if (self->engine != NULL)
spelling_engine_invalidate_all (self->engine);
}
static void
spelling_text_buffer_adapter_finalize (GObject *object)
{
SpellingTextBufferAdapter *self = (SpellingTextBufferAdapter *)object;
self->tag = NULL;
self->insert_mark = NULL;
g_clear_pointer (&self->word_under_cursor, g_free);
g_clear_object (&self->checker);
g_clear_object (&self->no_spell_check_tag);
g_clear_object (&self->buffer_signals);
g_weak_ref_clear (&self->buffer_wr);
G_OBJECT_CLASS (spelling_text_buffer_adapter_parent_class)->finalize (object);
}
static void
spelling_text_buffer_adapter_dispose (GObject *object)
{
SpellingTextBufferAdapter *self = (SpellingTextBufferAdapter *)object;
g_autoptr(GtkTextBuffer) buffer = NULL;
if ((buffer = g_weak_ref_get (&self->buffer_wr)))
{
gtk_text_buffer_remove_commit_notify (buffer, self->commit_handler);
self->commit_handler = 0;
g_weak_ref_set (&self->buffer_wr, NULL);
}
g_signal_group_set_target (self->buffer_signals, NULL);
g_clear_object (&self->engine);
g_clear_object (&self->menu);
g_clear_object (&self->top_menu);
G_OBJECT_CLASS (spelling_text_buffer_adapter_parent_class)->dispose (object);
}
static void
spelling_text_buffer_adapter_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SpellingTextBufferAdapter *self = SPELLING_TEXT_BUFFER_ADAPTER (object);
switch (prop_id)
{
case PROP_BUFFER:
g_value_take_object (value, g_weak_ref_get (&self->buffer_wr));
break;
case PROP_CHECKER:
g_value_set_object (value, spelling_text_buffer_adapter_get_checker (self));
break;
case PROP_ENABLED:
g_value_set_boolean (value, spelling_text_buffer_adapter_get_enabled (self));
break;
case PROP_LANGUAGE:
g_value_set_string (value, spelling_text_buffer_adapter_get_language (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_text_buffer_adapter_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SpellingTextBufferAdapter *self = SPELLING_TEXT_BUFFER_ADAPTER (object);
switch (prop_id)
{
case PROP_BUFFER:
spelling_text_buffer_adapter_set_buffer (self, g_value_get_object (value));
break;
case PROP_CHECKER:
spelling_text_buffer_adapter_set_checker (self, g_value_get_object (value));
break;
case PROP_ENABLED:
spelling_text_buffer_adapter_set_enabled (self, g_value_get_boolean (value));
break;
case PROP_LANGUAGE:
spelling_text_buffer_adapter_set_language (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
spelling_text_buffer_adapter_class_init (SpellingTextBufferAdapterClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = spelling_text_buffer_adapter_dispose;
object_class->finalize = spelling_text_buffer_adapter_finalize;
object_class->get_property = spelling_text_buffer_adapter_get_property;
object_class->set_property = spelling_text_buffer_adapter_set_property;
/**
* SpellingTextBufferAdapter:buffer:
*
* The [class@GtkSource.Buffer].
*/
properties[PROP_BUFFER] =
g_param_spec_object ("buffer", NULL, NULL,
GTK_SOURCE_TYPE_BUFFER,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
/**
* SpellingTextBufferAdapter:checker:
*
* The [class@Spelling.Checker].
*/
properties[PROP_CHECKER] =
g_param_spec_object ("checker", NULL, NULL,
SPELLING_TYPE_CHECKER,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* SpellingTextBufferAdapter:enabled:
*
* Whether spellcheck is enabled.
*/
properties[PROP_ENABLED] =
g_param_spec_boolean ("enabled", NULL, NULL,
TRUE,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
/**
* SpellingTextBufferAdapter:language:
*
* The language code, such as `en_US`.
*/
properties[PROP_LANGUAGE] =
g_param_spec_string ("language", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
spelling_text_buffer_adapter_init (SpellingTextBufferAdapter *self)
{
g_weak_ref_init (&self->buffer_wr, NULL);
self->enabled = TRUE;
spelling_text_buffer_adapter_set_action_state (self,
"enabled",
g_variant_new_boolean (TRUE));
self->buffer_signals = g_signal_group_new (GTK_SOURCE_TYPE_BUFFER);
g_signal_group_connect_object (self->buffer_signals,
"cursor-moved",
G_CALLBACK (spelling_text_buffer_adapter_cursor_moved),
self,
G_CONNECT_SWAPPED);
g_signal_group_connect_object (self->buffer_signals,
"notify::loading",
G_CALLBACK (spelling_text_buffer_adapter_notify_loading_cb),
self,
G_CONNECT_SWAPPED);
self->engine = spelling_engine_new (&adapter_funcs, G_OBJECT (self));
}
/**
* spelling_text_buffer_adapter_get_checker:
* @self: a `SpellingTextBufferAdapter`
*
* Gets the checker used by the adapter.
*
* Returns: (transfer none) (nullable): a `SpellingChecker` or %NULL
*/
SpellingChecker *
spelling_text_buffer_adapter_get_checker (SpellingTextBufferAdapter *self)
{
g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL);
return self->checker;
}
static void
spelling_text_buffer_adapter_checker_notify_language (SpellingTextBufferAdapter *self,
GParamSpec *pspec,
SpellingChecker *checker)
{
const char *code;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (SPELLING_IS_CHECKER (checker));
if (!(code = spelling_checker_get_language (checker)))
code = "";
spelling_text_buffer_adapter_set_action_state (self, "language", g_variant_new_string (code));
}
/**
* spelling_text_buffer_adapter_set_checker:
* @self: a `SpellingTextBufferAdapter`
* @checker: a `SpellingChecker`
*
* Set the [class@Spelling.Checker] used for spellchecking.
*/
void
spelling_text_buffer_adapter_set_checker (SpellingTextBufferAdapter *self,
SpellingChecker *checker)
{
const char *code = "";
g_return_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_return_if_fail (!checker || SPELLING_IS_CHECKER (checker));
if (self->checker == checker)
return;
if (self->checker)
g_signal_handlers_disconnect_by_func (self->checker,
G_CALLBACK (spelling_text_buffer_adapter_checker_notify_language),
self);
g_set_object (&self->checker, checker);
if (checker)
{
g_signal_connect_object (self->checker,
"notify::language",
G_CALLBACK (spelling_text_buffer_adapter_checker_notify_language),
self,
G_CONNECT_SWAPPED);
if (!(code = spelling_checker_get_language (checker)))
code = "";
}
spelling_engine_invalidate_all (self->engine);
spelling_text_buffer_adapter_set_action_state (self, "language", g_variant_new_string (code));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHECKER]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]);
}
/**
* spelling_text_buffer_adapter_get_buffer:
* @self: a `SpellingTextBufferAdapter`
*
* Gets the underlying buffer for the adapter.
*
* Returns: (transfer none) (nullable): a `GtkSourceBuffer`
*/
GtkSourceBuffer *
spelling_text_buffer_adapter_get_buffer (SpellingTextBufferAdapter *self)
{
g_autoptr(GtkTextBuffer) buffer = NULL;
g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL);
buffer = g_weak_ref_get (&self->buffer_wr);
/* return borrowed instance only */
return GTK_SOURCE_BUFFER (buffer);
}
/**
* spelling_text_buffer_adapter_get_language:
* @self: a `SpellingTextBufferAdapter`
*
* Gets the checker language.
*
* Returns: (transfer none) (nullable): a language code
*/
const char *
spelling_text_buffer_adapter_get_language (SpellingTextBufferAdapter *self)
{
g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL);
return self->checker ? spelling_checker_get_language (self->checker) : NULL;
}
/**
* spelling_text_buffer_adapter_set_language:
* @self: a `SpellingTextBufferAdapter`
* @language: the language to use
*
* Sets the language code to use by the checker, such as `en_US`.
*/
void
spelling_text_buffer_adapter_set_language (SpellingTextBufferAdapter *self,
const char *language)
{
g_return_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
if (self->checker == NULL && language == NULL)
return;
if (self->checker == NULL)
{
self->checker = spelling_checker_new (NULL, language);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHECKER]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]);
}
else if (g_strcmp0 (language, spelling_text_buffer_adapter_get_language (self)) != 0)
{
spelling_checker_set_language (self->checker, language);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LANGUAGE]);
}
spelling_text_buffer_adapter_invalidate_all (self);
}
/**
* spelling_text_buffer_adapter_get_tag:
* @self: a `SpellingTextBufferAdapter`
*
* Gets the tag used for potentially misspelled words.
*
* Returns: (transfer none) (nullable): a `GtkTextTag` or %NULL
*/
GtkTextTag *
spelling_text_buffer_adapter_get_tag (SpellingTextBufferAdapter *self)
{
g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL);
return self->tag;
}
/**
* spelling_text_buffer_adapter_get_enabled:
* @self: a `SpellingTextBufferAdapter`
*
* Gets if the spellcheck is enabled.
*
* Returns: %TRUE if enabled
*/
gboolean
spelling_text_buffer_adapter_get_enabled (SpellingTextBufferAdapter *self)
{
g_return_val_if_fail (!self || SPELLING_IS_TEXT_BUFFER_ADAPTER (self), FALSE);
if (self == NULL)
return FALSE;
return self->enabled;
}
/**
* spelling_text_buffer_adapter_get_menu_model:
* @self: a `SpellingTextBufferAdapter`
*
* Gets the menu model containing corrections
*
* Returns: (transfer none): a `GMenuModel`
*/
GMenuModel *
spelling_text_buffer_adapter_get_menu_model (SpellingTextBufferAdapter *self)
{
g_return_val_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self), NULL);
if (self->menu == NULL)
{
self->menu = spelling_menu_new ();
self->top_menu = g_menu_new ();
g_menu_append_section (self->top_menu, NULL, self->menu);
}
return G_MENU_MODEL (self->top_menu);
}
static void
spelling_add_action (SpellingTextBufferAdapter *self,
GVariant *param)
{
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (self->word_under_cursor != NULL);
if (self->checker != NULL)
{
spelling_checker_add_word (self->checker, self->word_under_cursor);
spelling_text_buffer_adapter_invalidate_all (self);
}
}
static void
spelling_ignore_action (SpellingTextBufferAdapter *self,
GVariant *param)
{
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (self->word_under_cursor != NULL);
if (self->checker != NULL)
{
spelling_checker_ignore_word (self->checker, self->word_under_cursor);
spelling_text_buffer_adapter_invalidate_all (self);
}
}
static void
spelling_enabled_action (SpellingTextBufferAdapter *self,
GVariant *param)
{
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
spelling_text_buffer_adapter_set_enabled (self,
!spelling_text_buffer_adapter_get_enabled (self));
}
static void
spelling_correct_action (SpellingTextBufferAdapter *self,
GVariant *param)
{
g_autofree char *slice = NULL;
g_autoptr(GtkTextBuffer) buffer = NULL;
const char *word;
GtkTextIter begin, end;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
if (!(buffer = g_weak_ref_get (&self->buffer_wr)))
return;
word = g_variant_get_string (param, NULL);
/* We don't deal with selections (yet?) */
if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
return;
if (!get_word_at_position (self, gtk_text_iter_get_offset (&begin), &begin, &end))
return;
slice = gtk_text_iter_get_slice (&begin, &end);
if (g_strcmp0 (slice, self->word_under_cursor) != 0)
{
g_debug ("Words do not match, will not replace.");
return;
}
gtk_text_buffer_begin_user_action (buffer);
gtk_text_buffer_delete (buffer, &begin, &end);
gtk_text_buffer_insert (buffer, &begin, word, -1);
gtk_text_buffer_end_user_action (buffer);
}
static void
spelling_language_action (SpellingTextBufferAdapter *self,
GVariant *param)
{
const char *code;
g_assert (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
code = g_variant_get_string (param, NULL);
if (self->checker)
spelling_checker_set_language (self->checker, code);
}
/**
* spelling_text_buffer_adapter_update_corrections:
* @self: a `SpellingTextBufferAdapter`
*
* Looks at the current cursor position and updates the list of
* corrections based on the current word.
*
* Use this to force an update immediately rather than after the
* automatic timeout caused by cursor movements.
*/
void
spelling_text_buffer_adapter_update_corrections (SpellingTextBufferAdapter *self)
{
g_return_if_fail (SPELLING_IS_TEXT_BUFFER_ADAPTER (self));
if (self->enabled == FALSE)
return;
remember_word_under_cursor (self);
}
0707010000003A000081A400000000000000000000000168A626C200000CD8000000000000000000000000000000000000003500000000libspelling-0.4.9/lib/spelling-text-buffer-adapter.h/*
* spelling-text-buffer-adapter.h
*
* Copyright 2021-2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gtksourceview/gtksource.h>
#include "spelling-types.h"
#include "spelling-version-macros.h"
G_BEGIN_DECLS
#define SPELLING_TYPE_TEXT_BUFFER_ADAPTER (spelling_text_buffer_adapter_get_type())
SPELLING_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SpellingTextBufferAdapter, spelling_text_buffer_adapter, SPELLING, TEXT_BUFFER_ADAPTER, GObject)
SPELLING_AVAILABLE_IN_ALL
SpellingTextBufferAdapter *spelling_text_buffer_adapter_new (GtkSourceBuffer *buffer,
SpellingChecker *checker);
SPELLING_AVAILABLE_IN_ALL
GtkSourceBuffer *spelling_text_buffer_adapter_get_buffer (SpellingTextBufferAdapter *self);
SPELLING_AVAILABLE_IN_ALL
gboolean spelling_text_buffer_adapter_get_enabled (SpellingTextBufferAdapter *self);
SPELLING_AVAILABLE_IN_ALL
void spelling_text_buffer_adapter_set_enabled (SpellingTextBufferAdapter *self,
gboolean enabled);
SPELLING_AVAILABLE_IN_ALL
SpellingChecker *spelling_text_buffer_adapter_get_checker (SpellingTextBufferAdapter *self);
SPELLING_AVAILABLE_IN_ALL
void spelling_text_buffer_adapter_set_checker (SpellingTextBufferAdapter *self,
SpellingChecker *checker);
SPELLING_AVAILABLE_IN_ALL
const char *spelling_text_buffer_adapter_get_language (SpellingTextBufferAdapter *self);
SPELLING_AVAILABLE_IN_ALL
void spelling_text_buffer_adapter_set_language (SpellingTextBufferAdapter *self,
const char *language);
SPELLING_AVAILABLE_IN_ALL
void spelling_text_buffer_adapter_invalidate_all (SpellingTextBufferAdapter *self);
SPELLING_AVAILABLE_IN_ALL
GtkTextTag *spelling_text_buffer_adapter_get_tag (SpellingTextBufferAdapter *self);
SPELLING_AVAILABLE_IN_ALL
GMenuModel *spelling_text_buffer_adapter_get_menu_model (SpellingTextBufferAdapter *self);
SPELLING_AVAILABLE_IN_ALL
void spelling_text_buffer_adapter_update_corrections (SpellingTextBufferAdapter *self);
G_END_DECLS
0707010000003B000081A400000000000000000000000168A626C200000962000000000000000000000000000000000000002700000000libspelling-0.4.9/lib/spelling-trace.h/* spelling-trace.h
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <glib.h>
#ifndef GETTEXT_PACKAGE
# error "config.h was not included before sysprof-trace.h."
#endif
#ifdef HAVE_SYSPROF
# include <sysprof-capture.h>
#endif
G_BEGIN_DECLS
#ifdef HAVE_SYSPROF
# define SPELLING_PROFILER_ENABLED 1
# define SPELLING_PROFILER_CURRENT_TIME SYSPROF_CAPTURE_CURRENT_TIME
# define SPELLING_PROFILER_ACTIVE (sysprof_collector_is_active())
# define SPELLING_PROFILER_BEGIN_MARK gint64 __begin_time = SYSPROF_CAPTURE_CURRENT_TIME
# define SPELLING_PROFILER_END_MARK(name, message) \
G_STMT_START { \
gint64 __duration = SYSPROF_CAPTURE_CURRENT_TIME - __begin_time; \
sysprof_collector_mark (__begin_time, __duration, "Spelling", name, message); \
} G_STMT_END
# define SPELLING_PROFILER_MARK(duration, name, message) \
G_STMT_START { \
sysprof_collector_mark (SYSPROF_CAPTURE_CURRENT_TIME - (duration), \
(duration), "Spelling", name, message); \
} G_STMT_END
# define SPELLING_PROFILER_LOG(format, ...) \
G_STMT_START { \
if (SPELLING_PROFILER_ACTIVE) \
sysprof_collector_log_printf(G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN, format, __VA_ARGS__); \
} G_STMT_END
#else
# undef SPELLING_PROFILER_ENABLED
# define SPELLING_PROFILER_ACTIVE (0)
# define SPELLING_PROFILER_CURRENT_TIME 0
# define SPELLING_PROFILER_MARK(duration, name, message) G_STMT_START {} G_STMT_END
# define SPELLING_PROFILER_BEGIN_MARK G_STMT_START {} G_STMT_END
# define SPELLING_PROFILER_END_MARK(name, message) G_STMT_START {} G_STMT_END
# define SPELLING_PROFILER_LOG(format, ...) G_STMT_START {} G_STMT_END
#endif
G_END_DECLS
0707010000003C000081A400000000000000000000000168A626C20000043B000000000000000000000000000000000000002700000000libspelling-0.4.9/lib/spelling-types.h/*
* spelling-types.h
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
typedef struct _SpellingChecker SpellingChecker;
typedef struct _SpellingDictionary SpellingDictionary;
typedef struct _SpellingLanguage SpellingLanguage;
typedef struct _SpellingProvider SpellingProvider;
G_END_DECLS
0707010000003D000081A400000000000000000000000168A626C200000ED7000000000000000000000000000000000000003000000000libspelling-0.4.9/lib/spelling-version-macros.h/*
* spelling-version-macros.h
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(LIBSPELLING_INSIDE) && !defined(LIBSPELLING_COMPILATION)
# error "Only <libspelling.h> can be included directly."
#endif
#include <glib.h>
#include "spelling-version.h"
#ifndef _SPELLING_EXTERN
# define _SPELLING_EXTERN extern
#endif
#define SPELLING_VERSION_CUR_STABLE (G_ENCODE_VERSION (SPELLING_MAJOR_VERSION, 0))
#ifdef SPELLING_DISABLE_DEPRECATION_WARNINGS
# define SPELLING_DEPRECATED _SPELLING_EXTERN
# define SPELLING_DEPRECATED_FOR(f) _SPELLING_EXTERN
# define SPELLING_UNAVAILABLE(maj,min) _SPELLING_EXTERN
#else
# define SPELLING_DEPRECATED G_DEPRECATED _SPELLING_EXTERN
# define SPELLING_DEPRECATED_FOR(f) G_DEPRECATED_FOR (f) _SPELLING_EXTERN
# define SPELLING_UNAVAILABLE(maj,min) G_UNAVAILABLE (maj, min) _SPELLING_EXTERN
#endif
#define SPELLING_VERSION_1_0 (G_ENCODE_VERSION (1, 0))
#if SPELLING_MAJOR_VERSION == SPELLING_VERSION_1_0
# define SPELLING_VERSION_PREV_STABLE (SPELLING_VERSION_1_0)
#else
# define SPELLING_VERSION_PREV_STABLE (G_ENCODE_VERSION (SPELLING_MAJOR_VERSION - 1, 0))
#endif
/**
* SPELLING_VERSION_MIN_REQUIRED:
*
* A macro that should be defined by the user prior to including
* the spelling.h header.
*
* The definition should be one of the predefined SPELLING version
* macros: %SPELLING_VERSION_1_0, ...
*
* This macro defines the lower bound for the Builder API to use.
*
* If a function has been deprecated in a newer version of Builder,
* it is possible to use this symbol to avoid the compiler warnings
* without disabling warning for every deprecated function.
*/
#ifndef SPELLING_VERSION_MIN_REQUIRED
# define SPELLING_VERSION_MIN_REQUIRED (SPELLING_VERSION_CUR_STABLE)
#endif
/**
* SPELLING_VERSION_MAX_ALLOWED:
*
* A macro that should be defined by the user prior to including
* the spelling.h header.
* The definition should be one of the predefined Builder version
* macros: %SPELLING_VERSION_1_0, %SPELLING_VERSION_1_2,...
*
* This macro defines the upper bound for the SPELLING API to use.
*
* If a function has been introduced in a newer version of Builder,
* it is possible to use this symbol to get compiler warnings when
* trying to use that function.
*/
#ifndef SPELLING_VERSION_MAX_ALLOWED
# if SPELLING_VERSION_MIN_REQUIRED > SPELLING_VERSION_PREV_STABLE
# define SPELLING_VERSION_MAX_ALLOWED (SPELLING_VERSION_MIN_REQUIRED)
# else
# define SPELLING_VERSION_MAX_ALLOWED (SPELLING_VERSION_CUR_STABLE)
# endif
#endif
#define SPELLING_AVAILABLE_IN_ALL _SPELLING_EXTERN
#if SPELLING_VERSION_MIN_REQUIRED >= SPELLING_VERSION_1_0
# define SPELLING_DEPRECATED_IN_1_0 SPELLING_DEPRECATED
# define SPELLING_DEPRECATED_IN_1_0_FOR(f) SPELLING_DEPRECATED_FOR(f)
#else
# define SPELLING_DEPRECATED_IN_1_0 _SPELLING_EXTERN
# define SPELLING_DEPRECATED_IN_1_0_FOR(f) _SPELLING_EXTERN
#endif
#if SPELLING_VERSION_MAX_ALLOWED < SPELLING_VERSION_1_0
# define SPELLING_AVAILABLE_IN_1_0 SPELLING_UNAVAILABLE(1, 0)
#else
# define SPELLING_AVAILABLE_IN_1_0 _SPELLING_EXTERN
#endif
0707010000003E000081A400000000000000000000000168A626C200000ACF000000000000000000000000000000000000002C00000000libspelling-0.4.9/lib/spelling-version.h.in/* libspelling-version.h.in
*
* Copyright 2023 Christian Hergert
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
/**
* SECTION:libspellingversion
* @short_description: libspelling version checking
*
* libspelling provides macros to check the version of the library
* at compile-time
*/
/**
* SPELLING_MAJOR_VERSION:
*
* libspelling major version component (e.g. 1 if %SPELLING_VERSION is 1.2.3)
*/
#define SPELLING_MAJOR_VERSION (@MAJOR_VERSION@)
/**
* SPELLING_MINOR_VERSION:
*
* libspelling minor version component (e.g. 2 if %SPELLING_VERSION is 1.2.3)
*/
#define SPELLING_MINOR_VERSION (@MINOR_VERSION@)
/**
* SPELLING_MICRO_VERSION:
*
* libspelling micro version component (e.g. 3 if %SPELLING_VERSION is 1.2.3)
*/
#define SPELLING_MICRO_VERSION (@MICRO_VERSION@)
/**
* SPELLING_VERSION
*
* libspelling version.
*/
#define SPELLING_VERSION (@VERSION@)
/**
* SPELLING_VERSION_S:
*
* libspelling version, encoded as a string, useful for printing and
* concatenation.
*/
#define SPELLING_VERSION_S "@VERSION@"
#define SPELLING_ENCODE_VERSION(major,minor,micro) \
((major) << 24 | (minor) << 16 | (micro) << 8)
/**
* SPELLING_VERSION_HEX:
*
* libspelling version, encoded as an hexadecimal number, useful for
* integer comparisons.
*/
#define SPELLING_VERSION_HEX \
(SPELLING_ENCODE_VERSION (SPELLING_MAJOR_VERSION, SPELLING_MINOR_VERSION, SPELLING_MICRO_VERSION))
/**
* SPELLING_CHECK_VERSION:
* @major: required major version
* @minor: required minor version
* @micro: required micro version
*
* Compile-time version checking. Evaluates to %TRUE if the version
* of libspelling is greater than the required one.
*/
#define SPELLING_CHECK_VERSION(major,minor,micro) \
(SPELLING_MAJOR_VERSION > (major) || \
(SPELLING_MAJOR_VERSION == (major) && SPELLING_MINOR_VERSION > (minor)) || \
(SPELLING_MAJOR_VERSION == (major) && SPELLING_MINOR_VERSION == (minor) && \
SPELLING_MICRO_VERSION >= (micro)))
0707010000003F000081A400000000000000000000000168A626C2000003CA000000000000000000000000000000000000002300000000libspelling-0.4.9/libspelling.doap<?xml version="1.0" encoding="UTF-8"?>
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:gnome="http://api.gnome.org/doap-extensions#"
xmlns="http://usefulinc.com/ns/doap#">
<name>libspelling</name>
<shortname>libspelling</shortname>
<shortdesc>A spellcheck library for GTK 4</shortdesc>
<description>
libspelling provides spellcheck integration for GTK 4.
</description>
<homepage rdf:resource="https://gitlab.gnome.org/GNOME/libspelling" />
<license rdf:resource="https://spdx.org/licenses/LGPL-2.1-or-later.html" />
<programming-language>C</programming-language>
<maintainer>
<foaf:Person>
<foaf:name>Christian Hergert</foaf:name>
<foaf:mbox rdf:resource="mailto:chergert@gnome.org" />
<gnome:userid>chergert</gnome:userid>
</foaf:Person>
</maintainer>
</Project>
07070100000040000081A400000000000000000000000168A626C2000010E0000000000000000000000000000000000000001E00000000libspelling-0.4.9/meson.buildproject('libspelling', 'c',
version: '0.4.9',
meson_version: '>= 0.62.0',
default_options: [ 'warning_level=2', 'werror=false', 'c_std=gnu11', ],
)
libgtk_dep = dependency('gtk4', version: '>= 4.15.5')
gtksource_dep = dependency('gtksourceview-5', version: '>= 5.10.0')
libgio_dep = dependency('gio-2.0')
cc = meson.get_compiler('c')
prefix = get_option('prefix')
datadir = join_paths(prefix, get_option('datadir'))
config_h = configuration_data()
config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
config_h.set_quoted('GETTEXT_PACKAGE', 'libspelling')
config_h.set_quoted('PACKAGE_LOCALE_DIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale'))
config_h.set10('HAVE_ENCHANT', get_option('enchant').enabled())
# libsysprof-capture support for profiling
if get_option('sysprof')
libsysprof_capture_dep = dependency('sysprof-capture-4',
required: get_option('sysprof'),
default_options: [
'examples=false',
'gtk=false',
'help=false',
'install-static=false',
'libsysprof=false',
'sysprofd=none',
'tests=false',
'tools=false',
],
fallback: ['sysprof', 'libsysprof_capture_dep'],
)
config_h.set('HAVE_SYSPROF', libsysprof_capture_dep.found())
profiler_enabled = true
else
libsysprof_capture_dep = disabler()
profiler_enabled = false
endif
# Detect and set symbol visibility
if get_option('default_library') != 'static'
if host_machine.system() == 'windows'
config_h.set('DLL_EXPORT', true)
if cc.get_id() == 'msvc'
config_h.set('_SPELLING_EXTERN', '__declspec(dllexport) extern')
elif cc.has_argument('-fvisibility=hidden')
config_h.set('_SPELLING_EXTERN', '__attribute__((visibility("default"))) __declspec(dllexport) extern')
endif
elif cc.has_argument('-fvisibility=hidden')
config_h.set('_SPELLING_EXTERN', '__attribute__((visibility("default"))) extern')
endif
endif
project_c_args = []
test_c_args = [
'-Wcast-align',
'-Wdeclaration-after-statement',
'-Werror=address',
'-Werror=array-bounds',
'-Werror=empty-body',
'-Werror=implicit',
'-Werror=implicit-function-declaration',
'-Werror=incompatible-pointer-types',
'-Werror=init-self',
'-Werror=int-conversion',
'-Werror=int-to-pointer-cast',
'-Werror=main',
'-Werror=misleading-indentation',
'-Werror=missing-braces',
'-Werror=missing-include-dirs',
'-Werror=nonnull',
'-Werror=overflow',
'-Werror=parenthesis',
'-Werror=pointer-arith',
'-Werror=pointer-to-int-cast',
'-Werror=redundant-decls',
'-Werror=return-type',
'-Werror=sequence-point',
'-Werror=shadow',
'-Werror=strict-prototypes',
'-Werror=trigraphs',
'-Werror=undef',
'-Werror=write-strings',
'-Wformat-nonliteral',
'-Wignored-qualifiers',
'-Wimplicit-function-declaration',
'-Wlogical-op',
'-Wmissing-declarations',
'-Wmissing-format-attribute',
'-Wmissing-include-dirs',
'-Wmissing-noreturn',
'-Wnested-externs',
'-Wno-cast-function-type',
'-Wno-dangling-pointer',
'-Wno-missing-field-initializers',
'-Wno-sign-compare',
'-Wno-unused-parameter',
'-Wold-style-definition',
'-Wpointer-arith',
'-Wredundant-decls',
'-Wstrict-prototypes',
'-Wswitch-default',
'-Wswitch-enum',
'-Wundef',
'-Wuninitialized',
'-Wunused',
'-fno-strict-aliasing',
['-Werror=format-security', '-Werror=format=2'],
]
if get_option('buildtype') != 'plain'
test_c_args += '-fstack-protector-strong'
endif
foreach arg: test_c_args
if cc.has_multi_arguments(arg)
project_c_args += arg
endif
endforeach
add_project_arguments(project_c_args, language: 'c')
release_args = []
global_link_args = []
test_link_args = [
'-Wl,-z,relro',
'-Wl,-z,now',
'-Wl,-z,noexecstack',
]
if not get_option('buildtype').startswith('debug')
add_global_arguments('-DG_DISABLE_CAST_CHECKS', language: 'c')
release_args += [ '-DG_DISABLE_ASSERT' ]
test_link_args += [
'-Wl,-Bsymbolic',
'-fno-plt',
]
endif
foreach link_arg: test_link_args
if cc.has_link_argument(link_arg)
global_link_args += link_arg
endif
endforeach
add_project_link_arguments(global_link_args, language: 'c')
configure_file(output: 'config.h', configuration: config_h)
subdir('lib')
subdir('po')
subdir('test')
subdir('testsuite')
if get_option('docs')
subdir('docs')
endif
07070100000041000081A400000000000000000000000168A626C20000026E000000000000000000000000000000000000002400000000libspelling-0.4.9/meson_options.txtoption('docs', type: 'boolean', value: true, description: 'Generate documentation')
option('enchant', type: 'feature', value: 'enabled', description: 'Use enchant for spellchecking')
option('introspection', type: 'feature', value: 'enabled', description: 'Generate gir data (requires gobject-introspection)')
option('sysprof', type: 'boolean', value: true, description: 'Generate profiler data using Sysprof')
option('vapi', type: 'boolean', value: true, description: 'Generate Vala vapi (Requires introspection)')
option('install-static', type: 'boolean', value: false, description: 'Install libspelling static archive')
07070100000042000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001500000000libspelling-0.4.9/po07070100000043000081A400000000000000000000000168A626C2000000B3000000000000000000000000000000000000001D00000000libspelling-0.4.9/po/LINGUASab
ar
be
bg
ca
cs
da
de
el
en_GB
eo
es
eu
fa
fi
fr
fur
gl
he
hi
hr
hu
ia
id
ie
is
it
ja
ka
kab
kk
ko
lt
lv
nb
ne
nl
oc
pa
pl
pt
pt_BR
ro
ru
sk
sl
sr
sv
th
tr
uk
uz
vi
zh_CN
zh_TW
07070100000044000081A400000000000000000000000168A626C2000000A3000000000000000000000000000000000000002100000000libspelling-0.4.9/po/POTFILES.in# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
lib/enchant/spelling-enchant-provider.c
lib/spelling-menu.c
07070100000045000081A400000000000000000000000168A626C200000000000000000000000000000000000000000000002300000000libspelling-0.4.9/po/POTFILES.skip07070100000046000081A400000000000000000000000168A626C2000002BA000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ab.pomsgid ""
msgstr ""
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"Last-Translator: Нанба Наала <naala-nanba@rambler.ru>\n"
"Language-Team: Abkhazian <daniel.abzakh@gmail.com>\n"
"Language: ab\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr ""
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr ""
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Аиашаҩыра агәаҭара"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr ""
07070100000047000081A400000000000000000000000168A626C2000003EB000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ar.po# Arabic translation for libspelling.
# Copyright (C) 2025 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Ahmed Najmawi <iramosu@protonmail.com>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/-/issues\n"
"POT-Creation-Date: 2025-05-02 10:40+0000\n"
"PO-Revision-Date: 2025-05-03 21:42+0300\n"
"Last-Translator: Ahmed Najmawi <iramosu@protonmail.com>\n"
"Language-Team: Arabic <Arabic>\n"
"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "أضِف إلى القاموس"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "تجاهَل"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "دقِّق الإملاء"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "اللغات"
07070100000048000081A400000000000000000000000168A626C200000497000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/be.po# Belarusian translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Yuras Shumovich <shumovichy@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling gnome-43\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-23 18:21+0300\n"
"Last-Translator: Yuras Shumovich <shumovichy@gmail.com>\n"
"Language-Team: Belarusian <i18n-bel-gnome@googlegroups.com>\n"
"Language: be\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Дадаць у слоўнік"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ігнараваць"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Праверка правапісу"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Мовы"
07070100000049000081A400000000000000000000000168A626C20000048B000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/bg.po# Bulgarian translation of libspelling po-file.
# Copyright (C) 2022, 2023, 2024 Alexander Shopov.
# Copyright (C) 2024 twlvnn kraftwerk.
# This file is distributed under the same license as the libspelling package.
# Alexander Shopov <ash@kambanaria.org> 2022, 2023, 2024.
# twlvnn kraftwerk <kraft_werk@tutanota.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-31 11:54+0200\n"
"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
"Language: bg\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Добавяне в речника"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Игнориране"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Проверка на правописа"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Езици"
0707010000004A000081A400000000000000000000000168A626C200000428000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ca.po# Catalan translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Jordi Mas i Hernàndez <jmas@softcatala.org>, 2021, 2023
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-22 21:38+0200\n"
"Last-Translator: Jordi Mas i Hernàndez <jmas@softcatala.org>\n"
"Language-Team: Catalan <gnome@llistes.softcatala.org>\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.4.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Afegeix al diccionari"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignora"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Comprova l'ortografia"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Idiomes"
0707010000004B000081A400000000000000000000000168A626C200000428000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/cs.po# Czech translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
#
# Marek Černocký <marek@manet.cz>, 2021, 2022, 2023.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-26 01:24+0200\n"
"Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
"Language-Team: Czech <gnome-cs-list@gnome.org>\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Přidat do slovníku"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorovat"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Kontrolovat pravopis"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Jazyky"
0707010000004C000081A400000000000000000000000168A626C200000410000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/da.po# Danish translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Alan Mortensen <alanmortensen.am@gmail.com>, 2022-24.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-10-06 23:59+0200\n"
"Last-Translator: Alan Mortensen <alanmortensen.am@gmail.com>\n"
"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
"Language: da\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Tilføj til ordbog"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorér"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Stavekontrol"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Sprog"
0707010000004D000081A400000000000000000000000168A626C2000004C1000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/de.po# German translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
#
#
# Philipp Kiemle <philipp.kiemle@gmail.com>, 2021-2023.
# Christian Kirbach <christian.kirbach@gmail.com>, 2021, 2022.
# Tim Sabsch <tim@sabsch.com>, 2022.
# Jürgen Benvenuti <gastornis@posteo.org>, 2022-2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-22 22:31+0200\n"
"Last-Translator: Jürgen Benvenuti <gastornis@posteo.org>\n"
"Language-Team: German <gnome-de@gnome.org>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Zum Wörterbuch hinzufügen"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorieren"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Rechtschreibung prüfen"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Sprachen"
0707010000004E000081A400000000000000000000000168A626C20000044A000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/el.po# Greek translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Efstathios Iosifidis <eiosifidis@gnome.org>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-09-01 23:22+0300\n"
"Last-Translator: Efstathios Iosifidis <eiosifidis@gnome.org>\n"
"Language-Team: Greek <gnome-el-list@gnome.org>\n"
"Language: el\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Προσθήκη στο λεξικό"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Παράβλεψη"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Έλεγχος ορθογραφίας"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Γλώσσες"
0707010000004F000081A400000000000000000000000168A626C20000043A000000000000000000000000000000000000001E00000000libspelling-0.4.9/po/en_GB.po# British English translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Bruce Cowan <bruce@bcowan.me.uk>, 2022-2024.
# Andi Chandler <andi@gowling.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-09-01 13:10+0100\n"
"Last-Translator: Bruce Cowan <bruce@bcowan.me.uk>\n"
"Language-Team: English - United Kingdom <en@li.org>\n"
"Language: en_GB\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Add to Dictionary"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignore"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Check Spelling"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Languages"
07070100000050000081A400000000000000000000000168A626C20000042F000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/eo.po# Esperanto translation for libspelling.
# Copyright (C) 2023 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>, 2023.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2023-09-14 17:08+0200\n"
"Last-Translator: Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>\n"
"Language-Team: Esperanto <gnome-eo-list@gnome.org>\n"
"Language: eo\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 42.0\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Aldoni al la vortaro"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignori"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Kontrolu literumadon"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Lingvoj"
07070100000051000081A400000000000000000000000168A626C2000004E6000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/es.po# Spanish translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# leo <daniel.mustieles@gmail.com>, 2021.
# Miguel Ángel Nieto <correo@miguelangelnieto.net>, 2021-2022.
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2022-2024.
# Daniel Mustieles García <daniel.mustieles@gmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-09-03 10:20+0200\n"
"Last-Translator: Daniel Mustieles García <daniel.mustieles@gmail.com>\n"
"Language-Team: Spanish - Spain <gnome-es-list@gnome.org>\n"
"Language: es_ES\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Generator: Gtranslator 46.1\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Añadir al diccionario"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorar"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Comprobar ortografía"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Idiomas"
07070100000052000081A400000000000000000000000168A626C200000414000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/eu.po# Basque translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Asier Sarasua Garmendia <asiersarasua@ni.eus>, 2021, 2022, 2023.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-25 01:54+0000\n"
"Last-Translator: Asier Sarasua Garmendia <asiersarasua@ni.eus>\n"
"Language-Team: Basque <librezale@librezale.eus>\n"
"Language: eu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);9\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Gehitu hiztegiari"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ez ikusi egin"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Ortografia-egiaztapena"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Hizkuntzak"
07070100000053000081A400000000000000000000000168A626C20000044D000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/fa.po# Persian translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Danial Behzadi <dani.behzi@ubuntu.com>, 2022-2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-27 14:39+0330\n"
"Last-Translator: Danial Behzadi <dani.behzi@ubuntu.com>\n"
"Language-Team: Persian <fa@li.org>\n"
"Language: fa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-DamnedLies-Scope: partial\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "افزودن به واژهنامه"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "چشمپوشی"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "بررسی نوشتاری"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "زبانها"
07070100000054000081A400000000000000000000000168A626C20000040D000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/fi.po# Finnish translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Jiri Groenroos <jiri.gronroos@iki.fi>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-24 15:31+0300\n"
"Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n"
"Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Lisää sanastoon"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ohita"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Oikolue"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Kielet"
07070100000055000081A400000000000000000000000168A626C20000051A000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/fr.po# French translation for libspelling
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Franck Albaret <contact@dansleru.sh>, 2022
# Charles Monzat <charles.monzat@free.fr>, 2022.
# Irénée THIRION <irenee.thirion@e.email>, 2022.
# Alexandre Franke <afranke@gnome.org>, 2022.
# Jean-Marc Tissières <jmetissieres@gmail.com>, 2023.
# Vincent Chatelain <vinchatl_gnome@proton.me>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-29 17:14+0200\n"
"Last-Translator: Irénée Thirion <irenee.thirion@e.email>\n"
"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Ajouter au dictionnaire"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorer"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Vérifier l’orthographe"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Langues"
07070100000056000081A400000000000000000000000168A626C200000422000000000000000000000000000000000000001C00000000libspelling-0.4.9/po/fur.po# Friulian translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Fabio Tomat <f.t.public@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-10-14 14:16+0000\n"
"Last-Translator: Fabio T. <f.t.public@gmail.com>\n"
"Language-Team: Friulian <f.t.public@gmail.com>\n"
"Language: fur\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Editor: HaiPO 2.0 beta\n"
"X-Generator: Poedit 3.4.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Zonte al dizionari"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignore"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Controle la ortografie"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Lenghis"
07070100000057000081A400000000000000000000000168A626C2000004DB000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/gl.po# Galician translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# Fran Diéguez <frandieguez@gnome.org>, 2022.
# Fran Dieguez <frandieguez@gnome.org>, 2021-2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-09-02 11:33+0200\n"
"Last-Translator: Fran Dieguez <fran.dieguez@gnome.org>\n"
"Language-Team: Galician <proxecto@trasno.gal>\n"
"Language: gl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-DL-Branch: main\n"
"X-DL-Domain: po\n"
"X-DL-Module: libspelling\n"
"X-DL-State: None\n"
"X-DL-Team: gl\n"
"X-Generator: Poedit 3.5\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Engadir ao dicionario"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorar"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Verificación ortográfica"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Linguaxes"
07070100000058000081A400000000000000000000000168A626C200000431000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/he.po# Hebrew translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Yosef Or Boczko <yoseforb@gmail.com>, 2021-2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling gnome-41\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-09 14:36+0300\n"
"Last-Translator: Yosef Or Boczko <yoseforb@gmail.com>\n"
"Language-Team: Hebrew\n"
"Language: he\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : n==2 ? 1 : n>10 && n%10==0 ? "
"2 : 3)\n"
"X-Generator: Gtranslator 46.1\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "הוספה למילון"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "התעלמות"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "בדיקת איות"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "שפות"
07070100000059000081A400000000000000000000000168A626C200000490000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/hi.po# Hindi translation for libspelling.
# Copyright (C) 2024 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Scrambled777 <weblate.scrambled777@simplelogin.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-07-05 22:15+0530\n"
"Last-Translator: Scrambled777 <weblate.scrambled777@simplelogin.com>\n"
"Language-Team: Hindi <indlinux-hindi@lists.sourceforge.net>\n"
"Language: hi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 46.1\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "शब्दकोश में जोड़ें"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "नजरअंदाज करें"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "वर्तनी जांचें"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "भाषाएं"
0707010000005A000081A400000000000000000000000168A626C200000437000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/hr.po# Croatian translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2022-10-04 15:57+0200\n"
"Last-Translator: gogo <trebelnik2@gmail.com>\n"
"Language-Team: Croatian <hr@li.org>\n"
"Language: hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 3.1.1\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Dodaj u rječnik"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Zanemari"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Provjera pravopisa"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Jezici"
0707010000005B000081A400000000000000000000000168A626C200000477000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/hu.po# Hungarian translation for libspelling.
# Copyright (C) 2022, 2023, 2024 Free Software Foundation, Inc.
# This file is distributed under the same license as the libspelling package.
#
# Balázs Meskó <mesko.balazs at fsf dot hu>, 2022, 2024.
# Balázs Úr <ur.balazs at fsf dot hu>, 2022, 2023, 2024.
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-06-28 16:49+0200\n"
"Last-Translator: Balázs Úr <ur.balazs at fsf dot hu>\n"
"Language-Team: Hungarian <openscope at fsf dot hu>\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 23.08.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Hozzáadás a szótárhoz"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Mellőzés"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Helyesírás-ellenőrzés"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Nyelvek"
0707010000005C000081A400000000000000000000000168A626C2000004C5000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ia.po# Interlingua translation for libspelling.
# Copyright (C) 2025 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# FULL NAME <EMAIL@ADDRESS>, 2025.
# Emilio Sepúlveda <emism.translations@gmail.com>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/-/issues\n"
"POT-Creation-Date: 2025-01-27 20:52+0000\n"
"PO-Revision-Date: 2025-02-04 12:01-0300\n"
"Last-Translator: Emilio Sepúlveda <emism.translations@gmail.com>\n"
"Language-Team: Interlingua <softinterlingua@gmail.com>\n"
"Language: ia\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-DL-Lang: ia\n"
"X-DL-Module: libspelling\n"
"X-DL-Branch: main\n"
"X-DL-Domain: po\n"
"X-DL-State: Translating\n"
"X-Generator: Gtranslator 47.1\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Adder al dictionario"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorar"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Verificar ortographia"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Linguas"
0707010000005D000081A400000000000000000000000168A626C2000003CC000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/id.po# Indonesian translation for libspelling.
# Copyright (C) 2025 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Andika Triwidada <atriwidada@gnome.org>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/-/issues\n"
"POT-Creation-Date: 2025-02-20 23:07+0000\n"
"PO-Revision-Date: 2025-02-25 10:45+0700\n"
"Last-Translator: Andika Triwidada <andika@gmail.com>\n"
"Language-Team: Indonesian <Indonesian>\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.5\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Tambah ke Kamus"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Abaikan"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Periksa Ejaan"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Bahasa"
0707010000005E000081A400000000000000000000000168A626C2000004B1000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ie.po# Interlingue translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# OIS <mistresssilvara@hotmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling gnome-43\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2022-12-22 16:32+0700\n"
"Last-Translator: OIS <mistresssilvara@hotmail.com>\n"
"Language-Team: Interlingue; Occidental <None>\n"
"Language: ie\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n!=1)\n"
"X-DL-Team: ie\n"
"X-DL-Module: libspelling\n"
"X-DL-Branch: gnome-43\n"
"X-DL-Domain: po\n"
"X-DL-State: Translating\n"
"X-Generator: Gtranslator 42.0\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Adjuncter al dictionarium"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorar"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Controlar ortografie"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Lingues"
0707010000005F000081A400000000000000000000000168A626C2000003BE000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/is.po# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Sveinn í Felli <sv1@fellsnet.is>, 2022, 2023.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2023-06-18 10:19+0000\n"
"Last-Translator: Sveinn í Felli <sv1@fellsnet.is>\n"
"Language-Team: Icelandic\n"
"Language: is\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Lokalize 21.12.3\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Bæta við orðalista"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Hunsa"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Yfirfara stafsetningu"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Tungumál"
07070100000060000081A400000000000000000000000168A626C20000043F000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/it.po# Italian translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling
# package.
# Davide Ferracin <davide.ferracin@protonmail.com>, 2021, 2022, 2023, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling gnome-41\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-31 12:33+0300\n"
"Last-Translator: Davide Ferracin <davide.ferracin@protonmail.com>\n"
"Language-Team: Italian <gnome-it-list@gnome.org>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Generated-By: Babel 2.15.0\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Aggiungi al dizionario"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignora"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Controllo ortografico"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Lingue"
07070100000061000081A400000000000000000000000168A626C20000041B000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ja.po# Japanese translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# sujiniku <sujinikusityuu@gmail.com>, 2022.
# sicklylife <translation@sicklylife.jp>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2022-03-15 22:00+0900\n"
"Last-Translator: sicklylife <translation@sicklylife.jp>\n"
"Language-Team: Japanese <gnome-translation@gnome.gr.jp>\n"
"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "辞書に追加"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "無視"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "スペルチェック"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "言語"
07070100000062000081A400000000000000000000000168A626C20000045D000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ka.po# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Ekaterine Papava <papava.e@gtu.ge>, 2023-2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-23 07:34+0200\n"
"Last-Translator: Ekaterine Papava <papava.e@gtu.ge>\n"
"Language-Team: Georgian <(nothing)>\n"
"Language: ka\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.3.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "სიტყვის ლექსიკონში ჩამატება"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "იგნორი"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "მართლწერის შემოწმება"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "ენები"
07070100000063000081A400000000000000000000000168A626C200000471000000000000000000000000000000000000001C00000000libspelling-0.4.9/po/kab.po# Kabyle translation for libspelling.
# Copyright (C) 2025 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# ButterflyOfFire <butterflyoffire@protonmail.com>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/-/issues\n"
"POT-Creation-Date: 2025-01-11 09:30+0000\n"
"PO-Revision-Date: 2025-03-30 12:42+0100\n"
"Last-Translator: ButterflyOfFire <butterflyoffire@protonmail.com>\n"
"Language-Team: Kabyle\n"
"Language: kab\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-DL-Lang: kab\n"
"X-DL-Module: libspelling\n"
"X-DL-Branch: main\n"
"X-DL-Domain: po\n"
"X-DL-State: Translating\n"
"Plural-Forms: nplurals=2; plural=n>1;\n"
"X-Generator: Gtranslator 48.0\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr ""
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr ""
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr ""
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr ""
07070100000064000081A400000000000000000000000168A626C20000042F000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/kk.po# Kazakh translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Baurzhan Muftakhidinov <baurthefirst@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-30 19:29+0500\n"
"Last-Translator: Baurzhan Muftakhidinov <baurthefirst@gmail.com>\n"
"Language-Team: Kazakh <kk_KZ@googlegroups.com>\n"
"Language: kk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Сөздікке қосу"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Елемеу"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Емлені тексеру"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Тілдер"
07070100000065000081A400000000000000000000000168A626C200000432000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ko.po# Korean translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Seong-ho Cho <shcho@gnome.org>, 2022, 2023, 2024.
# DaeHyun Sung <sungdh86+git@gmail.com> 2023, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-27 15:32+0900\n"
"Last-Translator: DaeHyun Sung <sungdh86+git@gmail.com>\n"
"Language-Team: Korean <gnome-kr@googlegroups.com>\n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.5\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "사전에 추가"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "무시"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "철자 검사"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "언어"
07070100000066000081A400000000000000000000000168A626C200000455000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/lt.po# Lithuanian translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Aurimas Černius <aurisc4@gmail.com>, 2022-2023.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-09-02 23:04+0300\n"
"Last-Translator: Aurimas Černius <aurisc4@gmail.com>\n"
"Language-Team: Lietuvių <gnome-lt@lists.akl.lt>\n"
"Language: lt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"(n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Pridėti į žodyną"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Nepaisyti"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Tikrinti rašybą"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Kalbos"
07070100000067000081A400000000000000000000000168A626C200000436000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/lv.po# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# SPDX-FileCopyrightText: 2022, 2023, 2024 Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-09-11 22:53+0300\n"
"Last-Translator: Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>\n"
"Language-Team: Latvian <lata-l10n@googlegroups.com>\n"
"Language: lv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : "
"2);\n"
"X-Generator: Lokalize 23.08.5\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Pievienot vārdnīcai"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorēt"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Pārbaudīt pareizrakstību"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Valodas"
07070100000068000081A400000000000000000000000168A626C20000009C000000000000000000000000000000000000002100000000libspelling-0.4.9/po/meson.buildi18n = import('i18n')
i18n.gettext('libspelling',
preset: 'glib',
args: ['--msgid-bugs-address=https://gitlab.gnome.org/GNOME/libspelling/issues'],
)
07070100000069000081A400000000000000000000000168A626C20000039F000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/nb.po# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-24 22:51+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: nb\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Legg til i ordliste"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorer"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Stavekontroll"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Språk"
0707010000006A000081A400000000000000000000000168A626C200000482000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ne.po# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: Gnome Nepali Translation Project\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-05-28 20:43+0545\n"
"Last-Translator: Pawan Chitrakar <chautari@gmail.com>\n"
"Language-Team: Nepali Team <chautari@gmail.com>\n"
"Language: ne\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "शब्दकोशमा थप्नुहोस्"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "वेवास्ता गर्नुहोस"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "र्हिज्जे जाँच गर्नुहोस्"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "भाषा"
0707010000006B000081A400000000000000000000000168A626C200000477000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/nl.po# Dutch translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
#
# Nathan Follens <nfollens@gnome.org>, 2021-2024.
# Hannie Dumoleyn <hannie@ubuntu-nl.org>, 2022.
# Philip Goto <philip.goto@gmail.com>, 2022.
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-02-18 13:03+0100\n"
"Last-Translator: Nathan Follens <nfollens@gnome.org>\n"
"Language-Team: GNOME-NL https://matrix.to/#/#nl:gnome.org\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Toevoegen aan woordenboek"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Negeren"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Spelling controleren"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Talen"
0707010000006C000081A400000000000000000000000168A626C2000003F8000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/oc.po# Occitan translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Quentin PAGÈS <pages_quentin@hotmail.com>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-03-11 20:03+0100\n"
"Last-Translator: Quentin PAGÈS\n"
"Language-Team: Occitan <totenoc@gmail.com>\n"
"Language: oc\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.4.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Apondre al diccionari"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorar"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Verificar l'ortografia"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Lengas"
0707010000006D000081A400000000000000000000000168A626C200000483000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/pa.po# Punjabi translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
#
# SPDX-FileCopyrightText: 2022, 2023, 2024 A S Alam <aalam@fedoraproject.org>
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-03-18 20:58-0500\n"
"Last-Translator: A S Alam <aalam@punlinux.org>\n"
"Language-Team: Punjabi <punjab-translation@googlegroups.com>\n"
"Language: pa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 23.08.5\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "ਡਿਕਸ਼ਨਰੀ ਵਿੱਚ ਜੋੜੋ"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "ਅਣਡਿੱਠਾ ਕਰੋ"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "ਸ਼ਬਦ-ਜੋੜ ਜਾਂਚ"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "ਭਾਸ਼ਾਵਾਂ"
0707010000006E000081A400000000000000000000000168A626C20000044D000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/pl.po# Polish translation for libspelling.
# Copyright © 2024 the libspelling authors.
# This file is distributed under the same license as the libspelling package.
# Piotr Drąg <piotrdrag@gmail.com>, 2024.
# Aviary.pl <community-poland@mozilla.org>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-21 14:12+0200\n"
"PO-Revision-Date: 2024-10-21 14:17+0200\n"
"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
"Language-Team: Polish <community-poland@mozilla.org>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Dodaj do słownika"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Zignoruj"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Sprawdzanie pisowni"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Języki"
0707010000006F000081A400000000000000000000000168A626C200000511000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/pt.po# Portuguese translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# Juliano de Souza Camargo <julianosc@protonmail.com>, 2022.
# Hugo Carvalho <hugokarvalho@hotmail.com>, 2022, 2023, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling gnome-41\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-30 16:01+0100\n"
"Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n"
"Language-Team: Portuguese < https://l10n.gnome.org/teams/pt/>\n"
"Language: pt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-DL-Team: pt\n"
"X-DL-Module: libspelling\n"
"X-DL-Branch: main\n"
"X-DL-Domain: po\n"
"X-DL-State: Translating\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Adicionar ao dicionário"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorar"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Verificar ortografia"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Idiomas"
07070100000070000081A400000000000000000000000168A626C200000583000000000000000000000000000000000000001E00000000libspelling-0.4.9/po/pt_BR.po# Brazilian Portuguese translation for libspelling.
# Copyright (C) 2024 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Enrico Nicoletto <hiko@duck.com>, 2021, 2022.
# Matheus Barbosa <mdpb.matheus@gmail.com>, 2022.
# Leônidas Araújo <leorusvellt@hotmail.com>, 2022.
# Rafael Fontenelle <rafaelff@gnome.org>, 2021-2023.
# Jhonata Fernandes <fjhonata14@gmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-03-28 19:22-0300\n"
"Last-Translator: Jhonata Fernandes <fjhonata14@gmail.com>\n"
"Language-Team: Brazilian Portuguese <https://br.gnome.org/traducao>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.3.1\n"
"X-DL-Team: pt_BR\n"
"X-DL-Module: libspelling\n"
"X-DL-Branch: main\n"
"X-DL-Domain: po\n"
"X-DL-State: Translating\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Adicionar dicionários"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorar"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Verificação ortográfica"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Idiomas"
07070100000071000081A400000000000000000000000168A626C200000488000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ro.po# Romanian translation for libspelling.
# Copyright (C) 2023 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Florentina Musat <deep.sky.028@gmail.com>, 2023.
# Antonio Marin <gnmer.6qxyg@slmail.me>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/-/issues\n"
"POT-Creation-Date: 2025-01-11 09:30+0000\n"
"PO-Revision-Date: 2025-06-04 10:43+0200\n"
"Last-Translator: Antonio Marin <gnmer.6qxyg@slmail.me>\n"
"Language-Team: Romanian <gnomero-list@lists.sourceforge.net>\n"
"Language: ro\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
"20)) ? 1 : 2);\n"
"X-Generator: Gtranslator 48.0\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Adaugă în dicționar"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignoră"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Verifică ortografia"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Limbi"
07070100000072000081A400000000000000000000000168A626C2000004A7000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/ru.po# Russian translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Ser82-png <sw@atrus.ru>, 2022.
# Ivan Molodetskikh <yalterz@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: unnamed project\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-06-23 16:35+0300\n"
"Last-Translator: Artur So <arturios2005@mail.ru>\n"
"Language-Team: Russian <gnome-cyr@gnome.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Добавить в словарь"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Игнорировать"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Проверять орфографию"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Языки"
07070100000073000081A400000000000000000000000168A626C20000044A000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/sk.po# Slovak translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Dušan Kazik <prescott66@gmail.com>, 2022.
# Jose Riha <jose1711@gmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-26 23:57+0200\n"
"Last-Translator: Jose Riha <jose1711@gmail.com>\n"
"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
"Language: sk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
"X-Generator: Poedit 3.4.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Pridať do slovníka"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorovať"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Kontrolovať pravopis"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Jazyky"
07070100000074000081A400000000000000000000000168A626C200000488000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/sl.po# Slovenian translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
#
# Matej Urbančič <mateju@svn.gnome.org>, 2021–2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-06-20 00:05+0200\n"
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
"Language: sl_SI\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
"n%100==4 ? 3 : 0);\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Generator: Poedit 2.2.1\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Dodaj v slovar"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Prezri"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Preveri črkovanje"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Jeziki"
07070100000075000081A400000000000000000000000168A626C2000004E9000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/sr.po# Serbian translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Марко Костић <marko.m.kostic@gmail.com>, 2022.
# Марко М. Костић <marko.m.kostic@gmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-06-20 01:11+0200\n"
"Last-Translator: Марко М. Костић <marko.m.kostic@gmail.com>\n"
"Language-Team: Serbian <gnome-sr@googlegroups.com>\n"
"Language: sr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Gtranslator 45.3\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Додај у речник"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Занемари"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Провера правописа"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Језици"
07070100000076000081A400000000000000000000000168A626C20000046B000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/sv.po# Swedish translation for libspelling.
# Copyright © 2021-2024 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Anders Jonsson <anders.jonsson@norsjovallen.se>, 2021, 2022, 2023, 2024.
# Luna Jernberg <droidbittin@gmail.com>, 2021, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-31 14:32+0200\n"
"Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.4\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Lägg till i ordbok"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ignorera"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Kontrollera stavning"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Språk"
07070100000077000081A400000000000000000000000168A626C200000417000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/th.po# Thai translation for libspelling.
# Copyright (C) 2024 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Aefgh Threenine <aefgh39622@gmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-01-16 23:36+0700\n"
"Last-Translator: \n"
"Language-Team: Thai <thai-l10n@googlegroups.com>\n"
"Language: th\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.4.2\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "เพิ่มในพจนานุกรม"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "ละเลย"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "ตรวจตัวสะกด"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "ภาษา"
07070100000078000081A400000000000000000000000168A626C200000424000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/tr.po# Turkish translation for libspelling.
# Copyright (C) 2024 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
#
# Sabri Ünal <yakushabb@gmail.com>, 2024.
# Emin Tufan Çetin <etcetin@gmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/-/issues\n"
"POT-Creation-Date: 2024-11-10 16:57+0000\n"
"PO-Revision-Date: 2024-11-15 21:44+0300\n"
"Last-Translator: Sabri Ünal <yakushabb@gmail.com>\n"
"Language-Team: Turkish <takim@gnome.org.tr>\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.4.3\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Sözlüğe Ekle"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Yok Say"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Yazımı Denetle"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Diller"
07070100000079000081A400000000000000000000000168A626C2000004AD000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/uk.po# Ukrainian translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
#
# Yuri Chornoivan <yurchor@ukr.net>, 2021, 2022, 2023, 2024.
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-23 15:58+0300\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
"Language: uk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Lokalize 23.04.3\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Додати до словника"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Ігнорувати"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Перевірка правопису"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Мови"
0707010000007A000081A400000000000000000000000168A626C2000003C8000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/uz.po# Uzbek (Latin) translation for libspelling.
# Copyright (C) 2025 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Baxrom Raxmatov <magdiyevbahrom@gmail.com>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/-/issues\n"
"POT-Creation-Date: 2025-03-15 19:19+0000\n"
"PO-Revision-Date: 2025-03-23 22:07+0500\n"
"Last-Translator: \n"
"Language-Team: Uzbek (Latin) <uz@li.org>\n"
"Language: uz\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.5\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Lug’atga qo’shish"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "E’tibor bermaslik"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Imloni tekshirish"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Tillar"
0707010000007B000081A400000000000000000000000168A626C2000003F2000000000000000000000000000000000000001B00000000libspelling-0.4.9/po/vi.po# Vietnamese translation for libspelling.
# This file is distributed under the same license as the libspelling package.
# Ngọc Quân Trần <vnwildman@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2022-05-29 14:29+0700\n"
"Last-Translator: Trần Ngọc Quân <vnwildman@gmail.com>\n"
"Language-Team: Vietnamese <gnome-vi-list@gnome.org>\n"
"Language: vi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Gtranslator 3.38.0\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "Thêm vào từ điển"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "Bỏ qua"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "Kiểm tra chính tả"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "Ngôn ngữ"
0707010000007C000081A400000000000000000000000168A626C20000041F000000000000000000000000000000000000001E00000000libspelling-0.4.9/po/zh_CN.po# Chinese (China) translation for libspelling.
# Copyright (C) 2021 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# D <afecr@foxmail.com>, 2021.
# lumingzh <lumingzh@qq.com>, 2021-2024.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2024-08-23 15:12+0800\n"
"Last-Translator: lumingzh <lumingzh@qq.com>\n"
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Gtranslator 46.1\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "添加到词典"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "忽略"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "检查拼写"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "语言"
0707010000007D000081A400000000000000000000000168A626C20000047B000000000000000000000000000000000000001E00000000libspelling-0.4.9/po/zh_TW.po# Chinese (Taiwan) translation for libspelling.
# Copyright (C) 2022 libspelling's COPYRIGHT HOLDER
# This file is distributed under the same license as the libspelling package.
# Freddy <freddy4212@gmail.com>, 2022.
# Freddy Cheng <freddy4212@gmail.com>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: libspelling main\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libspelling/issues\n"
"POT-Creation-Date: 2024-10-18 12:30-0700\n"
"PO-Revision-Date: 2023-03-07 05:00+0000\n"
"Last-Translator: Weblate Admin <pesder@mail.edu.tw>\n"
"Language-Team: Chinese (Traditional) <http://darkbear.mercusysddns.com/"
"projects/gnome-44/libspelling-main/zh_Hant/>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.16.1\n"
#: lib/spelling-menu.c:273
msgid "Add to Dictionary"
msgstr "新增至字典"
#: lib/spelling-menu.c:274
msgid "Ignore"
msgstr "忽略拼寫錯誤"
#: lib/spelling-menu.c:275
msgid "Check Spelling"
msgstr "檢查拼寫"
#: lib/spelling-menu.c:284
msgid "Languages"
msgstr "語言"
0707010000007E000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001E00000000libspelling-0.4.9/subprojects0707010000007F000081A400000000000000000000000168A626C2000000C1000000000000000000000000000000000000002700000000libspelling-0.4.9/subprojects/gtk.wrap[wrap-git]
directory=gtk
url=https://gitlab.gnome.org/GNOME/gtk.git
push-url=ssh://git@gitlab.gnome.org:GNOME/gtk.git
revision=main
depth=1
[provide]
gtk4 = libgtk_dep
dependency_names = gtk4
07070100000080000081A400000000000000000000000168A626C2000000FE000000000000000000000000000000000000003100000000libspelling-0.4.9/subprojects/gtksourceview.wrap[wrap-git]
directory=gtksourceview
url=https://gitlab.gnome.org/GNOME/gtksourceview.git
push-url=ssh://git@ssh.gitlab.gnome.org:GNOME/gtksourceview.git
revision=master
depth=1
[provide]
gtksourceview-5 = gtksource_dep
dependency_names = gtksourceview-5
07070100000081000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001700000000libspelling-0.4.9/test07070100000082000081A400000000000000000000000168A626C2000000E4000000000000000000000000000000000000002300000000libspelling-0.4.9/test/meson.buildtest_spelling = executable('test-spelling', 'test-spelling.c',
dependencies: [libspelling_static_dep],
install: false,
include_directories: [include_directories('..'), include_directories('.')],
)
07070100000083000081A400000000000000000000000168A626C2000019C7000000000000000000000000000000000000002700000000libspelling-0.4.9/test/test-spelling.c/* test-spelling.c
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <unistd.h>
#include <libspelling.h>
static void
on_click_pressed_cb (SpellingTextBufferAdapter *adapter,
int n_press,
double x,
double y,
GtkGestureClick *click)
{
GdkEventSequence *sequence;
GtkTextBuffer *buffer;
GtkTextIter iter, begin, end;
GtkWidget *widget;
GdkEvent *event;
int buf_x, buf_y;
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (click));
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (click));
event = gtk_gesture_get_last_event (GTK_GESTURE (click), sequence);
if (n_press != 1 || !gdk_event_triggers_context_menu (event))
return;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
return;
gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (widget),
GTK_TEXT_WINDOW_WIDGET,
x, y, &buf_x, &buf_y);
gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (widget), &iter, buf_x, buf_y);
gtk_text_buffer_select_range (buffer, &iter, &iter);
spelling_text_buffer_adapter_update_corrections (adapter);
}
int
main (int argc,
char *argv[])
{
g_autoptr(SpellingTextBufferAdapter) adapter = NULL;
g_autoptr(GOptionContext) context = NULL;
g_autoptr(GError) error = NULL;
GtkSourceLanguage *language = NULL;
GtkSourceStyleScheme *scheme = NULL;
SpellingChecker *checker = NULL;
GtkScrolledWindow *scroller;
GtkSourceBuffer *source_buffer;
GtkSourceView *source_view;
GMenuModel *extra_menu;
GtkWindow *window;
GMainLoop *main_loop;
g_autofree char *contents = NULL;
g_autofree char *language_id = NULL;
g_autofree char *scheme_id = NULL;
GtkGesture *gesture;
const GOptionEntry entries[] = {
{ "language", 'l', 0, G_OPTION_ARG_STRING, &language_id, "The GtkSourceView language ID to use", "c" },
{ "scheme", 's', 0, G_OPTION_ARG_STRING, &scheme_id, "The GtkSourceView style scheme ID to use", "Adwaita" },
{ 0 }
};
gtk_init ();
gtk_source_init ();
spelling_init ();
context = g_option_context_new ("- test spellcheck text-adapter");
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
if (!g_option_context_parse (context, &argc, &argv, &error))
{
g_printerr ("%s\n", error->message);
return EXIT_FAILURE;
}
if (language_id != NULL)
{
language = gtk_source_language_manager_get_language (gtk_source_language_manager_get_default (), language_id);
if (language == NULL)
g_warning ("No such GtkSourceView programming language with id \"%s\"", language_id);
}
if (scheme_id == NULL)
scheme_id = g_strdup ("Adwaita");
scheme = gtk_source_style_scheme_manager_get_scheme (gtk_source_style_scheme_manager_get_default (), scheme_id);
if (scheme == NULL)
g_warning ("No such GtkSourceView style scheme with id \"%s\"", scheme_id);
if (argc == 2)
g_file_get_contents (argv[1], &contents, NULL, NULL);
main_loop = g_main_loop_new (NULL, FALSE);
window = g_object_new (GTK_TYPE_WINDOW,
"default-width", 800,
"default-height", 600,
"title", "Test Spelling",
NULL);
scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
"hscrollbar-policy", GTK_POLICY_NEVER,
NULL);
source_buffer = g_object_new (GTK_SOURCE_TYPE_BUFFER,
"language", language,
"style-scheme", scheme,
"enable-undo", TRUE,
"text", contents ? contents : "",
NULL);
source_view = g_object_new (GTK_SOURCE_TYPE_VIEW,
"buffer", source_buffer,
"show-line-numbers", TRUE,
"monospace", TRUE,
"top-margin", 6,
"left-margin", 6,
"right-margin", 6,
"bottom-margin", 6,
NULL);
gtk_window_set_child (window, GTK_WIDGET (scroller));
gtk_scrolled_window_set_child (scroller, GTK_WIDGET (source_view));
g_signal_connect_swapped (window,
"close-request",
G_CALLBACK (g_main_loop_quit),
main_loop);
/* Setup spellchecking */
checker = spelling_checker_get_default ();
adapter = spelling_text_buffer_adapter_new (source_buffer, checker);
extra_menu = spelling_text_buffer_adapter_get_menu_model (adapter);
gtk_text_view_set_extra_menu (GTK_TEXT_VIEW (source_view), extra_menu);
gtk_widget_insert_action_group (GTK_WIDGET (source_view), "spelling", G_ACTION_GROUP (adapter));
spelling_text_buffer_adapter_set_enabled (adapter, TRUE);
gesture = gtk_gesture_click_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
GTK_PHASE_CAPTURE);
g_signal_connect_object (gesture,
"pressed",
G_CALLBACK (on_click_pressed_cb),
adapter,
G_CONNECT_SWAPPED);
gtk_widget_add_controller (GTK_WIDGET (source_view),
GTK_EVENT_CONTROLLER (gesture));
gtk_window_present (window);
g_main_loop_run (main_loop);
return 0;
}
07070100000084000081ED00000000000000000000000168A626C20000035D000000000000000000000000000000000000001F00000000libspelling-0.4.9/test/test.js#!/usr/bin/env gjs
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const GtkSource = imports.gi.GtkSource;
const Spelling = imports.gi.Spelling;
Gtk.init();
GtkSource.init();
Spelling.init();
let mainLoop = GLib.MainLoop.new(null, false);
let win = Gtk.Window.new();
let scroller = Gtk.ScrolledWindow.new();
let view = new GtkSource.View();
let buffer = view.get_buffer();
let checker = Spelling.Checker.get_default();
let adapter = Spelling.TextBufferAdapter.new(buffer, checker);
win.set_child(scroller);
scroller.set_child(view);
buffer.set_style_scheme(GtkSource.StyleSchemeManager.get_default().get_scheme('Adwaita'));
view.insert_action_group('spelling', adapter);
view.set_extra_menu(adapter.get_menu_model());
adapter.set_enabled(true)
win.connect('close-request', function() {
mainLoop.quit();
});
win.present();
mainLoop.run();
07070100000085000041ED00000000000000000000000268A626C200000000000000000000000000000000000000000000001C00000000libspelling-0.4.9/testsuite07070100000086000081A400000000000000000000000168A626C200000337000000000000000000000000000000000000002800000000libspelling-0.4.9/testsuite/meson.buildlibspelling_test_env = [
'G_DEBUG=gc-friendly',
'GSETTINGS_BACKEND=memory',
'MALLOC_CHECK_=2',
]
libspelling_testsuite_c_args = [
'-DG_LOG_DOMAIN="libspelling"',
'-DG_ENABLE_DEBUG',
'-UG_DISABLE_ASSERT',
'-UG_DISABLE_CAST_CHECKS',
]
libspelling_testsuite = {
'test-cursor' : {},
'test-engine' : {},
'test-job' : {},
'test-region' : {},
}
libspelling_testsuite_deps = [
libspelling_static_dep,
]
foreach test, params: libspelling_testsuite
test_exe = executable(test,
['@0@.c'.format(test)],
c_args: libspelling_testsuite_c_args,
dependencies: libspelling_testsuite_deps,
include_directories: [include_directories('..'), include_directories('.')],
)
if not params.get('skip', false)
test(test, test_exe, env: libspelling_test_env)
endif
endforeach
07070100000087000081A400000000000000000000000168A626C200001DE5000000000000000000000000000000000000002A00000000libspelling-0.4.9/testsuite/test-cursor.c/* test-cursor.c
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <locale.h>
#include <glib/gi18n.h>
#include <gtksourceview/gtksource.h>
#include <libspelling.h>
#include "spelling-cursor-private.h"
#include "cjhtextregionprivate.h"
#undef WANT_DISPLAY_TESTS
static const char *test_text = "this is a series of words";
static const char *test_text_2 = "it's possible we're going to have join-words.";
#ifdef WANT_DISPLAY_TESTS
static const char *test_text_3 = "\
/* ide-buffer.c\
*\
* Copyright 2018-2023 Christian Hergert <chergert@redhat.com>\
*\
* 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.\
*/\
";
#endif
static char *
next_word (SpellingCursor *cursor)
{
GtkTextIter begin, end;
if (spelling_cursor_next (cursor, &begin, &end))
return gtk_text_iter_get_slice (&begin, &end);
return NULL;
}
static void
test_cursor (void)
{
g_autoptr(GtkTextBuffer) buffer = gtk_text_buffer_new (NULL);
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
g_autoptr(SpellingCursor) cursor = spelling_cursor_new (buffer, region, NULL, NULL);
char *word;
gtk_text_buffer_set_text (buffer, test_text, -1);
_cjh_text_region_insert (region, 0, strlen (test_text), NULL);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "this");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "is");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "a");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "series");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "of");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "words");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, NULL);
g_clear_pointer (&word, g_free);
_cjh_text_region_free (region);
}
#ifdef WANT_DISPLAY_TESTS
static void
test_cursor2 (void)
{
g_autoptr(GtkTextBuffer) buffer = GTK_TEXT_BUFFER (gtk_source_buffer_new (NULL));
GtkWindow *window;
GtkSourceView *view;
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
g_autoptr(SpellingCursor) cursor = spelling_cursor_new (buffer, region, NULL, NULL);
GtkSourceLanguage *l = gtk_source_language_manager_get_language (gtk_source_language_manager_get_default (), "c");
static const char *words[] = {
"ide", "buffer", "c",
"Copyright", "2018", "2019", "Christian", "Hergert", "chergert", "redhat", "com",
"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",
};
gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer), l);
gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (buffer), TRUE);
gtk_text_buffer_set_text (buffer, test_text_3, -1);
_cjh_text_region_insert (region, 0, strlen (test_text_3), NULL);
window = g_object_new (GTK_TYPE_WINDOW, NULL);
view = GTK_SOURCE_VIEW (gtk_source_view_new ());
gtk_text_view_set_buffer (GTK_TEXT_VIEW (view), buffer);
gtk_window_set_child (window, GTK_WIDGET (view));
gtk_window_present (window);
while (g_main_context_pending (NULL))
g_main_context_iteration (NULL, TRUE);
for (guint i = 0; i < G_N_ELEMENTS (words); i++)
{
char *word = next_word (cursor);
g_assert_cmpstr (word, ==, words[i]);
g_free (word);
}
_cjh_text_region_free (region);
}
#endif
static void
test_cursor_in_word (void)
{
g_autoptr(GtkTextBuffer) buffer = gtk_text_buffer_new (NULL);
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
g_autoptr(SpellingCursor) cursor = spelling_cursor_new (buffer, region, NULL, NULL);
const char *pos = strstr (test_text, "ries "); /* se|ries */
gsize offset = pos - test_text;
char *word;
gtk_text_buffer_set_text (buffer, test_text, -1);
_cjh_text_region_insert (region, 0, strlen (test_text), GINT_TO_POINTER (1));
_cjh_text_region_replace (region, offset, strlen (test_text) - offset, NULL);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "series");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "of");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "words");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, NULL);
g_clear_pointer (&word, g_free);
_cjh_text_region_free (region);
}
static void
test_cursor_join_words (void)
{
g_autoptr(GtkTextBuffer) buffer = gtk_text_buffer_new (NULL);
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
g_autoptr(SpellingCursor) cursor = spelling_cursor_new (buffer, region, NULL, "-'");
char *word;
gtk_text_buffer_set_text (buffer, test_text_2, -1);
_cjh_text_region_insert (region, 0, strlen (test_text_2), NULL);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "it's");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "possible");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "we're");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "going");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "to");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "have");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, "join-words");
g_clear_pointer (&word, g_free);
word = next_word (cursor);
g_assert_cmpstr (word, ==, NULL);
g_clear_pointer (&word, g_free);
_cjh_text_region_free (region);
}
int
main (int argc,
char *argv[])
{
setlocale (LC_ALL, "C");
bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
#ifdef WANT_DISPLAY_TESTS
gtk_init ();
gtk_source_init ();
#endif
spelling_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/Spelling/Cursor/basic", test_cursor);
#ifdef WANT_DISPLAY_TESTS
g_test_add_func ("/Spelling/Cursor/basic2", test_cursor2);
#endif
g_test_add_func ("/Spelling/Cursor/in_word", test_cursor_in_word);
g_test_add_func ("/Spelling/Cursor/join_words", test_cursor_join_words);
return g_test_run ();
}
07070100000088000081A400000000000000000000000168A626C200001E58000000000000000000000000000000000000002A00000000libspelling-0.4.9/testsuite/test-engine.c/* test-engine.c
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <libspelling.h>
#include "spelling-engine-private.h"
static GString *buffer;
static GtkBitset *mispelled;
static SpellingDictionary *dictionary;
static guint cursor;
typedef struct
{
GString *str;
const char *pos;
guint offset;
gunichar ch;
} StringIter;
static inline gboolean
string_iter_is_end (StringIter *iter)
{
return iter->pos >= iter->str->str + iter->str->len;
}
static inline void
string_iter_init (StringIter *iter,
GString *str,
guint offset)
{
iter->str = str;
iter->pos = g_utf8_offset_to_pointer (str->str, offset);
iter->offset = offset;
iter->ch = g_utf8_get_char (iter->pos);
}
static inline gboolean
string_iter_backward (StringIter *iter)
{
if (iter->pos <= iter->str->str)
return FALSE;
iter->pos = g_utf8_prev_char (iter->pos);
iter->ch = g_utf8_get_char (iter->pos);
iter->offset--;
return TRUE;
}
static inline gboolean
string_iter_forward (StringIter *iter)
{
if (iter->pos >= iter->str->str + iter->str->len)
return FALSE;
iter->pos = g_utf8_next_char (iter->pos);
iter->ch = g_utf8_get_char (iter->pos);
iter->offset++;
return TRUE;
}
static char *
copy_text (gpointer instance,
guint position,
guint length)
{
const char *begin = g_utf8_offset_to_pointer (buffer->str, position);
const char *end = g_utf8_offset_to_pointer (begin, length);
return g_strndup (begin, end - begin);
}
static void
clear_tag (gpointer instance,
guint position,
guint length)
{
gtk_bitset_remove_range (mispelled, position, length);
}
static void
apply_tag (gpointer instance,
guint position,
guint length)
{
gtk_bitset_add_range (mispelled, position, length);
}
static gboolean
is_word_char (gunichar ch)
{
const char *extra_word_chars = spelling_dictionary_get_extra_word_chars (dictionary);
for (const char *c = extra_word_chars; c && *c; c = g_utf8_next_char (c))
{
if (ch == g_utf8_get_char (c))
return TRUE;
}
return g_unichar_isalnum (ch) || ch == '_';
}
static gboolean
backward_word_start (gpointer instance,
guint *position)
{
StringIter iter;
StringIter peek;
string_iter_init (&iter, buffer, *position);
/* Move back one char first */
if (!string_iter_backward (&iter))
return FALSE;
/* If we have left a word (into space, etc), walk back until
* we get to the end of a word.
*/
if (!is_word_char (iter.ch))
{
while (string_iter_backward (&iter))
{
if (!is_word_char (iter.ch))
continue;
break;
}
if (iter.offset == 0 && !is_word_char (iter.ch))
return FALSE;
}
/* Walk backwards saving our last valid position */
peek = iter;
while (string_iter_backward (&peek) && is_word_char (peek.ch))
iter = peek;
*position = iter.offset;
return TRUE;
}
static gboolean
forward_word_end (gpointer instance,
guint *position)
{
StringIter iter;
string_iter_init (&iter, buffer, *position);
/* Move forward one char first */
if (!string_iter_forward (&iter))
return FALSE;
/* If we are no longer on a word character, then walk forward
* until we reach a word character.
*/
if (!is_word_char (iter.ch))
{
while (string_iter_forward (&iter))
{
if (!is_word_char (iter.ch))
continue;
break;
}
if (string_iter_is_end (&iter))
return FALSE;
}
/* Walk forward saving our last valid position */
while (string_iter_forward (&iter))
if (!is_word_char (iter.ch))
break;
*position = iter.offset;
return TRUE;
}
static void
intersect_spellcheck_region (gpointer instance,
GtkBitset *bitset)
{
}
static guint
get_cursor (gpointer instance)
{
return cursor;
}
static PangoLanguage *
get_language (gpointer instance)
{
return pango_language_get_default ();
}
static SpellingDictionary *
get_dictionary (gpointer instance)
{
return dictionary;
}
static gboolean
check_enabled (gpointer instance)
{
return TRUE;
}
static const SpellingAdapter adapter = {
.check_enabled = check_enabled,
.get_cursor = get_cursor,
.copy_text = copy_text,
.clear_tag = clear_tag,
.apply_tag = apply_tag,
.backward_word_start = backward_word_start,
.forward_word_end = forward_word_end,
.intersect_spellcheck_region = intersect_spellcheck_region,
.get_dictionary = get_dictionary,
.get_language = get_language,
};
static inline void
assert_string (const char *str)
{
g_assert_cmpstr (buffer->str, ==, str);
}
static void
insert (SpellingEngine *engine,
const char *text,
guint position,
const char *expected)
{
const char *ptr;
guint n_chars;
if (position == 0)
ptr = buffer->str;
else
ptr = g_utf8_offset_to_pointer (buffer->str, position);
n_chars = g_utf8_strlen (text, -1);
spelling_engine_before_insert_text (engine, position, n_chars);
g_string_insert (buffer, ptr - buffer->str, text);
gtk_bitset_splice (mispelled, position, 0, n_chars);
cursor = position + n_chars;
spelling_engine_after_insert_text (engine, position, n_chars);
if (expected)
assert_string (expected);
spelling_engine_iteration (engine);
}
static void
delete (SpellingEngine *engine,
guint position,
guint n_chars,
const char *expected)
{
const char *ptr = g_utf8_offset_to_pointer (buffer->str, position);
const char *endptr = g_utf8_offset_to_pointer (ptr, n_chars);
cursor = position;
spelling_engine_before_delete_range (engine, position, n_chars);
gtk_bitset_splice (mispelled, position, n_chars, 0);
g_string_erase (buffer, ptr - buffer->str, endptr - ptr);
spelling_engine_after_delete_range (engine, position);
if (expected)
assert_string (expected);
spelling_engine_iteration (engine);
}
static void
test_engine_basic (void)
{
g_autoptr(SpellingEngine) engine = NULL;
g_autoptr(GObject) instance = g_object_new (G_TYPE_OBJECT, NULL);
SpellingProvider *provider = spelling_provider_get_default ();
const char *default_code = spelling_provider_get_default_code (provider);
dictionary = spelling_provider_load_dictionary (provider, default_code);
buffer = g_string_new (NULL);
mispelled = gtk_bitset_new_empty ();
engine = spelling_engine_new (&adapter, instance);
insert (engine, "2", 0, "2");
insert (engine, "1", 0, "12");
insert (engine, "0", 0, "012");
delete (engine, 2, 1, "01");
delete (engine, 1, 1, "0");
delete (engine, 0, 1, "");
g_string_free (buffer, TRUE);
gtk_bitset_unref (mispelled);
g_object_unref (dictionary);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/Spelling/Engine/basic", test_engine_basic);
return g_test_run ();
}
07070100000089000081A400000000000000000000000168A626C200002B4D000000000000000000000000000000000000002700000000libspelling-0.4.9/testsuite/test-job.c/* test-job.c
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <locale.h>
#include <glib/gi18n.h>
#include <libspelling.h>
#include "spelling-dictionary-internal.h"
#include "spelling-provider-internal.h"
#include "spelling-job-private.h"
static const char *test_dictionary[] = {
"Straße",
"a",
"calle",
"caminar",
"die",
"entlang",
"gehen",
"has",
"it",
"it's",
"la",
"misspelled",
"not",
"por",
"say'",
"text",
"this",
"word",
"गच्छन्तु",
"वीथिं",
"དུ",
"འགྲོ",
"ལམ",
"སྲང",
NULL
};
#define TEST_TYPE_DICTIONARY (test_dictionary_get_type())
G_DECLARE_FINAL_TYPE (TestDictionary, test_dictionary, TEST, DICTIONARY, SpellingDictionary)
struct _TestDictionary
{
SpellingDictionary parent_instance;
};
G_DEFINE_FINAL_TYPE (TestDictionary, test_dictionary, SPELLING_TYPE_DICTIONARY)
static gboolean
test_dictionary_contains_word (SpellingDictionary *dictionary,
const char *word,
gssize word_len)
{
g_autofree char *copy = g_strndup (word, word_len < 0 ? strlen (word) : word_len);
return g_strv_contains ((const char * const *)test_dictionary, copy);
}
static const char *
test_dictionary_get_extra_word_chars (SpellingDictionary *dictionary)
{
return "'";
}
static void
test_dictionary_class_init (TestDictionaryClass *klass)
{
SpellingDictionaryClass *dictionary_class = SPELLING_DICTIONARY_CLASS (klass);
dictionary_class->contains_word = test_dictionary_contains_word;
dictionary_class->get_extra_word_chars = test_dictionary_get_extra_word_chars;
}
static void
test_dictionary_init (TestDictionary *self)
{
}
#define TEST_TYPE_PROVIDER (test_provider_get_type())
G_DECLARE_FINAL_TYPE (TestProvider, test_provider, TEST, PROVIDER, SpellingProvider)
struct _TestProvider
{
SpellingProvider parent_instance;
TestDictionary *dictionary;
};
G_DEFINE_FINAL_TYPE (TestProvider, test_provider, SPELLING_TYPE_PROVIDER)
static SpellingDictionary *
test_provider_load_dictionary (SpellingProvider *provider,
const char *language)
{
TestProvider *self = TEST_PROVIDER (provider);
return g_object_ref (SPELLING_DICTIONARY (self->dictionary));
}
static const char *
test_provider_get_default_code (SpellingProvider *provider)
{
return "C";
}
static void
test_provider_finalize (GObject *object)
{
TestProvider *self = (TestProvider *)object;
g_clear_object (&self->dictionary);
G_OBJECT_CLASS (test_provider_parent_class)->finalize (object);
}
static void
test_provider_class_init (TestProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SpellingProviderClass *provider_class = SPELLING_PROVIDER_CLASS (klass);
object_class->finalize = test_provider_finalize;
provider_class->load_dictionary = test_provider_load_dictionary;
provider_class->get_default_code = test_provider_get_default_code;
}
static void
test_provider_init (TestProvider *self)
{
self->dictionary = g_object_new (TEST_TYPE_DICTIONARY, NULL);
}
static GMainLoop *main_loop;
typedef struct _TestJob
{
const char *text;
guint n_mistakes;
const SpellingBoundary *mistakes;
} TestJob;
static void
validate (SpellingMistake *mistakes,
guint n_mistakes,
TestJob *test)
{
g_assert_cmpint (n_mistakes, ==, test->n_mistakes);
g_assert_true (mistakes != NULL || test->n_mistakes == 0);
for (guint i = 0; i < n_mistakes; i++)
{
g_assert_cmpint (test->mistakes[i].offset, ==, mistakes[i].offset);
g_assert_cmpint (test->mistakes[i].length, ==, mistakes[i].length);
}
}
static void
job_finished (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autofree SpellingMistake *mistakes = NULL;
g_autofree SpellingBoundary *fragments = NULL;
TestJob *test = user_data;
GError *error = NULL;
guint n_mistakes;
guint n_fragments;
g_assert (SPELLING_IS_JOB (object));
spelling_job_run_finish (SPELLING_JOB (object), result, &fragments, &n_fragments, &mistakes, &n_mistakes);
#if 0
for (guint i = 0; i < n_mistakes; i++)
{
g_autofree char *word = g_strndup (&test->text[mistakes[i].byte_offset], mistakes[i].byte_length);
g_print ("Misspelled: \"%s\"\n", word);
}
#endif
g_assert_no_error (error);
validate (mistakes, n_mistakes, test);
g_main_loop_quit (main_loop);
}
static void
test_job_basic (void)
{
g_autoptr(SpellingProvider) provider = g_object_new (TEST_TYPE_PROVIDER, NULL);
const char *default_code = spelling_provider_get_default_code (provider);
g_autoptr(SpellingDictionary) dictionary = spelling_provider_load_dictionary (provider, default_code);
TestJob tests[] = {
{ "it's not a misspelled word", 0, NULL },
{ "not", 0, NULL },
{ "it's", 0, NULL },
{ "it's ", 0, NULL },
{ "\t\nsay' \t\n", 0, NULL },
{ "say'", 0, NULL },
{ "die Straße entlang gehen", 0, NULL },
{ "वीथिं गच्छन्तु", 0, NULL },
{ "སྲང་ལམ་དུ་འགྲོ།", 0, NULL },
{ "caminar por la calle", 0, NULL },
{ "it'",
1,
(const SpellingBoundary[]) {
{ .offset = 0, .length = 3 },
},
},
{ "it' ",
1,
(const SpellingBoundary[]) {
{ .offset = 0, .length = 3 },
},
},
{ "this text has a misplled word",
1,
(const SpellingBoundary[]) {
{ .offset = 16, .length = 8 },
},
},
};
for (guint i = 0; i < G_N_ELEMENTS (tests); i++)
{
g_autoptr(GBytes) bytes = g_bytes_new (tests[i].text, strlen (tests[i].text));
g_autoptr(SpellingJob) job = spelling_job_new (dictionary, pango_language_get_default ());
g_autofree SpellingMistake *mistakes = NULL;
g_autofree SpellingBoundary *fragments = NULL;
guint n_mistakes;
guint n_fragments;
spelling_job_add_fragment (job, bytes, 0, g_utf8_strlen (tests[i].text, -1));
/* Test async version */
spelling_job_run (job, job_finished, &tests[i]);
g_main_loop_run (main_loop);
/* Now test sync version */
spelling_job_run_sync (job, &fragments, &n_fragments, &mistakes, &n_mistakes);
validate (mistakes, n_mistakes, &tests[i]);
}
}
static void
test_job_discard (void)
{
g_autoptr(SpellingProvider) provider = g_object_new (TEST_TYPE_PROVIDER, NULL);
const char *default_code = spelling_provider_get_default_code (provider);
g_autoptr(SpellingDictionary) dictionary = spelling_provider_load_dictionary (provider, default_code);
g_autoptr(GBytes) bytes = g_bytes_new ("misplled word", 13);
g_autoptr(SpellingJob) job = NULL;
g_autofree SpellingMistake *mistakes = NULL;
guint n_mistakes = 0;
/* First make sure things work */
job = spelling_job_new (dictionary, pango_language_get_default ());
spelling_job_add_fragment (job, bytes, 1, 13);
spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes);
g_assert_cmpint (n_mistakes, ==, 1);
g_assert_cmpint (mistakes[0].offset, ==, 1);
g_assert_cmpint (mistakes[0].length, ==, 8);
g_clear_pointer (&mistakes, g_free);
g_clear_object (&job);
/* Now try to do an INSERT that SHOULD NOT collide */
job = spelling_job_new (dictionary, pango_language_get_default ());
spelling_job_add_fragment (job, bytes, 1, 13);
spelling_job_notify_insert (job, 0, 3);
spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes);
g_assert_cmpint (n_mistakes, ==, 1);
g_assert_cmpint (mistakes[0].offset, ==, 1 + 3);
g_assert_cmpint (mistakes[0].length, ==, 8);
g_clear_pointer (&mistakes, g_free);
g_clear_object (&job);
/* Now try to do an INSERT that SHOULD collide */
job = spelling_job_new (dictionary, pango_language_get_default ());
spelling_job_add_fragment (job, bytes, 1, 13);
spelling_job_notify_insert (job, 1, 3);
spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes);
g_assert_cmpint (n_mistakes, ==, 0);
g_assert_null (mistakes);
g_clear_pointer (&mistakes, g_free);
g_clear_object (&job);
/* Now try to do a DELETE that SHOULD NOT collide */
job = spelling_job_new (dictionary, pango_language_get_default ());
spelling_job_add_fragment (job, bytes, 2, 13);
spelling_job_notify_delete (job, 0, 1);
spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes);
g_assert_cmpint (n_mistakes, ==, 1);
g_assert_cmpint (mistakes[0].offset, ==, 1);
g_assert_cmpint (mistakes[0].length, ==, 8);
g_clear_pointer (&mistakes, g_free);
g_clear_object (&job);
/* Now try to do a DELETE that SHOULD collide */
job = spelling_job_new (dictionary, pango_language_get_default ());
spelling_job_add_fragment (job, bytes, 1, 13);
spelling_job_notify_delete (job, 0, 1);
spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes);
g_assert_cmpint (n_mistakes, ==, 0);
g_assert_null (mistakes);
g_clear_pointer (&mistakes, g_free);
g_clear_object (&job);
/* Now try to do a DELETE that SHOULD collide */
job = spelling_job_new (dictionary, pango_language_get_default ());
spelling_job_add_fragment (job, bytes, 0, 13);
spelling_job_notify_delete (job, 13, 1);
spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes);
g_assert_cmpint (n_mistakes, ==, 0);
g_assert_null (mistakes);
g_clear_pointer (&mistakes, g_free);
g_clear_object (&job);
/* Now try to do a DELETE that SHOULD NOT collide */
job = spelling_job_new (dictionary, pango_language_get_default ());
spelling_job_add_fragment (job, bytes, 0, 13);
spelling_job_notify_delete (job, 14, 1);
spelling_job_run_sync (job, NULL, NULL, &mistakes, &n_mistakes);
g_assert_cmpint (n_mistakes, ==, 1);
g_assert_cmpint (mistakes[0].offset, ==, 0);
g_assert_cmpint (mistakes[0].length, ==, 8);
g_clear_pointer (&mistakes, g_free);
g_clear_object (&job);
}
int
main (int argc,
char *argv[])
{
main_loop = g_main_loop_new (NULL, FALSE);
setlocale (LC_ALL, "");
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/Spelling/Job/basic", test_job_basic);
g_test_add_func ("/Spelling/Job/discard", test_job_discard);
return g_test_run ();
}
0707010000008A000081A400000000000000000000000168A626C200004EBB000000000000000000000000000000000000002A00000000libspelling-0.4.9/testsuite/test-region.c/* test-region.c
*
* Copyright 2024 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library 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
* Lesser 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "cjhtextregionprivate.h"
#include "cjhtextregionbtree.h"
static void
get_run_at_offset (void)
{
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
const CjhTextRegionRun *run;
gsize offset;
_cjh_text_region_insert (region, 0, 10, GUINT_TO_POINTER (1));
_cjh_text_region_insert (region, 10, 10, GUINT_TO_POINTER (2));
_cjh_text_region_replace (region, 15, 1, GUINT_TO_POINTER (3));
run = _cjh_text_region_get_run_at_offset (region, 15, &offset);
g_assert_true (run != NULL);
g_assert_cmpint (offset, ==, 15);
g_assert_cmpint (run->length, ==, 1);
g_assert_true (run->data == GUINT_TO_POINTER (3));
_cjh_text_region_free (region);
}
static void
assert_leaves_empty (CjhTextRegion *region)
{
CjhTextRegionNode *leaf = _cjh_text_region_get_first_leaf (region);
guint count = 0;
for (; leaf; leaf = leaf->leaf.next, count++)
{
CjhTextRegionNode *parent = cjh_text_region_node_get_parent (leaf);
guint length = cjh_text_region_node_length (leaf);
guint length_in_parent = 0;
SORTED_ARRAY_FOREACH (&parent->branch.children, CjhTextRegionChild, child, {
if (child->node == leaf)
{
length_in_parent = child->length;
break;
}
});
if (length || length_in_parent)
g_error ("leaf %p %u has length of %u in %u runs. Parent thinks it has length of %u.",
leaf, count, length, SORTED_ARRAY_LENGTH (&leaf->leaf.runs), length_in_parent);
}
}
static guint
count_leaves (CjhTextRegion *region)
{
CjhTextRegionNode *leaf = _cjh_text_region_get_first_leaf (region);
guint count = 0;
for (; leaf; leaf = leaf->leaf.next)
count++;
return count;
}
static guint
count_internal_recuse (CjhTextRegionNode *node)
{
guint count = 1;
g_assert (!cjh_text_region_node_is_leaf (node));
SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, {
g_assert (child->node != NULL);
if (!cjh_text_region_node_is_leaf (child->node))
count += count_internal_recuse (child->node);
});
return count;
}
static guint
count_internal (CjhTextRegion *region)
{
return count_internal_recuse (®ion->root);
}
G_GNUC_UNUSED static inline void
print_tree (CjhTextRegionNode *node,
guint depth)
{
for (guint i = 0; i < depth; i++)
g_print (" ");
g_print ("%p %s Length=%"G_GSIZE_MODIFIER"u Items=%u Prev<%p> Next<%p>\n",
node,
cjh_text_region_node_is_leaf (node) ? "Leaf" : "Branch",
cjh_text_region_node_length (node),
cjh_text_region_node_is_leaf (node) ?
SORTED_ARRAY_LENGTH (&node->leaf.runs) :
SORTED_ARRAY_LENGTH (&node->branch.children),
cjh_text_region_node_is_leaf (node) ? node->leaf.prev : node->branch.prev,
cjh_text_region_node_is_leaf (node) ? node->leaf.next : node->branch.next);
if (!cjh_text_region_node_is_leaf (node))
{
SORTED_ARRAY_FOREACH (&node->branch.children, CjhTextRegionChild, child, {
print_tree (child->node, depth+1);
});
}
}
static void
assert_empty (CjhTextRegion *region)
{
#if 0
print_tree (®ion->root, 0);
#endif
g_assert_cmpint (_cjh_text_region_get_length (region), ==, 0);
assert_leaves_empty (region);
g_assert_cmpint (1, ==, count_internal (region));
g_assert_cmpint (1, ==, count_leaves (region));
}
static gboolean
non_overlapping_insert_remove_cb (gsize offset,
const CjhTextRegionRun *run,
gpointer user_data)
{
g_assert_cmpint (offset, ==, GPOINTER_TO_UINT (run->data));
return FALSE;
}
static void
non_overlapping_insert_remove (void)
{
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
assert_empty (region);
for (guint i = 0; i < 100000; i++)
{
_cjh_text_region_insert (region, i, 1, GUINT_TO_POINTER (i));
g_assert_cmpint (_cjh_text_region_get_length (region), ==, i + 1);
}
g_assert_cmpint (_cjh_text_region_get_length (region), ==, 100000);
_cjh_text_region_foreach (region, non_overlapping_insert_remove_cb, NULL);
for (guint i = 0; i < 100000; i++)
_cjh_text_region_remove (region, 100000-1-i, 1);
g_assert_cmpint (_cjh_text_region_get_length (region), ==, 0);
assert_empty (region);
_cjh_text_region_free (region);
}
typedef struct {
gsize offset;
gsize length;
gpointer data;
} SplitRunCheck;
typedef struct {
gsize index;
gsize count;
const SplitRunCheck *checks;
} SplitRun;
static gboolean
split_run_cb (gsize offset,
const CjhTextRegionRun *run,
gpointer user_data)
{
SplitRun *state = user_data;
g_assert_cmpint (offset, ==, state->checks[state->index].offset);
g_assert_cmpint (run->length, ==, state->checks[state->index].length);
g_assert_true (run->data == state->checks[state->index].data);
state->index++;
return FALSE;
}
static void
split_run (void)
{
static const SplitRunCheck checks[] = {
{ 0, 1, NULL },
{ 1, 1, GSIZE_TO_POINTER (1) },
{ 2, 1, NULL },
};
SplitRun state = { 0, 3, checks };
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
_cjh_text_region_insert (region, 0, 2, NULL);
g_assert_cmpint (_cjh_text_region_get_length (region), ==, 2);
_cjh_text_region_insert (region, 1, 1, GSIZE_TO_POINTER (1));
g_assert_cmpint (_cjh_text_region_get_length (region), ==, 3);
_cjh_text_region_foreach (region, split_run_cb, &state);
_cjh_text_region_free (region);
}
static gboolean
can_join_cb (gsize offset,
const CjhTextRegionRun *left,
const CjhTextRegionRun *right)
{
return left->data == right->data;
}
static void
no_split_run (void)
{
static const SplitRunCheck checks[] = {
{ 0, 3, NULL },
};
SplitRun state = { 0, 1, checks };
CjhTextRegion *region = _cjh_text_region_new (can_join_cb, NULL);
_cjh_text_region_insert (region, 0, 2, NULL);
g_assert_cmpint (_cjh_text_region_get_length (region), ==, 2);
_cjh_text_region_insert (region, 1, 1, NULL);
g_assert_cmpint (_cjh_text_region_get_length (region), ==, 3);
_cjh_text_region_foreach (region, split_run_cb, &state);
_cjh_text_region_free (region);
}
static void
random_insertion (void)
{
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
gsize expected = 0;
for (guint i = 0; i < 10000; i++)
{
guint pos = g_random_int_range (0, region->length + 1);
guint len = g_random_int_range (1, 20);
_cjh_text_region_insert (region, pos, len, GUINT_TO_POINTER (i));
expected += len;
}
g_assert_cmpint (expected, ==, region->length);
_cjh_text_region_replace (region, 0, region->length, NULL);
g_assert_cmpint (count_leaves (region), ==, 1);
g_assert_cmpint (count_internal (region), ==, 1);
_cjh_text_region_free (region);
}
static void
random_deletion (void)
{
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
_cjh_text_region_insert (region, 0, 10000, NULL);
while (region->length > 0)
{
guint pos = region->length > 1 ? g_random_int_range (0, region->length-1) : 0;
guint len = region->length - pos > 1 ? g_random_int_range (1, region->length - pos) : 1;
_cjh_text_region_remove (region, pos, len);
}
_cjh_text_region_free (region);
}
static void
random_insert_deletion (void)
{
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
guint expected = 0;
guint i = 0;
while (region->length < 10000)
{
guint pos = g_random_int_range (0, region->length + 1);
guint len = g_random_int_range (1, 20);
_cjh_text_region_insert (region, pos, len, GUINT_TO_POINTER (i));
expected += len;
i++;
}
g_assert_cmpint (expected, ==, region->length);
while (region->length > 0)
{
guint pos = region->length > 1 ? g_random_int_range (0, region->length-1) : 0;
guint len = region->length - pos > 1 ? g_random_int_range (1, region->length - pos) : 1;
g_assert (pos + len <= region->length);
_cjh_text_region_remove (region, pos, len);
}
_cjh_text_region_free (region);
}
static void
test_val_queue (void)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
VAL_QUEUE_NODE(guint8, 32) field;
guint8 pos;
VAL_QUEUE_INIT (&field);
for (guint i = 0; i < 32; i++)
VAL_QUEUE_PUSH_TAIL (&field, i);
g_assert_cmpint (VAL_QUEUE_LENGTH (&field), ==, 32);
for (guint i = 0; i < 32; i++)
{
VAL_QUEUE_NTH (&field, i, pos);
g_assert_cmpint (pos, ==, i);
}
for (guint i = 0; i < 32; i++)
{
VAL_QUEUE_POP_HEAD (&field, pos);
g_assert_cmpint (pos, ==, i);
}
g_assert_cmpint (VAL_QUEUE_LENGTH (&field), ==, 0);
for (guint i = 0; i < 32; i++)
VAL_QUEUE_PUSH_TAIL (&field, i);
g_assert_cmpint (VAL_QUEUE_LENGTH (&field), ==, 32);
for (guint i = 0; i < 32; i++)
{
VAL_QUEUE_POP_TAIL (&field, pos);
g_assert_cmpint (pos, ==, 31-i);
}
g_assert_cmpint (VAL_QUEUE_LENGTH (&field), ==, 0);
for (guint i = 0; i < 32; i++)
VAL_QUEUE_PUSH_TAIL (&field, i);
while (VAL_QUEUE_LENGTH (&field))
VAL_QUEUE_POP_NTH (&field, VAL_QUEUE_LENGTH (&field)/2, pos);
#pragma GCC diagnostic pop
}
typedef struct {
int v;
} Dummy;
static void
sorted_array (void)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
SORTED_ARRAY_FIELD (Dummy, 32) field;
Dummy d;
guint i;
SORTED_ARRAY_INIT (&field);
d.v = 0; SORTED_ARRAY_INSERT_VAL (&field, 0, d);
d.v = 2; SORTED_ARRAY_INSERT_VAL (&field, 1, d);
d.v = 1; SORTED_ARRAY_INSERT_VAL (&field, 1, d);
i = 0;
g_assert_cmpint (SORTED_ARRAY_LENGTH (&field), ==, 3);
SORTED_ARRAY_FOREACH (&field, Dummy, dummy, {
g_assert_cmpint (dummy->v, ==, i++);
});
g_assert_cmpint (i, ==, 3);
SORTED_ARRAY_POP_HEAD (&field, d); g_assert_cmpint (d.v, ==, 0);
SORTED_ARRAY_POP_HEAD (&field, d); g_assert_cmpint (d.v, ==, 1);
SORTED_ARRAY_POP_HEAD (&field, d); g_assert_cmpint (d.v, ==, 2);
for (i = 0; i < 10; i++)
{ d.v = i * 2;
SORTED_ARRAY_INSERT_VAL (&field, i, d); }
for (i = 0; i < 10; i++)
{ d.v = i * 2 + 1;
SORTED_ARRAY_INSERT_VAL (&field, i*2+1, d); }
i = 0;
g_assert_cmpint (SORTED_ARRAY_LENGTH (&field), ==, 20);
SORTED_ARRAY_FOREACH (&field, Dummy, dummy, {
g_assert_cmpint (dummy->v, ==, i++);
});
g_assert_cmpint (i, ==, 20);
SORTED_ARRAY_FOREACH (&field, Dummy, dummy, {
(void)dummy;
SORTED_ARRAY_FOREACH_REMOVE (&field);
});
g_assert_cmpint (SORTED_ARRAY_LENGTH (&field), ==, 0);
for (i = 0; i < 32; i++)
{
d.v = i;
SORTED_ARRAY_PUSH_TAIL (&field, d);
}
g_assert_cmpint (32, ==, SORTED_ARRAY_LENGTH (&field));
i = 0;
SORTED_ARRAY_FOREACH (&field, Dummy, dummy, {
g_assert_cmpint (dummy->v, ==, i);
g_assert_cmpint (SORTED_ARRAY_LENGTH (&field), ==, 32-i);
SORTED_ARRAY_FOREACH_REMOVE (&field);
i++;
});
g_assert_cmpint (0, ==, SORTED_ARRAY_LENGTH (&field));
for (i = 0; i < 32; i++)
{
d.v = i;
SORTED_ARRAY_PUSH_TAIL (&field, d);
}
g_assert_cmpint (32, ==, SORTED_ARRAY_LENGTH (&field));
i = 31;
SORTED_ARRAY_FOREACH_REVERSE (&field, Dummy, dummy, {
g_assert_cmpint (dummy->v, ==, i);
SORTED_ARRAY_REMOVE_INDEX (&field, i, d);
i--;
});
#pragma GCC diagnostic pop
}
static gboolean
replace_part_of_long_run_join (gsize offset,
const CjhTextRegionRun *left,
const CjhTextRegionRun *right)
{
return FALSE;
}
static void
replace_part_of_long_run_split (gsize offset,
const CjhTextRegionRun *run,
CjhTextRegionRun *left,
CjhTextRegionRun *right)
{
left->data = run->data;
right->data = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (run->data) + left->length);
}
static void
replace_part_of_long_run (void)
{
CjhTextRegion *region = _cjh_text_region_new (replace_part_of_long_run_join,
replace_part_of_long_run_split);
static const SplitRunCheck checks0[] = {
{ 0, 5, NULL },
};
static const SplitRunCheck checks1[] = {
{ 0, 1, NULL },
{ 1, 3, GSIZE_TO_POINTER (2) },
};
static const SplitRunCheck checks2[] = {
{ 0, 1, GSIZE_TO_POINTER (0) },
{ 1, 1, GSIZE_TO_POINTER ((1L<<31)|1) },
{ 2, 3, GSIZE_TO_POINTER (2) },
};
static const SplitRunCheck checks3[] = {
{ 0, 1, GSIZE_TO_POINTER (0) },
{ 1, 1, GSIZE_TO_POINTER ((1L<<31)|1) },
{ 2, 1, GSIZE_TO_POINTER (2) },
{ 3, 1, GSIZE_TO_POINTER (4) },
};
static const SplitRunCheck checks4[] = {
{ 0, 1, GSIZE_TO_POINTER (0) },
{ 1, 1, GSIZE_TO_POINTER ((1L<<31)|1) },
{ 2, 1, GSIZE_TO_POINTER (2) },
{ 3, 1, GSIZE_TO_POINTER ((1L<<31)|2) },
{ 4, 1, GSIZE_TO_POINTER (4) },
};
SplitRun state0 = { 0, 1, checks0 };
SplitRun state1 = { 0, 2, checks1 };
SplitRun state2 = { 0, 3, checks2 };
SplitRun state3 = { 0, 4, checks3 };
SplitRun state4 = { 0, 5, checks4 };
_cjh_text_region_insert (region, 0, 5, NULL);
_cjh_text_region_foreach (region, split_run_cb, &state0);
_cjh_text_region_remove (region, 1, 1);
_cjh_text_region_foreach (region, split_run_cb, &state1);
_cjh_text_region_insert (region, 1, 1, GSIZE_TO_POINTER ((1L<<31)|1));
_cjh_text_region_foreach (region, split_run_cb, &state2);
_cjh_text_region_remove (region, 3, 1);
_cjh_text_region_foreach (region, split_run_cb, &state3);
_cjh_text_region_insert (region, 3, 1, GSIZE_TO_POINTER ((1L<<31)|2));
_cjh_text_region_foreach (region, split_run_cb, &state4);
_cjh_text_region_free (region);
}
typedef struct
{
char *original;
char *changes;
GString *res;
} wordstate;
static gboolean
word_foreach_cb (gsize offset,
const CjhTextRegionRun *run,
gpointer data)
{
wordstate *state = data;
gsize sdata = GPOINTER_TO_SIZE (run->data);
gsize soff = sdata & ~(1L<<31);
char *src;
if (sdata == soff)
src = state->original;
else
src = state->changes;
#if 0
g_print ("%lu len %lu (%s at %lu) %s\n",
offset, run->length, sdata == soff ? "original" : "changes", soff,
sdata == soff && src[sdata] == '\n' ? "is-newline" : "");
#endif
g_string_append_len (state->res, src + soff, run->length);
return FALSE;
}
static gboolean
join_word_cb (gsize offset,
const CjhTextRegionRun *left,
const CjhTextRegionRun *right)
{
return FALSE;
}
static void
split_word_cb (gsize offset,
const CjhTextRegionRun *run,
CjhTextRegionRun *left,
CjhTextRegionRun *right)
{
gsize sdata = GPOINTER_TO_SIZE (run->data);
left->data = run->data;
right->data = GSIZE_TO_POINTER (sdata + left->length);
}
static void
test_words_database (void)
{
CjhTextRegion *region;
g_autofree char *contents = NULL;
g_autoptr(GString) str = NULL;
g_autoptr(GString) res = NULL;
const char *word;
const char *iter;
gsize len;
wordstate state;
if (!g_file_get_contents ("/usr/share/dict/words", &contents, &len, NULL))
{
g_test_skip ("Words database not available");
return;
}
region = _cjh_text_region_new (join_word_cb, split_word_cb);
str = g_string_new (NULL);
res = g_string_new (NULL);
/* 0 offset of base buffer */
_cjh_text_region_insert (region, 0, len, NULL);
/* For each each word, remove it and replace it with a word added to str.
* At the end we'll create the buffer and make sure we get the same.
*/
word = contents;
iter = contents;
for (;;)
{
if (*iter == 0)
break;
if (g_unichar_isspace (g_utf8_get_char (iter)))
{
gsize pos = str->len;
g_string_append_len (str, word, iter - word);
_cjh_text_region_replace (region, word - contents, iter - word, GSIZE_TO_POINTER ((1L<<31)|pos));
while (*iter && g_unichar_isspace (g_utf8_get_char (iter)))
iter = g_utf8_next_char (iter);
word = iter;
}
else
iter = g_utf8_next_char (iter);
}
state.original = contents;
state.changes = str->str;
state.res = res;
_cjh_text_region_foreach (region, word_foreach_cb, &state);
_cjh_text_region_free (g_steal_pointer (®ion));
g_assert_true (g_str_equal (contents, res->str));
}
static gboolean
foreach_cb (gsize offset,
const CjhTextRegionRun *run,
gpointer user_data)
{
guint *count = user_data;
g_assert_cmpint (GPOINTER_TO_SIZE (run->data), ==, offset);
(*count)++;
return FALSE;
}
static void
foreach_in_range (void)
{
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
guint count;
for (guint i = 0; i < 100000; i++)
{
_cjh_text_region_insert (region, i, 1, GUINT_TO_POINTER (i));
g_assert_cmpint (_cjh_text_region_get_length (region), ==, i + 1);
}
count = 0;
_cjh_text_region_foreach_in_range (region, 0, 100000, foreach_cb, &count);
g_assert_cmpint (count, ==, 100000);
count = 0;
_cjh_text_region_foreach_in_range (region, 1000, 5000, foreach_cb, &count);
g_assert_cmpint (count, ==, 4000);
_cjh_text_region_replace (region, 0, 10000, NULL);
count = 0;
_cjh_text_region_foreach_in_range (region, 1000, 5000, foreach_cb, &count);
g_assert_cmpint (count, ==, 1);
_cjh_text_region_free (region);
}
static void
full_tail_node (void)
{
CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
for (guint i = 0; i < CJH_TEXT_REGION_MAX_RUNS-3; i++)
_cjh_text_region_insert (region, i, 1, GUINT_TO_POINTER (i));
_cjh_text_region_insert (region, CJH_TEXT_REGION_MAX_RUNS-3, 100, GUINT_TO_POINTER (1000));
_cjh_text_region_remove (region, CJH_TEXT_REGION_MAX_RUNS-1, 1);
_cjh_text_region_remove (region, CJH_TEXT_REGION_MAX_RUNS, 1);
_cjh_text_region_remove (region, CJH_TEXT_REGION_MAX_RUNS+1, 1);
_cjh_text_region_free (region);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/Cjh/TextRegion/val_queue", test_val_queue);
g_test_add_func ("/Cjh/TextRegion/sorted_array", sorted_array);
g_test_add_func ("/Cjh/TextRegion/non_overlapping_insert_remove", non_overlapping_insert_remove);
g_test_add_func ("/Cjh/TextRegion/foreach_in_range", foreach_in_range);
g_test_add_func ("/Cjh/TextRegion/split_run", split_run);
g_test_add_func ("/Cjh/TextRegion/no_split_run", no_split_run);
g_test_add_func ("/Cjh/TextRegion/random_insertion", random_insertion);
g_test_add_func ("/Cjh/TextRegion/random_deletion", random_deletion);
g_test_add_func ("/Cjh/TextRegion/random_insert_deletion", random_insert_deletion);
g_test_add_func ("/Cjh/TextRegion/replace_part_of_long_run", replace_part_of_long_run);
g_test_add_func ("/Cjh/TextRegion/words_database", test_words_database);
g_test_add_func ("/Cjh/TextRegion/get_run_at_offset", get_run_at_offset);
g_test_add_func ("/Cjh/TextRegion/full_tail_node", full_tail_node);
return g_test_run ();
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!965 blocks