wpeditor-2.18/0000755000175000001440000000000011040151472012517 5ustar stevenuserswpeditor-2.18/autogen.sh0000755000175000001440000000006010620565455014531 0ustar stevenusers#!/bin/sh set -x autoreconf --install --force wpeditor-2.18/doxygen/0000755000175000001440000000000010772655066014217 5ustar stevenuserswpeditor-2.18/doxygen/Doxyfile0000644000175000001440000002100710476777244015731 0ustar stevenusers# Doxyfile 1.4.4 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = OSSO-Notes PROJECT_NUMBER = 0.1 OUTPUT_DIRECTORY = doxygen CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English USE_WINDOWS_ENCODING = NO BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = . STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES DISTRIBUTE_GROUP_DOC = NO SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 2 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = YES HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = YES FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = NO WARN_IF_UNDOCUMENTED = NO WARN_IF_DOC_ERROR = NO WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = . FILE_PATTERNS = *.h *.c RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO USE_HTAGS = NO VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 1024 MAX_DOT_GRAPH_HEIGHT = 1024 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO wpeditor-2.18/doxygen/generate0000755000175000001440000000012510457426115015724 0ustar stevenusers#!/bin/sh cd .. rm -rf debian/tmp rm -rf debian/wpeditor-dev doxygen doxygen/Doxyfilewpeditor-2.18/COPYING0000644000175000001440000006262510515353176013600 0ustar stevenusers GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, 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. 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 an 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 St, 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 wpeditor-2.18/config.sub0000644000175000001440000007547010772655266014541 0ustar stevenusers#! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. timestamp='2005-04-22' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Please send patches to . Submit a context # diff and a properly formatted ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit 0 ;; --version | -v ) echo "$version" ; exit 0 ;; --help | --h* | -h ) echo "$usage"; exit 0 ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit 0;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray) os= basic_machine=$1 ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ | bfin \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64vr | mips64vrel \ | mips64orion | mips64orionel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | msp430 \ | ns16k | ns32k \ | openrisc | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b \ | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ | v850 | v850e \ | we32k \ | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \ | z8k) basic_machine=$basic_machine-unknown ;; m6811 | m68hc11 | m6812 | m68hc12) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64vr-* | mips64vrel-* \ | mips64orion-* | mips64orionel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | msp430-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ | romp-* | rs6000-* \ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tron-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \ | xstormy16-* | xtensa-* \ | ymp-* \ | z8k-*) ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; c90) basic_machine=c90-cray os=-unicos ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16c) basic_machine=cr16c-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; # I'm not sure what "Sysv32" means. Should this be sysv3.2? i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; mvs) basic_machine=i370-ibm os=-mvs ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; or32 | or32-*) basic_machine=or32-unknown os=-coff ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc) basic_machine=powerpc-unknown ;; ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tic54x | c54x*) basic_machine=tic54x-unknown os=-coff ;; tic55x | c55x*) basic_machine=tic55x-unknown os=-coff ;; tic6x | c6x*) basic_machine=tic6x-unknown os=-coff ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sh64) basic_machine=sh64-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* \ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -kaos*) os=-kaos ;; -zvmoe) os=-zvmoe ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 # This also exists in the configure program, but was not the # default. # os=-sunos4 ;; m68*-cisco) os=-aout ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit 0 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: wpeditor-2.18/config.guess0000644000175000001440000012546610772655266015077 0ustar stevenusers#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. timestamp='2005-04-22' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner . # Please send patches to . Submit a context # diff and a properly formatted ChangeLog entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # The plan is that this can be called by configure scripts if you # don't specify an explicit build system type. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit 0 ;; --version | -v ) echo "$version" ; exit 0 ;; --help | --h* | -h ) echo "$usage"; exit 0 ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep __ELF__ >/dev/null then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit 0 ;; amd64:OpenBSD:*:*) echo x86_64-unknown-openbsd${UNAME_RELEASE} exit 0 ;; amiga:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; cats:OpenBSD:*:*) echo arm-unknown-openbsd${UNAME_RELEASE} exit 0 ;; hp300:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; luna88k:OpenBSD:*:*) echo m88k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mac68k:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; macppc:OpenBSD:*:*) echo powerpc-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvme68k:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvme88k:OpenBSD:*:*) echo m88k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvmeppc:OpenBSD:*:*) echo powerpc-unknown-openbsd${UNAME_RELEASE} exit 0 ;; sgi:OpenBSD:*:*) echo mips64-unknown-openbsd${UNAME_RELEASE} exit 0 ;; sun3:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; *:OpenBSD:*:*) echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} exit 0 ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit 0 ;; macppc:MirBSD:*:*) echo powerppc-unknown-mirbsd${UNAME_RELEASE} exit 0 ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit 0 ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` exit 0 ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit 0 ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit 0 ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit 0;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit 0 ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit 0 ;; *:OS/390:*:*) echo i370-ibm-openedition exit 0 ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit 0 ;; *:OS400:*:*) echo powerpc-ibm-os400 exit 0 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit 0;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit 0;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit 0 ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit 0 ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit 0 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7 && exit 0 ;; esac ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; i86pc:SunOS:5.*:*) echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit 0 ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit 0 ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit 0 ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit 0 ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit 0 ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit 0 ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit 0 ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit 0 ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit 0 ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit 0 ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit 0 ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit 0 ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit 0 ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c \ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ && exit 0 echo mips-mips-riscos${UNAME_RELEASE} exit 0 ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit 0 ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit 0 ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit 0 ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit 0 ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit 0 ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit 0 ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit 0 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit 0 ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit 0 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit 0 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit 0 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit 0 ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit 0 ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit 0 ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit 0 ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 echo rs6000-ibm-aix3.2.5 elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit 0 ;; *:AIX:*:[45]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit 0 ;; *:AIX:*:*) echo rs6000-ibm-aix exit 0 ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit 0 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit 0 ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit 0 ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit 0 ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit 0 ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit 0 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then # avoid double evaluation of $set_cc_for_build test -n "$CC_FOR_BUILD" || eval $set_cc_for_build if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit 0 ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit 0 ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 echo unknown-hitachi-hiuxwe2 exit 0 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit 0 ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit 0 ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit 0 ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit 0 ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit 0 ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit 0 ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit 0 ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit 0 ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit 0 ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit 0 ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit 0 ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit 0 ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit 0 ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit 0 ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit 0 ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit 0 ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit 0 ;; *:FreeBSD:*:*) echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit 0 ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit 0 ;; i*:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit 0 ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit 0 ;; x86:Interix*:[34]*) echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' exit 0 ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit 0 ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit 0 ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit 0 ;; amd64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit 0 ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit 0 ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit 0 ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit 0 ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit 0 ;; arm*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; cris:Linux:*:*) echo cris-axis-linux-gnu exit 0 ;; crisv32:Linux:*:*) echo crisv32-axis-linux-gnu exit 0 ;; frv:Linux:*:*) echo frv-unknown-linux-gnu exit 0 ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; mips:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef mips #undef mipsel #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=mipsel #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=mips #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 ;; mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef mips64 #undef mips64el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=mips64el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=mips64 #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit 0 ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit 0 ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit 0 ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit 0 ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit 0 ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit 0 ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit 0 ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so # first see if it will tell us. cd to the root directory to prevent # problems with other programs or directories called `ld' in the path. # Set LC_ALL=C to ensure ld outputs messages in English. ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ | sed -ne '/supported targets:/!d s/[ ][ ]*/ /g s/.*supported targets: *// s/ .*// p'` case "$ld_supported_targets" in elf32-i386) TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" ;; a.out-i386-linux) echo "${UNAME_MACHINE}-pc-linux-gnuaout" exit 0 ;; coff-i386) echo "${UNAME_MACHINE}-pc-linux-gnucoff" exit 0 ;; "") # Either a pre-BFD a.out linker (linux-gnuoldld) or # one that does not give us useful --help. echo "${UNAME_MACHINE}-pc-linux-gnuoldld" exit 0 ;; esac # Determine whether the default compiler is a.out or elf eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include #ifdef __ELF__ # ifdef __GLIBC__ # if __GLIBC__ >= 2 LIBC=gnu # else LIBC=gnulibc1 # endif # else LIBC=gnulibc1 # endif #else #ifdef __INTEL_COMPILER LIBC=gnu #else LIBC=gnuaout #endif #endif #ifdef __dietlibc__ LIBC=dietlibc #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit 0 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit 0 ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit 0 ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit 0 ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit 0 ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit 0 ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit 0 ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit 0 ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit 0 ;; i*86:*:5:[78]*) case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit 0 ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit 0 ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i386. echo i386-pc-msdosdjgpp exit 0 ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit 0 ;; paragon:*:*:*) echo i860-intel-osf1 exit 0 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit 0 ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit 0 ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit 0 ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit 0 ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && echo i486-ncr-sysv4.3${OS_REL} && exit 0 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && echo i486-ncr-sysv4 && exit 0 ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit 0 ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit 0 ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit 0 ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit 0 ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit 0 ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit 0 ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit 0 ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit 0 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit 0 ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit 0 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit 0 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit 0 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit 0 ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit 0 ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit 0 ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit 0 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit 0 ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit 0 ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit 0 ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit 0 ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit 0 ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit 0 ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit 0 ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit 0 ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit 0 ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in *86) UNAME_PROCESSOR=i686 ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit 0 ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit 0 ;; *:QNX:*:4*) echo i386-pc-qnx exit 0 ;; NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit 0 ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit 0 ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit 0 ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit 0 ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit 0 ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit 0 ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit 0 ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit 0 ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit 0 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit 0 ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit 0 ;; *:ITS:*:*) echo pdp10-unknown-its exit 0 ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit 0 ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit 0 ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms && exit 0 ;; I*) echo ia64-dec-vms && exit 0 ;; V*) echo vax-dec-vms && exit 0 ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit 0 ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit 0 ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit 0 ;; c34*) echo c34-convex-bsd exit 0 ;; c38*) echo c38-convex-bsd exit 0 ;; c4*) echo c4-convex-bsd exit 0 ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: wpeditor-2.18/Makefile.am0000644000175000001440000000015110637734303014563 0ustar stevenusersSUBDIRS = src EXTRA_DIST = \ COPYING pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = wpeditor.pc wpeditor-2.18/src/0000755000175000001440000000000010772655066013331 5ustar stevenuserswpeditor-2.18/src/color_buffer.c0000644000175000001440000001314410647343013016132 0ustar stevenusers/** * @file color_buffer.c * Implementation for GTK color tag handling. */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "color_buffer.h" #include ColorBuffer * color_buffer_create(GtkTextBuffer * text_buffer, const gchar * tag_attribute, gint size) { ColorBuffer *color_buffer = NULL; color_buffer = g_new0(ColorBuffer, 1); color_buffer->text_buffer = text_buffer; color_buffer->tag_attribute = tag_attribute; color_buffer->size = size; color_buffer->current_size = 0; color_buffer->last = 0; color_buffer->elements = g_new0(ColorBufferElement, size); return color_buffer; } void color_buffer_destroy(ColorBuffer * color_buffer) { g_free(color_buffer->elements); color_buffer->text_buffer = NULL; /* Tag attribute string is not freed. */ color_buffer->tag_attribute = NULL; color_buffer->elements = NULL; color_buffer->size = 0; color_buffer->current_size = 0; color_buffer->last = 0; g_free(color_buffer); } void color_buffer_add(ColorBuffer * color_buffer, const GdkColor * color, GtkTextTag * tag) { gint current_size = 0; gint index = 0; /* Do not allow items with NULL tag. */ current_size = color_buffer->current_size; if (current_size < color_buffer->size) { color_buffer->elements[current_size].color = *color; color_buffer->elements[current_size].tag = tag; color_buffer->last = current_size; color_buffer->current_size++; } else { index = color_buffer->last + 1; if (index >= color_buffer->size) { index = 0; } color_buffer->elements[index].color = *color; color_buffer->elements[index].tag = tag; color_buffer->last = index; color_buffer->current_size = color_buffer->size; } } int color_buffer_compare_elements(const void *elem1, const void *elem2) { const ColorBufferElement *el1 = NULL; const ColorBufferElement *el2 = NULL; el1 = (const ColorBufferElement *) elem1; el2 = (const ColorBufferElement *) elem2; if (el1->color.red < el2->color.red) { return -1; } else if (el1->color.red > el2->color.red) { return 1; } else if (el1->color.green < el2->color.green) { return -1; } else if (el1->color.green > el2->color.green) { return 1; } else if (el1->color.blue < el2->color.blue) { return -1; } else if (el1->color.blue > el2->color.blue) { return 1; } else if (el1->color.pixel < el2->color.pixel) { return -1; } else if (el1->color.pixel > el2->color.pixel) { return 1; } else { return 0; } } ColorBufferElement * color_buffer_search(ColorBuffer * color_buffer, const GdkColor * color) { ColorBufferElement *element = NULL; ColorBufferElement target; size_t current_size = 0; target.color = *color; target.tag = NULL; current_size = color_buffer->current_size; element = lfind(&target, color_buffer->elements, ¤t_size, sizeof(ColorBufferElement), color_buffer_compare_elements); return element; } GtkTextTag * color_buffer_query_tag(ColorBuffer * color_buffer, const GdkColor * color) { ColorBufferElement *element = NULL; element = color_buffer_search(color_buffer, color); if (element != NULL) { return element->tag; } else { return NULL; } } GtkTextTag * color_buffer_create_tag(ColorBuffer * color_buffer, const GdkColor * color, gint priority) { GtkTextTag *tag = NULL; GtkTextTagTable *tbl = gtk_text_buffer_get_tag_table(color_buffer->text_buffer); /* Does the tag have a copy of the color? */ gchar *tmp = g_strdup_printf("wp-text-color-%02x%02x%02x", color->red / 256, color->green / 256, color->blue / 256); tag = gtk_text_tag_table_lookup(tbl, tmp); if (!tag) tag = gtk_text_buffer_create_tag(color_buffer->text_buffer, tmp, color_buffer->tag_attribute, color, NULL); g_free(tmp); gtk_text_tag_set_priority(tag, priority); return tag; } GtkTextTag * color_buffer_get_tag(ColorBuffer * color_buffer, const GdkColor * color, gint priority) { ColorBufferElement *element = NULL; GtkTextTag *tag = NULL; element = color_buffer_search(color_buffer, color); if (element != NULL) { return element->tag; } else { tag = color_buffer_create_tag(color_buffer, color, priority); color_buffer_add(color_buffer, color, tag); return tag; } } wpeditor-2.18/src/gtksourceiter.h0000644000175000001440000000404510647343013016362 0ustar stevenusers/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * gtksourceiter.h Copyright (C) 2000, 2002 Paolo Maggi Copyright (C) 2002, * 2003 Jeroen Zwartepoorte This program is free software; you can * redistribute it and/or modify it under the terms of the GNU Library * General Public License as published by the Free Software Foundation; * either version 2 of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public * License for more details. You should have received a copy of the GNU * Library General Public License along with this program; if not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ #ifndef __GTK_SOURCE_ITER_H__ #define __GTK_SOURCE_ITER_H__ #include G_BEGIN_DECLS typedef enum { GTK_SOURCE_SEARCH_VISIBLE_ONLY = 1 << 0, GTK_SOURCE_SEARCH_TEXT_ONLY = 1 << 1, GTK_SOURCE_SEARCH_CASE_INSENSITIVE = 1 << 2 /* Possible future plans: SEARCH_REGEXP */ } GtkSourceSearchFlags; gboolean gtk_source_iter_forward_search(const GtkTextIter * iter, const gchar * str, GtkSourceSearchFlags flags, GtkTextIter * match_start, GtkTextIter * match_end, const GtkTextIter * limit); gboolean gtk_source_iter_backward_search(const GtkTextIter * iter, const gchar * str, GtkSourceSearchFlags flags, GtkTextIter * match_start, GtkTextIter * match_end, const GtkTextIter * limit); G_END_DECLS #endif /* __GTK_SOURCE_ITER_H__ */ wpeditor-2.18/src/wpundo.h0000644000175000001440000001761510647343013015013 0ustar stevenusers/** * @file wpundo.h * * Header file for undo/redo functionality */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #ifndef _WP_UNDO_H #define _WP_UNDO_H #include G_BEGIN_DECLS #define WP_TYPE_UNDO (wp_undo_get_type ()) #define WP_UNDO(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WP_TYPE_UNDO, WPUndo)) #define WP_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WP_TYPE_UNDO, WPUndoClass)) #define WP_IS_UNDO(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WP_TYPE_UNDO)) #define WP_IS_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WP_TYPE_UNDO)) #define WP_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WP_TYPE_UNDO, WPUndoClass)) typedef struct _WPUndoPrivate WPUndoPrivate; typedef struct _WPUndo WPUndo; typedef struct _WPUndoClass WPUndoClass; /** Undo object */ struct _WPUndo { GObject base; WPUndoPrivate *priv; }; /** Undo class */ struct _WPUndoClass { GObjectClass parent_class; /** * Called when the undo state changed * @param undo pointer to the undo object * @param can_undo is enabled when there is something to undo */ void (*can_undo) (WPUndo * undo, gboolean can_undo); /** * Called when the redo state changed * @param undo pointer to the undo object * @param can_redo is enabled when there is something to redo */ void (*can_redo) (WPUndo * undo, gboolean can_redo); /** * Called when the formatting has changed from rich text to normal text or * viceversa * @param undo pointer to the undo object * @param rich_text enabled when the buffer contains rich text */ void (*fmt_changed) (WPUndo * undo, gboolean rich_text); /** * Called when the last line justification changed * @param undo pointer to the undo object * @param last_line_justification can be GTK_JUSTIFY_LEFT, * GTK_JUSTIFY_CENTER, GTK_JUSTIFY_RIGHT */ void (*last_line_justify) (WPUndo * undo, gint last_line_justification); /** * Called when there is not enough memory to perform an operation * @param undo pointer to the undo object */ void (*no_memory) (WPUndo * undo); }; GType wp_undo_get_type(void) G_GNUC_CONST; /** * Create a new undo object for the given buffer * @param buffer a gtk text buffer * @return the newly created undo object */ WPUndo *wp_undo_new(GtkTextBuffer * buffer); /** * Destroy the given undo object * @param undo pointer to the undo object */ void wp_undo_free(WPUndo * undo); /** * Querys the undo capability * @param undo pointer to the undo object * @return TRUE if the undo queue is not empty */ gboolean wp_undo_can_undo(const WPUndo * undo); /** * Querys the redo capability * @param undo pointer to the undo object * @return TRUE if the redo queue is not empty */ gboolean wp_undo_can_redo(const WPUndo * undo); /** * Undo the last operation * @param undo pointer to the undo object */ void wp_undo_undo(WPUndo * undo); /** * Redo the last operation * @param undo pointer to the undo object */ void wp_undo_redo(WPUndo * undo); /** * Freeze the undo buffer. Can be called several times. * In this time the undo queue is not registering new actions. * @param undo pointer to the undo object */ void wp_undo_freeze(WPUndo * undo); /** * Unfreeze the undo buffer. Can be called several times. * @param undo pointer to the undo object */ void wp_undo_thaw(WPUndo * undo); /** * Start a group undo operation. The group operations will be * handled as a single operation at undo/redo step. * @param undo pointer to the undo object */ void wp_undo_start_group(WPUndo * undo); /** * Ends a group undo operation. * @param undo pointer to the undo object */ void wp_undo_end_group(WPUndo * undo); /** * Register a new insert operation to the undo queue * @param undo pointer to the undo object * @param pos contains the position where the insert happended * @param text contains a pointer to the inserted text * @param length contains the length of the inserted text */ void wp_undo_insert_text(WPUndo * undo, GtkTextIter * pos, const gchar * text, gint length); /** * Register a new delete operation to the undo queue * @param undo pointer to the undo object * @param start contains the start position of the delete * @param end contains the end position of the delete */ void wp_undo_delete_range(WPUndo * undo, GtkTextIter * start, GtkTextIter * end); /** * Register a new tag change operation to the undo queue * @param undo pointer to the undo object * @param start contains the start position of the tag * @param end contains the end position of the tag * @param tag contains the tag which has been applied/removed * @param enable TRUE if the tag has been applied */ void wp_undo_apply_tag(WPUndo * undo, const GtkTextIter * start, const GtkTextIter * end, GtkTextTag * tag, gboolean enable); /** * Register a new justification change operation to the undo queue. * This usually happens when a two lines were merged * @param undo pointer to the undo object * @param start contains the start position of the tag * @param end contains the end position of the tag * @param orig_tag contains the old justification tag * @param tag contains the new justification tag */ void wp_undo_simple_justification(WPUndo * undo, GtkTextIter * start, GtkTextIter * end, GtkTextTag * orig_tag, GtkTextTag * tag); /** * Register a new selection change operation to the undo queue. * @param undo pointer to the undo object * @param start contains the start position of the selection * @param end contains the end position of the selection */ void wp_undo_selection_changed(WPUndo * undo, GtkTextIter * start, GtkTextIter * end); /** * Register a new format change operation to the undo queue. * @param undo pointer to the undo object * @param rich_text is TRUE if the new buffer contains rich text */ void wp_undo_format_changed(WPUndo * undo, gboolean rich_text); /** * Register a last line justification change operation to the undo queue. * @param undo pointer to the undo object * @param old_line_justify contains the old line justification * @param new_line_justify contains the new line justification */ void wp_undo_last_line_justify(WPUndo * undo, gint old_line_justify, gint new_line_justify); /** * Queries the undo enable state * @param undo pointer to the undo object * @return TRUE if undo is enabled */ gboolean wp_undo_is_enabled(WPUndo * undo); /** * Set the last undo operation as not mergeable * @param undo pointer to the undo object */ void wp_undo_reset_mergeable(const WPUndo * undo); /** * Clear the undo and redo queues * @param undo pointer to the undo object */ void wp_undo_reset(WPUndo * undo); G_END_DECLS #endif /* _WP_UNDO_H */ wpeditor-2.18/src/color_buffer.h0000644000175000001440000000777510647343013016154 0ustar stevenusers/** * @file color_buffer.h * Header file for GTK color tag handling. */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef COLOR_BUFFER_H #define COLOR_BUFFER_H /*GTK*/ #include #include typedef struct { GdkColor color; GtkTextTag *tag; } ColorBufferElement; typedef struct { GtkTextBuffer *text_buffer; const gchar *tag_attribute; gint size; gint current_size; gint last; ColorBufferElement *elements; } ColorBuffer; /** Create a ColorBuffer object. @param text_buffer GtkTextBuffer object @param tag_attribute GTK attribute name for tags @param size size of the color buffer @return created ColorBuffer object */ ColorBuffer *color_buffer_create(GtkTextBuffer * text_buffer, const gchar * tag_attribute, gint size); /** Destroy a ColorBuffer object @param color_buffer ColorBuffer object */ void color_buffer_destroy(ColorBuffer * color_buffer); /** Add a pair consisting a color and GTK tag to a color buffer @param color_buffer ColorBuffer object @param color GDK color @param tag GTK tag for the color */ void color_buffer_add(ColorBuffer * color_buffer, const GdkColor * color, GtkTextTag * tag); /** Compare two ColorBufferElement objects. Used internally for searching. @param elem1 element 1 @param elem2 element 2 @return -1 if element 1 < element 2 0 if element 1 == element 2 1 if element 1 > element 2 */ int color_buffer_compare_elements(const void *elem1, const void *elem2); /** Search an element from a color buffer by color @param color_buffer ColorBuffer object @param color GDK color @return pointer to the element in the color buffer NULL if no element for the given color is not found in the color buffer */ ColorBufferElement *color_buffer_search(ColorBuffer * color_buffer, const GdkColor * color); /** Query a tag from a color buffer @param color_buffer ColorBuffer object @param color GDK color @return GTK tag for the color NULL if the color is not found in the color buffer */ GtkTextTag *color_buffer_query_tag(ColorBuffer * color_buffer, const GdkColor * color); /** Create a color tag. The created GTK tag has attribute given by color_buffer->tag_attribute set to the color. @param color_buffer ColorBuffer object @param color GDK color @param priority is the priority of the created tag in the tag table @return GTK tag for the color */ GtkTextTag *color_buffer_create_tag(ColorBuffer * color_buffer, const GdkColor * color, gint priority); /** Get a color tag from a color buffer. If a tag for the given color is found in the buffer it is returned. Otherwise a new color tag is created, stored in the color buffer, and returned. @param color_buffer ColorBuffer object @param color GDK color @param priority is the priority of the created tag in the tag table @return GTK tag for the color */ GtkTextTag *color_buffer_get_tag(ColorBuffer * color_buffer, const GdkColor * color, gint priority); #endif wpeditor-2.18/src/wptextview.h0000644000175000001440000000435610647343013015723 0ustar stevenusers/** * @file wptextview.h * * Header file for WordPad Text View */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #ifndef _WP_TEXT_VIEW_H #define _WP_TEXT_VIEW_H #include G_BEGIN_DECLS #define WP_TYPE_TEXT_VIEW (wp_text_view_get_type ()) #define WP_TEXT_VIEW(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WP_TYPE_TEXT_VIEW, WPTextView)) #define WP_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WP_TYPE_TEXT_VIEW, WPTextViewClass)) #define WP_IS_TEXT_VIEW(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WP_TYPE_TEXT_VIEW)) #define WP_IS_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WP_TYPE_TEXT_VIEW)) #define WP_TEXT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WP_TYPE_TEXT_VIEW, WPTextViewClass)) typedef struct _WPTextView WPTextView; typedef struct _WPTextViewPrivate WPTextViewPrivate; typedef struct _WPTextViewClass WPTextViewClass; /** WPTextView object */ struct _WPTextView { GtkTextView parent; gint mx, my; gboolean in_action; }; /** WPTextView class */ struct _WPTextViewClass { GtkTextViewClass parent_class; }; GType wp_text_view_get_type(void) G_GNUC_CONST; /** * Creates a new #WPTextView object * @return the newly created object */ GtkWidget *wp_text_view_new(void); void wp_text_view_reset_and_show_im(WPTextView * view); G_END_DECLS #endif /* _WP_TEXT_VIEW_H */ wpeditor-2.18/src/wpundo.c0000644000175000001440000013676710755017015015020 0ustar stevenusers/** * @file wpundo.c * * Implementation file for undo/redo functionality */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #include #include #include #include #include "wpundo.h" #include "wptextbuffer.h" #include "wptextbuffer-private.h" /** Minimum undo level */ #define MIN_UNDO_LEVEL 5 /** Maximum undo level */ #define MAX_UNDO_LEVEL 200 /** Default undo level */ #define DEF_UNDO_LEVEL 5 /** Type of the undo operation */ typedef enum { WP_UNDO_INSERT, WP_UNDO_DELETE, WP_UNDO_TAG, WP_UNDO_SIMPLE_JUSTIFY, WP_UNDO_SELECT, WP_UNDO_FMT, WP_UNDO_LAST_LINE_JUSTIFY, } WPUndoType; /** An undo operation type */ typedef struct { WPUndoType type; gint start; gint end; gchar *text; GtkTextTag *orig_tag; GtkTextTag *tag; GSList *orig_tags; GSList *tags; gint sel_start; gint sel_end; gint old_line_justify; gint new_line_justify; gchar mergeable:1; gchar backspace:1; gchar rich_text:1; } WPUndoOperation; /** Apply tag type undo operation */ typedef struct { gboolean apply; gint start; gint end; GtkTextTag *tag; } WPUndoTag; /** Private structure to pass as userdata to gtk+ functions */ typedef struct { GSList *tags; const GtkTextIter *end; } WPHashData; /** The object's private variables */ struct _WPUndoPrivate { /** Contain a GSList of WPUndoOperations, used for undo */ GSList *undo_queue; /** Contain a GSList of WPUndoOperations, used for redo */ GSList *redo_queue; /** Hash table used for finding the toggled tags in the buffer */ GHashTable *hash; /** Contain the recent added operation list */ GSList *current_op_list; /** Contain the recent added operation */ WPUndoOperation *current_op; /** True if the last inserted character was a whitespace */ gint last_char_is_space:1; /** True if the operation is the first operation after group start */ gint first_in_group:1; /** Reference to the group number. If >0 then a group is started */ gint group; /** Reference to the undo disabled variable. If >0 then the undo is disabled */ gint undo_disabled; /** Maximum undo level */ gint max_undo_level; /** Current undo queue length */ gint undo_queue_len; /** Last undo status signaled */ gint undo_sent:1; /** Last redo status signaled */ gint redo_sent:1; /** True if there is a low memory situation. In this the undo is disabled */ gint low_mem:1; /** True if there was not enough memory during inserting a new operation to the * undo queue. If is enabled, no more operations will be registered until the group * is ended */ gint disable_this_group:1; /** Pointer a #GtkTextBuffer associated with the undo */ GtkTextBuffer *text_buffer; }; /** Signals */ enum { /** Sent when undo state has changed */ CAN_UNDO, /** Sent when redo state has changed */ CAN_REDO, /** Sent when formatting has changed (rich text <-> plain text) */ FMT_CHANGED, /** Sent when last line justification has changed */ LAST_LINE_JUSTIFY, /** Sent when there is not enough memory for the current operation */ NO_MEMORY, LAST_SIGNAL }; /** Properties */ enum { PROP_0, /** R/W. Pointer. Specify a GtkTextBuffer */ PROP_DOCUMENT, /** R/W. Integer. Specify the number of undo levels */ PROP_UNDO_LEVEL, /** R/W. Boolean. Specify if there is a low memory situation */ PROP_LOW_MEM }; static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE(WPUndo, wp_undo, G_TYPE_OBJECT); static void wp_undo_finalize(GObject * object); static void wp_undo_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void wp_undo_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); /** * Free a #GSList of WPUndoTags * @param tags contains the list of tags * @return NULL */ static GSList *wp_undo_free_tags(GSList * tags); /** * Free an operation list. An operation list contains a #GSList of * #GSList of WPUndoTags. * @param queue pointer to the #GSList * @return NULL */ static GSList *wp_undo_free_op_list(GSList * queue); /** * Add a new operation to the undo queue * @param undo pointer to the undo object * @param op contains the operation to be added */ static void wp_undo_add_queue(WPUndo * undo, WPUndoOperation * op); /** * Create a new WPUndoTag from the given parameters. * @param start is the start offset the tag * @param end contains the end iterator of the applied tag * @param tag contains the tag itself * @param enable is TRUE if the tag is applied on the range * @return a newly allocated WPUndoTag */ static WPUndoTag *wp_undo_create_tag(gint start, const GtkTextIter * end, GtkTextTag * tag, gboolean enable); /** * Send the undo and redo signals if the state of the queue has changed from * the last call. * @param undo pointer to the undo object */ static void wp_undo_send_signals(const WPUndo * undo); /** * Send the no memory signal. * @param undo pointer to the undo object */ static void emit_no_memory(WPUndo * undo); static void wp_undo_class_init(WPUndoClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->set_property = wp_undo_set_property; object_class->get_property = wp_undo_get_property; object_class->finalize = wp_undo_finalize; klass->can_undo = NULL; klass->can_redo = NULL; klass->fmt_changed = NULL; klass->last_line_justify = NULL; klass->no_memory = NULL; g_object_class_install_property(object_class, PROP_DOCUMENT, g_param_spec_pointer("document", "document", "GtkTextView document on which the undo/redo is working", G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_UNDO_LEVEL, g_param_spec_int("undo_levels", "undo_levels", "Maximum undo levels allowed", MIN_UNDO_LEVEL, MAX_UNDO_LEVEL, DEF_UNDO_LEVEL, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_LOW_MEM, g_param_spec_boolean("low_memory", "low_memory", "Low memory situation (undo disabled)", FALSE, G_PARAM_READWRITE)); signals[CAN_UNDO] = g_signal_new("can_undo", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPUndoClass, can_undo), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[CAN_REDO] = g_signal_new("can_redo", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPUndoClass, can_redo), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[FMT_CHANGED] = g_signal_new("fmt_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPUndoClass, fmt_changed), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[LAST_LINE_JUSTIFY] = g_signal_new("last_line_justify", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPUndoClass, last_line_justify), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); signals[NO_MEMORY] = g_signal_new("no_memory", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPUndoClass, no_memory), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void wp_undo_init(WPUndo * undo) { undo->priv = g_new0(WPUndoPrivate, 1); undo->priv->max_undo_level = DEF_UNDO_LEVEL; undo->priv->hash = g_hash_table_new(g_direct_hash, g_direct_equal); } static void wp_undo_finalize(GObject * object) { WPUndo *undo; undo = WP_UNDO(object); undo->priv->undo_queue = wp_undo_free_op_list(undo->priv->undo_queue); undo->priv->redo_queue = wp_undo_free_op_list(undo->priv->redo_queue); g_hash_table_destroy(undo->priv->hash); g_free(undo->priv); G_OBJECT_CLASS(wp_undo_parent_class)->finalize(object); } static void wp_undo_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { WPUndo *undo = WP_UNDO(object); gint new_size, size, qsize; GSList *tmp; switch (prop_id) { case PROP_DOCUMENT: undo->priv->text_buffer = g_value_get_pointer(value); break; case PROP_UNDO_LEVEL: new_size = g_value_get_int(value); if ((size = new_size - undo->priv->max_undo_level) < 0) { size = -size; qsize = g_slist_length(undo->priv->redo_queue); if (qsize > size) { tmp = g_slist_nth(undo->priv->redo_queue, qsize - size - 1); tmp->next = wp_undo_free_op_list(tmp->next); size = 0; } else { undo->priv->redo_queue = wp_undo_free_op_list(undo->priv->redo_queue); size -= qsize; } if (size > 0) { if (undo->priv->undo_queue_len > size) { tmp = g_slist_nth(undo->priv->redo_queue, undo->priv->undo_queue_len - size - 1); tmp->next = wp_undo_free_op_list(tmp->next); undo->priv->undo_queue_len -= size; } else { undo->priv->undo_queue = wp_undo_free_op_list(undo->priv->undo_queue); undo->priv->undo_queue_len = 0; } } } undo->priv->max_undo_level = new_size; wp_undo_send_signals(undo); break; case PROP_LOW_MEM: undo->priv->low_mem = g_value_get_boolean(value); if (undo->priv->low_mem) wp_undo_reset(undo); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void wp_undo_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { WPUndo *undo = WP_UNDO(object); switch (prop_id) { case PROP_DOCUMENT: g_value_set_pointer(value, undo->priv->text_buffer); break; case PROP_UNDO_LEVEL: g_value_set_int(value, undo->priv->max_undo_level); break; case PROP_LOW_MEM: g_value_set_boolean(value, undo->priv->low_mem != FALSE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } WPUndo * wp_undo_new(GtkTextBuffer * buffer) { WPUndo *undo = WP_UNDO(g_object_new(WP_TYPE_UNDO, NULL)); undo->priv->text_buffer = buffer; return undo; } void wp_undo_freeze(WPUndo * undo) { g_return_if_fail(WP_IS_UNDO(undo)); ++undo->priv->undo_disabled; } void wp_undo_thaw(WPUndo * undo) { g_return_if_fail(WP_IS_UNDO(undo)); --undo->priv->undo_disabled; } gboolean wp_undo_is_enabled(WPUndo * undo) { g_return_val_if_fail(WP_IS_UNDO(undo), FALSE); return undo->priv->undo_disabled == 0 && undo->priv->low_mem == FALSE; } void wp_undo_start_group(WPUndo * undo) { g_return_if_fail(WP_IS_UNDO(undo)); if (++undo->priv->group == 1) { undo->priv->first_in_group = TRUE; undo->priv->disable_this_group = FALSE; } } void wp_undo_end_group(WPUndo * undo) { g_return_if_fail(WP_IS_UNDO(undo)); if (--undo->priv->group == 0) undo->priv->disable_this_group = FALSE; } static void wp_undo_send_signals(const WPUndo * undo) { gboolean enable = undo->priv->low_mem == FALSE; if (undo->priv->redo_sent != (undo->priv->redo_queue != NULL) && enable) { undo->priv->redo_sent = undo->priv->redo_queue != NULL && enable; g_signal_emit(G_OBJECT(undo), signals[CAN_REDO], 0, undo->priv->redo_sent); } if (undo->priv->undo_sent != (undo->priv->undo_queue != NULL) && enable) { undo->priv->undo_sent = undo->priv->undo_queue != NULL && enable; g_signal_emit(G_OBJECT(undo), signals[CAN_UNDO], 0, undo->priv->undo_sent); } } gboolean wp_undo_can_undo(const WPUndo * undo) { g_return_val_if_fail(WP_IS_UNDO(undo), FALSE); return undo->priv->undo_queue != NULL && undo->priv->low_mem == FALSE; } gboolean wp_undo_can_redo(const WPUndo * undo) { g_return_val_if_fail(WP_IS_UNDO(undo), FALSE); return undo->priv->redo_queue != NULL && undo->priv->low_mem == FALSE; } void wp_undo_reset_mergeable(const WPUndo * undo) { g_return_if_fail(WP_IS_UNDO(undo)); if (undo->priv->current_op) { undo->priv->current_op->mergeable = FALSE; undo->priv->current_op = NULL; undo->priv->current_op_list = NULL; } } /** * Apply a #GSList of WPUndoTag to a #GtkTextBuffer. * @param buffer is a #GtkTextBuffer associated with the undo * @param tags pointer to the list of tags */ static void wp_undo_apply_saved_tags(GtkTextBuffer * buffer, GSList * tags) { WPUndoTag *tag; GtkTextIter s, e; GSList *current = tags; /* First iteration will apply the remove tag commands, second iteration * will apply the add tag commands. Sometimes there's an apply and * a removed for the same tag in the taglist (it can come from incorrect * use of changesets in wp_text_buffer_set_format). If the apply is the * first in the list and remove is the second (which can be the case with * red), the property belonging to that tag would be restored to it's * default. To avoid this, we can first execute the remove, then the apply * operations, or we can maintain the taglist. Since the second one is * more error-prone, and requires more resources, the first approach is * used. */ while (current) { tag = (WPUndoTag *) current->data; if (!tag->apply) { gtk_text_buffer_get_iter_at_offset(buffer, &s, tag->start); gtk_text_buffer_get_iter_at_offset(buffer, &e, tag->end); gtk_text_buffer_remove_tag(buffer, tag->tag, &s, &e); } current = current->next; } current = tags; while (current) { tag = (WPUndoTag *) current->data; if (tag->apply) { gchar *tag_name; g_object_get (G_OBJECT (tag->tag), "name", &tag_name, NULL); gtk_text_buffer_get_iter_at_offset(buffer, &s, tag->start); gtk_text_buffer_get_iter_at_offset(buffer, &e, tag->end); if (tag_name != NULL && g_str_has_prefix (tag_name, "image-tag-") && !g_str_has_prefix (tag_name, "image-tag-replace-")) { gchar *new_name; const gchar *image_id; image_id = g_object_get_data (G_OBJECT (tag->tag), "image-index"); if (image_id != NULL) { GtkTextTagTable *tag_table; gtk_text_buffer_remove_tag (buffer, tag->tag, &s, &e); new_name = g_strdup_printf ("image-tag-replace-%s", image_id); tag_table = gtk_text_buffer_get_tag_table (buffer); tag->tag = gtk_text_tag_table_lookup (tag_table, new_name); if (tag->tag == NULL) tag->tag = gtk_text_buffer_create_tag (buffer, new_name, NULL); g_free (new_name); gtk_text_buffer_apply_tag (buffer, tag->tag, &s, &e); } } else { gtk_text_buffer_apply_tag(buffer, tag->tag, &s, &e); } } current = current->next; } } /** * Redo/Undo a selection. If there is something selected in the textbuffer * and the undo/redo operation is selecting the same text, then skip that * undo/redo. If the next operation is also a selection, apply that, otherwise * unselect the text. * @param text_buffer is a #GtkTextBuffer associated with the undo * @param queue contains either the undo or redo queue * @param op contains the current undo/redo operation * @param first if the function was started for the first time */ static void wp_undo_redo_selection(GtkTextBuffer * text_buffer, GSList * queue, WPUndoOperation * op, gboolean first) { GtkTextIter start, end, sstart, send; WPUndoOperation *pop; gboolean repeat = FALSE; gtk_text_buffer_get_selection_bounds(text_buffer, &sstart, &send); if (op->type == WP_UNDO_SELECT) { gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->sel_start); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, op->sel_end); } if (gtk_text_iter_equal(&sstart, &start) && gtk_text_iter_equal(&send, &end)) { if (queue && queue->data) { pop = (WPUndoOperation *) ((GSList *) queue->data)->data; repeat = first && pop && pop->type == WP_UNDO_SELECT; } if (!repeat) gtk_text_buffer_place_cursor(text_buffer, &end); else wp_undo_redo_selection(text_buffer, queue, pop, FALSE); } else gtk_text_buffer_select_range(text_buffer, &start, &end); } /** * Skip a bullet tag at the cursor position if there is any. * @param text_buffer is a #GtkTextBuffer associated with the undo */ static void wp_undo_skip_bullet_at_cursor(GtkTextBuffer * text_buffer) { WPTextBuffer *buffer = WP_TEXT_BUFFER(text_buffer); GtkTextTag *tag = _wp_text_buffer_get_bullet_tag(buffer); GtkTextIter pos; gtk_text_buffer_get_iter_at_mark(text_buffer, &pos, gtk_text_buffer_get_insert(text_buffer)); if (_wp_text_iter_skip_bullet(&pos, tag, TRUE)) gtk_text_buffer_place_cursor(text_buffer, &pos); } void wp_undo_undo(WPUndo * undo) { WPUndoOperation *op = NULL; GSList *lact; GtkTextBuffer *text_buffer; GtkTextIter start, end; gint proposed_cursor_pos = -1; g_return_if_fail(WP_IS_UNDO(undo)); if (!undo->priv->undo_queue || undo->priv->low_mem) return; wp_undo_freeze(undo); gtk_text_buffer_begin_user_action(undo->priv->text_buffer); text_buffer = undo->priv->text_buffer; lact = undo->priv->undo_queue; undo->priv->undo_queue = undo->priv->undo_queue->next; lact->next = undo->priv->redo_queue; undo->priv->redo_queue = lact; undo->priv->undo_queue_len--; lact = (GSList *) lact->data; while (lact) { op = (WPUndoOperation *) lact->data; lact = lact->next; g_return_if_fail(op != NULL); switch (op->type) { case WP_UNDO_DELETE: gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); gtk_text_buffer_insert(text_buffer, &start, op->text, (int) strlen(op->text)); end = start; gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); proposed_cursor_pos = op->backspace ? op->end : op->start; gtk_text_buffer_remove_all_tags(text_buffer, &start, &end); wp_undo_apply_saved_tags(text_buffer, op->tags); break; case WP_UNDO_INSERT: gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, op->end); /* Bullet are also handled in insert */ if( gtk_text_iter_toggles_tag( &start, _wp_text_buffer_get_bullet_tag( WP_TEXT_BUFFER( text_buffer ) ) ) ) { GtkTextIter at = start; while( gtk_text_iter_ends_line( &at ) ) { ++proposed_cursor_pos; gtk_text_buffer_get_iter_at_offset(text_buffer, &at, 1 ); } } else { proposed_cursor_pos = op->start; } gtk_text_buffer_delete(text_buffer, &start, &end); break; case WP_UNDO_TAG: gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, op->end); gtk_text_buffer_select_range(text_buffer, &start, &end); gtk_text_buffer_remove_all_tags(text_buffer, &start, &end); wp_undo_apply_saved_tags(text_buffer, op->orig_tags); break; case WP_UNDO_SELECT: wp_undo_redo_selection(text_buffer, undo->priv->undo_queue, op, TRUE); break; case WP_UNDO_FMT: if (!op->rich_text) { GtkTextIter at; /* Mark current position */ GtkTextMark * mark = gtk_text_buffer_get_insert( text_buffer ); wp_undo_apply_saved_tags(text_buffer, op->tags); /* Set focus to current position */ gtk_text_buffer_get_iter_at_mark( text_buffer, &at, mark ); gtk_text_buffer_place_cursor( text_buffer, &at ); /* Undo changes to cursor pos */ proposed_cursor_pos = -1; } else { gtk_text_buffer_get_bounds(text_buffer, &start, &end); gtk_text_buffer_remove_all_tags(text_buffer, &start, &end); } g_signal_emit(G_OBJECT(undo), signals[FMT_CHANGED], 0, !op->rich_text); break; case WP_UNDO_SIMPLE_JUSTIFY: gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, op->end); if (op->tag) gtk_text_buffer_remove_tag(text_buffer, op->tag, &start, &end); gtk_text_buffer_apply_tag(text_buffer, op->orig_tag, &start, &end); break; case WP_UNDO_LAST_LINE_JUSTIFY: g_signal_emit(G_OBJECT(undo), signals[LAST_LINE_JUSTIFY], 0, op->old_line_justify); break; default: g_warning ("wp_undo_undo. Unknown undo tag. This should not happen."); return; } } if (proposed_cursor_pos != -1) { gtk_text_buffer_get_iter_at_offset(text_buffer, &start, proposed_cursor_pos); gtk_text_buffer_place_cursor(text_buffer, &start); } gtk_text_buffer_end_user_action(undo->priv->text_buffer); wp_undo_thaw(undo); wp_undo_reset_mergeable(undo); wp_undo_send_signals(undo); } void wp_undo_redo(WPUndo * undo) { WPUndoOperation *op = NULL; GSList *lact, *lact_head; GtkTextBuffer *text_buffer; GtkTextIter start, end; gint proposed_cursor_pos = -1; WPUndoTag *utag; g_return_if_fail(WP_IS_UNDO(undo)); if (!undo->priv->redo_queue || undo->priv->low_mem) return; wp_undo_freeze(undo); gtk_text_buffer_begin_user_action(undo->priv->text_buffer); text_buffer = undo->priv->text_buffer; lact = undo->priv->redo_queue; undo->priv->redo_queue = undo->priv->redo_queue->next; lact->next = undo->priv->undo_queue; undo->priv->undo_queue = lact; undo->priv->undo_queue_len++; lact = lact_head = g_slist_reverse((GSList *) lact->data); while (lact) { op = (WPUndoOperation *) lact->data; lact = lact->next; g_return_if_fail(op != NULL); switch (op->type) { case WP_UNDO_DELETE: gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, op->end); gtk_text_buffer_delete(text_buffer, &start, &end); proposed_cursor_pos = op->backspace ? op->end : op->start; break; case WP_UNDO_INSERT: gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); gtk_text_buffer_insert(text_buffer, &start, op->text, (int) strlen(op->text)); proposed_cursor_pos = op->end; wp_undo_apply_saved_tags(text_buffer, op->tags); break; case WP_UNDO_TAG: utag = (WPUndoTag *) op->tags->data; gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, op->end); gtk_text_buffer_select_range(text_buffer, &start, &end); wp_undo_apply_saved_tags(text_buffer, op->tags); break; case WP_UNDO_SELECT: wp_undo_redo_selection(text_buffer, undo->priv->redo_queue, op, TRUE); break; case WP_UNDO_FMT: if (op->rich_text) { wp_undo_apply_saved_tags(text_buffer, op->tags); wp_undo_skip_bullet_at_cursor(text_buffer); } else { gtk_text_buffer_get_bounds(text_buffer, &start, &end); gtk_text_buffer_remove_all_tags(text_buffer, &start, &end); } g_signal_emit(G_OBJECT(undo), signals[FMT_CHANGED], 0, op->rich_text); break; case WP_UNDO_SIMPLE_JUSTIFY: gtk_text_buffer_get_iter_at_offset(text_buffer, &start, op->start); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, op->end); gtk_text_buffer_remove_tag(text_buffer, op->orig_tag, &start, &end); if (op->tag) gtk_text_buffer_apply_tag(text_buffer, op->tag, &start, &end); break; case WP_UNDO_LAST_LINE_JUSTIFY: g_signal_emit(G_OBJECT(undo), signals[LAST_LINE_JUSTIFY], 0, op->new_line_justify); break; default: g_warning ("wp_undo_redo. Unknown undo tag. This should not happen."); return; } undo->priv->current_op = NULL; undo->priv->current_op_list = NULL; } lact_head = g_slist_reverse(lact_head); if (proposed_cursor_pos != -1) { gtk_text_buffer_get_iter_at_offset(text_buffer, &start, proposed_cursor_pos); gtk_text_buffer_place_cursor(text_buffer, &start); } gtk_text_buffer_end_user_action(undo->priv->text_buffer); wp_undo_thaw(undo); wp_undo_reset_mergeable(undo); wp_undo_send_signals(undo); } static GSList * wp_undo_free_tags(GSList * tags) { GSList *tmp = tags; while (tmp) { g_free(tmp->data); tmp = tmp->next; } g_slist_free(tmp); return NULL; } static GSList * wp_undo_free_op_list(GSList * queue) { GSList *tmp_queue; GSList *action_list, *tmp; WPUndoOperation *act; tmp_queue = queue; while (tmp_queue) { action_list = tmp = (GSList *) tmp_queue->data; tmp_queue = tmp_queue->next; while (tmp) { act = (WPUndoOperation *) tmp->data; tmp = tmp->next; if (act) { g_free(act->text); wp_undo_free_tags(act->orig_tags); wp_undo_free_tags(act->tags); } g_free(act); } g_slist_free(action_list); } g_slist_free(queue); return NULL; } static WPUndoTag * wp_undo_create_tag(gint start, const GtkTextIter * end, GtkTextTag * tag, gboolean enable) { WPUndoTag *result = g_new(WPUndoTag, 1); result->start = start; result->end = gtk_text_iter_get_offset(end); result->tag = tag; result->apply = enable; // printf("Create-tag: %s, %d-%d, %d\n", result->tag->name, // result->start, result->end, enable); return result; } /** * Callback to close the opened tags in the hash table. * @key pointer to a #GtkTextTag * @value contains the start offset of the tag * @user_data pointer to a #WPHashData structure * @return TRUE to remove the element from the table */ static gboolean wp_undo_close_opened_tags(gpointer key, gpointer value, gpointer user_data) { WPHashData *data = (WPHashData *) user_data; data->tags = g_slist_prepend(data->tags, wp_undo_create_tag(GPOINTER_TO_INT(value) - 1, data->end, GTK_TEXT_TAG(key), TRUE)); return TRUE; } /** * Creates a tag list for the specified range * @param undo pointer to the undo object * @param start contains the start iterator of the range * @param end contains the end iterator of the range * @return NULL */ static GSList * wp_undo_get_toggled_tags(WPUndo * undo, const GtkTextIter * start, const GtkTextIter * end) { GSList *tags = NULL; GHashTable *hash = undo->priv->hash; GtkTextIter tmp = *start; gint tmp_start; GSList *list, *list_head; WPHashData data; /* TODO maybe is enough to save only the real toggled tags */ /* list = list_head = gtk_text_iter_get_toggled_tags(&tmp, FALSE); while * (list) { tags = g_slist_prepend(tags, * wp_undo_create_tag(gtk_text_iter_get_offset(start), end, * GTK_TEXT_TAG(list->data), FALSE)); list = list->next; } * g_slist_free(list_head); */ // list = list_head = gtk_text_iter_get_toggled_tags(&tmp, TRUE); list = list_head = gtk_text_iter_get_tags(&tmp); while (list) { g_hash_table_insert(hash, list->data, GINT_TO_POINTER(gtk_text_iter_get_offset(&tmp) + 1)); list = list->next; } g_slist_free(list_head); while (gtk_text_iter_forward_to_tag_toggle(&tmp, NULL)) { if (gtk_text_iter_compare(&tmp, end) >= 0) break; list = list_head = gtk_text_iter_get_toggled_tags(&tmp, FALSE); while (list) { if ((tmp_start = GPOINTER_TO_INT(((GtkTextIter *) (g_hash_table_lookup(hash, list->data)))))) { tags = g_slist_prepend(tags, wp_undo_create_tag(tmp_start - 1, &tmp, GTK_TEXT_TAG (list->data), TRUE)); g_hash_table_remove(hash, list->data); } list = list->next; } g_slist_free(list_head); list = list_head = gtk_text_iter_get_toggled_tags(&tmp, TRUE); while (list) { g_hash_table_insert(hash, list->data, GINT_TO_POINTER(gtk_text_iter_get_offset (&tmp) + 1)); list = list->next; } g_slist_free(list_head); } data.tags = tags; data.end = end; g_hash_table_foreach_remove(hash, wp_undo_close_opened_tags, &data); return g_slist_reverse(data.tags); } /** * Modified the tag range in a #GSList of WPUndoTag * @param tags pointer to a #GSList of tags * @param start is the start offset of the tag * @param end is the end offset of the tag */ static void update_tags_range(GSList * tags, gint start, gint end) { GSList *iter = tags; WPUndoTag *tag; while (iter) { tag = (WPUndoTag *) iter->data; tag->start = start; tag->end = end; iter = iter->next; } } void wp_undo_insert_text(WPUndo * undo, GtkTextIter * pos, const gchar * text, gint length) { WPUndoOperation co = { 0 }; WPUndoOperation *op; WPUndoOperation *la; gboolean is_space = FALSE; g_return_if_fail(WP_IS_UNDO(undo)); if (undo->priv->undo_disabled > 0 || undo->priv->low_mem) return; g_return_if_fail(strlen(text) == (guint) length); la = undo->priv->current_op; co.start = gtk_text_iter_get_offset(pos); co.end = co.start + g_utf8_strlen(text, length); co.mergeable = !((co.end - co.start > 1) || (g_utf8_get_char(text) == '\n')); if (co.mergeable) is_space = g_unichar_isspace(g_utf8_get_char(text)); if (co.mergeable && la && la->mergeable && la->type == WP_UNDO_INSERT) { if (la->end == co.start && (is_space || !undo->priv->last_char_is_space)) { la->text = g_strconcat(la->text, text, NULL); update_tags_range(la->tags, la->start, la->end); undo->priv->last_char_is_space = is_space; la->end = co.end; return; } else la->mergeable = FALSE; } op = g_new0(WPUndoOperation, 1); if (!op) { emit_no_memory(undo); return; } *op = co; op->type = WP_UNDO_INSERT; op->text = g_strdup(text); undo->priv->last_char_is_space = is_space; if (!op->text) { emit_no_memory(undo); g_free(op); return; } wp_undo_add_queue(undo, op); } void wp_undo_delete_range(WPUndo * undo, GtkTextIter * start, GtkTextIter * end) { WPUndoOperation co = { 0 }; WPUndoOperation *op; WPUndoOperation *la; GtkTextIter pos; gboolean mergeable = FALSE; gboolean is_space; gchar *str = NULL; g_return_if_fail(WP_IS_UNDO(undo)); if (undo->priv->undo_disabled > 0 || undo->priv->low_mem) return; la = undo->priv->current_op; gtk_text_buffer_get_iter_at_mark(undo->priv->text_buffer, &pos, gtk_text_buffer_get_insert(undo-> priv-> text_buffer)); co.backspace = co.start < gtk_text_iter_get_offset(&pos); co.start = gtk_text_iter_get_offset(start); co.end = gtk_text_iter_get_offset(end); co.text = gtk_text_buffer_get_slice(undo->priv->text_buffer, start, end, TRUE); if (!co.text) { emit_no_memory(undo); return; } is_space = g_unichar_isspace(g_utf8_get_char(co.text)); co.mergeable = !(((co.end - co.start) > 1) || (g_utf8_get_char(co.text) == '\n')); if (co.mergeable) mergeable = co.backspace ? !gtk_text_iter_toggles_tag(start, NULL) : !gtk_text_iter_toggles_tag(end, NULL); if (co.mergeable && la && la->mergeable && la->type == WP_UNDO_DELETE && la->backspace == co.backspace) { if (co.backspace && la->start == co.end && (is_space || !undo->priv->last_char_is_space)) { str = g_strconcat(co.text, la->text, NULL); la->start = co.start; } else if (!co.backspace && la->end == co.start && (is_space || !undo->priv->last_char_is_space)) { str = g_strconcat(la->text, co.text, NULL); la->end = co.end; } if (str) { g_free(la->text); la->text = str; update_tags_range(la->tags, la->start, la->end); undo->priv->last_char_is_space = is_space; la->mergeable = mergeable; return; } else la->mergeable = FALSE; } op = g_new(WPUndoOperation, 1); *op = co; op->type = WP_UNDO_DELETE; op->mergeable = mergeable; op->tags = wp_undo_get_toggled_tags(undo, start, end); undo->priv->last_char_is_space = is_space; wp_undo_add_queue(undo, op); } void wp_undo_apply_tag(WPUndo * undo, const GtkTextIter * start, const GtkTextIter * end, GtkTextTag * tag, gboolean enable) { WPUndoOperation *op; gint is, ie; g_return_if_fail(WP_IS_UNDO(undo)); if (undo->priv->undo_disabled > 0 || undo->priv->low_mem) return; if (undo->priv->current_op && tag) { op = undo->priv->current_op; switch (op->type) { case WP_UNDO_INSERT: op->tags = g_slist_append(op->tags, wp_undo_create_tag (gtk_text_iter_get_offset(start), (GtkTextIter *) end, tag, enable)); break; case WP_UNDO_TAG: is = gtk_text_iter_get_offset(start); ie = gtk_text_iter_get_offset(end); if (is >= op->start && ie <= op->end) { op->tags = g_slist_prepend(op->tags, wp_undo_create_tag(is, end, tag, enable)); } break; case WP_UNDO_FMT: is = gtk_text_iter_get_offset(start); ie = gtk_text_iter_get_offset(end); op->tags = g_slist_prepend(op->tags, wp_undo_create_tag(is, end, tag, enable)); break; default: g_return_if_fail(FALSE); } } else if (!tag) { op = g_new0(WPUndoOperation, 1); if (!op) { emit_no_memory(undo); return; } op->type = WP_UNDO_TAG; op->orig_tags = wp_undo_get_toggled_tags(undo, start, end); op->start = gtk_text_iter_get_offset(start); op->end = gtk_text_iter_get_offset(end); if (tag) op->tags = g_slist_prepend(NULL, wp_undo_create_tag(op->start, end, tag, enable)); else op->tags = NULL; op->mergeable = FALSE; wp_undo_add_queue(undo, op); } } void wp_undo_simple_justification(WPUndo * undo, GtkTextIter * start, GtkTextIter * end, GtkTextTag * orig_tag, GtkTextTag * tag) { WPUndoOperation *op; if (undo->priv->undo_disabled > 0 || undo->priv->low_mem) return; op = g_new0(WPUndoOperation, 1); if (!op) { emit_no_memory(undo); return; } op->type = WP_UNDO_SIMPLE_JUSTIFY; op->orig_tag = orig_tag; op->tag = tag; op->start = gtk_text_iter_get_offset(start); op->end = gtk_text_iter_get_offset(end); op->mergeable = FALSE; wp_undo_add_queue(undo, op); } void wp_undo_selection_changed(WPUndo * undo, GtkTextIter * start, GtkTextIter * end) { WPUndoOperation *op; gint istart, iend; g_return_if_fail(WP_IS_UNDO(undo)); if (undo->priv->undo_disabled > 0 || undo->priv->low_mem) return; istart = gtk_text_iter_get_offset(start); iend = gtk_text_iter_get_offset(end); op = undo->priv->current_op; if (op && op->type == WP_UNDO_SELECT) { if ((op->sel_start == istart || op->sel_end == iend) && op->mergeable) { op->sel_start = istart; op->sel_end = iend; return; } else if (op) op->mergeable = FALSE; } if (istart != iend) { op = g_new0(WPUndoOperation, 1); if (!op) { emit_no_memory(undo); return; } op->type = WP_UNDO_SELECT; op->sel_start = istart; op->sel_end = iend; op->mergeable = TRUE; wp_undo_add_queue(undo, op); } } void wp_undo_format_changed(WPUndo * undo, gboolean rich_text) { WPUndoOperation *op; GtkTextIter start, end; g_return_if_fail(WP_IS_UNDO(undo)); if (undo->priv->undo_disabled > 0 || undo->priv->low_mem) return; op = g_new0(WPUndoOperation, 1); if (!op) { emit_no_memory(undo); return; } op->type = WP_UNDO_FMT; op->rich_text = rich_text; gtk_text_buffer_get_bounds(undo->priv->text_buffer, &start, &end); op->tags = wp_undo_get_toggled_tags(undo, &start, &end); wp_undo_add_queue(undo, op); } void wp_undo_last_line_justify(WPUndo * undo, gint old_line_justify, gint new_line_justify) { WPUndoOperation *op; g_return_if_fail(WP_IS_UNDO(undo)); if (undo->priv->undo_disabled > 0 || undo->priv->low_mem) return; op = g_new0(WPUndoOperation, 1); if (!op) { emit_no_memory(undo); return; } op->type = WP_UNDO_LAST_LINE_JUSTIFY; op->old_line_justify = old_line_justify; op->new_line_justify = new_line_justify; wp_undo_add_queue(undo, op); } void remove_image_tags (gchar **string) { GString *new_string; gchar *current; if ((string == NULL)||(*string == NULL)) return; new_string = g_string_new (""); current = *string; while (*current != '\0') { gunichar c = g_utf8_get_char (current); gchar output[6]; if (c == 0xFFFC) { new_string = g_string_append_c (new_string, ' '); } else { gint output_size; output_size = g_unichar_to_utf8 (c, output); new_string = g_string_append_len (new_string, output, output_size); } current = g_utf8_next_char (current); } g_free (*string); *string = g_string_free (new_string, FALSE); } static void wp_undo_add_queue(WPUndo * undo, WPUndoOperation * op) { GSList *tmp; WPUndoPrivate *priv = undo->priv; gboolean first_in_group = priv->first_in_group; /* first we remove all image text tags */ remove_image_tags (&(op->text)); if (priv->disable_this_group) { g_free(op); return; } if (!priv->group || priv->first_in_group) { priv->first_in_group = FALSE; wp_undo_reset_mergeable(undo); } priv->current_op_list = g_slist_prepend(priv->current_op_list, op); if (!priv->current_op) priv->undo_queue = g_slist_prepend(priv->undo_queue, priv->current_op_list); else priv->undo_queue->data = priv->current_op_list; priv->current_op = op; priv->redo_queue = wp_undo_free_op_list(priv->redo_queue); if (first_in_group && ++priv->undo_queue_len > priv->max_undo_level) { /* printf("Undo queue len: %d, %d\n", undo->priv->undo_queue_len, * undo->priv->max_undo_level); */ undo->priv->undo_queue_len = undo->priv->max_undo_level; tmp = g_slist_nth(undo->priv->undo_queue, undo->priv->undo_queue_len - 1); if (tmp) tmp->next = wp_undo_free_op_list(tmp->next); } wp_undo_send_signals(undo); } void wp_undo_reset(WPUndo * undo) { g_return_if_fail(WP_IS_UNDO(undo)); undo->priv->undo_queue = wp_undo_free_op_list(undo->priv->undo_queue); undo->priv->redo_queue = wp_undo_free_op_list(undo->priv->redo_queue); wp_undo_send_signals(undo); } static void emit_no_memory(WPUndo * undo) { WPUndoPrivate *priv = undo->priv; g_signal_emit(undo, signals[NO_MEMORY], 0); if (!priv->first_in_group && !priv->disable_this_group && priv->undo_queue) { GSList *tmp = priv->undo_queue; priv->undo_queue = tmp->next; tmp->next = NULL; wp_undo_free_op_list(tmp); wp_undo_send_signals(undo); } priv->disable_this_group = TRUE; } wpeditor-2.18/src/wptextbuffer.c0000644000175000001440000044435110770166450016225 0ustar stevenusers/** * @file wptextbuffer.c * * Implementation file for WordPad Text Buffer */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #include #include #include #include #include #include "wptextbuffer.h" #include "color_buffer.h" #include "wptextbuffer-private.h" #include "wpundo.h" #include "wphtmlparser.h" #define WPT_ID "wpt-id" #define MIN_FONT_SCALE 0.1 #define MAX_FONT_SCALE 5 #define DEF_FONT_SCALE 1.5 #define DEF_FONT "Sans" #define DEF_FONT_SIZE 3 #define DEF_PLAIN_FONT "Monospace" #define DEF_PLAIN_FONT_SIZE 3 const gint wp_font_size[] = { 6, 8, 10, 12, 16, 24, 32 }; /** The object's private variables */ struct _WPTextBufferPrivate { /** TRUE if the buffer hold a selection */ gint has_selection:1; /** TRUE if the buffer is empty */ gint is_empty:1; /** TRUE if the buffer contains rich text */ gint is_rich_text:1; /** TRUE if the tags should be copied automatically at insert */ gint insert_preserve_tags:1; /** TRUE if the buffer buffer is in fast mode. In this mode the * insert, delete, apply_tag, remove_tag are skipped */ gint fast_mode:1; /** > 0 if the cursor is frozen, and the refresh_attributes signal is not * emmited */ gint cursor_moved_frozen; /** TRUE if the cursor has changed it's location */ gint cursor_moved:1; /** offset of the last cursor position */ gint last_cursor_pos; /** Font scaling factor */ gdouble font_scaling_factor; /** Background color */ GdkColor *background_color; /** Holds the current attributes, default attributes for rich text, and * default attributes for plain text */ WPTextBufferFormat fmt, default_fmt, default_plain_fmt; /** Pointer to the undo object */ WPUndo *undo; /** Undo reset is queued for next end user action */ gboolean queue_undo_reset; /** Holds the deleted tags, when a selection was deleted */ GSList *delete_tags; /** Last line justification from the deleted text */ gint delete_last_line_justification; /** #GtkTextTag array of tags less then WPT_LASTTAG */ GtkTextTag *tags[WPT_LASTTAG]; /** Pointer to the ColorBuffer */ ColorBuffer *color_tags; /** #GtkTextTag array of font size tags */ GtkTextTag *font_size_tags[WP_FONT_SIZE_COUNT]; /** #GtkTextTag array of superscript tags */ GtkTextTag *font_size_sup_tags[WP_FONT_SIZE_COUNT]; /** #GtkTextTag array of subscript tags */ GtkTextTag *font_size_sub_tags[WP_FONT_SIZE_COUNT]; /** #GtkTextTag array of font face tags */ GtkTextTag **fonts; /** Idle id, used to emit refresh_attributes signal */ gint source_refresh_attributes; /** Last line justification */ gint last_line_justification; /** Temporarly start and end of the removed justification tag */ gint just_start, just_end; /** Temporarly removed justification #GtkTextTag */ GtkTextTag *tmp_just; /** Buffer to hold the last invalid utf8 character used at plain file opening */ gchar last_utf8_invalid_char[13]; /** The saved incomplete utf8 character size */ gint last_utf8_size; /** Pointer to the #WPHTMLParser object */ WPHTMLParser *parser; /** Tag rememberence status. It is needed for special IM cases */ gint remember_tag:1; /** True is insert was the last operation */ gint last_is_insert:1; gint force_copy:1; gint convert_tag:1; GSList *copy_insert_tags; GtkTextIter copy_start, copy_end; GHashTable *tag_hash; }; /** HTML tag types */ typedef enum { TP_FONTNAME = 0, TP_FONTSIZE, TP_FONTCOLOR, TP_BOLD, TP_UNDERLINE, TP_ITALIC, TP_STRIKE, TP_SUBSCRIPT, TP_SUPERSCRIPT, TP_LAST } HTMLTag; /** HTML opening tags */ const gchar *html_open_tags[TP_LAST] = { "", "", "", "", "", "", "", "", "" }; /** HTML closing tags */ const gchar *html_close_tags[TP_LAST] = { "", "", "", "", "", "", "", "", "" }; /** HTML header */ const gchar *html_header = "\n" "\n" " \n" " \n" " " "\n"; const gchar *body_start = "\n"; const gchar *body_bgcolor_start = "\n"; /** HTML footer */ const gchar *html_footer = "\n" "\n"; static GObject *wp_text_buffer_constructor(GType type, guint n_construct_properties, GObjectConstructParam * construct_param); static void wp_text_buffer_finalize(GObject * object); static void wp_text_buffer_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void wp_text_buffer_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); /** * Callback to notify when a mark has changed it's position. The selection and * cursor position is intrested only * @param buffer is a #GtkTextBuffer * @param iter a position in the buffer * @param mark the #GtkTextMark which has been set */ static void wp_text_buffer_mark_set(GtkTextBuffer * buffer, const GtkTextIter * iter, GtkTextMark * mark); static void wp_text_buffer_check_apply_tag(WPTextBuffer * buffer); /** * Callback to insert text into the buffer at the given pos * @param buffer is a #GtkTextBuffer * @param pos a position in the buffer * @param text contains the text to be inserted * @param length contains the length of the text */ static void wp_text_buffer_insert_text(GtkTextBuffer * buffer, GtkTextIter * pos, const gchar * text, gint length); /** * Callback to apply a tag in the buffer between the start * and end interval * @param buffer is a #GtkTextBuffer * @param tag is a #GtkTextTag * @param start a position in the buffer * @param end a position in the buffer */ static void wp_text_buffer_apply_tag(GtkTextBuffer * buffer, GtkTextTag * tag, const GtkTextIter * start, const GtkTextIter * end); /** * Callback to remove a tag in the buffer between the start * and end interval * @param buffer is a #GtkTextBuffer * @param tag is a #GtkTextTag * @param start a position in the buffer * @param end a position in the buffer */ static void wp_text_buffer_remove_tag(GtkTextBuffer * buffer, GtkTextTag * tag, const GtkTextIter * start, const GtkTextIter * end); /** * Callback to delete the text in the buffer between the start * and end interval * @param buffer is a #GtkTextBuffer * @param start a position in the buffer * @param end a position in the buffer */ static void wp_text_buffer_delete_range(GtkTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end); /** * Callback to notify a new group has been started in the buffer. It is * used to group undo/redo functionality * @param buffer is a #GtkTextBuffer */ static void wp_text_buffer_begin_user_action(GtkTextBuffer * buffer); /** * Callback to notify a the group action has been ended * @param buffer is a #GtkTextBuffer */ static void wp_text_buffer_end_user_action(GtkTextBuffer * buffer); /** * Send the refresh_attributes signal only if the last cursor position differs * from the position pointer by iter * @param buffer is a #WPTextBuffer * @param a position in the buffer */ static void emit_refresh_attributes(WPTextBuffer * buffer, const GtkTextIter * iter); /** * Send the default_font_changed signal * @param buffer is a #WPTextBuffer */ static void emit_default_font_changed(WPTextBuffer * buffer); /** * Send the default_justification_changed signal, only if the justification * is different from the old one, and updates the old justification. * @param buffer is a #WPTextBuffer * @param justification is on of the #GTK_JUSTIFY_LEFT, #GTK_JUSTIFY_CENTER, * #GTK_JUSTIFY_RIGHT */ static void emit_default_justification_changed(WPTextBuffer * buffer, gint justification); /** * Freeze the cursor movement. If the cursor is frozen there will be no emition of * refresh_attributes signal. * @param buffer is a #WPTextBuffer */ static void freeze_cursor_moved(WPTextBuffer * buffer); /** * Unfreeze the cursor movement. If there was cursor movement while the cursor * was frozen, a refresh_attributes signal will be emited * @param buffer is a #WPTextBuffer */ static void thaw_cursor_moved(WPTextBuffer * buffer); /** * Initialize the #GtkTextTag's used by the buffer * @param buffer is a #WPTextBuffer */ static void wp_text_buffer_init_tags(WPTextBuffer * buffer); /** * Copies the tags in the buffer only if the * buffer->priv->fmt->cs doesn't contain them * @param buffer is a #GtkTextBuffer * @param tags is a #GSList of #GtkTextTag's * @param start a position in the buffer * @param end a position in the buffer */ static void wp_text_buffer_copy_tag_attributes(WPTextBuffer * buffer, GSList * tags, GtkTextIter * start, GtkTextIter * end); /** * Apply fmt to the buffer between start and end * interval * @param buffer is a #GtkTextBuffer * @param start a position in the buffer * @param end a position in the buffer * @param undo is TRUE if the operations should be saved in undo * @param fmt is a #WPTextBufferFormat which contains the formatting tags. A tag * is applied/removed only if is present in the changeset (fmt.cs) * @return TRUE if a tag was applied */ static gboolean wp_text_buffer_apply_attributes(WPTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end, gboolean undo, WPTextBufferFormat * fmt); /** * Get the formatting attributes from the text. * @param buffer pointer to a #WPTextBuffer * @param pointer to a #WPTextBufferFormat which will be filled with the current * attributes from the cursor position * @param set_changed is TRUE if the fmt->cs need to be cleared * @param parse_selection means that the selection should be parsed, to detect * if the tag is toggled multiple times. If it is toggled multiple * times, then the fmt->cs.* will be set accordingly. */ static gboolean _wp_text_buffer_get_attributes(WPTextBuffer * buffer, WPTextBufferFormat * fmt, gboolean set_changed, gboolean parse_selection); /** * Put bullets in front of the selected area or the actual line * @param buffer pointer to a #WPTextBuffer */ static void _wp_text_buffer_put_bullet(WPTextBuffer * buffer); /** * Remove bullets from the front of the selected area or the actual line * @param buffer pointer to a #WPTextBuffer */ static void _wp_text_buffer_remove_bullet(WPTextBuffer * buffer); /** * Callback called when the undo's object redo state has been changed * @param undo pointer to a #WPUndo * @param enable set if redo is enabled * @param buffer pointer to a #WPTextBuffer */ static void wp_text_buffer_can_redo_cb(WPUndo * undo, gboolean enable, gpointer buffer); /** * Callback called when the undo's object undo state has been changed * @param undo pointer to a #WPUndo * @param enable set if undo is enabled * @param buffer pointer to a #WPTextBuffer */ static void wp_text_buffer_can_undo_cb(WPUndo * undo, gboolean enable, gpointer buffer); /** * Callback called when the undo's object changed the format of the buffer * @param undo pointer to a #WPUndo * @param rich_text set if the buffer contains rich text * @param buffer pointer to a #WPTextBuffer */ static void wp_text_buffer_format_changed_cb(WPUndo * undo, gboolean rich_text, WPTextBuffer * buffer); /** * Callback called when the undo's object last line justification changed * @param undo pointer to a #WPUndo * @param last_line_justification contains the new last line justification * @param buffer pointer to a #WPTextBuffer */ static void wp_text_buffer_last_line_justify_cb(WPUndo * undo, gint last_line_justification, WPTextBuffer * buffer); /** * Callback called when undo's object doesn't have enough memory for the current * operation. * @param undo pointer to a #WPUndo * @param last_line_justification contains the new last line justification * @param buffer pointer to a #WPTextBuffer */ static void wp_text_buffer_no_memory_cb(WPUndo * undo, WPTextBuffer * buffer); /** * Resize the fonts to the new scaling factor. * @param buffer pointer to a #WPTextBuffer */ static void wp_text_buffer_resize_font(WPTextBuffer * buffer); /** * Marks the user action to reset the buffer */ static void wp_text_buffer_insert_pixbuf (GtkTextBuffer *buffer, GtkTextIter *location, GdkPixbuf *pixbuf); /** Signals */ enum { /** Sent when the attributes need to be refreshed */ REFRESH_ATTRIBUTES, /** Sent when redo state has changed */ CAN_REDO, /** Sent when undo state has changed */ CAN_UNDO, /** Sent when formatting has changed (rich text<->plain text) */ FMT_CHANGED, /** Sent when the default font has been changed */ DEF_FONT_CHANGED, /** Sent when the default justification has been changed */ DEF_JUSTIFICATION_CHANGED, /** Sent when background color has been changed */ BACKGROUND_COLOR_CHANGED, /** Sent when there is not enough memory to perform the operation */ NO_MEMORY, LAST_SIGNAL }; /** Properties */ enum { PROP_0, /** R/W. Boolean. Specify if the buffer contains rich text */ PROP_RICH_TEXT, /** R. Boolean. Specify if there buffer has selection */ PROP_HAS_SELECTION, /** R. Boolean. Specify if the buffer is empty */ PROP_IS_EMPTY, /** R/W. Double. Specify the font scaling factor */ PROP_FONT_SCALING_FACTOR, /** R/W. String. Specify the default font for rich text */ PROP_DEF_FONT, /** R/W. Integer. Specify the default font size for rich text */ PROP_DEF_FONT_SIZE, /** R/W. String. Specify the default font for plain text */ PROP_DEF_PLAIN_FONT, /** R/W. Integer. Specify the default font size for plain text */ PROP_DEF_PLAIN_FONT_SIZE, /** R. Integer. Specify the default attributes */ PROP_DEF_ATTR, /** R/W. Color. Specify the background color */ PROP_BACKGROUND_COLOR, /** R/W. Boolean. Specify if there is a low memory situation */ PROP_LOW_MEM }; static guint signals[LAST_SIGNAL]; /* WP_TYPE_TEXT_BUFFER */ G_DEFINE_TYPE(WPTextBuffer, wp_text_buffer, GTK_TYPE_TEXT_BUFFER) /** Name of the tags */ const gchar *tagnames[] = { "wp-text-bold", "wp-text-italic", "wp-text-underline", "wp-text-strike", "wp-text-left", "wp-text-center", "wp-text-right", "wp-text-bullet", "wp-text-forecolor", "wp-text-font", "wp-text-fontsize", "wp-text-sup-srpt", "wp-text-sub-srpt", "wp-text-backcolor", "\0" }; /** * Inline function to round a double value to integer * @param value is a double value * @return the rounded value */ static inline gint iround(double value) { return (gint) (value + 0.5); } static void wp_text_buffer_class_init(WPTextBufferClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); GtkTextBufferClass *buffer_class = GTK_TEXT_BUFFER_CLASS(klass); object_class->set_property = wp_text_buffer_set_property; object_class->get_property = wp_text_buffer_get_property; object_class->constructor = wp_text_buffer_constructor; object_class->finalize = wp_text_buffer_finalize; buffer_class->mark_set = wp_text_buffer_mark_set; buffer_class->insert_text = wp_text_buffer_insert_text; buffer_class->delete_range = wp_text_buffer_delete_range; buffer_class->apply_tag = wp_text_buffer_apply_tag; buffer_class->remove_tag = wp_text_buffer_remove_tag; buffer_class->begin_user_action = wp_text_buffer_begin_user_action; buffer_class->end_user_action = wp_text_buffer_end_user_action; buffer_class->insert_pixbuf = wp_text_buffer_insert_pixbuf; klass->refresh_attributes = NULL; klass->can_redo = NULL; klass->can_undo = NULL; klass->fmt_changed = NULL; klass->def_font_changed = NULL; klass->def_justification_changed = NULL; klass->background_color_changed = NULL; klass->no_memory = NULL; g_object_class_install_property(object_class, PROP_RICH_TEXT, g_param_spec_boolean("rich_text", "rich_text", "The buffer can contain rich text", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_HAS_SELECTION, g_param_spec_boolean("has_selection", "has_selection", "True if something is selected", FALSE, G_PARAM_READABLE)); g_object_class_install_property(object_class, PROP_IS_EMPTY, g_param_spec_boolean("is_empty", "is_empty", "True if there is no text in the buffer", TRUE, G_PARAM_READABLE)); g_object_class_install_property(object_class, PROP_FONT_SCALING_FACTOR, g_param_spec_double("font_scale", "font_scale", "The font scaling factor for the buffer", MIN_FONT_SCALE, MAX_FONT_SCALE, DEF_FONT_SCALE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_DEF_FONT, g_param_spec_string("def_font", "def_font", "Default font for the buffer", DEF_FONT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_DEF_FONT_SIZE, g_param_spec_int("def_font_size", "def_font_size", "Default font size for the buffer", 0, wp_font_size [WP_FONT_SIZE_COUNT - 1], wp_font_size [DEF_FONT_SIZE], G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_DEF_PLAIN_FONT, g_param_spec_string("def_plain_font", "def_plain_font", "Default font for the plain text", DEF_PLAIN_FONT, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_DEF_PLAIN_FONT_SIZE, g_param_spec_int("def_plain_font_size", "def_plain_font_size", "Default font size for plain text", 0, wp_font_size [WP_FONT_SIZE_COUNT - 1], wp_font_size [DEF_PLAIN_FONT_SIZE], G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_DEF_ATTR, g_param_spec_pointer("def_attr", "def_attr", "Default attributes", G_PARAM_READABLE)); g_object_class_install_property(object_class, PROP_BACKGROUND_COLOR, g_param_spec_pointer("background_color", "backgroun_color", "Background color", G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_LOW_MEM, g_param_spec_boolean("low_memory", "low_memory", "Low memory situation (undo disabled)", FALSE, G_PARAM_READWRITE)); signals[REFRESH_ATTRIBUTES] = g_signal_new("refresh_attributes", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPTextBufferClass, refresh_attributes), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[CAN_UNDO] = g_signal_new("can_undo", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPTextBufferClass, can_undo), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[CAN_REDO] = g_signal_new("can_redo", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPTextBufferClass, can_redo), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[FMT_CHANGED] = g_signal_new("fmt_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPTextBufferClass, fmt_changed), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[DEF_FONT_CHANGED] = g_signal_new("def_font_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPTextBufferClass, def_font_changed), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[DEF_JUSTIFICATION_CHANGED] = g_signal_new("def_justification_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPTextBufferClass, def_justification_changed), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); signals[BACKGROUND_COLOR_CHANGED] = g_signal_new("background_color_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPTextBufferClass, background_color_changed), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[NO_MEMORY] = g_signal_new("no_memory", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(WPTextBufferClass, no_memory), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void wp_text_buffer_init(WPTextBuffer * buffer) { WPTextBufferPrivate *priv; buffer->priv = priv = g_new0(WPTextBufferPrivate, 1); priv->last_cursor_pos = 0; /* Default font properties */ memset(&priv->default_fmt, 0, sizeof(WPTextBufferFormat)); priv->default_fmt.cs.justification = TRUE; priv->default_fmt.justification = GTK_JUSTIFY_LEFT; priv->default_fmt.cs.font = TRUE; priv->default_fmt.font = 1; priv->default_fmt.cs.font_size = TRUE; priv->default_fmt.font_size = 3; priv->default_fmt.cs.text_position = TRUE; priv->default_fmt.text_position = TEXT_POSITION_NORMAL; priv->default_plain_fmt = priv->default_fmt; priv->fmt = priv->default_fmt; priv->is_rich_text = TRUE; priv->insert_preserve_tags = TRUE; priv->font_scaling_factor = 1.5; priv->background_color = NULL; priv->is_empty = TRUE; priv->last_line_justification = GTK_JUSTIFY_LEFT; priv->undo = wp_undo_new(GTK_TEXT_BUFFER(buffer)); priv->queue_undo_reset = FALSE; g_signal_connect(G_OBJECT(priv->undo), "can_redo", G_CALLBACK(wp_text_buffer_can_redo_cb), buffer); g_signal_connect(G_OBJECT(priv->undo), "can_undo", G_CALLBACK(wp_text_buffer_can_undo_cb), buffer); g_signal_connect(G_OBJECT(priv->undo), "fmt_changed", G_CALLBACK(wp_text_buffer_format_changed_cb), buffer); g_signal_connect(G_OBJECT(priv->undo), "last_line_justify", G_CALLBACK(wp_text_buffer_last_line_justify_cb), buffer); g_signal_connect(G_OBJECT(priv->undo), "no_memory", G_CALLBACK(wp_text_buffer_no_memory_cb), buffer); priv->color_tags = color_buffer_create(GTK_TEXT_BUFFER(buffer), "foreground_gdk", 500); priv->parser = wp_html_parser_new(buffer); priv->tag_hash = g_hash_table_new(NULL, NULL); } static GObject * wp_text_buffer_constructor(GType type, guint n_construct_properties, GObjectConstructParam * construct_param) { GObject *object; WPTextBuffer *buffer; object = G_OBJECT_CLASS(wp_text_buffer_parent_class)->constructor(type, n_construct_properties, construct_param); buffer = WP_TEXT_BUFFER(object); wp_text_buffer_init_tags(buffer); return object; } static void wp_text_buffer_finalize(GObject * object) { WPTextBuffer *buffer = WP_TEXT_BUFFER(object); WPTextBufferPrivate *priv = buffer->priv; g_free(priv->fonts); g_slist_free(priv->delete_tags); color_buffer_destroy(priv->color_tags); g_hash_table_destroy(priv->tag_hash); g_object_unref(priv->undo); if (priv->background_color) gdk_color_free(priv->background_color); g_free(priv); buffer->priv = NULL; G_OBJECT_CLASS(wp_text_buffer_parent_class)->finalize(object); } static void wp_text_buffer_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { WPTextBuffer *buffer = WP_TEXT_BUFFER(object); WPTextBufferPrivate *priv = buffer->priv; gint idx; // printf("Set property: %d\n", prop_id); switch (prop_id) { case PROP_RICH_TEXT: wp_text_buffer_enable_rich_text(buffer, g_value_get_boolean(value)); break; case PROP_FONT_SCALING_FACTOR: wp_text_buffer_set_font_scaling_factor(buffer, g_value_get_double(value)); break; case PROP_DEF_FONT: idx = wp_get_font_index(g_value_get_string(value), priv->default_fmt.font); if (idx != priv->default_fmt.font) { priv->default_fmt.font = idx; if (priv->is_rich_text) emit_default_font_changed(buffer); } break; case PROP_DEF_FONT_SIZE: idx = wp_get_font_size_index(g_value_get_int(value), priv->default_fmt.font_size); if (idx != priv->default_fmt.font_size) { priv->default_fmt.font_size = idx; if (priv->is_rich_text) emit_default_font_changed(buffer); } break; case PROP_DEF_PLAIN_FONT: idx = wp_get_font_index(g_value_get_string(value), priv->default_plain_fmt.font); if (idx != priv->default_plain_fmt.font) { priv->default_plain_fmt.font = idx; if (!priv->is_rich_text) emit_default_font_changed(buffer); } break; case PROP_DEF_PLAIN_FONT_SIZE: idx = wp_get_font_size_index(g_value_get_int(value), priv->default_plain_fmt.font_size); if (idx != priv->default_plain_fmt.font_size) { priv->default_plain_fmt.font_size = idx; if (!priv->is_rich_text) emit_default_font_changed(buffer); } break; case PROP_BACKGROUND_COLOR: wp_text_buffer_set_background_color(buffer, g_value_get_pointer(value)); break; case PROP_LOW_MEM: if (priv->undo) g_object_set(priv->undo, "low_memory", g_value_get_boolean(value), NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void wp_text_buffer_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { WPTextBuffer *buffer = WP_TEXT_BUFFER(object); switch (prop_id) { case PROP_RICH_TEXT: g_value_set_boolean(value, buffer->priv->is_rich_text); break; case PROP_HAS_SELECTION: g_value_set_boolean(value, buffer->priv->has_selection); break; case PROP_IS_EMPTY: g_value_set_boolean(value, buffer->priv->is_empty); break; case PROP_FONT_SCALING_FACTOR: g_value_set_double(value, buffer->priv->font_scaling_factor); break; case PROP_DEF_FONT: g_value_set_string(value, wp_get_font_name(buffer->priv->default_fmt.font)); break; case PROP_DEF_FONT_SIZE: g_value_set_int(value, buffer->priv->default_fmt.font_size); break; case PROP_DEF_PLAIN_FONT: g_value_set_string(value, wp_get_font_name(buffer->priv-> default_plain_fmt.font)); break; case PROP_DEF_PLAIN_FONT_SIZE: g_value_set_int(value, buffer->priv->default_plain_fmt.font_size); break; case PROP_DEF_ATTR: g_value_set_pointer(value, &buffer->priv->default_fmt); break; case PROP_BACKGROUND_COLOR: g_value_set_pointer(value, buffer->priv->background_color); break; case PROP_LOW_MEM: if (buffer->priv->undo) { gboolean low_mem; g_object_get(buffer->priv->undo, "low_memory", &low_mem, NULL); g_value_set_boolean(value, low_mem); } else g_value_set_boolean(value, TRUE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } WPTextBuffer * wp_text_buffer_new(GtkTextTagTable * table) { return g_object_new(WP_TYPE_TEXT_BUFFER, "tag-table", table, NULL); } static void wp_text_buffer_begin_user_action(GtkTextBuffer * text_buffer) { WPTextBuffer *buffer = WP_TEXT_BUFFER(text_buffer); if (buffer->priv->fast_mode) return; freeze_cursor_moved(buffer); wp_undo_start_group(buffer->priv->undo); buffer->priv->queue_undo_reset = FALSE; } static void wp_text_buffer_end_user_action(GtkTextBuffer * text_buffer) { WPTextBuffer *buffer = WP_TEXT_BUFFER(text_buffer); WPTextBufferPrivate *priv = buffer->priv; GtkTextIter start, end; if (buffer->priv->queue_undo_reset) { wp_undo_reset (priv->undo); buffer->priv->queue_undo_reset = FALSE; } if (priv->fast_mode) return; // printf("End user action: %p, %d\n", priv->delete_tags, // priv->remember_tag); if (priv->delete_tags) { g_slist_free(priv->delete_tags); priv->delete_tags = NULL; } wp_text_buffer_check_apply_tag(buffer); if (!priv->insert_preserve_tags && priv->tmp_just) { gtk_text_buffer_get_iter_at_offset(text_buffer, &start, priv->just_start); gtk_text_buffer_get_iter_at_offset(text_buffer, &end, priv->just_end); gtk_text_buffer_apply_tag(text_buffer, priv->tmp_just, &start, &end); priv->tmp_just = NULL; } thaw_cursor_moved(buffer); wp_undo_end_group(priv->undo); } /** * Update the selection in the buffer and store also in undo * @param buffer is a #GtkTextBuffer */ static void wp_text_buffer_update_selection(WPTextBuffer * buffer) { GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER(buffer); gboolean has_selection, old_selection = buffer->priv->has_selection != FALSE; GtkTextIter start, end; has_selection = gtk_text_buffer_get_selection_bounds(text_buffer, &start, &end); wp_undo_selection_changed(buffer->priv->undo, &start, &end); buffer->priv->has_selection = has_selection; if (old_selection != has_selection) { buffer->priv->last_cursor_pos = -1; emit_refresh_attributes(buffer, NULL); } } static void wp_text_buffer_mark_set(GtkTextBuffer * text_buffer, const GtkTextIter * iter, GtkTextMark * mark) { WPTextBuffer *buffer = WP_TEXT_BUFFER(text_buffer); if (buffer->priv->fast_mode) return; GtkTextMark *insert = gtk_text_buffer_get_insert(text_buffer); GtkTextMark *sel_bound = gtk_text_buffer_get_selection_bound(text_buffer); if (GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)->mark_set) GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)-> mark_set(text_buffer, iter, mark); if (mark == insert || mark == sel_bound) wp_text_buffer_update_selection(buffer); if (mark == insert) emit_refresh_attributes(buffer, iter); } /** * Find a justification tag from the head tag list * @param head is a #GSList of #GtkTextTag * @param free is set if the head should be freed. * @return the found justification tag or NULL if it not found */ static GtkTextTag * find_justification_tag(GSList * head, gboolean free) { GtkTextTag *result = NULL, *tmp; GSList *iter = head; while (iter) { tmp = GTK_TEXT_TAG(iter->data); if (tmp->justification_set) { result = tmp; break; } iter = iter->next; } if (free) g_slist_free(head); return result; } static void wp_text_buffer_check_apply_tag(WPTextBuffer * buffer) { WPTextBufferPrivate *priv = buffer->priv; // printf("Check apply tags: %d\n", priv->last_is_insert); if (priv->last_is_insert) { priv->last_is_insert = FALSE; GSList *tmp = priv->copy_insert_tags; priv->copy_insert_tags = NULL; wp_text_buffer_copy_tag_attributes(buffer, tmp, &priv->copy_start, &priv->copy_end); g_slist_free(tmp); } } static void wp_text_buffer_insert_text(GtkTextBuffer * text_buffer, GtkTextIter * pos, const gchar * text, gint length) { WPTextBuffer *buffer = WP_TEXT_BUFFER(text_buffer); WPTextBufferPrivate *priv = buffer->priv; GSList *tags = NULL; gint start_offset = 0; GtkTextIter start; gboolean selection_deleted = buffer->priv->delete_tags != NULL; gboolean copy_tag; gchar pixbuf_str [6]; gboolean has_image; pixbuf_str[g_unichar_to_utf8 (0xfffc, pixbuf_str)] = '\0'; has_image = (strstr (text, pixbuf_str) != NULL); if (!text[0]) return; if (priv->fast_mode) { GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)-> insert_text(text_buffer, pos, text, length); priv->is_empty = FALSE; return; } wp_text_buffer_check_apply_tag(buffer); wp_undo_insert_text(priv->undo, pos, text, length); priv->is_empty = FALSE; copy_tag = wp_undo_is_enabled(priv->undo) && priv->insert_preserve_tags && priv->is_rich_text; /* printf("Insert text: %s, %p, %d, %d\n", text, priv->delete_tags, * priv->insert_preserve_tags, copy_tag); */ if (copy_tag) { if (buffer->priv->delete_tags) { tags = buffer->priv->delete_tags; buffer->priv->delete_tags = NULL; } else if ((gtk_text_iter_starts_line(pos) && !gtk_text_iter_ends_line(pos) && !gtk_text_iter_is_end(pos)) || gtk_text_iter_is_start(pos)) tags = gtk_text_iter_get_toggled_tags(pos, TRUE); else tags = gtk_text_iter_get_toggled_tags(pos, FALSE); } start_offset = gtk_text_iter_get_offset(pos); GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)-> insert_text(text_buffer, pos, text, length); start = *pos; gtk_text_iter_set_offset(&start, start_offset); priv->convert_tag = FALSE; if (!priv->insert_preserve_tags) { gtk_text_buffer_remove_all_tags(text_buffer, &start, pos); } else if (priv->force_copy) { priv->force_copy = FALSE; wp_text_buffer_copy_tag_attributes(buffer, tags, &start, pos); g_slist_free(tags); } else if (copy_tag) { priv->last_is_insert = TRUE; priv->copy_insert_tags = tags; priv->copy_start = start; priv->copy_end = *pos; } else { g_slist_free(priv->copy_insert_tags); priv->copy_insert_tags = NULL; /* debug_print_tags(&start, 0); debug_print_tags(pos, 0); */ if (!priv->tmp_just) { priv->tmp_just = find_justification_tag(gtk_text_iter_get_tags(pos), TRUE); if (priv->tmp_just) { priv->just_start = start_offset; gtk_text_buffer_remove_tag(text_buffer, priv->tmp_just, &start, pos); } } if (priv->tmp_just) { priv->just_end = gtk_text_iter_get_offset(pos); /* printf("Temporarly removing justification tag: %d-%d\n", * priv->just_start, priv->just_end); */ } } if (priv->insert_preserve_tags && !selection_deleted && priv->is_rich_text) emit_refresh_attributes(buffer, pos); if (has_image) buffer->priv->queue_undo_reset = TRUE; } /** * Remove orig_tag and apply tag for the interval between * start and end in buffer * @param buffer is a #GtkTextBuffer * @param start a position in the buffer * @param end a position in the buffer * @param orig_tag is a #GtkTextTag * @param tag is a #GtkTextTag */ static void apply_justification_tag(WPTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end, GtkTextTag * orig_tag, GtkTextTag * tag) { GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER(buffer); WPTextBufferPrivate *priv = buffer->priv; wp_undo_freeze(priv->undo); wp_text_buffer_remove_tag(text_buffer, orig_tag, start, end); if (tag) wp_text_buffer_apply_tag(text_buffer, tag, start, end); wp_undo_thaw(priv->undo); if (tag && gtk_text_iter_is_end(end)) emit_default_justification_changed(buffer, tag->values->justification); wp_undo_simple_justification(priv->undo, start, end, orig_tag, tag); } void _wp_text_buffer_adjust_justification(WPTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end, GtkTextTag * def_tag, gboolean align_to_right) { GtkTextTag *orig_tag, *tag; GtkTextIter *tmp = start ? start : end, pos; orig_tag = find_justification_tag(gtk_text_iter_get_toggled_tags (tmp, start != NULL), TRUE); if (!orig_tag) { if (!def_tag && gtk_text_iter_is_end(tmp)) { tag = find_justification_tag(gtk_text_iter_get_toggled_tags (tmp, FALSE), TRUE); if (tag) emit_default_justification_changed(buffer, tag->values-> justification); } return; } tag = find_justification_tag(gtk_text_iter_get_toggled_tags(tmp, !start), TRUE); if (!tag && def_tag) tag = def_tag; if (start && !end) { pos = *start; gtk_text_iter_forward_to_line_end(&pos); apply_justification_tag(buffer, start, &pos, orig_tag, tag); } else if (start) apply_justification_tag(buffer, start, end, orig_tag, tag); else /* if (!align_to_right) { pos = *end; * gtk_text_iter_forward_to_line_end(&pos); * apply_justification_tag(buffer, end, &pos, orig_tag, tag); } else */ { pos = *end; gtk_text_iter_set_line_offset(&pos, 0); apply_justification_tag(buffer, &pos, end, orig_tag, tag); } } static void wp_text_buffer_delete_range(GtkTextBuffer * text_buffer, GtkTextIter * start, GtkTextIter * end) { WPTextBuffer *buffer = WP_TEXT_BUFFER(text_buffer); WPTextBufferPrivate *priv = buffer->priv; GtkTextTag *tag = NULL; gboolean undo, copy_tag, iter_end, different_line; gboolean has_image; gchar pixbuf_char[6]; pixbuf_char[g_unichar_to_utf8 (0xfffc, pixbuf_char)] = 0; if (priv->fast_mode) { GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)-> delete_range(text_buffer, start, end); return; } wp_text_buffer_check_apply_tag(buffer); if (priv->delete_tags) { g_slist_free(priv->delete_tags); priv->delete_tags = NULL; } // printf("Delete range: %d-%d\n", gtk_text_iter_get_offset(start), // gtk_text_iter_get_offset(end)); has_image = gtk_text_iter_forward_search (start, pixbuf_char, 0, NULL, NULL, end); undo = wp_undo_is_enabled(priv->undo); copy_tag = undo && priv->insert_preserve_tags; /* if the start and end iterator is in different line we need to apply * the justification till the end of the newline */ different_line = undo && (gtk_text_iter_get_line(start) != gtk_text_iter_get_line(end)); priv->is_empty = (iter_end = gtk_text_iter_is_end(end)) && gtk_text_iter_is_start(start); if (priv->is_empty && priv->insert_preserve_tags) _wp_text_buffer_get_attributes(buffer, &priv->fmt, TRUE, FALSE); wp_undo_delete_range(priv->undo, start, end); priv->convert_tag = FALSE; if (!priv->is_empty && copy_tag) { if (iter_end || different_line) tag = find_justification_tag(gtk_text_iter_get_tags(start), TRUE); if (priv->has_selection || priv->remember_tag) { priv->delete_tags = iter_end ? gtk_text_iter_get_tags(start) : gtk_text_iter_get_toggled_tags(start, TRUE); } if (iter_end && tag) emit_default_justification_changed(buffer, tag->values->justification); } /* Don't know why, but if is a large portion to delete, and the interval * contains several tag toggles, it takes eternity for the textbuffer to * delete. So we add a little hack, first clear the tags */ if (abs(gtk_text_iter_get_offset(end) - gtk_text_iter_get_offset(start)) > 100) { if (undo) wp_undo_freeze(priv->undo); gtk_text_buffer_remove_all_tags(text_buffer, start, end); if (undo) wp_undo_thaw(priv->undo); } GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)-> delete_range(text_buffer, start, end); if (!priv->is_empty) { if (different_line) _wp_text_buffer_adjust_justification(buffer, start, NULL, tag, FALSE); } else { emit_default_justification_changed(buffer, priv->fmt.justification); } // TODO: only emit cursor moved if the delete is not happend with the // backspace key buffer->priv->last_cursor_pos = -1; wp_text_buffer_update_selection(buffer); emit_refresh_attributes(buffer, start); if (has_image) buffer->priv->queue_undo_reset = TRUE; } gboolean wp_text_buffer_has_selection(WPTextBuffer * buffer) { g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer), FALSE); return buffer->priv->has_selection; } static void _apply_tag(WPTextBufferPrivate * priv, GtkTextBuffer * buffer, GtkTextTag * tag, const GtkTextIter * start, const GtkTextIter * end) { if (!priv->fast_mode) { wp_undo_apply_tag(priv->undo, start, end, tag, TRUE); } GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)->apply_tag(buffer, tag, start, end); /* printf("Apply tag: %s, %d-%d\n", tag->name ? tag->name : "(null)", * gtk_text_iter_get_offset(start), gtk_text_iter_get_offset(end)); */ } static void wp_text_buffer_apply_tag(GtkTextBuffer * buffer, GtkTextTag * tag, const GtkTextIter * start, const GtkTextIter * end) { if (tag == NULL) { return; } WPTextBufferPrivate *priv = WP_TEXT_BUFFER(buffer)->priv; if (!priv->is_rich_text && wp_undo_is_enabled(priv->undo)) return; if (!priv->fast_mode && priv->last_is_insert) { // printf("Apply tag: ** removing insert tags **\n"); priv->last_is_insert = FALSE; g_slist_free(priv->copy_insert_tags); priv->copy_insert_tags = NULL; gtk_text_buffer_remove_all_tags(buffer, &priv->copy_start, &priv->copy_end); priv->convert_tag = TRUE; } /* Dirty little hack, to fix the rich-text copy and paste problems. Fix * this, when a proper text_buffer deserializer is done in the Gtk. It is * already there in the gtk 2.10 */ if (priv->convert_tag) { // printf("=== Convert tag: %s ===\n", tag->name); if (!g_hash_table_lookup(priv->tag_hash, tag)) { if (tag->name && strncmp(tag->name, "wp-text-bullet", 14) == 0) _apply_tag(priv, buffer, priv->tags[WPT_BULLET], start, end); else { if (tag->values->font) { gint size; const gchar *name; PangoFontDescription *font = tag->values->font; if (pango_font_description_get_style(font)) _apply_tag(priv, buffer, priv->tags[WPT_ITALIC], start, end); if (pango_font_description_get_weight(font) != PANGO_WEIGHT_NORMAL) _apply_tag(priv, buffer, priv->tags[WPT_BOLD], start, end); if ((size = pango_font_description_get_size(font))) { if (tag->name && strncmp(tag->name, "wp-text-", 8) == 0) { gchar no[2], *p; no[1] = 0; p = strrchr(tag->name, '-'); if (*(p - 1) >= '0' && *(p - 1) <= '9') no[0] = *(p - 1); else no[0] = *(p + 1); // printf("Text size: %s\n", no); size = atoi(no); if (tag->values->appearance.rise == 0) _apply_tag(priv, buffer, priv->font_size_tags[size], start, end); else if (tag->values->appearance.rise < 0) _apply_tag(priv, buffer, priv->font_size_sub_tags[size], start, end); else _apply_tag(priv, buffer, priv->font_size_sup_tags[size], start, end); } else { size = wp_get_font_size_index(iround (size / priv-> font_scaling_factor / PANGO_SCALE), priv->default_fmt. font_size); _apply_tag(priv, buffer, priv->font_size_tags[size], start, end); } } if ((name = pango_font_description_get_family(font))) { gint idx = wp_get_font_index(name, priv->default_fmt.font); _apply_tag(priv, buffer, priv->fonts[idx], start, end); } } if (tag->underline_set) _apply_tag(priv, buffer, priv->tags[WPT_UNDERLINE], start, end); if (tag->strikethrough_set) _apply_tag(priv, buffer, priv->tags[WPT_STRIKE], start, end); if (tag->justification_set) { if (tag->values->justification == GTK_JUSTIFY_LEFT) _apply_tag(priv, buffer, priv->tags[WPT_LEFT], start, end); else if (tag->values->justification == GTK_JUSTIFY_CENTER) _apply_tag(priv, buffer, priv->tags[WPT_CENTER], start, end); else _apply_tag(priv, buffer, priv->tags[WPT_RIGHT], start, end); } if (tag->fg_color_set) { GtkTextTag *t = color_buffer_get_tag(priv->color_tags, &tag->values-> appearance.fg_color, priv-> tags[WPT_RIGHT]-> priority + 1); _apply_tag(priv, buffer, t, start, end); g_hash_table_insert(priv->tag_hash, t, NULL); } } tag = NULL; } } if (tag) { _apply_tag(priv, buffer, tag, start, end); } if (!priv->insert_preserve_tags && tag && tag->justification_set && priv->tmp_just) { priv->tmp_just = NULL; priv->just_start = 0; } } static void wp_text_buffer_remove_tag(GtkTextBuffer * buffer, GtkTextTag * tag, const GtkTextIter * start, const GtkTextIter * end) { if (!WP_TEXT_BUFFER(buffer)->priv->fast_mode) wp_undo_apply_tag(WP_TEXT_BUFFER(buffer)->priv->undo, start, end, tag, FALSE); WPTextBufferPrivate *priv = WP_TEXT_BUFFER(buffer)->priv; if (priv->last_is_insert) { // printf("Remove tag: ** removing insert tags **\n"); priv->last_is_insert = FALSE; g_slist_free(priv->copy_insert_tags); priv->copy_insert_tags = NULL; } GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)->remove_tag(buffer, tag, start, end); /* printf("Remove tag: %s, %d-%d, %d\n", tag->name ? tag->name : * "(null)", gtk_text_iter_get_offset(start), * gtk_text_iter_get_offset(end), * wp_undo_is_enabled(WP_TEXT_BUFFER(buffer)->priv->undo)); */ } void wp_text_buffer_freeze(WPTextBuffer * buffer) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); freeze_cursor_moved(buffer); buffer->priv->insert_preserve_tags = FALSE; } void wp_text_buffer_thaw(WPTextBuffer * buffer) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); thaw_cursor_moved(buffer); buffer->priv->insert_preserve_tags = TRUE; } /** * Emit background color change signal */ static void emit_background_color_change(WPTextBuffer * buffer) { g_signal_emit(buffer, signals[BACKGROUND_COLOR_CHANGED], 0, buffer->priv->background_color); } /** * Callback from timeout, to send the refresh_attributes signal * @param data is a #GtkTextBuffer */ static gboolean idle_emit_refresh_attributes(gpointer data) { if ((data) && WP_IS_TEXT_BUFFER(data)) { WP_TEXT_BUFFER(data)->priv->source_refresh_attributes = 0; g_signal_emit(data, signals[REFRESH_ATTRIBUTES], 0); } return FALSE; } static void emit_refresh_attributes(WPTextBuffer * buffer, const GtkTextIter * where) { if (where == NULL) return; gint tmp = where ? gtk_text_iter_get_offset(where) : 0; if (!buffer->priv->cursor_moved_frozen) { if (tmp != buffer->priv->last_cursor_pos) { // if (1) { buffer->priv->last_cursor_pos = tmp; if (!gtk_text_iter_is_start(where) && !gtk_text_iter_is_end(where)) *(int *) &buffer->priv->fmt.cs = 0; if (buffer->priv->source_refresh_attributes) g_source_remove(buffer->priv->source_refresh_attributes); buffer->priv->source_refresh_attributes = g_timeout_add(400, idle_emit_refresh_attributes, buffer); } } else buffer->priv->cursor_moved = TRUE; } static void freeze_cursor_moved(WPTextBuffer * buffer) { buffer->priv->cursor_moved_frozen++; } static void thaw_cursor_moved(WPTextBuffer * buffer) { g_return_if_fail(buffer->priv->cursor_moved_frozen > 0); if (!--buffer->priv->cursor_moved_frozen && buffer->priv->cursor_moved) { GtkTextIter iter; GtkTextMark *insert = gtk_text_buffer_get_insert(GTK_TEXT_BUFFER(buffer)); gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(buffer), &iter, insert); emit_refresh_attributes(buffer, &iter); buffer->priv->cursor_moved = FALSE; } } /** * Check if a tag is a tag from the base, and find the * difference number between the tag's id and the base. It is used * to retrieve the font size from a font tag * @param tag is a #GtkTextTag * @param base is a number identifying the base (#WPT_FONT_SIZE, #WPT_SUP_SRPT, * #WPT_SUB_SRPT, #WPT_FONT) * @param nr pointer to a number which will hold the difference * @return TRUE if the tag is of type base */ static gboolean check_tag_type(GtkTextTag * tag, gint base, gint * nr) { gint val = (gint) g_object_get_data(G_OBJECT(tag), WPT_ID); gboolean result = val >= base && val <= base + 999; if (result && nr) *nr = val - base; return result; } /** * Check if a tag is modifying the font size * @param tag is a #GtkTextTag * @return TRUE if the tag is modifying the size */ static gboolean check_tag_fontsize_type(GtkTextTag * tag) { gint val = (gint) g_object_get_data(G_OBJECT(tag), WPT_ID); return (val >= WPT_FONT_SIZE && val <= WPT_FONT_SIZE + 999) || (val >= WPT_SUB_SRPT && val <= WPT_SUB_SRPT + 999) || (val >= WPT_SUP_SRPT && val <= WPT_SUP_SRPT + 999); } #define HILDON_BASE_COLOR_NUM 15 static void wp_text_buffer_init_tags(WPTextBuffer * buffer) { int i; GtkTextBuffer *b = GTK_TEXT_BUFFER(buffer); gchar *tmp; WPTextBufferPrivate *priv = buffer->priv; static char *base_colours[HILDON_BASE_COLOR_NUM] = { "#FFFFFF", "#FF0000", "#660000", "#0000FF", "#000066", "#FF33FF", "#660066", "#33CC33", "#006600", "#FFCC00", "#CC9900", "#999999", "#666666", "#00CCCC", "#006666" }; GdkColor color = { 0 }; priv->tags[WPT_BOLD] = gtk_text_buffer_create_tag(b, tagnames[WPT_BOLD], "weight", PANGO_WEIGHT_BOLD, NULL); g_hash_table_insert(priv->tag_hash, priv->tags[WPT_BOLD], NULL); priv->tags[WPT_ITALIC] = gtk_text_buffer_create_tag(b, tagnames[WPT_ITALIC], "style", PANGO_STYLE_ITALIC, NULL); g_hash_table_insert(priv->tag_hash, priv->tags[WPT_ITALIC], NULL); priv->tags[WPT_UNDERLINE] = gtk_text_buffer_create_tag(b, tagnames[WPT_UNDERLINE], "underline", PANGO_UNDERLINE_SINGLE, NULL); g_hash_table_insert(priv->tag_hash, priv->tags[WPT_UNDERLINE], NULL); priv->tags[WPT_STRIKE] = gtk_text_buffer_create_tag(b, tagnames[WPT_STRIKE], "strikethrough", TRUE, NULL); g_hash_table_insert(priv->tag_hash, priv->tags[WPT_STRIKE], NULL); priv->tags[WPT_LEFT] = gtk_text_buffer_create_tag(b, tagnames[WPT_LEFT], "justification", GTK_JUSTIFY_LEFT, NULL); g_hash_table_insert(priv->tag_hash, priv->tags[WPT_LEFT], NULL); priv->tags[WPT_CENTER] = gtk_text_buffer_create_tag(b, tagnames[WPT_CENTER], "justification", GTK_JUSTIFY_CENTER, NULL); g_hash_table_insert(priv->tag_hash, priv->tags[WPT_CENTER], NULL); priv->tags[WPT_RIGHT] = gtk_text_buffer_create_tag(b, tagnames[WPT_RIGHT], "justification", GTK_JUSTIFY_RIGHT, NULL); g_hash_table_insert(priv->tag_hash, priv->tags[WPT_RIGHT], NULL); for (i = 0; i < WP_FONT_SIZE_COUNT; i++) { /* Normal size */ tmp = g_strdup_printf("wp-text-font-size-%d", i); priv->font_size_tags[i] = gtk_text_buffer_create_tag(b, tmp, NULL); g_object_set_data(G_OBJECT(priv->font_size_tags[i]), WPT_ID, GINT_TO_POINTER(WPT_FONT_SIZE + i)); g_free(tmp); g_hash_table_insert(priv->tag_hash, priv->font_size_tags[i], NULL); /* Superscript size */ tmp = g_strdup_printf("wp-text-sup-%d", i); priv->font_size_sup_tags[i] = gtk_text_buffer_create_tag(b, tmp, NULL); g_object_set_data(G_OBJECT(priv->font_size_sup_tags[i]), WPT_ID, GINT_TO_POINTER(WPT_SUP_SRPT + i)); g_free(tmp); g_hash_table_insert(priv->tag_hash, priv->font_size_sup_tags[i], NULL); /* Subscript size */ tmp = g_strdup_printf("wp-text-sub-%d", i); priv->font_size_sub_tags[i] = gtk_text_buffer_create_tag(b, tmp, NULL); g_object_set_data(G_OBJECT(priv->font_size_sub_tags[i]), WPT_ID, GINT_TO_POINTER(WPT_SUB_SRPT + i)); g_free(tmp); g_hash_table_insert(priv->tag_hash, priv->font_size_sub_tags[i], NULL); } wp_text_buffer_resize_font(buffer); /* Create font tags from all available fonts */ priv->fonts = g_new(GtkTextTag *, wp_get_font_count()); for (i = 0; i < wp_get_font_count(); i++) { tmp = g_strdup_printf("wp-text-font-%s", wp_get_font_name(i)); priv->fonts[i] = gtk_text_buffer_create_tag(b, tmp, "family", wp_get_font_name(i), NULL); g_free(tmp); g_object_set_data(G_OBJECT(priv->fonts[i]), WPT_ID, GINT_TO_POINTER(WPT_FONT + i)); g_hash_table_insert(priv->tag_hash, priv->fonts, NULL); // printf("Ordered font: %s\n", priv->font_name_list[i]); } // Create the bullet last to have the highest priority priv->tags[WPT_BULLET] = gtk_text_buffer_create_tag(b, tagnames[WPT_BULLET], "weight", PANGO_WEIGHT_NORMAL, "style", PANGO_STYLE_NORMAL, "underline", PANGO_UNDERLINE_NONE, "font", "fixed", "strikethrough", FALSE, "indent", 8, NULL); g_hash_table_insert(priv->tag_hash, priv->tags[WPT_BULLET], NULL); for (i = 0; i < HILDON_BASE_COLOR_NUM; i++) { gdk_color_parse(base_colours[i], &color); g_hash_table_insert(priv->tag_hash, color_buffer_get_tag(priv->color_tags, &color, priv->tags[WPT_RIGHT]-> priority + 1), NULL); } } GtkTextTag * wp_text_buffer_get_tag(WPTextBuffer * buffer, gint tagno) { g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer) && tagno < WPT_LASTTAG, NULL); return buffer->priv->tags[tagno]; } /* TODO optimization if a font-size type is copied and cs.font_size is set, * because does an unnecessary apply/remove attributes (also the case of * sub/superscript) */ static void wp_text_buffer_copy_tag_attributes(WPTextBuffer * buffer, GSList * tags, GtkTextIter * start, GtkTextIter * end) { GtkTextBuffer *text_buffer; GtkTextTag *tag; WPTextBufferFormatChangeSet cs; GtkTextTag **ttags; // printf("wp_text_buffer_copy_tag_attributes\n"); // debug_print_tags(start, 0); text_buffer = GTK_TEXT_BUFFER(buffer); cs = buffer->priv->fmt.cs; ttags = buffer->priv->tags; while (tags) { tag = GTK_TEXT_TAG(tags->data); tags = tags->next; // printf("Tag: %p, %s\n", tag, tag->name ? tag->name : "(null)"); // if (tag != ttags[WPT_BULLET]) { if ((tag == ttags[WPT_BOLD] && !cs.bold) || (tag == ttags[WPT_ITALIC] && !cs.italic) || (tag == ttags[WPT_UNDERLINE] && !cs.underline) || (tag == ttags[WPT_STRIKE] && !cs.strikethrough) || (tag->rise_set && (!cs.font_size || !cs.text_position)) || (tag->justification_set && !cs.justification) || (tag->fg_color_set && !cs.color) || (!cs.font && tag->values->font && check_tag_type(tag, WPT_FONT, NULL)) || ((!cs.font_size || !cs. text_position) && tag->values-> font && check_tag_type (tag, WPT_FONT_SIZE, NULL))) { if (tag->justification_set && gtk_text_iter_is_end(end) && tag->values->justification != buffer->priv->last_line_justification) { buffer->priv->fmt.justification = buffer->priv->last_line_justification; buffer->priv->fmt.cs.justification = TRUE; } else gtk_text_buffer_apply_tag(text_buffer, tag, start, end); } } } wp_text_buffer_apply_attributes(buffer, start, end, FALSE, NULL); } /** * Remove all the tags with id tagid between start and * end interval. * @param buffer is a #GtkTextBuffer * @param start a position in the buffer * @param end a position in the buffer * @param tagid is the id identifying the tag to be removed (WPT_x) */ static void remove_tags_with_id(WPTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end, gint tagid) { GSList *tags, *tags_head; GtkTextTag *tag = NULL; GtkTextIter tmp; GtkTextBuffer *text_buffer; g_return_if_fail(buffer); tags_head = tags = gtk_text_iter_get_tags(start); tmp = *start; text_buffer = GTK_TEXT_BUFFER(buffer); do { while (tags) { tag = GTK_TEXT_TAG(tags->data); tags = tags->next; switch (tagid) { case WPT_FORECOLOR: if (tag->fg_color_set) gtk_text_buffer_remove_tag(text_buffer, tag, start, end); break; case WPT_FONT: if (tag->values->font && check_tag_type(tag, WPT_FONT, NULL)) gtk_text_buffer_remove_tag(text_buffer, tag, start, end); break; case WPT_FONT_SIZE: if (tag->values->font && check_tag_type(tag, WPT_FONT_SIZE, NULL)) gtk_text_buffer_remove_tag(text_buffer, tag, start, end); break; case WPT_SUB_SRPT: if (tag->values->font && check_tag_type(tag, WPT_SUB_SRPT, NULL)) gtk_text_buffer_remove_tag(text_buffer, tag, start, end); break; case WPT_SUP_SRPT: if (tag->values->font && check_tag_type(tag, WPT_SUP_SRPT, NULL)) gtk_text_buffer_remove_tag(text_buffer, tag, start, end); break; case WPT_ALL_FONT_SIZE: if (tag->values->font && check_tag_fontsize_type(tag)) gtk_text_buffer_remove_tag(text_buffer, tag, start, end); break; } } g_slist_free(tags_head); if (!gtk_text_iter_forward_to_tag_toggle(&tmp, NULL) || (gtk_text_iter_compare(&tmp, end) >= 0)) break; tags_head = tags = gtk_text_iter_get_toggled_tags(&tmp, TRUE); } while (1); } /** * Remove tag from buffer, and sets end to the tag * end or buffer_end if the tag toggle is after the end * @param buffer is a #GtkTextBuffer * @param tag is a #GtkTextTag * @param start a position in the buffer * @param end a position in the buffer, this is return parameter * @param buffer_end a position in the buffer */ static void remove_buffer_tag(GtkTextBuffer * text_buffer, GtkTextTag * tag, GtkTextIter * start, GtkTextIter * end, GtkTextIter * buffer_end) { *end = *start; if (gtk_text_iter_forward_to_tag_toggle(end, tag)) { if (gtk_text_iter_compare(end, buffer_end) > 0) *end = *buffer_end; } else *end = *buffer_end; gtk_text_buffer_remove_tag(text_buffer, tag, start, end); } /** * Change the font tags in buffer between the start and * end interval to the new size size and position pos * @param buffer is a #GtkTextBuffer * @param start a position in the buffer * @param end a position in the buffer, this is return parameter * @param size new font size * @param size new text position or NULL */ static void change_font_tags(WPTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end, gint size, TextPosition * pos) { GSList *tags, *tags_head; GtkTextTag *tag = NULL; GtkTextIter tmp, tmp_end; GtkTextBuffer *text_buffer; struct _WPTextBufferPrivate *priv; gint n; gboolean font_size_tag_found = FALSE; g_return_if_fail(buffer); tags_head = tags = gtk_text_iter_get_tags(start); tmp = *start; text_buffer = GTK_TEXT_BUFFER(buffer); priv = buffer->priv; do { while (tags) { tag = GTK_TEXT_TAG(tags->data); tags = tags->next; if (tag->values->font) { if (check_tag_type(tag, WPT_FONT_SIZE, &n) && (!pos || *pos != TEXT_POSITION_NORMAL)) { remove_buffer_tag(text_buffer, tag, &tmp, &tmp_end, end); font_size_tag_found = TRUE; if (!pos) gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_tags[size], &tmp, &tmp_end); else if (*pos == TEXT_POSITION_SUBSCRIPT) gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_sub_tags[n], &tmp, &tmp_end); else gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_sup_tags[n], &tmp, &tmp_end); } else if (check_tag_type(tag, WPT_SUB_SRPT, &n) && (!pos || *pos != TEXT_POSITION_SUBSCRIPT)) { remove_buffer_tag(text_buffer, tag, &tmp, &tmp_end, end); font_size_tag_found = TRUE; if (!pos) gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_sub_tags[size], &tmp, &tmp_end); else if (*pos == TEXT_POSITION_NORMAL) gtk_text_buffer_apply_tag(text_buffer, priv->font_size_tags[n], &tmp, &tmp_end); else gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_sup_tags[n], &tmp, &tmp_end); } else if (check_tag_type(tag, WPT_SUP_SRPT, &n) && (!pos || *pos != TEXT_POSITION_SUPERSCRIPT)) { remove_buffer_tag(text_buffer, tag, &tmp, &tmp_end, end); font_size_tag_found = TRUE; if (!pos) gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_sup_tags[size], &tmp, &tmp_end); else if (*pos == TEXT_POSITION_NORMAL) gtk_text_buffer_apply_tag(text_buffer, priv->font_size_tags[n], &tmp, &tmp_end); else gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_sub_tags[n], &tmp, &tmp_end); } } } g_slist_free(tags_head); if (!gtk_text_iter_forward_to_tag_toggle(&tmp, NULL) || (gtk_text_iter_compare(&tmp, end) >= 0)) break; tags_head = tags = gtk_text_iter_get_toggled_tags(&tmp, TRUE); } while (1); /* If no font size tag was found, we need to set at least one new font * size tag */ if (!font_size_tag_found) { tmp = *start; if (gtk_text_iter_compare (start, end) == 0) { gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(buffer), &tmp_end); } else { tmp_end = *end; } if (!pos || *pos == TEXT_POSITION_NORMAL) gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_tags[size], &tmp, &tmp_end); else if (*pos == TEXT_POSITION_SUBSCRIPT) gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_sub_tags[size], &tmp, &tmp_end); else gtk_text_buffer_apply_tag(text_buffer, priv-> font_size_sup_tags[size], &tmp, &tmp_end); } } static gboolean wp_text_buffer_apply_attributes(WPTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end, gboolean undo, WPTextBufferFormat * fmt) { WPTextBufferFormatChangeSet cs; GtkTextTag **ttags; GtkTextIter siter, eiter; GtkTextBuffer *text_buffer; gboolean set_justification = FALSE; gboolean clear_set = FALSE; gboolean buffer_end = FALSE; gboolean result = FALSE; WPTextBufferPrivate *priv = buffer->priv; gtk_text_buffer_begin_user_action(GTK_TEXT_BUFFER(buffer)); wp_text_buffer_check_apply_tag(buffer); if (!fmt) { fmt = &buffer->priv->fmt; clear_set = TRUE; } cs = fmt->cs; if (*(gint *) & cs != 0) { ttags = buffer->priv->tags; text_buffer = GTK_TEXT_BUFFER(buffer); if (gtk_text_iter_ends_tag(start, buffer->priv->tags[WPT_BULLET])) { gtk_text_iter_backward_char(start); _wp_text_iter_skip_bullet(start, buffer->priv->tags[WPT_BULLET], FALSE); } if (cs.justification) { siter = *start; eiter = *end; if (undo || fmt->bullet) gtk_text_iter_set_line_offset(&siter, 0); if (undo) { if (!gtk_text_iter_ends_line(&eiter)) gtk_text_iter_forward_to_line_end(&eiter); gtk_text_iter_forward_char(&eiter); } if ((buffer_end = gtk_text_iter_is_end(&eiter))) emit_default_justification_changed(buffer, fmt->justification); if (gtk_text_iter_equal(&siter, &eiter)) { if (buffer_end) { gtk_text_buffer_end_user_action(GTK_TEXT_BUFFER(buffer)); return result; } else set_justification = TRUE; } else { // TODO: It would be better if we only save the justification result = TRUE; if (undo) wp_undo_apply_tag(buffer->priv->undo, &siter, &eiter, NULL, FALSE); gtk_text_buffer_set_modified(text_buffer, TRUE); cs.justification = FALSE; /* printf("Line: %d-%d\n", gtk_text_iter_get_offset(&siter), * gtk_text_iter_get_offset(&eiter)); */ if (fmt->justification == GTK_JUSTIFY_LEFT) gtk_text_buffer_apply_tag(text_buffer, ttags[WPT_LEFT], &siter, &eiter); else if (undo) gtk_text_buffer_remove_tag(text_buffer, ttags[WPT_LEFT], &siter, &eiter); if (fmt->justification == GTK_JUSTIFY_CENTER) gtk_text_buffer_apply_tag(text_buffer, ttags[WPT_CENTER], &siter, &eiter); else if (undo) gtk_text_buffer_remove_tag(text_buffer, ttags[WPT_CENTER], &siter, &eiter); if (fmt->justification == GTK_JUSTIFY_RIGHT) gtk_text_buffer_apply_tag(text_buffer, ttags[WPT_RIGHT], &siter, &eiter); else if (undo) gtk_text_buffer_remove_tag(text_buffer, ttags[WPT_RIGHT], &siter, &eiter); } } if ((*(gint *) & cs) != 0 && !gtk_text_iter_equal(start, end)) { gtk_text_buffer_set_modified(text_buffer, TRUE); result = TRUE; if (undo) wp_undo_apply_tag(buffer->priv->undo, start, end, NULL, FALSE); if (cs.bold) { if (fmt->bold) gtk_text_buffer_apply_tag(text_buffer, ttags[WPT_BOLD], start, end); else gtk_text_buffer_remove_tag(text_buffer, ttags[WPT_BOLD], start, end); } if (cs.italic) { if (fmt->italic) gtk_text_buffer_apply_tag(text_buffer, ttags[WPT_ITALIC], start, end); else gtk_text_buffer_remove_tag(text_buffer, ttags[WPT_ITALIC], start, end); } if (cs.underline) { if (fmt->underline) gtk_text_buffer_apply_tag(text_buffer, ttags[WPT_UNDERLINE], start, end); else gtk_text_buffer_remove_tag(text_buffer, ttags[WPT_UNDERLINE], start, end); } if (cs.strikethrough) { if (fmt->strikethrough) gtk_text_buffer_apply_tag(text_buffer, ttags[WPT_STRIKE], start, end); else gtk_text_buffer_remove_tag(text_buffer, ttags[WPT_STRIKE], start, end); } // maybe for the black color don't need to have a separate tag! if (cs.color) { if (undo) remove_tags_with_id(buffer, start, end, WPT_FORECOLOR); if (fmt->color.red || fmt->color.blue || fmt->color.green) { GtkTextTag *tag = color_buffer_get_tag(buffer->priv->color_tags, &fmt->color, ttags[WPT_RIGHT]->priority + 1); gtk_text_buffer_apply_tag(text_buffer, tag, start, end); g_hash_table_insert(priv->tag_hash, tag, NULL); } } if (cs.font) { if (undo) remove_tags_with_id(buffer, start, end, WPT_FONT); gtk_text_buffer_apply_tag(text_buffer, buffer->priv->fonts[fmt->font], start, end); } if (cs.font_size && cs.text_position) { if (undo) remove_tags_with_id(buffer, start, end, WPT_ALL_FONT_SIZE); switch (fmt->text_position) { case TEXT_POSITION_NORMAL: gtk_text_buffer_apply_tag(text_buffer, buffer->priv-> font_size_tags[fmt-> font_size], start, end); break; case TEXT_POSITION_SUPERSCRIPT: gtk_text_buffer_apply_tag(text_buffer, buffer->priv-> font_size_sup_tags[fmt-> font_size], start, end); break; case TEXT_POSITION_SUBSCRIPT: gtk_text_buffer_apply_tag(text_buffer, buffer->priv-> font_size_sub_tags[fmt-> font_size], start, end); break; } } else if ((cs.font_size && !cs.text_position) || (!cs.font_size && cs.text_position)) { change_font_tags(buffer, start, end, fmt->font_size, cs.font_size ? NULL : &fmt->text_position); } } if (clear_set) { *(gint *) & buffer->priv->fmt.cs = 0; if (set_justification) buffer->priv->fmt.cs.justification = TRUE; } } gtk_text_buffer_end_user_action(GTK_TEXT_BUFFER(buffer)); return result; } void wp_text_buffer_insert_with_attribute(WPTextBuffer * buffer, GtkTextIter * pos, gchar * text, gint len, WPTextBufferFormat * fmt, gboolean disable_undo) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); gint offset; GtkTextIter start, end; GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER(buffer); WPTextBufferPrivate *priv = buffer->priv; if (disable_undo) wp_undo_freeze(priv->undo); gtk_text_buffer_begin_user_action(text_buffer); offset = gtk_text_iter_get_offset(pos); gtk_text_buffer_insert(text_buffer, pos, text, len); gtk_text_buffer_get_iter_at_offset(text_buffer, &start, offset); wp_text_buffer_apply_attributes(buffer, &start, pos, !disable_undo, fmt); if (fmt->bullet) { GtkTextTag *tag = _wp_text_buffer_get_bullet_tag(buffer); if (_wp_text_iter_put_bullet_line(&start, tag)) { end = start; gtk_text_iter_set_line_offset(&start, 0); wp_text_buffer_apply_attributes(buffer, &start, &end, !disable_undo, fmt); } } gtk_text_buffer_end_user_action(text_buffer); if (disable_undo) wp_undo_thaw(buffer->priv->undo); } void wp_text_buffer_insert_image(WPTextBuffer * buffer, GtkTextIter * pos, const gchar * image_id, GdkPixbuf * pixbuf) { GtkTextTag *pixbuf_tag; GtkTextTagTable *tag_table; GtkTextIter iter2; gchar *tag_id; gchar *img_id_copy = NULL; g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); g_return_if_fail(image_id); img_id_copy = g_strdup(image_id); tag_id = g_strdup_printf("image-tag-%s", image_id); tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer)); pixbuf_tag = gtk_text_tag_table_lookup (tag_table, tag_id); if (pixbuf_tag == NULL) pixbuf_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer), tag_id, NULL); g_object_set_data_full (G_OBJECT (pixbuf_tag), "image-index", img_id_copy, (GDestroyNotify) g_free); g_object_set_data(G_OBJECT(pixbuf_tag), "image-set", GINT_TO_POINTER(TRUE)); gtk_text_buffer_insert_pixbuf(GTK_TEXT_BUFFER(buffer), pos, pixbuf); iter2 = *pos; gtk_text_iter_backward_char(&iter2); gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(buffer), pixbuf_tag, &iter2, pos); g_free(tag_id); buffer->priv->queue_undo_reset = TRUE; wp_undo_reset (buffer->priv->undo); } void wp_text_buffer_replace_image (WPTextBuffer *buffer, const gchar *image_id, GdkPixbuf *pixbuf) { GtkTextIter iter, start, end; gchar *replace_tag_id; g_return_if_fail (WP_IS_TEXT_BUFFER(buffer)); g_return_if_fail (image_id); GtkTextTagTable *tag_table; GtkTextTag *tag; replace_tag_id = g_strdup_printf ("image-tag-replace-%s", image_id); tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer)); tag = gtk_text_tag_table_lookup (tag_table, (const gchar *) replace_tag_id); if (tag) { gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter); wp_undo_freeze (buffer->priv->undo); while (!gtk_text_iter_is_end (&iter)) { if (gtk_text_iter_begins_tag (&iter, tag)) { GtkTextIter end; wp_text_buffer_insert_image (buffer, &iter, image_id, pixbuf); end = iter; gtk_text_iter_forward_char (&iter); gtk_text_buffer_delete(GTK_TEXT_BUFFER (buffer), &iter, &end); } gtk_text_iter_forward_char (&iter); } wp_undo_thaw (buffer->priv->undo); gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start); gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &end); gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), tag, &start, &end); } } void wp_text_buffer_insert_image_replacement (WPTextBuffer *buffer, GtkTextIter *pos, const gchar *image_id) { GtkTextTag *pixbuf_tag; GtkTextTagTable *tag_table; gchar *tag_id; gchar * img_id_copy = NULL; g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); g_return_if_fail (image_id); img_id_copy = g_strdup (image_id); tag_id = g_strdup_printf("image-tag-replace-%s", image_id); tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer)); pixbuf_tag = gtk_text_tag_table_lookup (tag_table, tag_id); if (pixbuf_tag == NULL) pixbuf_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer), tag_id, NULL); gtk_text_buffer_insert_with_tags (GTK_TEXT_BUFFER (buffer), pos, " ", 1, pixbuf_tag, NULL); g_free (tag_id); } void wp_text_buffer_delete_image(WPTextBuffer * buffer, const gchar * image_id) { GtkTextTagTable *tag_table; gchar *tag_id; GtkTextTag *tag; GtkTextIter start, end; g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); tag_table = gtk_text_buffer_get_tag_table(GTK_TEXT_BUFFER(buffer)); tag_id = g_strdup_printf("image-tag-%s", image_id); tag = gtk_text_tag_table_lookup(tag_table, tag_id); g_free(tag_id); if (tag != NULL) { gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(buffer), &start); gtk_text_iter_forward_to_tag_toggle(&start, tag); end = start; gtk_text_iter_forward_to_tag_toggle(&end, tag); gtk_text_buffer_remove_tag(GTK_TEXT_BUFFER(buffer), tag, &start, &end); gtk_text_buffer_delete(GTK_TEXT_BUFFER(buffer), &start, &end); } buffer->priv->queue_undo_reset = TRUE; wp_undo_reset (buffer->priv->undo); } gboolean wp_text_buffer_set_attribute(WPTextBuffer * buffer, gint tagid, gpointer data) { g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer), FALSE); WPTextBufferPrivate *priv = buffer->priv; gboolean enable = (gboolean) data; // wp_undo_reset_mergeable(buffer->priv->undo); switch (tagid) { case WPT_BOLD: priv->fmt.bold = enable; priv->fmt.cs.bold = TRUE; break; case WPT_ITALIC: priv->fmt.italic = enable; priv->fmt.cs.italic = TRUE; break; case WPT_UNDERLINE: priv->fmt.underline = enable; priv->fmt.cs.underline = TRUE; break; case WPT_STRIKE: priv->fmt.strikethrough = enable; priv->fmt.cs.strikethrough = TRUE; break; case WPT_SUP_SRPT: priv->fmt.text_position = enable ? TEXT_POSITION_SUPERSCRIPT : TEXT_POSITION_NORMAL; priv->fmt.cs.text_position = TRUE; break; case WPT_SUB_SRPT: priv->fmt.text_position = enable ? TEXT_POSITION_SUBSCRIPT : TEXT_POSITION_NORMAL; priv->fmt.cs.text_position = TRUE; break; case WPT_LEFT: priv->fmt.justification = GTK_JUSTIFY_LEFT; priv->fmt.cs.justification = TRUE; break; case WPT_RIGHT: priv->fmt.justification = GTK_JUSTIFY_RIGHT; priv->fmt.cs.justification = TRUE; break; case WPT_CENTER: priv->fmt.justification = GTK_JUSTIFY_CENTER; priv->fmt.cs.justification = TRUE; break; case WPT_BULLET: if (enable) _wp_text_buffer_put_bullet(buffer); else _wp_text_buffer_remove_bullet(buffer); return TRUE; break; case WPT_FONT: priv->fmt.font = (gint) data; priv->fmt.cs.font = TRUE; break; case WPT_FONT_SIZE: priv->fmt.font_size = (gint) data; priv->fmt.cs.font_size = TRUE; break; case WPT_FORECOLOR: priv->fmt.color = *(GdkColor *) data; priv->fmt.cs.color = TRUE; break; default: // unknown id return FALSE; } return wp_text_buffer_set_format(buffer, NULL); } gboolean wp_text_buffer_set_format(WPTextBuffer * buffer, WPTextBufferFormat * fmt) { g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer), FALSE); GtkTextIter start, end; WPTextBufferPrivate *priv = buffer->priv; gboolean send = TRUE; wp_undo_reset_mergeable(priv->undo); if (fmt) priv->fmt = *fmt; if (!priv->is_empty || priv->fmt.cs.justification) { if (gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER(buffer), &start, &end) || priv->fmt.cs.justification) send = !wp_text_buffer_apply_attributes(buffer, &start, &end, TRUE, NULL); else if (gtk_text_iter_inside_word(&start) && !gtk_text_iter_starts_word(&start)) { end = start; // if (!gtk_text_iter_starts_word(&start)) gtk_text_iter_backward_word_start(&start); gtk_text_iter_forward_word_end(&end); send = !wp_text_buffer_apply_attributes(buffer, &start, &end, TRUE, NULL); } } // printf("Attributes: %d\n", send); if (fmt && send) g_signal_emit(buffer, signals[REFRESH_ATTRIBUTES], 0); return !send; } /** * Update the toggled attributes in cs for the selection * between start and end in the buffer * @param buffer is a #GtkTextBuffer * @param start a position in the buffer * @param end a position in the buffer * @param fmt is a #WPTextBufferFormat which contains the formatting tags. * @param cs is a #WPTextBufferFormatChangeSet which contains the formatting tags * change set. It will be set if there is a tag toggle. */ static void update_toggled_attributes(WPTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end, WPTextBufferFormat * fmt, WPTextBufferFormatChangeSet * cs) { GtkTextTag *tag; GtkTextTag **ttags; GtkTextIter iter; GSList *tags, *tags_head; gint bullet_last_line = -1, line; // printf("update_toggled_attributes\n"); if (fmt->bullet) bullet_last_line = gtk_text_iter_get_line(start); ttags = buffer->priv->tags; iter = *start; while (gtk_text_iter_forward_to_tag_toggle(&iter, NULL)) { if (gtk_text_iter_compare(&iter, end) >= 0) break; // printf("Offset: %d\n", gtk_text_iter_get_offset(&iter)); tags_head = gtk_text_iter_get_toggled_tags(&iter, FALSE); tags = tags_head = g_slist_concat(tags_head, gtk_text_iter_get_toggled_tags(&iter, TRUE)); while (tags) { tag = GTK_TEXT_TAG(tags->data); tags = tags->next; // printf("Tag toggle: %p, %s\n", tag, tag->name ? tag->name : // "(null)"); if (tag == ttags[WPT_BOLD]) cs->bold = TRUE; else if (tag == ttags[WPT_ITALIC]) cs->italic = TRUE; else if (tag == ttags[WPT_UNDERLINE]) cs->underline = TRUE; else if (tag == ttags[WPT_STRIKE]) cs->strikethrough = TRUE; else if (tag == ttags[WPT_BULLET] && !cs->bullet) { if (!fmt->bullet) cs->bullet = TRUE; else { line = gtk_text_iter_get_line(&iter); if (line - bullet_last_line > 1) cs->bullet = TRUE; else bullet_last_line = line; } } else if (tag->rise_set) cs->text_position = TRUE; else if (tag->justification_set) cs->justification = TRUE; else if (tag->fg_color_set) cs->color = TRUE; else if (!cs->font && tag->values->font && check_tag_type(tag, WPT_FONT, NULL)) cs->font = TRUE; else if (!cs->font_size && tag->values->font && check_tag_type(tag, WPT_FONT_SIZE, NULL)) cs->font_size = TRUE; } g_slist_free(tags_head); } if (fmt->bullet && !cs->bullet) { iter = *end; cs->bullet = !_wp_text_iter_has_bullet(&iter, ttags[WPT_BULLET]); } } static gboolean _wp_text_buffer_get_attributes(WPTextBuffer * buffer, WPTextBufferFormat * fmt, gboolean set_changed, gboolean parse_selection) { GtkTextBuffer *text_buffer; GtkTextIter start, end, tag_place, tmp; GtkTextTag *tag; GtkTextTag **ttags; GSList *tags, *tags_head; gboolean selection; gint n; g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer), FALSE); if (!fmt) return FALSE; // printf("wp_text_buffer_get_attributes\n"); text_buffer = GTK_TEXT_BUFFER(buffer); selection = gtk_text_buffer_get_selection_bounds(text_buffer, &start, &end); if(!selection){ GtkTextMark *mark; mark = gtk_text_buffer_get_insert(text_buffer); gtk_text_buffer_get_iter_at_mark(text_buffer, &start, mark); tag_place = end = start; gtk_text_iter_backward_char(&tag_place); } else tag_place = start; tags = tags_head = gtk_text_iter_get_tags(&tag_place); ttags = buffer->priv->tags; memset(fmt, 0, sizeof(WPTextBufferFormat)); fmt->font_size = buffer->priv->default_fmt.font_size; fmt->font = buffer->priv->default_fmt.font; while (tags) { tag = GTK_TEXT_TAG(tags->data); tags = tags->next; // printf("Tag: %p, %s\n", tag, tag->name ? tag->name : "(null)"); if (tag == ttags[WPT_BOLD]) { fmt->bold = TRUE; fmt->cs.bold = set_changed; } else if (tag == ttags[WPT_ITALIC]) { fmt->italic = TRUE; fmt->cs.italic = set_changed; } else if (tag == ttags[WPT_UNDERLINE]) { fmt->underline = TRUE; fmt->cs.underline = set_changed; } else if (tag == ttags[WPT_STRIKE]) { fmt->strikethrough = TRUE; fmt->cs.strikethrough = set_changed; } else if (tag->rise_set) { n = (gint) g_object_get_data(G_OBJECT(tag), WPT_ID); if (n >= WPT_SUB_SRPT) { fmt->text_position = TEXT_POSITION_SUBSCRIPT; fmt->font_size = n - WPT_SUB_SRPT; } else if (n >= WPT_SUP_SRPT) { fmt->text_position = TEXT_POSITION_SUPERSCRIPT; fmt->font_size = n - WPT_SUP_SRPT; } else continue; fmt->cs.text_position = set_changed; fmt->cs.font_size = set_changed; } else if (tag->justification_set) { fmt->justification = tag == ttags[WPT_LEFT] ? GTK_JUSTIFY_LEFT : tag == ttags[WPT_CENTER] ? GTK_JUSTIFY_CENTER : GTK_JUSTIFY_RIGHT; fmt->cs.justification = set_changed; } else if (tag->fg_color_set) { fmt->color = tag->values->appearance.fg_color; fmt->cs.color = set_changed; } else if (tag->values->font && check_tag_type(tag, WPT_FONT, &n)) { fmt->font = n; fmt->cs.font = set_changed; } else if (tag->values->font && check_tag_type(tag, WPT_FONT_SIZE, &n)) { fmt->font_size = n; fmt->cs.text_position = set_changed; fmt->cs.font_size = set_changed; } } g_slist_free(tags_head); tmp = start; /*iter will be modifyed by asking for bullet*/ fmt->bullet = _wp_text_iter_has_bullet(&tmp, ttags[WPT_BULLET]); if (gtk_text_iter_is_end(&end)) fmt->justification = buffer->priv->last_line_justification; if (selection && parse_selection && !set_changed) update_toggled_attributes(buffer, &start, &end, fmt, &fmt->cs); return selection && parse_selection && !set_changed; } void wp_text_buffer_get_attributes(WPTextBuffer * buffer, WPTextBufferFormat * fmt, gboolean parse_selection) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); if (buffer->priv->is_empty) { buffer->priv->fmt.bullet = FALSE; *fmt = buffer->priv->fmt; memset(&fmt->cs, 0, sizeof(WPTextBufferFormatChangeSet)); } else { if (!_wp_text_buffer_get_attributes(buffer, fmt, FALSE, parse_selection)) { WPTextBufferFormat *old = &buffer->priv->fmt; WPTextBufferFormatChangeSet cs = old->cs; if (cs.bold) fmt->bold = old->bold; if (cs.italic) fmt->italic = old->italic; if (cs.underline) fmt->underline = old->underline; if (cs.strikethrough) fmt->strikethrough = old->strikethrough; if (cs.text_position) fmt->text_position = old->text_position; if (cs.color) fmt->color = old->color; if (cs.font) fmt->font = old->font; if (cs.font_size) fmt->font_size = old->font_size; } } fmt->rich_text = buffer->priv->is_rich_text; } void wp_text_buffer_get_current_state(WPTextBuffer * buffer, WPTextBufferFormat * fmt) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); _wp_text_buffer_get_attributes(buffer, fmt, FALSE, FALSE); memset(&fmt->cs, 0, sizeof(WPTextBufferFormatChangeSet)); } /******** * Bullets */ gboolean _wp_text_iter_is_bullet(GtkTextIter * iter, GtkTextTag * tag) { return gtk_text_iter_has_tag(iter, tag); } gboolean _wp_text_iter_skip_bullet(GtkTextIter * iter, GtkTextTag * tag, gboolean forward) { gboolean result; if ((result = gtk_text_iter_has_tag(iter, tag))) { if (forward) { if (!gtk_text_iter_ends_tag(iter, tag)) gtk_text_iter_forward_to_tag_toggle(iter, tag); /* while (!gtk_text_iter_ends_tag(iter, tag)) * gtk_text_iter_forward_char(iter); */ } else { if (!gtk_text_iter_begins_tag(iter, tag)) gtk_text_iter_backward_to_tag_toggle(iter, tag); /* while (!gtk_text_iter_begins_tag(iter, tag)) * gtk_text_iter_backward_char(iter); */ } } return result; } gboolean _wp_text_iter_has_bullet(GtkTextIter * iter, GtkTextTag * tag) { if (!gtk_text_iter_starts_line(iter)) gtk_text_iter_set_line_offset(iter, 0); // return _wp_text_iter_is_bullet(iter, tag); return gtk_text_iter_toggles_tag(iter, tag); } gboolean _wp_text_iter_put_bullet_line(GtkTextIter * iter, GtkTextTag * tag) { gboolean result; if ((result = !_wp_text_iter_has_bullet(iter, tag))) { GtkTextBuffer *buffer = gtk_text_iter_get_buffer(iter); WP_TEXT_BUFFER(buffer)->priv->force_copy = TRUE; gtk_text_buffer_insert_with_tags(buffer, iter, "\xe2\x80\xa2\xc2\xa0\xc2\xa0", -1, tag, NULL); } return result; } void _wp_text_iter_remove_bullet_line(GtkTextIter * iter, GtkTextTag * tag) { GtkTextIter end; if (_wp_text_iter_has_bullet(iter, tag)) { end = *iter; _wp_text_iter_skip_bullet(&end, tag, TRUE); gtk_text_buffer_delete(gtk_text_iter_get_buffer(iter), iter, &end); } } static void _wp_text_buffer_put_bullet(WPTextBuffer * buffer) { GtkTextIter iter, start, end; GtkTextTag *bullet = _wp_text_buffer_get_bullet_tag(buffer); GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER(buffer); gint count; freeze_cursor_moved(buffer); gtk_text_buffer_begin_user_action(text_buffer); if (gtk_text_buffer_get_selection_bounds(text_buffer, &start, &end)) { iter = start; count = gtk_text_iter_get_line(&end) - gtk_text_iter_get_line(&start); while (count-- >= 0) { _wp_text_iter_put_bullet_line(&iter, bullet); if (!gtk_text_iter_forward_line(&iter)) break; } } else { /* gtk_text_buffer_get_iter_at_mark (text_buffer, &iter, * gtk_text_buffer_get_insert (text_buffer)); */ _wp_text_iter_put_bullet_line(&start, bullet); } gtk_text_buffer_end_user_action(text_buffer); thaw_cursor_moved(buffer); } static void _wp_text_buffer_remove_bullet(WPTextBuffer * buffer) { GtkTextIter iter, start, end; GtkTextTag *bullet = _wp_text_buffer_get_bullet_tag(buffer); GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER(buffer); gint count; freeze_cursor_moved(buffer); gtk_text_buffer_begin_user_action(text_buffer); if (gtk_text_buffer_get_selection_bounds(text_buffer, &start, &end)) { iter = start; count = gtk_text_iter_get_line(&end) - gtk_text_iter_get_line(&start); while (count-- >= 0) { _wp_text_iter_remove_bullet_line(&iter, bullet); if (!gtk_text_iter_forward_line(&iter)) break; } } else { /* gtk_text_buffer_get_iter_at_mark (text_buffer, &iter, * gtk_text_buffer_get_insert (text_buffer)); */ _wp_text_iter_remove_bullet_line(&start, bullet); } gtk_text_buffer_end_user_action(text_buffer); thaw_cursor_moved(buffer); } GtkTextTag * _wp_text_buffer_get_bullet_tag(WPTextBuffer * buffer) { g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer), NULL); return WP_TEXT_BUFFER(buffer)->priv->tags[WPT_BULLET]; } void wp_text_buffer_undo(WPTextBuffer * buffer) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); wp_undo_undo(buffer->priv->undo); g_signal_emit(buffer, signals[REFRESH_ATTRIBUTES], 0); } void wp_text_buffer_redo(WPTextBuffer * buffer) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); wp_undo_redo(buffer->priv->undo); g_signal_emit(buffer, signals[REFRESH_ATTRIBUTES], 0); } static void wp_text_buffer_can_redo_cb(WPUndo * undo, gboolean enable, gpointer buffer) { g_signal_emit(buffer, signals[CAN_REDO], 0, enable); } static void wp_text_buffer_can_undo_cb(WPUndo * undo, gboolean enable, gpointer buffer) { g_signal_emit(buffer, signals[CAN_UNDO], 0, enable); } static void wp_text_buffer_format_changed_cb(WPUndo * undo, gboolean rich_text, WPTextBuffer * buffer) { // printf("FCB: %d\n", rich_text); buffer->priv->is_rich_text = rich_text; gtk_text_buffer_set_modified(GTK_TEXT_BUFFER(buffer), TRUE); g_signal_emit(buffer, signals[FMT_CHANGED], 0, rich_text); emit_default_font_changed(buffer); } static void wp_text_buffer_last_line_justify_cb(WPUndo * undo, gint last_line_justification, WPTextBuffer * buffer) { // printf("LLJ: %d\n", last_line_justification); emit_default_justification_changed(buffer, last_line_justification); } static void wp_text_buffer_no_memory_cb(WPUndo * undo, WPTextBuffer * buffer) { g_signal_emit(buffer, signals[NO_MEMORY], 0); } void wp_text_buffer_enable_rich_text(WPTextBuffer * buffer, gboolean enable) { GtkTextIter start, end; GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER(buffer); WPTextBufferPrivate *priv = buffer->priv; gboolean rich_text = priv->is_rich_text != FALSE; if (enable != rich_text) { gtk_text_buffer_begin_user_action(text_buffer); gtk_text_buffer_get_start_iter(text_buffer, &start); gtk_text_buffer_get_end_iter(text_buffer, &end); wp_undo_format_changed(priv->undo, enable); buffer->priv->is_rich_text = enable; if (enable) { if (priv->is_empty) priv->fmt = priv->default_fmt; else wp_text_buffer_apply_attributes(buffer, &start, &end, FALSE, &priv->default_plain_fmt); } else { GtkTextIter found1, found2, found3; gint foundbackup; gtk_text_buffer_get_start_iter(text_buffer, &start); // Change the unicode char of the bullets to " * " while (gtk_text_iter_forward_search (&start, "\xe2\x80\xa2\xc2\xa0\xc2\xa0", 0, &found1, &found2, NULL)) { // delete the unicode character foundbackup = gtk_text_iter_get_offset(&found1); gtk_text_buffer_delete(text_buffer, &found1, &found2); // Re-initialize the iterator gtk_text_buffer_get_start_iter(text_buffer, &found1); gtk_text_buffer_get_iter_at_offset(text_buffer, &found3, foundbackup); // Insert the new text // gtk_text_buffer_insert (text_buffer, &found3, " * ", 3); gtk_text_buffer_get_iter_at_offset(text_buffer, &start, foundbackup); } gtk_text_buffer_get_start_iter(text_buffer, &start); // Remove all the images while (gtk_text_iter_forward_search (&start, "\xef\xbf\xbc", 0, &found1, &found2, NULL)) { // delete the unicode character foundbackup = gtk_text_iter_get_offset(&found1); gtk_text_buffer_delete(text_buffer, &found1, &found2); // Re-initialize the iterator gtk_text_buffer_get_start_iter(text_buffer, &found1); gtk_text_buffer_get_iter_at_offset(text_buffer, &found3, foundbackup); gtk_text_buffer_get_iter_at_offset(text_buffer, &start, foundbackup); } wp_undo_freeze(priv->undo); gtk_text_buffer_get_start_iter(text_buffer, &start); gtk_text_buffer_get_end_iter(text_buffer, &end); gtk_text_buffer_remove_all_tags(text_buffer, &start, &end); wp_undo_thaw(priv->undo); emit_default_justification_changed(buffer, GTK_JUSTIFY_LEFT); } g_signal_emit(buffer, signals[FMT_CHANGED], 0, enable); g_signal_emit(buffer, signals[REFRESH_ATTRIBUTES], 0); emit_default_font_changed(buffer); /* Only mark modified if there is content in text buffer */ { gboolean empty; g_object_get( buffer, "is_empty", &empty, NULL ); if( !empty ) { gtk_text_buffer_set_modified(GTK_TEXT_BUFFER(buffer), TRUE); } } gtk_text_buffer_end_user_action(text_buffer); } } gboolean wp_text_buffer_is_rich_text(WPTextBuffer * buffer) { g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer), FALSE); return buffer->priv->is_rich_text; } gboolean wp_text_buffer_is_modified(WPTextBuffer * buffer) { g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer), FALSE); return gtk_text_buffer_get_modified(GTK_TEXT_BUFFER(buffer)); } static void wp_text_buffer_resize_font(WPTextBuffer * buffer) { gint i; gint font_size; gint actual_size; gint rise; WPTextBufferPrivate *priv = buffer->priv; double scale = priv->font_scaling_factor; if (scale == -1) scale = priv->font_scaling_factor; else priv->font_scaling_factor = scale; for (i = 0; i < WP_FONT_SIZE_COUNT; i++) { /* Normal size */ font_size = wp_font_size[i]; actual_size = iround(scale * font_size * PANGO_SCALE); g_object_set(G_OBJECT(priv->font_size_tags[i]), "size", actual_size, NULL); /* Superscript size */ actual_size = iround((scale * font_size * PANGO_SCALE * SUP_SUB_SIZE_MULT) / SUP_SUB_SIZE_DIV); rise = iround((scale * font_size * PANGO_SCALE * SUP_RISE_MULT) / SUP_RISE_DIV); g_object_set(G_OBJECT(priv->font_size_sup_tags[i]), "rise", rise, "size", actual_size, NULL); /* Subscript size */ rise = -iround((scale * font_size * PANGO_SCALE * SUB_RISE_MULT) / SUB_RISE_DIV); g_object_set(G_OBJECT(priv->font_size_sub_tags[i]), "rise", rise, "size", actual_size, NULL); } } void wp_text_buffer_set_font_scaling_factor(WPTextBuffer * buffer, double scale) { WPTextBufferPrivate *priv = buffer->priv; if (priv->font_scaling_factor != scale) { priv->font_scaling_factor = scale; emit_default_font_changed(buffer); // If the fonts tags are created, resize them if (priv->font_size_tags[0]) wp_text_buffer_resize_font(buffer); } } void wp_text_buffer_set_background_color(WPTextBuffer * buffer, const GdkColor * color) { WPTextBufferPrivate *priv = buffer->priv; if (priv->background_color != NULL) gdk_color_free(priv->background_color); if (color != NULL) priv->background_color = gdk_color_copy(color); emit_background_color_change(buffer); } const GdkColor * wp_text_buffer_get_background_color(WPTextBuffer * buffer) { WPTextBufferPrivate *priv = buffer->priv; return priv->background_color; } static void emit_default_font_changed(WPTextBuffer * buffer) { PangoFontDescription *desc; WPTextBufferPrivate *priv = buffer->priv; desc = pango_font_description_new(); if (priv->is_rich_text) { /* printf("Font: %s, size: %d\n", * wp_get_font_name(priv->default_fmt.font), * wp_font_size[priv->default_fmt.font_size]); */ pango_font_description_set_family_static(desc, wp_get_font_name(priv-> default_fmt. font)); pango_font_description_set_size(desc, iround(priv->font_scaling_factor * wp_font_size[priv->default_fmt. font_size] * PANGO_SCALE)); } else { /* printf("Font: %s, size: %d\n", * wp_get_font_name(priv->default_plain_fmt.font), * wp_font_size[priv->default_plain_fmt.font_size]); */ pango_font_description_set_family_static(desc, wp_get_font_name(priv-> default_plain_fmt. font)); pango_font_description_set_size(desc, iround(priv->font_scaling_factor * wp_font_size[priv-> default_plain_fmt. font_size] * PANGO_SCALE)); } g_signal_emit(G_OBJECT(buffer), signals[DEF_FONT_CHANGED], 0, desc); pango_font_description_free(desc); wp_html_parser_update_default_attributes(priv->parser, &priv->default_fmt); } static void emit_default_justification_changed(WPTextBuffer * buffer, gint justification) { WPTextBufferPrivate *priv = buffer->priv; if (!priv->fast_mode && priv->last_line_justification != justification) { wp_undo_last_line_justify(priv->undo, priv->last_line_justification, justification); buffer->priv->last_line_justification = justification; g_signal_emit(G_OBJECT(buffer), signals[DEF_JUSTIFICATION_CHANGED], 0, justification); } } void wp_text_buffer_reset_buffer(WPTextBuffer * buffer, gboolean rich_text) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER(buffer); WPTextBufferPrivate *priv = buffer->priv; wp_undo_freeze(priv->undo); gtk_text_buffer_set_text(text_buffer, "", -1); gtk_text_buffer_set_modified(text_buffer, FALSE); priv->fmt = priv->default_fmt; g_object_set(G_OBJECT(buffer), "rich_text", rich_text, NULL); g_object_set(G_OBJECT(buffer), "background_color", NULL, NULL); wp_undo_reset(priv->undo); emit_default_font_changed(buffer); emit_default_justification_changed(buffer, GTK_JUSTIFY_LEFT); if (!priv->fast_mode) g_signal_emit(G_OBJECT(buffer), signals[REFRESH_ATTRIBUTES], 0); wp_undo_thaw(priv->undo); } /********************************************** * Document loading **********************************************/ void wp_text_buffer_load_document_begin(WPTextBuffer * buffer, gboolean html) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); buffer->priv->fast_mode = TRUE; wp_undo_freeze(buffer->priv->undo); wp_text_buffer_freeze(buffer); wp_text_buffer_reset_buffer(buffer, html); if (html) wp_html_parser_begin(buffer->priv->parser); else buffer->priv->last_utf8_size = 0; } void wp_text_buffer_load_document_write(WPTextBuffer * buffer, gchar * data, gint size) { g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); WPTextBufferPrivate *priv = buffer->priv; if (buffer->priv->is_rich_text) wp_html_parser_write(priv->parser, data, size); else { gchar *invalid_offset, *p; guint len; GtkTextIter pos; /* Strip the \r */ p = data; while ((p = memchr(p, '\r', (guint) size - (p - data)))) { memmove(p, p + 1, (guint) size - (p + 1 - data)); size--; } if (priv->last_utf8_size) { len = (guint) wp_html_parser_validate_invalid_utf8(priv-> last_utf8_invalid_char, priv-> last_utf8_size, data, size); if (data) { data += len; size -= len; } if (*priv->last_utf8_invalid_char) { gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(buffer), &pos); gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffer), &pos, priv->last_utf8_invalid_char, -1); } priv->last_utf8_size = 0; } p = data; while (size > 0 && !g_utf8_validate(p, data + size - p, (const gchar **) &invalid_offset)) { g_assert(data + size >= invalid_offset); len = g_utf8_skip[*(guchar *) invalid_offset]; if (invalid_offset + len < data + size) { memmove(invalid_offset, invalid_offset + 1, (guint) size - (invalid_offset + 1 - data)); size--; } else { priv->last_utf8_size = (data + size) - invalid_offset; memcpy(priv->last_utf8_invalid_char, invalid_offset, priv->last_utf8_size); size -= priv->last_utf8_size; break; } p = invalid_offset; } if (size > 0) { gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(buffer), &pos); gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffer), &pos, data, size); } } } void wp_text_buffer_load_document_end(WPTextBuffer * buffer) { GtkTextBuffer *text_buffer; GtkTextIter pos; gint last_line_justification; g_return_if_fail(WP_IS_TEXT_BUFFER(buffer)); if (buffer->priv->is_rich_text) last_line_justification = wp_html_parser_end(buffer->priv->parser); else { if (buffer->priv->last_utf8_size) wp_text_buffer_load_document_write(buffer, NULL, 0); last_line_justification = GTK_JUSTIFY_LEFT; } text_buffer = GTK_TEXT_BUFFER(buffer); gtk_text_buffer_get_start_iter(text_buffer, &pos); if (buffer->priv->is_rich_text) _wp_text_iter_skip_bullet(&pos, buffer->priv->tags[WPT_BULLET], TRUE); gtk_text_buffer_place_cursor(text_buffer, &pos); gtk_text_buffer_set_modified(text_buffer, FALSE); buffer->priv->cursor_moved = FALSE; wp_text_buffer_thaw(buffer); wp_undo_thaw(buffer->priv->undo); buffer->priv->fast_mode = FALSE; memset(&buffer->priv->fmt.cs, 0, sizeof(WPTextBufferFormatChangeSet)); emit_default_justification_changed(buffer, last_line_justification); g_signal_emit(buffer, signals[REFRESH_ATTRIBUTES], 0); } /********************************************** * Document saving **********************************************/ /** * Reallocate buffer if needed. * @param buffer is a pointer to a character buffer * @param buffer_size is the size of the buffer * @param addsize is the requested new size to be added * @param cur is the current cursor in the buffer * @param source is the start of the source buffer * @param slen is the length of the source buffer */ static void buffer_resize_if_needed(gchar ** buffer, gint * buffer_size, gint addsize, gchar ** cur, gchar * source, gint slen) { if (!*buffer) { *buffer_size = MAX(strlen(source) * 2, addsize + slen + 1); *buffer = g_realloc(NULL, *buffer_size); memcpy(*buffer, source, slen); *cur = *buffer + slen; } else { gint blen = *cur - *buffer; if (*buffer_size < blen + addsize) { *buffer_size = MAX(*buffer_size * 2, addsize + blen + 1); *buffer = g_realloc(*buffer, *buffer_size); *cur = *buffer + blen; } } } /** * Encode special characters in the buffer between start and end * interval and call the save callback for it. * @param buffer is a #WPTextBuffer * @param start a position in the buffer * @param end a position in the buffer * @param save is the #WPDocumentSaveCallback callback * @param user_data is the user supplied pointer */ static gint encode_text(GtkTextIter * start, GtkTextIter * end, WPDocumentSaveCallback save, gpointer user_data) { gchar *text, *p, *buffer = NULL, *cur, *encoded = NULL; gint result = 0, n = 0; gint buffer_size; gboolean space = FALSE; text = gtk_text_iter_get_text(start, end); if (text && *text) { buffer_size = 0; for (p = text; *p; p++) { switch ((guchar) * p) { case '&': encoded = "&"; space = FALSE; break; case '<': encoded = "<"; break; case '>': encoded = ">"; break; case '\t': encoded = " "; break; case ' ': if (space) encoded = " "; break; case 0xc2: if ((guchar) * (p + 1) == 0xa0) { encoded = " "; n = 1; } break; } space = *p == ' '; if (encoded || buffer) { gint len = encoded ? strlen(encoded) : 1; buffer_resize_if_needed(&buffer, &buffer_size, len, &cur, text, p - text); if (encoded) { memcpy(cur, encoded, len); encoded = NULL; if (n) { p += n; n = 0; } } else *cur = *p + n; cur += len; } } if (buffer) { buffer_resize_if_needed(&buffer, &buffer_size, 1, &cur, NULL, 0); *cur = 0; } else { buffer = text; text = NULL; } result = save(buffer, user_data); g_free(buffer); } g_free(text); return result; } /** * Get's the tag id, and parameters (ex. font size or color) * @param pointer to an array of #GtkTextTag * @param tag is a #GtkTextTag * @param id will contain the font size or font number if needed * @param color will contain the color if needed * @return the id of the tag */ static gint convert_tag(GtkTextTag ** ttags, GtkTextTag * tag, gint * id, GdkColor * color) { if (tag == ttags[WPT_BOLD]) return TP_BOLD; else if (tag == ttags[WPT_ITALIC]) return TP_ITALIC; else if (tag == ttags[WPT_UNDERLINE]) return TP_UNDERLINE; else if (tag == ttags[WPT_STRIKE]) return TP_STRIKE; else if (tag->rise_set) { *id = (gint) g_object_get_data(G_OBJECT(tag), WPT_ID); if (*id >= WPT_SUB_SRPT) { *id -= WPT_SUB_SRPT; return TP_SUBSCRIPT; } else if (*id >= WPT_SUP_SRPT) { *id -= WPT_SUP_SRPT; return TP_SUPERSCRIPT; } } else if (tag->fg_color_set) { *color = tag->values->appearance.fg_color; return TP_FONTCOLOR; } else if (tag->values->font && check_tag_type(tag, WPT_FONT, id)) return TP_FONTNAME; else if (tag->values->font && check_tag_type(tag, WPT_FONT_SIZE, id)) return TP_FONTSIZE; return TP_BOLD; } /** * Converts the list of tags to HTML tags, and call the * save callback. * @param priv is a pointer to #WPTextBufferPrivate * @param tags is a #GSList of #GtkTextTag * @param htags is an array of number containing the opened tags * @param save contains the user save callback which will be called, when a new * chunk is available and has to be written * @param user_data user supplied user data, which will be forwarded * to the save callback * @param opened is TRUE if the tags are opened * @return the result from the save callback */ static gint write_tags(WPTextBufferPrivate * priv, GSList * tags, guchar * htags, WPDocumentSaveCallback save, gpointer user_data, gboolean opened) { GSList *tmp; GtkTextTag *tag; GdkColor color; gint id, info; gchar *s; gint result = 0; tmp = tags; while (tmp && !result) { gpointer is_image_ptr; gint is_image; tag = GTK_TEXT_TAG(tmp->data); tmp = tmp->next; is_image_ptr = g_object_get_data(G_OBJECT(tag), "image-set"); if (is_image_ptr == NULL) is_image = FALSE; else is_image = GPOINTER_TO_INT(is_image_ptr); if (opened && is_image) { gchar *image_id; gchar *html_image; image_id = g_object_get_data(G_OBJECT(tag), "image-index"); html_image = g_strdup_printf("", image_id); save(html_image, user_data); g_free(html_image); } else if (!tag->justification_set && tag != priv->tags[WPT_BULLET]) { id = convert_tag(priv->tags, tag, &info, &color); if (!opened) { switch (id) { case TP_FONTNAME: if (info != priv->default_fmt.font) { result = save(html_close_tags[id], user_data); htags[TP_FONTNAME]--; } break; case TP_FONTSIZE: case TP_SUBSCRIPT: case TP_SUPERSCRIPT: if (info != priv->default_fmt.font_size) { s = g_strdup_printf(html_open_tags[TP_FONTSIZE], info); result = save(html_close_tags[TP_FONTSIZE], user_data); htags[TP_FONTSIZE]--; } if (id != TP_FONTSIZE && !result) { result = save(html_close_tags[id], user_data); htags[id]--; } break; default: result = save(html_close_tags[id], user_data); htags[id]--; } } else { switch (id) { case TP_FONTNAME: if (info != priv->default_fmt.font) { s = g_strdup_printf(html_open_tags[id], wp_get_font_name(info)); result = save(s, user_data); htags[id]++; g_free(s); } break; case TP_FONTSIZE: case TP_SUBSCRIPT: case TP_SUPERSCRIPT: if (info != priv->default_fmt.font_size) { s = g_strdup_printf(html_open_tags[TP_FONTSIZE], info + 1); result = save(s, user_data); g_free(s); htags[TP_FONTSIZE]++; } if (id != TP_FONTSIZE && !result) { result = save(html_open_tags[id], user_data); htags[id]++; } break; case TP_FONTCOLOR: s = g_strdup_printf(html_open_tags[id], color.red >> 8, color.green >> 8, color.blue >> 8); result = save(s, user_data); htags[id]++; g_free(s); break; default: result = save(html_open_tags[id], user_data); htags[id]++; } } } } g_slist_free(tags); return result; } /** * Begins a new paragraph. Writes all the opened tags at the start position * @param priv is a pointer to #WPTextBufferPrivate * @param start is a position in a buffer * @param htags is an array of number containing the opened tags * @param save contains the user save callback which will be called, when a new * chunk is available and has to be written * @param user_data user supplied user data, which will be forwarded * to the save callback * @return the result from the save callback */ static gint begin_paragraph(WPTextBufferPrivate * priv, GtkTextIter * start, guchar * htags, WPDocumentSaveCallback save, gpointer user_data) { GSList *tags; GtkTextTag *tag; gint result = 0; tags = gtk_text_iter_get_tags(start); // tags = g_slist_reverse(tags); tag = find_justification_tag(tags, FALSE); if (tag && tag->values->justification != GTK_JUSTIFY_LEFT) { if (tag->values->justification == GTK_JUSTIFY_CENTER) result = save("

", user_data); else result = save("

", user_data); } else result = save("

", user_data); if (!result) result = write_tags(priv, tags, htags, save, user_data, TRUE); return result; } /** * Ends a paragraph. Close all the opened tags. * @param priv is a pointer to #WPTextBufferPrivate * @param start is a position in a buffer * @param htags is an array of number containing the opened tags * @param save contains the user save callback which will be called, when a new * chunk is available and has to be written * @param user_data user supplied user data, which will be forwarded * to the save callback * @return the result from the save callback */ static gint end_paragraph(WPTextBufferPrivate * priv, GtkTextIter * start, guchar * htags, WPDocumentSaveCallback save, gpointer user_data) { gint i; gint result = 0; for (i = 0; i < TP_LAST && !result; i++) while (htags[i]-- > 0 && !result) result = save(html_close_tags[i], user_data); if (!result) result = save("

\n", user_data); return result; } gint wp_text_buffer_save_document(WPTextBuffer * buffer, WPDocumentSaveCallback save, gpointer user_data) { g_return_val_if_fail(WP_IS_TEXT_BUFFER(buffer), 1); WPTextBufferPrivate *priv = buffer->priv; GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER(buffer); GtkTextIter start, bend, end, tagtoggle; guchar htags[TP_LAST]; gboolean bullet; gboolean list = FALSE; gboolean result = 0; gtk_text_buffer_get_start_iter(text_buffer, &start); gtk_text_buffer_get_end_iter(text_buffer, &bend); if (priv->is_rich_text) { tagtoggle = start; result = save(html_header, user_data); if (buffer->priv->background_color) { gchar *tmp; gchar *bgcolor_str; bgcolor_str = g_strdup_printf("#%02x%02x%02x", buffer->priv->background_color->red >> 8, buffer->priv->background_color->green >> 8, buffer->priv->background_color->blue >> 8); tmp = g_strdup_printf(body_bgcolor_start, bgcolor_str); save(tmp, user_data); } else { save(body_start, user_data); } if (!result) do { memset(htags, 0, sizeof(htags)); bullet = _wp_text_iter_skip_bullet(&start, buffer->priv->tags[WPT_BULLET], TRUE); if (bullet ^ list) { if (list) result = save("\n", user_data); else result = save("
    \n", user_data); list = bullet; } if (!result && bullet) { result = save("\t
  • ", user_data); } if (!result) result = begin_paragraph(priv, &start, htags, save, user_data); if (!result && !gtk_text_iter_ends_line(&start)) { end = start; gtk_text_iter_forward_to_line_end(&end); do { while (gtk_text_iter_compare(&tagtoggle, &start) <= 0) { if (!gtk_text_iter_forward_to_tag_toggle (&tagtoggle, NULL)) { tagtoggle = bend; break; } } if (gtk_text_iter_compare(&tagtoggle, &end) < 0) { result = encode_text(&start, &tagtoggle, save, user_data); if (!result) result = write_tags(priv, gtk_text_iter_get_toggled_tags (&tagtoggle, FALSE), htags, save, user_data, FALSE); if (!result) result = write_tags(priv, gtk_text_iter_get_toggled_tags (&tagtoggle, TRUE), htags, save, user_data, TRUE); start = tagtoggle; } else break; } while (!result); if (!result) result = encode_text(&start, &end, save, user_data); } if (!result) result = end_paragraph(priv, &start, htags, save, user_data); } while (!result && gtk_text_iter_forward_line(&start)); if (!result && list) result = save("
\n", user_data); /* check if last line ends with newline */ if (!result && !gtk_text_iter_is_start(&start)) { gtk_text_iter_backward_char(&start); if (gtk_text_iter_ends_line(&start)) { result = save("

\n", user_data); } } if (!result) result = save(html_footer, user_data); } else { gint offset; gchar *text; // Search for an existed BOM sign at the start of the text content GtkTextBuffer *buf; GtkTextIter BOMIter; gunichar BOM; buf = GTK_TEXT_BUFFER(text_buffer); gtk_text_buffer_get_start_iter(buf, &BOMIter); BOM = gtk_text_iter_get_char(&BOMIter); if (BOM != 65279) save("\xEF\xBB\xBF", user_data); offset = 0; do { offset += 20480; gtk_text_buffer_get_iter_at_offset(text_buffer, &end, offset); text = gtk_text_iter_get_text(&start, &end); if (text && *text) result = save(text, user_data); g_free(text); start = end; } while (!result && gtk_text_iter_compare(&end, &bend) < 0); } if (!result) gtk_text_buffer_set_modified(text_buffer, FALSE); return result; } void wp_text_buffer_remember_tag(WPTextBuffer * buffer, gboolean enable) { g_assert(WP_IS_TEXT_BUFFER(buffer)); WPTextBufferPrivate *priv = buffer->priv; priv->remember_tag = enable; } void debug_print_tags(GtkTextIter * giter, gint what) { GSList *head, *iter; printf("==============\nTag list at %d, %d:\n", gtk_text_iter_get_offset(giter), what); if (what == 0) head = iter = gtk_text_iter_get_tags(giter); else head = iter = gtk_text_iter_get_toggled_tags(giter, what == 1); while (iter) { printf(" %s\n", GTK_TEXT_TAG(iter->data)->name ? GTK_TEXT_TAG(iter->data)-> name : "(null)"); iter = iter->next; } printf("-------------------\n"); g_slist_free(head); } /********************************************** * Font and font size handling **********************************************/ const gchar **font_name_list; const gchar **font_name_list_casefold; static gint wp_font_num = 0; /** * Compare two font families. * @param a is a pointer to a character pointer * @param b is a pointer to a character pointer * @return the result -1 if a < b, 0 if a = b or 1 if a > b * static int cmp_families(const void *a, const void *b) { return g_utf8_collate(*(gchar **) a, *(gchar **) b); } */ /** * Check if the supplied name is an internal font. Used for filtering. * @param name is a null terminated string * @return TRUE if is an internal font */ static gboolean is_internal_font(const gchar * name) { /* Maybe update this if more internals come in */ return strcmp(name, "DeviceSymbols") == 0 || strcmp(name, "Nokia Smiley") == 0 || strcmp(name, "NewCourier") == 0 || strcmp(name, "NewTimes") == 0 || strcmp(name, "SwissA") == 0 || strcmp(name, "Nokia Sans") == 0 || strcmp(name, "Nokia Sans Cn") == 0; } void wp_text_buffer_library_init() { GdkScreen *screen; PangoContext *context; PangoFontFamily **families; int i, font_num, n; const gchar *name; screen = gdk_screen_get_default(); context = gdk_pango_context_get_for_screen(screen); pango_context_list_families(context, &families, &font_num); font_name_list = g_new(const gchar *, font_num); n = 0; for (i = 0; i < font_num; i++) { name = pango_font_family_get_name(families[i]); if (!is_internal_font(name)) { font_name_list[n++] = g_strdup(name); } } // reallocate memory for the "wasted space" to became usable again font_name_list = g_realloc(font_name_list, n * sizeof(gchar *)); wp_font_num = n; /* For some changing order of fonts breaks font handling. This can be reenabled when the actual error is found, NB#70305 qsort(font_name_list, n, sizeof(gchar *), cmp_families); */ font_name_list_casefold = g_new(const gchar *, n); for (i = 0; i < wp_font_num; i++) font_name_list_casefold[i] = g_utf8_casefold(font_name_list[i], -1); g_free(families); g_object_unref(context); } void wp_text_buffer_library_done() { int i; for (i = 0; i < wp_font_num; i++) { g_free((gchar *) font_name_list[i]); g_free((gchar *) font_name_list_casefold[i]); } g_free(font_name_list); g_free(font_name_list_casefold); finalize_html_parser_library(); } const gchar * wp_get_font_name(gint index) { if (!font_name_list) wp_text_buffer_library_init(); if (index >= 0 && index < wp_font_num) return font_name_list[index]; else return DEF_FONT; } gint wp_get_font_index(const gchar * font_name, gint def) { gint font; gchar *case_fold = g_utf8_casefold(font_name, -1); if (!font_name_list) wp_text_buffer_library_init(); for (font = 0; font < wp_font_num; font++) if (g_utf8_collate(font_name_list_casefold[font], case_fold) == 0) { g_free(case_fold); return font; } g_free(case_fold); // g_warning("Non existing font: %s", font_name); return def; } gint wp_get_font_count() { if (!font_name_list) wp_text_buffer_library_init(); return wp_font_num; } gint wp_get_font_size_index(gint font_size, gint def) { gint size, result = def; for (size = 0; size < WP_FONT_SIZE_COUNT && font_size >= wp_font_size[size]; size++) if (font_size >= wp_font_size[size]) result = size; return result; } static void wp_text_buffer_insert_pixbuf (GtkTextBuffer *buffer, GtkTextIter *location, GdkPixbuf *pixbuf) { GTK_TEXT_BUFFER_CLASS(wp_text_buffer_parent_class)-> insert_pixbuf(buffer, location, pixbuf); ((WPTextBuffer *) buffer)->priv->queue_undo_reset = TRUE; } wpeditor-2.18/src/wptextview.c0000644000175000001440000014107310647343013015714 0ustar stevenusers/** * @file wptextview.c * * Implementation file for WordPad Text View */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Some parts based on: GTK - The GIMP Toolkit gtktextview.c * Copyright (C) 2000 Red Hat, Inc. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #include #include #include #include #include #include #include /* claim we really know what we are doing to be able to use the * gtk_text_layout_get_iter_at_pixel and gtk_text_layout_set_preedit_string * functions */ #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API #include #include #include #include "wptextview.h" #include "wptextbuffer.h" #include "wptextbuffer-private.h" /* Disable surrounding retrieval for speedup, currently non of the im methods * use this. Uncomment it, if is needed in the future */ // #define DISABLE_SURROUNDING 1 static GObject *wp_text_view_constructor(GType type, guint n_construct_properties, GObjectConstructParam * construct_param); static void wp_text_view_finalize(GObject * object); /* static gboolean wp_text_view_expose(GtkWidget * widget, GdkEventExpose * * event); */ /** * Callback happening when the enter was pressed. Needed for bullets. * @param view is a #GtkTextView * @param event is a #GdkEventKey */ static gboolean handle_enter(WPTextView * view, G_GNUC_UNUSED GdkEventKey * event); /** * Callback happening at a key press * @param widget is a #GtkWidget * @param event is a #GdkEventKey */ static int wp_text_view_key_press_event(GtkWidget * widget, GdkEventKey * event); /** * Callback happening when the cursor was moved. Needed to skip bullets. * @param text_view is a #GtkTextView * @param step is a #GtkMovementStep * @param count is the number of moves * @param extend_selection is set if the selection is extended */ static void wp_text_view_move_cursor(GtkTextView * text_view, GtkMovementStep step, gint count, gboolean extend_selection); /** * Callback happening at the drag and drop motion. * @param widget is a #GtkWidget * @param context is a #GdkDragContext * @param x is the x coordinate of the motion * @param y is the y coordinate of the motion * @param time is the time of the event */ static gboolean wp_text_view_drag_motion(GtkWidget * widget, GdkDragContext * context, gint x, gint y, guint time); /** * Callback happening at the drop phase of drag and drop * @param widget is a #GtkWidget * @param context is a #GdkDragContext * @param x is the x coordinate of the motion * @param y is the y coordinate of the motion * @param selection_data is a #GtkSelectionData * @param time is the time of the event */ static void wp_text_view_drag_data_received(GtkWidget * widget, GdkDragContext * context, gint x, gint y, GtkSelectionData * selection_data, guint info, guint time); /** * Callback happening at the mouse button press. * @param widget is a #GtkWidget * @param event is a #GdkEventButton */ static gint wp_text_view_button_press_event(GtkWidget * widget, GdkEventButton * event); /** * Callback happening when the backspace was pressed. Needed to delete bullets. * @param text_view is a #GtkTextView */ static void wp_text_view_backspace(GtkTextView * text_view); /** * Callback happening when the delete was pressed. Needed to delete bullets. * @param text_view is a #GtkTextView * @param type is a #GtkDeleteType * @param count is the number of deletes */ static void wp_text_view_delete_from_cursor(GtkTextView * text_view, GtkDeleteType type, gint count); /** * Callback happening when at the paste operation. Needed for bullets and * justification update. * @param text_view is a #GtkTextView */ static void wp_text_view_paste_clipboard(GtkTextView * text_view); /** * Callback happening when the default font has been changed in the buffer * @param buffer is a #GtkTextBuffer * @param desc is a #PangoFontDescription containing the new font * @param view is a #GtkTextView */ static void wp_text_view_def_font_changed(WPTextBuffer * buffer, PangoFontDescription * desc, GtkWidget * view); /** * Callback happening when the default justification has been changed in the buffer * @param buffer is a #GtkTextBuffer * @param justification is on of the #GTK_JUSTIFY_LEFT, #GTK_JUSTIFY_CENTER, * #GTK_JUSTIFY_RIGHT * @param text_view is a #GtkTextView */ static void wp_text_view_def_justification_changed(WPTextBuffer * buffer, gint justification, GtkTextView * text_view); /** * Callback happening when the background color has been changed in the buffer * @param buffer is a #GtkTextBuffer * @param color is a #GdkColor * @param text_view is a #GtkTextView */ static void wp_text_view_background_color_changed(WPTextBuffer * buffer, const GdkColor * color, GtkTextView * text_view); static void wp_text_view_commit_handler(GtkIMContext * context, const gchar * str, GtkTextView * text_view); static void wp_text_view_commit_text(GtkTextView * text_view, const gchar * text); static void wp_text_view_preedit_changed_handler(GtkIMContext * context, GtkTextView * text_view); static gboolean wp_text_view_retrieve_surrounding_handler(GtkIMContext * context, GtkTextView * text_view); static gboolean wp_text_view_delete_surrounding_handler(GtkIMContext * context, gint offset, gint n_chars, GtkTextView * text_view); static gboolean wp_text_view_has_selection_handler(GtkIMContext * context, GtkTextView * text_view); #ifdef HAVE_HILDON static void wp_text_view_clipboard_operation_handler(GtkIMContext * context, GtkIMContextClipboardOperation op, GtkTextView * text_view); #endif /* WP_TYPE_TEXT_VIEW */ G_DEFINE_TYPE(WPTextView, wp_text_view, GTK_TYPE_TEXT_VIEW); static void wp_text_view_class_init(WPTextViewClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS(klass); gobject_class->constructor = wp_text_view_constructor; gobject_class->finalize = wp_text_view_finalize; widget_class->key_press_event = wp_text_view_key_press_event; widget_class->drag_motion = wp_text_view_drag_motion; widget_class->drag_data_received = wp_text_view_drag_data_received; widget_class->button_press_event = wp_text_view_button_press_event; // widget_class->expose_event = wp_text_view_expose; text_view_class->move_cursor = wp_text_view_move_cursor; text_view_class->backspace = wp_text_view_backspace; text_view_class->delete_from_cursor = wp_text_view_delete_from_cursor; text_view_class->paste_clipboard = wp_text_view_paste_clipboard; } static void wp_text_view_init(WPTextView * view) { char *name; GtkTextView *text_view = GTK_TEXT_VIEW(view); name = g_strdup_printf("wp-text-view-%p", view); gtk_widget_set_name(GTK_WIDGET(view), name); g_free(name); g_object_unref(text_view->im_context); text_view->im_context = gtk_im_multicontext_new(); g_signal_connect(text_view->im_context, "commit", G_CALLBACK(wp_text_view_commit_handler), text_view); g_signal_connect(text_view->im_context, "preedit_changed", G_CALLBACK(wp_text_view_preedit_changed_handler), text_view); g_signal_connect(text_view->im_context, "retrieve_surrounding", G_CALLBACK(wp_text_view_retrieve_surrounding_handler), text_view); g_signal_connect(text_view->im_context, "delete_surrounding", G_CALLBACK(wp_text_view_delete_surrounding_handler), text_view); g_signal_connect(text_view->im_context, "has_selection", G_CALLBACK(wp_text_view_has_selection_handler), text_view); #ifdef HAVE_HILDON g_signal_connect(text_view->im_context, "clipboard_operation", G_CALLBACK(wp_text_view_clipboard_operation_handler), text_view); #endif } static GObject * wp_text_view_constructor(GType type, guint n_construct_properties, GObjectConstructParam * construct_param) { GObject *object; WPTextView *view; WPTextBuffer *buffer; object = G_OBJECT_CLASS(wp_text_view_parent_class)->constructor(type, n_construct_properties, construct_param); view = WP_TEXT_VIEW(object); buffer = wp_text_buffer_new(NULL); gtk_text_view_set_buffer(GTK_TEXT_VIEW(view), GTK_TEXT_BUFFER(buffer)); g_object_unref(buffer); g_signal_connect(G_OBJECT(buffer), "def_font_changed", G_CALLBACK(wp_text_view_def_font_changed), view); g_signal_connect(G_OBJECT(buffer), "def_justification_changed", G_CALLBACK(wp_text_view_def_justification_changed), view); g_signal_connect(G_OBJECT(buffer), "background_color_changed", G_CALLBACK(wp_text_view_background_color_changed), view); return object; } static void wp_text_view_finalize(GObject * object) { G_OBJECT_CLASS(wp_text_view_parent_class)->finalize(object); } GtkWidget * wp_text_view_new(void) { GtkWidget *view = g_object_new(WP_TYPE_TEXT_VIEW, NULL); return view; } /********************************************************************/ /* Drawing and stuff */ /* static void draw_bullet_at_iter(GtkTextView * text_view, GdkEventExpose * * event, GtkTextIter * iter) { GdkRectangle rect; gint radius; * * gtk_text_view_get_iter_location(text_view, iter, &rect); * gtk_text_view_buffer_to_window_coords(text_view, GTK_TEXT_WINDOW_TEXT, * rect.x, rect.y, &rect.x, &rect.y); radius = MIN(rect.width / 2, * rect.height / 2) / 3; * * gdk_draw_arc(event->window, * GTK_WIDGET(text_view)->style->text_gc[GTK_STATE_NORMAL], TRUE, rect.x + * rect.width / 2 - radius, rect.y + rect.height / 2 - radius, radius * 2, * radius * 2, 0, 360 * 64); } * * * static void wp_text_view_draw_bullets(GtkTextView * text_view, * GdkEventExpose * event, const GtkTextIter * start, const GtkTextIter * * end) { GtkTextIter iter = *start; GtkTextTag *bullet = * _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER (gtk_text_view_get_buffer * (text_view))); * * if (!_wp_text_iter_is_bullet(&iter, bullet)) * gtk_text_iter_forward_to_tag_toggle(&iter, bullet); * * while (gtk_text_iter_compare(&iter, end) < 0) { // if * (gtk_text_iter_get_char (&iter) == '\t') draw_bullet_at_iter(text_view, * event, &iter); gtk_text_iter_forward_to_tag_toggle(&iter, bullet); * gtk_text_iter_forward_to_tag_toggle(&iter, bullet); } } * * * static gboolean wp_text_view_expose(GtkWidget * widget, GdkEventExpose * * event) { gboolean handled; GtkTextView *text_view = GTK_TEXT_VIEW(widget); * GtkTextIter start, end; int first_line = 0, last_line = 100000; * * GdkRectangle rect = event->area; * * gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_TEXT, * rect.x, rect.y, &rect.x, &rect.y); * * gtk_text_view_get_line_at_y (text_view, &start, rect.y, NULL); * gtk_text_view_get_line_at_y (text_view, &end, rect.y + rect.height, NULL); * gtk_text_iter_forward_line (&end); * * first_line = gtk_text_iter_get_line (&start); last_line = * gtk_text_iter_get_line (&end); * * handled = GTK_WIDGET_CLASS(wp_text_view_parent_class)->expose_event(widget, * event); * * if (last_line - first_line < 2000) wp_text_view_draw_bullets (text_view, * event, &start, &end); * * return handled; } */ /****************** * Keyboard & mouse */ static int wp_text_view_key_press_event(GtkWidget * widget, GdkEventKey * event) { WPTextView *view; GtkTextView *text_view; GtkTextBuffer *buffer; gboolean handled = FALSE; int keyval = event->keyval; view = WP_TEXT_VIEW(widget); text_view = GTK_TEXT_VIEW(widget); buffer = gtk_text_view_get_buffer(text_view); #ifdef HAVE_HILDON if (text_view->editable && gtk_im_context_filter_keypress(text_view->im_context, event)) { text_view->need_im_reset = TRUE; return TRUE; } #endif // printf("<> Keypress: %d <>\n", keyval); if (!(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK))) { switch (keyval) { case GDK_KP_Enter: case GDK_Return: handled = handle_enter(view, event); break; } } if (handled) return TRUE; // gtk_text_buffer_begin_user_action (buffer); handled = GTK_WIDGET_CLASS(wp_text_view_parent_class)-> key_press_event(widget, event); // gtk_text_buffer_end_user_action (buffer); return handled; } static gboolean handle_enter(WPTextView * view, G_GNUC_UNUSED GdkEventKey * event) { GtkTextBuffer *buffer; GtkTextIter start, end, iter; gboolean has_selection, has_bullet, just_remove_bullet; GtkTextTag *bullet; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)); has_selection = gtk_text_buffer_get_selection_bounds(buffer, &start, &end); if (!gtk_text_iter_can_insert(&start, view->parent.editable)) return FALSE; iter = start; bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); has_bullet = _wp_text_iter_has_bullet(&iter, bullet) && !gtk_text_iter_begins_tag(&start, bullet); just_remove_bullet = gtk_text_iter_ends_tag(&start, bullet) && gtk_text_iter_ends_line(&start); gtk_text_buffer_begin_user_action(buffer); if (has_selection) gtk_text_buffer_delete(buffer, &start, &end); if (just_remove_bullet) _wp_text_iter_remove_bullet_line(&start, bullet); else { gtk_text_buffer_insert(buffer, &start, "\n", 1); if (has_bullet) _wp_text_iter_put_bullet_line(&start, bullet); } gtk_text_buffer_end_user_action(buffer); gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(view), gtk_text_buffer_get_insert(buffer)); return TRUE; } static void wp_text_view_move_cursor(GtkTextView * text_view, GtkMovementStep step, gint count, gboolean extend_selection) { GtkTextBuffer *buffer; GtkTextMark *insert; GtkTextIter iter; GtkTextTag *bullet; GTK_TEXT_VIEW_CLASS(wp_text_view_parent_class)->move_cursor(text_view, step, count, extend_selection); if (!text_view->cursor_visible) return; buffer = gtk_text_view_get_buffer(text_view); bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); insert = gtk_text_buffer_get_insert(buffer); gtk_text_buffer_get_iter_at_mark(buffer, &iter, insert); if ( /* gtk_text_iter_begins_tag(&iter, bullet) || */ _wp_text_iter_is_bullet(&iter, bullet) // && /* !(gtk_text_iter_ends_tag(&iter, bullet)) */ ) { if (count < 0 && (step == GTK_MOVEMENT_LOGICAL_POSITIONS || step == GTK_MOVEMENT_VISUAL_POSITIONS || step == GTK_MOVEMENT_WORDS)) { _wp_text_iter_skip_bullet(&iter, bullet, FALSE); if (gtk_text_iter_is_start(&iter)) _wp_text_iter_skip_bullet(&iter, bullet, TRUE); else gtk_text_iter_backward_char(&iter); } else { _wp_text_iter_skip_bullet(&iter, bullet, TRUE); } if (extend_selection) gtk_text_buffer_move_mark(buffer, insert, &iter); else gtk_text_buffer_place_cursor(buffer, &iter); } } /* taken from gtk */ #define DND_SCROLL_MARGIN 0.20 static gint drag_scan_timeout(gpointer data) { GtkTextView *text_view = NULL; GtkTextBuffer *buffer = NULL; GtkTextTag *bullet = NULL; GdkWindow *wnd = NULL; GtkTextIter newplace; gint x, y; GDK_THREADS_ENTER(); /* if (data != (gpointer)text_view) { */ text_view = GTK_TEXT_VIEW(data); buffer = gtk_text_view_get_buffer(text_view); bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); // TODO: It is safe to use as static ? wnd = gtk_text_view_get_window(text_view, GTK_TEXT_WINDOW_TEXT); // } gdk_window_get_pointer(wnd, &x, &y, NULL); gtk_text_view_window_to_buffer_coords(text_view, GTK_TEXT_WINDOW_TEXT, x, y, &x, &y); gtk_text_layout_get_iter_at_pixel(text_view->layout, &newplace, x, y); if (_wp_text_iter_is_bullet(&newplace, bullet)) _wp_text_iter_skip_bullet(&newplace, bullet, TRUE); gtk_text_buffer_move_mark(buffer, text_view->dnd_mark, &newplace); gtk_text_view_scroll_to_mark(text_view, text_view->dnd_mark, DND_SCROLL_MARGIN, FALSE, 0.0, 0.0); GDK_THREADS_LEAVE(); return TRUE; } static gboolean wp_text_view_drag_motion(GtkWidget * widget, GdkDragContext * context, gint x, gint y, guint time) { gboolean handled = GTK_WIDGET_CLASS(wp_text_view_parent_class)-> drag_motion(widget, context, x, y, time); GtkTextView *text_view = GTK_TEXT_VIEW(widget); GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view); GtkTextIter iter; GtkTextTag *bullet; gtk_text_buffer_begin_user_action(buffer); if (handled) { bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); gtk_text_buffer_get_iter_at_mark(buffer, &iter, text_view->dnd_mark); if (_wp_text_iter_is_bullet(&iter, bullet)) { _wp_text_iter_skip_bullet(&iter, bullet, TRUE); gtk_text_buffer_move_mark(buffer, text_view->dnd_mark, &iter); /* Remove the timeout installed by gtk, and install ours */ if (text_view->scroll_timeout != 0) g_source_remove(text_view->scroll_timeout); text_view->scroll_timeout = g_timeout_add(50, drag_scan_timeout, text_view); } } gtk_text_buffer_end_user_action(buffer); return handled; } static void wp_text_view_drag_data_received(GtkWidget * widget, GdkDragContext * context, gint x, gint y, GtkSelectionData * selection_data, guint info, guint time) { GtkTextView *text_view = GTK_TEXT_VIEW(widget); GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view); GtkTextTag *bullet = NULL; GtkTextIter start, iter; gboolean has_bullet = FALSE, adjust_justification; gint len = 0; if (!text_view->dnd_mark) return; adjust_justification = (selection_data->target == gdk_atom_intern("GTK_TEXT_BUFFER_CONTENTS", FALSE)); gtk_text_buffer_get_iter_at_mark(buffer, &start, text_view->dnd_mark); if (gtk_text_iter_can_insert(&start, text_view->editable)) { /* need to add bullet, if the droped line had bullet */ bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); has_bullet = _wp_text_iter_has_bullet(&start, bullet); } else return; gtk_text_buffer_begin_user_action(buffer); if (adjust_justification) { gtk_text_buffer_get_selection_bounds(buffer, &start, &iter); len = gtk_text_iter_get_offset(&iter) - gtk_text_iter_get_offset(&start); wp_text_buffer_freeze(WP_TEXT_BUFFER(buffer)); } GTK_WIDGET_CLASS(wp_text_view_parent_class)-> drag_data_received(widget, context, x, y, selection_data, info, time); if (adjust_justification) wp_text_buffer_thaw(WP_TEXT_BUFFER(buffer)); if (adjust_justification) { gtk_text_buffer_get_iter_at_mark(buffer, &iter, gtk_text_buffer_get_insert(buffer)); start = iter; gtk_text_iter_backward_chars(&start, len); /* printf("Dnd: %d - %d\n", gtk_text_iter_get_offset(&start), * gtk_text_iter_get_offset(&iter)); */ if (gtk_text_iter_get_line(&start) != gtk_text_iter_get_line(&iter)) { if (!gtk_text_iter_starts_line(&start)) _wp_text_buffer_adjust_justification(WP_TEXT_BUFFER(buffer), &start, NULL, NULL, FALSE); if (!gtk_text_iter_ends_line(&iter)) _wp_text_buffer_adjust_justification(WP_TEXT_BUFFER(buffer), NULL, &iter, NULL, FALSE); } else _wp_text_buffer_adjust_justification(WP_TEXT_BUFFER(buffer), &start, &iter, NULL, FALSE); } if (bullet) { gtk_text_buffer_get_iter_at_mark(buffer, &iter, gtk_text_buffer_get_insert(buffer)); if (has_bullet) _wp_text_iter_put_bullet_line(&iter, bullet); else { if (!gtk_text_iter_ends_line(&iter)) _wp_text_iter_remove_bullet_line(&iter, bullet); } } gtk_text_buffer_end_user_action(buffer); } static inline void get_mouse_coords(GtkTextView * text_view, gint * x, gint * y) { GdkModifierType state; gdk_window_get_pointer(gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), x, y, &state); gtk_text_view_window_to_buffer_coords(text_view, GTK_TEXT_WINDOW_TEXT, *x, *y, x, y); } /* Took from gtk and modified a little because we don't want to select * bullets */ static void move_mark_to_pointer_and_scroll(GtkTextView * text_view, const gchar * mark_name, GtkTextTag * bullet) { gint x, y; GtkTextIter newplace; get_mouse_coords(text_view, &x, &y); gtk_text_layout_get_iter_at_pixel(text_view->layout, &newplace, x, y); if (_wp_text_iter_is_bullet(&newplace, bullet)) _wp_text_iter_skip_bullet(&newplace, bullet, TRUE); { GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view); GtkTextMark *mark = gtk_text_buffer_get_mark(buffer, mark_name); /* This may invalidate the layout */ gtk_text_buffer_move_mark(buffer, mark, &newplace); gtk_text_view_scroll_mark_onscreen(text_view, mark); } } static gint selection_scan_timeout(gpointer data) { GtkTextView *text_view; GDK_THREADS_ENTER(); text_view = GTK_TEXT_VIEW(data); gtk_text_view_scroll_mark_onscreen(text_view, gtk_text_buffer_get_mark (gtk_text_view_get_buffer (text_view), "insert")); GDK_THREADS_LEAVE(); return TRUE; /* remain installed. */ } typedef enum { SELECT_CHARACTERS, SELECT_WORDS, SELECT_LINES } SelectionGranularity; /* * Move @start and @end to the boundaries of the selection unit (indicated by * @granularity) which contained @start initially. Return wether @start was * contained in a selection unit at all (which may not be the case for words). */ static gboolean extend_selection(GtkTextView * text_view, SelectionGranularity granularity, GtkTextIter * start, GtkTextIter * end, GtkTextTag * bullet) { gboolean extend = TRUE; *end = *start; if (granularity == SELECT_WORDS) { if (gtk_text_iter_inside_word(start)) { if (!gtk_text_iter_starts_word(start)) gtk_text_iter_backward_visible_word_start(start); if (!gtk_text_iter_ends_word(end)) { if (!gtk_text_iter_forward_visible_word_end(end)) gtk_text_iter_forward_to_end(end); } } else extend = FALSE; } else if (granularity == SELECT_LINES) { if (gtk_text_view_starts_display_line(text_view, start)) { /* If on a display line boundary, we assume the user clicked off * the end of a line and we therefore select the line before the * boundary. */ gtk_text_view_backward_display_line_start(text_view, start); } else { /* start isn't on the start of a line, so we move it to the * start, and move end to the end unless it's already there. */ gtk_text_view_backward_display_line_start(text_view, start); if (!gtk_text_view_starts_display_line(text_view, end)) gtk_text_view_forward_display_line_end(text_view, end); } if (_wp_text_iter_is_bullet(start, bullet)) _wp_text_iter_skip_bullet(start, bullet, TRUE); } return extend; } static gint selection_motion_event_handler(GtkTextView * text_view, GdkEventMotion * event, gpointer data) { SelectionGranularity granularity = GPOINTER_TO_INT(data); GtkTextBuffer *buffer; GtkTextTag *bullet; gint x, y; WPTextView *view = WP_TEXT_VIEW(text_view); if (event->is_hint) gdk_device_get_state(event->device, event->window, NULL, NULL); buffer = gtk_text_view_get_buffer(text_view); bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); get_mouse_coords(text_view, &x, &y); #define MIN_MOVE 6 if (abs(x - view->mx) < MIN_MOVE && abs(y - view->my) < MIN_MOVE) return TRUE; view->mx = x; view->my = y; if (granularity == SELECT_CHARACTERS) { move_mark_to_pointer_and_scroll(text_view, "insert", bullet); } else { GtkTextIter start, end; GtkTextIter old_start, old_end; GtkTextIter ins, bound; gtk_text_layout_get_iter_at_pixel(text_view->layout, &start, x, y); if (_wp_text_iter_is_bullet(&start, bullet)) _wp_text_iter_skip_bullet(&start, bullet, TRUE); if (extend_selection(text_view, granularity, &start, &end, bullet)) { /* Extend selection */ gtk_text_buffer_get_iter_at_mark(buffer, &ins, gtk_text_buffer_get_insert (buffer)); gtk_text_buffer_get_iter_at_mark(buffer, &bound, gtk_text_buffer_get_selection_bound (buffer)); if (gtk_text_iter_compare(&ins, &bound) < 0) { old_start = ins; old_end = bound; } else { old_start = bound; old_end = ins; } if (gtk_text_iter_compare(&start, &old_start) < 0) { /* newly selected unit before the current selection */ ins = start; bound = old_end; } else if (gtk_text_iter_compare(&old_end, &end) < 0) { /* newly selected unit after the current selection */ ins = end; bound = old_start; } else if (gtk_text_iter_equal(&ins, &old_start)) { /* newly selected unit inside the current selection at the * start */ if (!gtk_text_iter_equal(&ins, &start)) ins = end; } else { /* newly selected unit inside the current selection at the * end */ if (!gtk_text_iter_equal(&ins, &end)) ins = start; } gtk_text_buffer_select_range(buffer, &ins, &bound); } gtk_text_view_scroll_mark_onscreen(text_view, gtk_text_buffer_get_mark(buffer, "insert")); } /* If we had to scroll offscreen, insert a timeout to do so again. Note * that in the timeout, even if the mouse doesn't move, due to this * scroll xoffset/yoffset will have changed and we'll need to scroll * again. */ if (text_view->scroll_timeout != 0) /* reset on every motion event */ g_source_remove(text_view->scroll_timeout); text_view->scroll_timeout = g_timeout_add(50, selection_scan_timeout, text_view); return TRUE; } static void gtk_text_view_start_selection_drag(GtkTextView * text_view, const GtkTextIter * iter, GdkEventButton * button) { GtkTextIter start, end; GtkTextBuffer *buffer; GtkTextTag *bullet; WPTextView *view = WP_TEXT_VIEW(text_view); SelectionGranularity granularity; g_return_if_fail(text_view->selection_drag_handler == 0); get_mouse_coords(text_view, &view->mx, &view->my); if (button->type == GDK_2BUTTON_PRESS) granularity = SELECT_WORDS; else if (button->type == GDK_3BUTTON_PRESS) granularity = SELECT_LINES; else granularity = SELECT_CHARACTERS; gtk_grab_add(GTK_WIDGET(text_view)); buffer = gtk_text_view_get_buffer(text_view); bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); start = *iter; if (_wp_text_iter_is_bullet(&start, bullet)) _wp_text_iter_skip_bullet(&start, bullet, TRUE); extend_selection(text_view, granularity, &start, &end, bullet); if (button->state & GDK_SHIFT_MASK) { /* Extend selection */ GtkTextIter old_start, old_end; gtk_text_buffer_get_selection_bounds(buffer, &old_start, &old_end); gtk_text_iter_order(&start, &old_start); gtk_text_iter_order(&old_end, &end); /* Now start is the first of the starts, and end is the last of the * ends */ } gtk_text_buffer_select_range(buffer, &end, &start); text_view->selection_drag_handler = g_signal_connect(text_view, "motion_notify_event", G_CALLBACK (selection_motion_event_handler), GINT_TO_POINTER (granularity)); } /* returns whether we were really dragging */ static gboolean gtk_text_view_end_selection_drag(GtkTextView * text_view, GdkEventButton * event) { if (text_view->selection_drag_handler == 0) return FALSE; g_signal_handler_disconnect(text_view, text_view->selection_drag_handler); text_view->selection_drag_handler = 0; if (text_view->scroll_timeout != 0) { g_source_remove(text_view->scroll_timeout); text_view->scroll_timeout = 0; } gtk_grab_remove(GTK_WIDGET(text_view)); return TRUE; } /* Took from the gtk with little modification, to not be able to select the * bullet */ static gint wp_text_view_button_press_event(GtkWidget * widget, GdkEventButton * event) { GtkTextView *text_view; text_view = GTK_TEXT_VIEW(widget); gtk_widget_grab_focus(widget); #ifdef HAVE_HILDON if (text_view->editable && hildon_gtk_im_context_filter_event(text_view->im_context, (GdkEvent *) event)) { text_view->need_im_reset = TRUE; return TRUE; } #endif // if (event->window != gtk_text_view_get_window(text_view, // GTK_TEXT_WINDOW_TEXT)) if (gtk_text_view_get_window_type(text_view, event->window) != GTK_TEXT_WINDOW_TEXT) { return GTK_WIDGET_CLASS(wp_text_view_parent_class)-> button_press_event(widget, event); } if (event->type == GDK_BUTTON_PRESS) { gtk_im_context_reset(text_view->im_context); if (event->button == 1) { /* If we're in the selection, start a drag copy/move of the * selection; otherwise, start creating a new selection. */ GtkTextIter iter; GtkTextIter start, end; gint x, y; gtk_text_view_window_to_buffer_coords(text_view, GTK_TEXT_WINDOW_TEXT, (int) event->x, (int) event->y, &x, &y); gtk_text_layout_get_iter_at_pixel(text_view->layout, &iter, x, y); if (gtk_text_buffer_get_selection_bounds (gtk_text_view_get_buffer(text_view), &start, &end) && gtk_text_iter_in_range(&iter, &start, &end)) { text_view->drag_start_x = event->x; text_view->drag_start_y = event->y; text_view->pending_place_cursor_button = event->button; } else { gtk_text_view_start_selection_drag(text_view, &iter, event); } return TRUE; } } else if ((event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) && event->button == 1) { GtkTextIter iter; gint x, y; gtk_text_view_end_selection_drag(text_view, event); gtk_text_view_window_to_buffer_coords(text_view, GTK_TEXT_WINDOW_TEXT, (int) event->x, (int) event->y, &x, &y); gtk_text_layout_get_iter_at_pixel(text_view->layout, &iter, x, y); gtk_text_view_start_selection_drag(text_view, &iter, event); return TRUE; } return FALSE; } static void wp_text_view_backspace(GtkTextView * text_view) { GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view); GtkTextIter end; gboolean run_parent = TRUE; gtk_text_buffer_begin_user_action(buffer); if (!gtk_text_buffer_get_selection_bounds(buffer, &end, NULL)) { GtkTextIter start, iter; GtkTextTag *bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); iter = end; if (gtk_text_iter_ends_tag(&end, bullet)) { if (gtk_text_iter_backward_line(&iter) && !_wp_text_iter_has_bullet(&iter, bullet)) run_parent = FALSE; start = end; gtk_text_iter_backward_char(&start); _wp_text_iter_skip_bullet(&start, bullet, FALSE); gtk_text_buffer_delete(buffer, &start, &end); } } if (run_parent) GTK_TEXT_VIEW_CLASS(wp_text_view_parent_class)->backspace(text_view); gtk_text_buffer_end_user_action(buffer); } static void wp_text_view_delete_from_cursor(GtkTextView * text_view, GtkDeleteType type, gint count) { GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view); gboolean had_selection = gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL); gtk_text_buffer_begin_user_action(buffer); GTK_TEXT_VIEW_CLASS(wp_text_view_parent_class)-> delete_from_cursor(text_view, type, count); if (!had_selection) { GtkTextIter start, end; GtkTextTag *bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); gtk_text_buffer_get_iter_at_mark(buffer, &start, gtk_text_buffer_get_insert(buffer)); if (gtk_text_iter_begins_tag(&start, bullet)) { end = start; _wp_text_iter_skip_bullet(&end, bullet, TRUE); gtk_text_buffer_delete(buffer, &start, &end); } } gtk_text_buffer_end_user_action(buffer); } /* TODO: maybe it would be better, if the WPTextView will use a separate * clipboard content than GtkTextBuffer. It is important for bullets. When * the bullets are on, and the clipboard is not containing a WPTextView * buffer, it should bulletize each line of the pasted text */ static void wp_text_view_paste_clipboard(GtkTextView * text_view) { GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view); /* GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET * (text_view), GDK_SELECTION_CLIPBOARD); gboolean simple_text; */ GtkTextTag *bullet = NULL; GtkTextIter start, iter; gint offset; gboolean has_bullet = FALSE; gtk_text_buffer_get_selection_bounds(buffer, &iter, NULL); offset = gtk_text_iter_get_offset(&iter); bullet = _wp_text_buffer_get_bullet_tag(WP_TEXT_BUFFER(buffer)); has_bullet = _wp_text_iter_has_bullet(&iter, bullet); gtk_text_buffer_begin_user_action(buffer); /* simple_text = !gtk_clipboard_wait_is_target_available (clipboard, * gdk_atom_intern ("WP_TEXT_VIEW", FALSE)); */ // printf("Paste begin\n"); wp_text_buffer_freeze(WP_TEXT_BUFFER(buffer)); GTK_TEXT_VIEW_CLASS(wp_text_view_parent_class)-> paste_clipboard(text_view); wp_text_buffer_thaw(WP_TEXT_BUFFER(buffer)); // printf("Paste end\n"); gtk_text_buffer_get_iter_at_mark(buffer, &iter, gtk_text_buffer_get_insert(buffer)); gtk_text_buffer_get_iter_at_offset(buffer, &start, offset); /* printf("Paste: %d - %d\n", gtk_text_iter_get_offset(&start), * gtk_text_iter_get_offset(&iter)); */ if (gtk_text_iter_get_line(&start) != gtk_text_iter_get_line(&iter)) { if (!gtk_text_iter_starts_line(&start)) _wp_text_buffer_adjust_justification(WP_TEXT_BUFFER(buffer), &start, NULL, NULL, FALSE); if (!gtk_text_iter_ends_line(&iter)) _wp_text_buffer_adjust_justification(WP_TEXT_BUFFER(buffer), NULL, &iter, NULL, FALSE); } else _wp_text_buffer_adjust_justification(WP_TEXT_BUFFER(buffer), &start, &iter, NULL, FALSE); if (bullet) { if (has_bullet) _wp_text_iter_put_bullet_line(&iter, bullet); else { if (!gtk_text_iter_ends_line(&iter)) _wp_text_iter_remove_bullet_line(&iter, bullet); } } gtk_text_buffer_end_user_action(buffer); } static void wp_text_view_def_font_changed(WPTextBuffer * buffer, PangoFontDescription * desc, GtkWidget * view) { gtk_widget_modify_font(view, desc); } static void wp_text_view_background_color_changed(WPTextBuffer * buffer, const GdkColor * color, GtkTextView * text_view) { gtk_widget_modify_base(GTK_WIDGET(text_view), GTK_STATE_NORMAL, color); } static void wp_text_view_def_justification_changed(WPTextBuffer * buffer, gint justification, GtkTextView * text_view) { gtk_text_view_set_justification(text_view, justification); } /* IM Handling - mostly taken from gtk */ static void wp_text_view_commit_handler(GtkIMContext * context, const gchar * str, GtkTextView * text_view) { // printf("WP Commit text: %s\n", str); if (*str) wp_text_view_commit_text(text_view, str); if (WP_TEXT_VIEW(text_view)->in_action) { gtk_text_buffer_end_user_action(gtk_text_view_get_buffer(text_view)); WP_TEXT_VIEW(text_view)->in_action = FALSE; } } static void wp_text_view_commit_text(GtkTextView * text_view, const gchar * text) { gboolean had_selection; gtk_text_buffer_begin_user_action(gtk_text_view_get_buffer(text_view)); had_selection = gtk_text_buffer_get_selection_bounds(gtk_text_view_get_buffer (text_view), NULL, NULL); gtk_text_buffer_delete_selection(gtk_text_view_get_buffer(text_view), TRUE, text_view->editable); if (!strcmp(text, "\n")) { gtk_text_buffer_insert_interactive_at_cursor(gtk_text_view_get_buffer (text_view), "\n", 1, text_view->editable); } else { if (!had_selection && text_view->overwrite_mode) { GtkTextIter insert; gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer (text_view), &insert, gtk_text_buffer_get_mark (gtk_text_view_get_buffer (text_view), "insert")); if (!gtk_text_iter_ends_line(&insert)) wp_text_view_delete_from_cursor(text_view, GTK_DELETE_CHARS, 1); } gtk_text_buffer_insert_interactive_at_cursor(gtk_text_view_get_buffer (text_view), text, -1, text_view->editable); } gtk_text_buffer_end_user_action(gtk_text_view_get_buffer(text_view)); gtk_text_view_scroll_mark_onscreen(text_view, gtk_text_buffer_get_mark (gtk_text_view_get_buffer(text_view), "insert")); } static void wp_text_view_preedit_changed_handler(GtkIMContext * context, GtkTextView * text_view) { gchar *str; PangoAttrList *attrs; gint cursor_pos; gtk_im_context_get_preedit_string(context, &str, &attrs, &cursor_pos); gtk_text_layout_set_preedit_string(text_view->layout, str, attrs, cursor_pos); pango_attr_list_unref(attrs); // printf("WP Preedit changed: %s\n", str); g_free(str); gtk_text_view_scroll_mark_onscreen(text_view, gtk_text_buffer_get_mark (gtk_text_view_get_buffer(text_view), "insert")); } static gboolean whitespace(gunichar ch, gpointer user_data) { return (ch == ' ' || ch == '\t'); } static gboolean not_whitespace_crlf(gunichar ch, gpointer user_data) { return !whitespace(ch, user_data) && ch != '\r' && ch != '\n'; } static gboolean wp_text_view_retrieve_surrounding_handler(GtkIMContext * context, GtkTextView * text_view) { GtkTextIter start; GtkTextIter end; GtkTextIter cursor; gint pos; gchar *text; gchar *text_between = NULL; #ifdef DISABLE_SURROUNDING return FALSE; #endif gtk_text_buffer_get_iter_at_mark(text_view->buffer, &cursor, gtk_text_buffer_get_insert(text_view-> buffer)); end = start = cursor; gtk_text_iter_set_line_offset(&start, 0); gtk_text_iter_forward_to_line_end(&end); /* we want to include the previous non-whitespace character in the * surroundings. */ if (gtk_text_iter_backward_char(&start)) gtk_text_iter_backward_find_char(&start, not_whitespace_crlf, NULL, NULL); text_between = gtk_text_iter_get_slice(&start, &cursor); if (text_between != NULL) pos = strlen(text_between); else pos = 0; text = gtk_text_iter_get_slice(&start, &end); // printf("WP Surronding: %d, %s\n", pos, text); gtk_im_context_set_surrounding(context, text, -1, pos); g_free(text); g_free(text_between); return TRUE; } static gboolean wp_text_view_delete_surrounding_handler(GtkIMContext * context, gint offset, gint n_chars, GtkTextView * text_view) { GtkTextIter start; GtkTextIter end; gtk_text_buffer_begin_user_action(gtk_text_view_get_buffer(text_view)); WP_TEXT_VIEW(text_view)->in_action = TRUE; gtk_text_buffer_get_iter_at_mark(text_view->buffer, &start, gtk_text_buffer_get_insert(text_view-> buffer)); end = start; gtk_text_iter_forward_chars(&start, offset); gtk_text_iter_forward_chars(&end, offset + n_chars); /* printf("WP Delete surrounding: %d-%d\n", * gtk_text_iter_get_offset(&start), gtk_text_iter_get_offset(&end)); */ wp_text_buffer_remember_tag(WP_TEXT_BUFFER(text_view->buffer), TRUE); gtk_text_buffer_delete_interactive(text_view->buffer, &start, &end, text_view->editable); wp_text_buffer_remember_tag(WP_TEXT_BUFFER(text_view->buffer), FALSE); return TRUE; } static gboolean wp_text_view_has_selection_handler(GtkIMContext * context, GtkTextView * text_view) { GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer(text_view); return gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL); } #ifdef HAVE_HILDON static void wp_text_view_clipboard_operation_handler(GtkIMContext * context, GtkIMContextClipboardOperation op, GtkTextView * text_view) { /* Similar to gtk_editable_*_clipboard(), handle these by sending signals * instead of directly calling our internal functions. That way the * application can hook into them if needed. */ switch (op) { case GTK_IM_CONTEXT_CLIPBOARD_OP_COPY: g_signal_emit_by_name(text_view, "copy_clipboard"); break; case GTK_IM_CONTEXT_CLIPBOARD_OP_CUT: g_signal_emit_by_name(text_view, "cut_clipboard"); break; case GTK_IM_CONTEXT_CLIPBOARD_OP_PASTE: g_signal_emit_by_name(text_view, "paste_clipboard"); break; } } #endif void wp_text_view_reset_and_show_im(WPTextView * view) { GtkTextView *text_view = GTK_TEXT_VIEW(view); gtk_im_context_reset(text_view->im_context); #ifdef HAVE_HILDON hildon_gtk_im_context_show(text_view->im_context); #endif } wpeditor-2.18/src/wptextbuffer.h0000644000175000001440000004266210651120171016215 0ustar stevenusers/** * @file wptextbuffer.h * * Header file for WordPad Text Buffer */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #ifndef _WP_TEXT_BUFFER_H #define _WP_TEXT_BUFFER_H #include #include G_BEGIN_DECLS #define WP_TYPE_TEXT_BUFFER (wp_text_buffer_get_type ()) #define WP_TEXT_BUFFER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WP_TYPE_TEXT_BUFFER, WPTextBuffer)) #define WP_TEXT_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WP_TYPE_TEXT_BUFFER, WPTextBufferClass)) #define WP_IS_TEXT_BUFFER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WP_TYPE_TEXT_BUFFER)) #define WP_IS_TEXT_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WP_TYPE_TEXT_BUFFER)) #define WP_TEXT_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WP_TYPE_TEXT_BUFFER, WPTextBufferClass)) typedef struct _WPTextBuffer WPTextBuffer; typedef struct _WPTextBufferPrivate WPTextBufferPrivate; typedef struct _WPTextBufferClass WPTextBufferClass; /** Constansts used for tag types */ enum { /** Tag contains bold style */ WPT_BOLD = 0, /** Tag contains italic style */ WPT_ITALIC, /** Tag contains underline style */ WPT_UNDERLINE, /** Tag contains strikethrough style */ WPT_STRIKE, /** Tag is an align to left style */ WPT_LEFT, /** Tag is an align to center style */ WPT_CENTER, /** Tag is an align to right style */ WPT_RIGHT, /** Tag contains a bullet or number */ WPT_BULLET, /** Tag contains a foreground color */ WPT_FORECOLOR, WPT_LASTTAG, /** Tag is a font size modifier */ WPT_FONT_SIZE = 1000, /** Tag is a superscript with font size modifier */ WPT_SUP_SRPT = 2000, /** Tag is a subscript with font size modifier */ WPT_SUB_SRPT = 3000, /** Tag is a font family modifier */ WPT_FONT = 4000, /** Tag is a group of #WPT_FONT_SIZE, #WPT_SUP_SRPT and #WPT_SUB_SRPT */ WPT_ALL_FONT_SIZE = 9000 }; /** Contains all the font sizes in points */ extern const gint wp_font_size[]; /** Number of font size */ #define WP_FONT_SIZE_COUNT 7 /** Scaling factor for superscript and subscript font size */ #define SUP_SUB_SIZE_MULT 3 #define SUP_SUB_SIZE_DIV 5 /** Scaling factor for superscript and subscript rise */ #define SUP_RISE_MULT 4 #define SUP_RISE_DIV 5 #define SUB_RISE_MULT 2 #define SUB_RISE_DIV 5 /** Text position type */ typedef enum { TEXT_POSITION_NORMAL = 0, TEXT_POSITION_SUPERSCRIPT, TEXT_POSITION_SUBSCRIPT } TextPosition; /** Format change set, used to notify when a specific style is set */ typedef struct { gint bold:1; gint italic:1; gint underline:1; gint strikethrough:1; gint justification:1; gint text_position:1; gint color:1; gint font_size:1; gint font:1; gint bullet:1; } WPTextBufferFormatChangeSet; /** Contains a format state */ typedef struct { gint bold:1; gint italic:1; gint underline:1; gint strikethrough:1; gint bullet:1; TextPosition text_position; gint justification; GdkColor color; gint font; gint font_size; gint rich_text:1; WPTextBufferFormatChangeSet cs; } WPTextBufferFormat; /** WPTextBuffer object */ struct _WPTextBuffer { GtkTextBuffer parent; WPTextBufferPrivate *priv; }; /** WPTextBuffer class */ struct _WPTextBufferClass { GtkTextBufferClass parent_class; /** * Called when refresh attributes should happen * @param buffer pointer to a #WPTextBuffer */ void (*refresh_attributes) (WPTextBuffer * buffer); /** * Called when the undo state has changed * @param buffer pointer to a #WPTextBuffer * @param can_undo is TRUE when there is something to undo */ void (*can_undo) (WPTextBuffer * buffer, gboolean can_undo); /** * Called when the undo state has changed * @param buffer pointer to a #WPTextBuffer * @param can_redo is TRUE when there is something to redo */ void (*can_redo) (WPTextBuffer * buffer, gboolean can_redo); /** * Called when the formatting has changed (rich text <-> plain text) * @param buffer pointer to a #WPTextBuffer * @param rich_text is TRUE when the buffer contains rich text */ void (*fmt_changed) (WPTextBuffer * buffer, gboolean rich_text); /** * Called when the default font has been changed * @param buffer pointer to a #WPTextBuffer * @param desc pointer to a #PangoFontDescription containing the new font */ void (*def_font_changed) (WPTextBuffer * buffer, PangoFontDescription * desc); /** * Called when the default justification has been changed * @param buffer pointer to a #WPTextBuffer * @param alignment contains the new alignment */ void (*def_justification_changed) (WPTextBuffer * buffer, gint alignment); /** * Called when the background color has been changed * @param buffer pointer to a #WPTextBuffer * @param color pointer to a #GdkColor containing the new color */ void (*background_color_changed) (WPTextBuffer * buffer, const GdkColor * color); /** * Called when there is not enough memory to perform the operation * @param buffer pointer to a #WPTextBuffer */ void (*no_memory) (WPTextBuffer * buffer); }; /** * A save callback type used to save the buffer to an external format * @param buffer a NULL terminated character list containing the * text to be written * @param user_data contains a user supplied pointer */ typedef gint(*WPDocumentSaveCallback) (const gchar * buffer, gpointer user_data); GType wp_text_buffer_get_type(void) G_GNUC_CONST; /** * Creates a new wordpad text buffer * @param table pointer to a #GtkTextTagTable or NULL * @return the newly created #WPTextBuffer */ WPTextBuffer *wp_text_buffer_new(GtkTextTagTable * table); /** * Queries the state of a selection in the buffer * @param buffer pointer to a #WPTextBuffer * @return TRUE if there is something selected */ gboolean wp_text_buffer_has_selection(WPTextBuffer * buffer); /** * Retrieve a specific tag from the buffer. The tag can be identified * with WPT_* * @param buffer pointer to a #WPTextBuffer * @param tagno contains the tag identifier. It should be between #WPT_BOLD and * WPT_LASTTAG */ GtkTextTag *wp_text_buffer_get_tag(WPTextBuffer * buffer, gint tagno); /** * Get the formatting attributes from the text. * @param buffer pointer to a #WPTextBuffer * @param pointer to a #WPTextBufferFormat which will be filled with the current * attributes from the cursor position * @param parse_selection means that the selection should be parsed, to detect * if the tag is toggled multiple times. If it is toggled multiple * times, then the fmt->cs.* will be set accordingly. */ void wp_text_buffer_get_attributes(WPTextBuffer * buffer, WPTextBufferFormat * fmt, gboolean parse_selection); /** * Get the formatting attributes from the text without parsing the selection * @param buffer pointer to a #WPTextBuffer * @param pointer to a #WPTextBufferFormat which will be filled with the current * attributes from the cursor position */ void wp_text_buffer_get_current_state(WPTextBuffer * buffer, WPTextBufferFormat * fmt); /** * Modify the current attribute to the given one. If there is something selected, * the attribute is applied for the whole selection, otherwise if the cursor is * on a word (not at the end), then the attribute will be applied to the word * otherwise the attribute is just toggled, waiting to be applied to the next * entered character. * @param buffer pointer to a #WPTextBuffer * @param tagno contains the tag identifier. * @param data can contain different formats.
    *
  • If tagid <= WPT_BULLET, then it is treated as (gboolean)data *
  • If tagid == WPT_COLOR, then it is treated as (GdkColor *)data *
  • If tagid == WPT_FONT or WPT_FONT_SIZE, then it is treated as (gint)data *
* @return TRUE if the attribute has been applied to a selection or a word */ gboolean wp_text_buffer_set_attribute(WPTextBuffer * buffer, gint tagid, gpointer data); /** * Same as #wp_text_buffer_set_attribute, only it can modify several attributes * at the same time. If fmt->cs.* is set then it will be applied using the same * rules. * @param buffer pointer to a #WPTextBuffer * @param fmt pointer to a #WPTextBufferFormat * @return TRUE if the attribute has been applied to a selection or a word */ gboolean wp_text_buffer_set_format(WPTextBuffer * buffer, WPTextBufferFormat * fmt); /** * Undo the last operation in the buffer. * @param buffer pointer to a #WPTextBuffer */ void wp_text_buffer_undo(WPTextBuffer * buffer); /** * Redo the last operation in the buffer. * @param buffer pointer to a #WPTextBuffer */ void wp_text_buffer_redo(WPTextBuffer * buffer); /** * Cursor movement detection and tag copying is freezed in the buffer. * It is a reference count, so it can be called several time. * @param buffer pointer to a #WPTextBuffer */ void wp_text_buffer_freeze(WPTextBuffer * buffer); /** * Cursor movement detection and tag copying is defreezed in the buffer * @param buffer pointer to a #WPTextBuffer */ void wp_text_buffer_thaw(WPTextBuffer * buffer); /** * Enable rich text in the buffer * @param buffer pointer to a #WPTextBuffer * @param rich_text is TRUE if rich text should be supported in the buffer */ void wp_text_buffer_enable_rich_text(WPTextBuffer * buffer, gboolean enable); /** * Queries the rich text state of the buffer * @param buffer pointer to a #WPTextBuffer * @return TRUE if the rich text is enabled */ gboolean wp_text_buffer_is_rich_text(WPTextBuffer * buffer); /** * Queries the modification state of the buffer * @param buffer pointer to a #WPTextBuffer * @return TRUE if the buffer contains modified text / attributes */ gboolean wp_text_buffer_is_modified(WPTextBuffer * buffer); /** * Set the font scaling factor to the buffer * @param buffer pointer to a #WPTextBuffer * @param scale the new font scaling factor */ void wp_text_buffer_set_font_scaling_factor(WPTextBuffer * buffer, double scale); /** * Set the current background color * @param buffer: pointer to a #WPTextBuffer * @param color: a #GdkColor * * establishes a new background color. */ void wp_text_buffer_set_background_color(WPTextBuffer * buffer, const GdkColor * color); /** * Get the current background color * @param buffer: pointer to a #WPTextBuffer * * obtains the current background color * * Returns: a #GdkColor. */ const GdkColor *wp_text_buffer_get_background_color(WPTextBuffer * buffer); /** * Insert a given text to a given position into the buffer, and apply the given * attributes to them. * @param buffer pointer to a #WPTextBuffer * @param pos contains the position iterator where the insert should happen * @param text a valid UTF-8 character array * @param len contains the length of the inserted text * @param fmt pointer to a #WPTextBufferFormat, holding the attributes of the new * text * @param disable_undo if the undo should be disable during this complex operation */ void wp_text_buffer_insert_with_attribute(WPTextBuffer * buffer, GtkTextIter * pos, gchar * text, gint len, WPTextBufferFormat * fmt, gboolean disable_undo); /** * Inserts an image inside the text buffer. * @param buffer pointer to a #WPTextBuffer * @param pos containing the position iterator where the insert should happen * @param image_id the image id, used to refer to the specific image * @param pixbuf is a #GdkPixbuf */ void wp_text_buffer_insert_image(WPTextBuffer * buffer, GtkTextIter * pos, const gchar * image_id, GdkPixbuf * pixbuf); /** * Inserts an image replacement inside the text buffer. THese replacement * can be replaced with the image later. * @param buffer pointer to a #WPTextBuffer * @param pos containing the position iterator where the insert should happen * @param image_id the image id, used to refer to the specific image */ void wp_text_buffer_insert_image_replacement (WPTextBuffer *buffer, GtkTextIter *pos, const gchar *image_id); /** * Replaces all image replacements with image_id with pixbuf. * @param buffer: a #WPTextBuffer * @param image_id the id of the image * @param pixbuf a #GdkPixbuf */ void wp_text_buffer_replace_image (WPTextBuffer *buffer, const gchar *image_id, GdkPixbuf *pixbuf); /** * Removes an image inside the text buffer. * @param buffer pointer to a #WPTextBuffer * @param image_id the image id, used to refer to the specific image */ void wp_text_buffer_remove_image(WPTextBuffer * buffer, const gchar * image_id); /** * Reset the buffer. Clear the text, clear the undo queue, set it to unmodified. * @param buffer pointer to a #WPTextBuffer * @param rich_text if the reseted buffer will contain rich text */ void wp_text_buffer_reset_buffer(WPTextBuffer * buffer, gboolean rich_text); /** * Prepare the buffer for document loading. The document is loaded in chunck, * for better memory usage, and interoperability. * @param buffer pointer to a #WPTextBuffer * @param html if the data will be read from an html file */ void wp_text_buffer_load_document_begin(WPTextBuffer * buffer, gboolean html); /** * Load the next chunk into the buffer * @param buffer pointer to a #WPTextBuffer * @param data pointer to the read data * @param size contains the length of the data */ void wp_text_buffer_load_document_write(WPTextBuffer * buffer, gchar * data, gint size); /** * Finalize the buffer loading. * @param buffer pointer to a #WPTextBuffer */ void wp_text_buffer_load_document_end(WPTextBuffer * buffer); /** * Save the content of the buffer. The save is happening in chuncks, * so a #WPDocumentSaveCallback must be supplied to the function. * The save process is interrupted if the return value of the save * callback is not 0. * @param buffer pointer to a #WPTextBuffer * @param save contains the user save callback which will be called, when a new * chunk is available and has to be written * @param user_data user supplied user data, which will be forwarded * to the save callback * @return the result of the save callback */ gint wp_text_buffer_save_document(WPTextBuffer * buffer, WPDocumentSaveCallback save, gpointer user_data); /** * Queries the name of the font family at the given index * @param index is a number between 0 and #wp_get_font_count * @return the font family name */ const gchar *wp_get_font_name(gint index); /** * Tries to find the insensitive font_name in the detected font list. * @param font_name is the name of the font we are trying to find * @param def is the index of the font for the situation when the font is not found * @return the font name index if is found otherwise return def */ gint wp_get_font_index(const gchar * font_name, gint def); /** * Tries to find the correct font size index from the defined one * @param font_size is the size in points of the font we are trying to find * @param def is the index of the default font size for the situation when the * font is not found * @return the font size index if is found otherwise return def */ gint wp_get_font_size_index(gint font_size, gint def); /** * Queries the number of fonts detected on the system. * @return the number of fonts */ gint wp_get_font_count(); /** * Initialize the WordPad Text Buffer library */ void wp_text_buffer_library_init(); /** * Finalize the WordPad Text Buffer library */ void wp_text_buffer_library_done(); G_END_DECLS #endif /* _WP_TEXT_BUFFER_H */ wpeditor-2.18/src/wptextbuffer-private.h0000644000175000001440000001060410647343013017663 0ustar stevenusers/** * @file wptextbuffer-private.h * * Private header file for WordPad Text Buffer used only between * WPTextView, WPUndo and WPHTMLParser */ /* * Osso Notes * Copyright (c) 2005-06 Nokia. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #ifndef _WP_TEXT_BUFFER_PRIVATE_H #define _WP_TEXT_BUFFER_PRIVATE_H #include #include G_BEGIN_DECLS /** * Queries if the position at iter contains opened tag * @param iter a position in the buffer * @param tag a #GtkTextTag, usually the bullet tag * @return TRUE if the tag is opened at iter */ gboolean _wp_text_iter_is_bullet(GtkTextIter * iter, GtkTextTag * tag); /** * Skip the tag at the iter position in forward direction * @param iter a position in the buffer * @param tag a #GtkTextTag, usually the bullet tag * @param forward set to TRUE if the skip is happening forward * @return TRUE if there was a tag at the iter position */ gboolean _wp_text_iter_skip_bullet(GtkTextIter * iter, GtkTextTag * tag, gboolean forward); /** * Queries if a the line specified by iter contains the tag at the * begining. Modify the position to iter to the begin of the line * @param iter a position in the buffer * @param tag a #GtkTextTag, usually the bullet tag * @return TRUE if the tag has been found */ gboolean _wp_text_iter_has_bullet(GtkTextIter * iter, GtkTextTag * tag); /** * Puts a tag at the begining of the line specified by iter * @param iter a position in the buffer * @param tag a #GtkTextTag, usually the bullet tag * @return TRUE if there was no tag at the begining of the line */ gboolean _wp_text_iter_put_bullet_line(GtkTextIter * iter, GtkTextTag * tag); /** * Removes a tag from the begining of the line specified by iter * @param iter a position in the buffer * @param tag a #GtkTextTag, usually the bullet tag */ void _wp_text_iter_remove_bullet_line(GtkTextIter * iter, GtkTextTag * tag); /** * Queries the #GtkTextTag used for delimiting a bullet * @param buffer pointer to a #WPTextBuffer * @return the bullet tag */ GtkTextTag *_wp_text_buffer_get_bullet_tag(WPTextBuffer * buffer); /** * Modify the justification of the text delimited by start and * end to be the same at the begining and at the end. Usually * called after two different line has been concatenated. * @param buffer pointer to a #WPTextBuffer * @param start a position in the buffer or NULL * @param end a position in the buffer or NULL * @param def_tag holds the default justification #GtkTextTag or NULL * @param align_to_right not used */ void _wp_text_buffer_adjust_justification(WPTextBuffer * buffer, GtkTextIter * start, GtkTextIter * end, GtkTextTag * def_tag, gboolean align_to_right); /** * Set the remember_tag flag to true. It is used, to remember the deleted tags * @param buffer pointer to a #WPTextBuffer */ void wp_text_buffer_remember_tag(WPTextBuffer * buffer, gboolean enable); /** * Print the tags to stdout toggled/untoggled at giter * Used only for debugging. * @param start a position in the buffer * @param what 0 is print all the tags, 1 for toggled on tags only, * 2 for toggled off tags only */ void debug_print_tags(GtkTextIter * giter, gint what); G_END_DECLS #endif /* _WP_TEXT_BUFFER_PRIVATE_H */ wpeditor-2.18/src/Makefile.am0000644000175000001440000000104010637734303015350 0ustar stevenuserswpeditorincludedir=$(includedir)/$(PACKAGE_INC) wpeditorinclude_HEADERS = \ wptextbuffer.h \ wptextview.h \ gtksourceiter.h wpeditor_LTLIBRARIES = libwpeditor.la wpeditordir = $(libdir) libwpeditor_la_SOURCES = \ wptextbuffer.c \ wptextbuffer.h \ wptextbuffer-private.h \ wptextview.c \ wptextview.h \ wpundo.c \ wpundo.h \ wphtmlparser.c \ wphtmlparser.h \ color_buffer.c \ color_buffer.h \ gtksourceiter.h \ gtksourceiter.c libwpeditor_la_CFLAGS = $(PACKAGE_CFLAGS) -DMAEMO_CHANGES libwpeditor_la_LIBADD = $(PACKAGE_LIBS) wpeditor-2.18/src/wphtmlparser.h0000644000175000001440000000624510647343013016224 0ustar stevenusers/** * @file wphtmlparser.h * * Header file for basic parsing a HTML file */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #ifndef _WP_HTML_PARSER_H #define _WP_HTML_PARSER_H #include "wptextbuffer.h" #define MAX_UTF8_LENGTH 13 // 2 * max utc4 size + 1 typedef struct _WPHTMLParser WPHTMLParser; /** * Initialize the html parser library. Fill the hash table with known html tags. */ void init_html_parser_library(); /** * Finalize the html parser library. Release the memory occupied by the hash table. */ void finalize_html_parser_library(); /** * Creates a new #WPHTMLParser for the buffer * @param buffer is a #WPTextBuffer * @return pointer to the newly created parser */ WPHTMLParser *wp_html_parser_new(WPTextBuffer * buffer); /** * Destroys a html parser * @param parser is a #WPHTMLParser */ void wp_html_parser_free(WPHTMLParser * parser); /** * Sets the default attributes for the parser * @param parser is a #WPHTMLParser * @param fmt is the default attributes */ void wp_html_parser_update_default_attributes(WPHTMLParser * parser, const WPTextBufferFormat * fmt); /** * Prepare the parser for a new document parsing * @param parser is a #WPHTMLParser */ void wp_html_parser_begin(WPHTMLParser * parser); /** * Writes a new chunk of data to the parser * @param parser is a #WPHTMLParser * @param buffer is a pointer to the data * @param size is the size of the buffer */ void wp_html_parser_write(WPHTMLParser * parser, gchar * buffer, gint size); /** * Finalize parsing of the document * @param parser is a #WPHTMLParser * @return the last line justification */ gint wp_html_parser_end(WPHTMLParser * parser); /** * Validates an invalid/partial utf8 character * @param buffer is a buffer holding the partial utf8 character * @param chars_in_buffer is the number of characters in buffer * @param source is the remaining bytes from uft8 character * @param max_source_len is the maximum length of source * @return number of bytes 'stolen' from source */ gint wp_html_parser_validate_invalid_utf8(gchar * buffer, gint chars_in_buffer, gchar * source, gint max_source_len); #endif /* _WP_HTML_PARSER_H */ wpeditor-2.18/src/wphtmlparser.c0000644000175000001440000010723110757561065016227 0ustar stevenusers/** * @file wphtmlparser.c * * Implementation file for basic parsing a HTML file */ /* * Osso Notes * Copyright (c) 2005-06 Nokia Corporation. All rights reserved. * Contact: Ouyang Qi * * 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 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Initial developer(s): Zsolt Simon */ #include #include #include #include #include #include "wphtmlparser.h" #define MAX_TAG_LENGTH 100 #define MAX_TAG_ATTR_LENGTH 100 #define MAX_TAG_VALUE_LENGTH 200 #define MAX_TEXT_LENGTH 4096 #define MAX_SPECIAL_CHAR 10 const gchar *tag_script = ""; #define SCRIPT_LEN 9 /** HTML state from the parsing state machine */ typedef enum { ST_TEXT, ST_TAG, ST_TAGNAME, ST_TAGNAMETXT, ST_TAGATTRNAME, ST_TAGATTRNAMETXT, ST_TAGATTRSEP, ST_TAGVALUE, ST_TAGVALUETXT, ST_TAGCLOSE, ST_COMMENT, ST_SCRIPT } HTMLState; /** List type */ typedef enum { LT_NONE, LT_BULLET, LT_NUM, LT_LC_ALPHA, LT_UC_ALPHA } HTMLListType; /** Font type */ typedef struct { gint font; gint font_size; GdkColor color; } HTMLFontType; /** Private structure */ struct _WPHTMLParser { /** The current state, the parser is in */ HTMLState state; /** Will hold last decoded text */ gchar last_text[MAX_TEXT_LENGTH + 2]; /** Pointer to the last character in the decoded text buffer */ gchar *last_text_pos; /** Pointer to the last special character '&' in the decoded buffer */ gchar *last_special_char; /** Last character was a space */ gint space:1; /** Is begin of line */ gint bol:1; /** Should text be skipped */ gint skip_text; /** Hold the last quote mark */ gchar last_quote_mark; /** Buffer to hold the last detected tag */ gchar last_tag[MAX_TAG_LENGTH + 1]; /** Pointer to the last character in the last_tag buffer */ gchar *last_tag_pos; /** Set if the tag is a close tag */ gint is_close_tag:1; /** Buffer to hold the last detected tag attribute */ gchar last_tag_attr[MAX_TAG_ATTR_LENGTH + 1]; /** Pointer to the last character in the last_tag_attr buffer */ gchar *last_tag_attr_pos; /** Set if is the first attribute */ gint is_first_attr:1; /** Buffer to hold the last detected tag attribute value */ gchar last_tag_value[MAX_TAG_VALUE_LENGTH + 1]; /** Pointer to the last character in the last_tag_value buffer */ gchar *last_tag_value_pos; /** Buffer to hold the partial utf8 character */ gchar last_char[MAX_UTF8_LENGTH]; /** Number of bytes the last broken uft8 character has */ gint last_char_bytes; /** Position in the script detection */ guchar script_pos; /** Set if currently we are in a script */ gint is_script:1; // Bullets and numbering /** Number of the last used list entry */ gint list_number; /** Type of the list */ HTMLListType list_type; /** Pointer to a #WPTextBuffer */ WPTextBuffer *buffer; /** List of font tags, need to know what will close a */ GSList *font_tags; /** Current format and default format of the text */ WPTextBufferFormat fmt, default_fmt; /** Last line justification */ gint last_line_justification; }; /** * Release the memory occupied by font_tags * @param parser is a #WPHTMLParser */ static void html_free_font_tags(WPHTMLParser * parser); typedef void (*ProcessTag) (WPHTMLParser * parser); /** * Process the HTML tag bold * @param parser is a #WPHTMLParser */ static void process_tag_bold(WPHTMLParser * parser); /** * Process the HTML tag italic * @param parser is a #WPHTMLParser */ static void process_tag_italic(WPHTMLParser * parser); /** * Process the HTML tag underline * @param parser is a #WPHTMLParser */ static void process_tag_underline(WPHTMLParser * parser); /** * Process the HTML tag strikethrough * @param parser is a #WPHTMLParser */ static void process_tag_strike(WPHTMLParser * parser); /** * Process the HTML tag subscript * @param parser is a #WPHTMLParser */ static void process_tag_sub(WPHTMLParser * parser); /** * Process the HTML tag superscript * @param parser is a #WPHTMLParser */ static void process_tag_sup(WPHTMLParser * parser); /** * Process the HTML tag div * @param parser is a #WPHTMLParser */ static void process_tag_div(WPHTMLParser * parser); /** * Process the HTML tag unordered list * @param parser is a #WPHTMLParser */ static void process_tag_ul(WPHTMLParser * parser); /** * Process the HTML tag ordered list * @param parser is a #WPHTMLParser */ static void process_tag_ol(WPHTMLParser * parser); /** * Process the HTML tag list item * @param parser is a #WPHTMLParser */ static void process_tag_li(WPHTMLParser * parser); /** * Process the HTML tag font * @param parser is a #WPHTMLParser */ static void process_tag_font(WPHTMLParser * parser); /** * Process the HTML tag img * @param parser is a #WPHTMLParser */ static void process_tag_img(WPHTMLParser *parser); /** * This tag will cause the text to be skippen * @param parser is a #WPHTMLParser */ static void process_skip_tag(WPHTMLParser * parser); /** * Process the HTML tag br * @param parser is a #WPHTMLParser */ static void process_tag_br(WPHTMLParser * parser); /** * Process the HTML tag paragraph * @param parser is a #WPHTMLParser */ static void process_tag_p(WPHTMLParser * parser); /** Hash table containing the HTML tags */ static GHashTable *tag_hash = NULL; /** * Add a new tag to the hash table tag_hash * @param tag is the tag name * @param func is the pointer to the processing function */ static inline void add_tag(gchar * tag, ProcessTag func) { g_hash_table_insert(tag_hash, tag, func); } void init_html_parser_library() { tag_hash = g_hash_table_new(g_str_hash, g_str_equal); add_tag("b", process_tag_bold); add_tag("strong", process_tag_bold); add_tag("i", process_tag_italic); add_tag("em", process_tag_italic); add_tag("cite", process_tag_italic); add_tag("u", process_tag_underline); add_tag("ins", process_tag_underline); add_tag("strike", process_tag_strike); add_tag("del", process_tag_strike); add_tag("s", process_tag_strike); add_tag("sub", process_tag_sub); add_tag("sup", process_tag_sup); add_tag("div", process_tag_div); add_tag("ul", process_tag_ul); add_tag("ol", process_tag_ol); add_tag("li", process_tag_li); add_tag("font", process_tag_font); add_tag("head", process_skip_tag); add_tag("br", process_tag_br); add_tag("p", process_tag_p); add_tag("img", process_tag_img); } void finalize_html_parser_library() { if (tag_hash) { g_hash_table_destroy(tag_hash); tag_hash = NULL; } } WPHTMLParser * wp_html_parser_new(WPTextBuffer * buffer) { WPHTMLParser *parser; if (!tag_hash) init_html_parser_library(); parser = g_new(WPHTMLParser, 1); if (parser) { parser->buffer = buffer; wp_html_parser_begin(parser); } return parser; } void wp_html_parser_free(WPHTMLParser * parser) { if (parser) { html_free_font_tags(parser); g_free(parser); } } void wp_html_parser_update_default_attributes(WPHTMLParser * parser, const WPTextBufferFormat * fmt) { parser->default_fmt = *fmt; parser->default_fmt.cs.color = TRUE; parser->fmt = parser->default_fmt; } /** * Writes the text to the WPTextBuffer * @param parser is a #WPHTMLParser */ static void html_write_text(WPHTMLParser * parser) { if (parser->last_special_char) { if (parser->last_text_pos - parser->last_special_char > MAX_SPECIAL_CHAR) { *parser->last_text_pos = 0; parser->last_special_char = NULL; } else *parser->last_special_char = 0; } else *parser->last_text_pos = 0; if (*parser->last_text) { GtkTextIter iter; gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(parser->buffer), &iter); wp_text_buffer_insert_with_attribute(parser->buffer, &iter, parser->last_text, -1, &parser->fmt, TRUE); parser->last_line_justification = parser->fmt.justification; // printf("Text: '%s'\n", parser->last_text); } if (parser->last_special_char) { gint len = parser->last_text_pos - parser->last_special_char; *parser->last_special_char = '&'; memcpy(parser->last_text, parser->last_special_char, len); parser->last_special_char = parser->last_text; parser->last_text_pos = parser->last_text + len; } else { parser->last_text_pos = parser->last_text; *parser->last_text = 0; // parser->space = !parser->bol; } } /** * Recognise and replace special HTML tags (ex. &) * @param parser is a #WPHTMLParser */ static void html_replace_special_char(WPHTMLParser * parser) { /* This function only recognise a small subset of special chars For more * see: http://www.pemberley.com/janeinfo/latin1.html */ gchar *pos = parser->last_special_char; *parser->last_text_pos = 0; parser->last_special_char = NULL; if (strcmp(pos + 1, "nbsp") == 0) pos += g_unichar_to_utf8(0xa0, pos) - 1; else if (strcmp(pos + 1, "gt") == 0) *pos = '>'; else if (strcmp(pos + 1, "lt") == 0) *pos = '<'; else if (strcmp(pos + 1, "amp") == 0) *pos = '&'; else if (strcmp(pos + 1, "quot") == 0) *pos = '"'; else if (strcmp(pos + 1, "space") == 0) *pos = ' '; else if (strcmp(pos + 1, "euro") == 0) { pos += g_unichar_to_utf8(0x20ac, pos) - 1; } else if (*(pos + 1) == '#') { gunichar ch; gchar *p = pos + 2; gint base = 10; if (*p == 'x') { p++; base = 16; } ch = (gunichar) strtol(p, NULL, base); if (g_unichar_isdefined(ch)) pos += g_unichar_to_utf8(ch, pos) - 1; else { *parser->last_text_pos = ';'; return; } } else { *parser->last_text_pos = ';'; return; } parser->last_text_pos = pos; } /** * Parse the retrieved HTML tag. It will look up in the hash table and call * the specific callback if found. * @param parser is a #WPHTMLParser */ static void html_parse_tag(WPHTMLParser * parser) { if (parser->last_tag_pos) { *parser->last_tag_pos = 0; // printf("New tag: %s,%d\n", parser->last_tag, // parser->is_close_tag); if (!parser->last_tag_attr_pos) parser->last_tag_attr_pos = parser->last_tag_attr; *parser->last_tag_attr_pos = 0; if (!parser->last_tag_value_pos) parser->last_tag_value_pos = parser->last_tag_value; *parser->last_tag_value_pos = 0; // printf("Value: %s='%s'\n", parser->last_tag_attr, // parser->last_tag_value); ProcessTag fnc = g_hash_table_lookup(tag_hash, parser->last_tag); if (fnc) fnc(parser); } } /** * Queries if at position pos is a close tag. * @param parser is a #WPHTMLParser * @param pos a position in a text * @return TRUE if it is a close tag. */ static gboolean html_is_tag_close(WPHTMLParser * parser, gchar * pos) { if (*pos == '/' && parser->state != ST_TAGCLOSE) { parser->state = ST_TAGCLOSE; return TRUE; } else if (*pos == '>') { parser->state = ST_TEXT; if (parser->last_tag_pos) html_parse_tag(parser); return TRUE; } return FALSE; } /** * Write a character to the last_text buffer from *pos. * Also check if the character is a partial utf8 character. * @param parser is a #WPHTMLParser * @param pos a position in the buffer * @param end a the end position of the buffer */ static gint html_write_char(WPHTMLParser * parser, gchar * pos, gchar * end) { if (parser->skip_text) return g_utf8_skip[*(guchar *) pos]; if (end && isspace(*pos)) { parser->space = !parser->bol; return 1; } else { guchar len; if (parser->space) { *parser->last_text_pos = ' '; parser->last_text_pos++; parser->space = FALSE; } parser->bol = FALSE; len = g_utf8_skip[*(guchar *) pos]; if (parser->last_text_pos - parser->last_text + len > MAX_TEXT_LENGTH) html_write_text(parser); // TODO: Find a faster validating method if (pos + len <= end || !end) { if (g_utf8_validate(pos, len, NULL)) { switch (len) { case 1: if (*pos == ';' && parser->last_special_char) html_replace_special_char(parser); else { if (*pos == '&') parser->last_special_char = parser->last_text_pos; *parser->last_text_pos = *pos; } break; case 2: *(gushort *) parser->last_text_pos = *(gushort *) pos; break; default: memcpy(parser->last_text_pos, pos, len); } parser->last_text_pos += len; } else len = 1; pos += len; } else { // broken UTF8 sequence at the end of the buffer parser->last_char_bytes = end - pos; memcpy(parser->last_char, pos, parser->last_char_bytes); } return len; } } /** * Inserts a new line to the buffer if needed or forced * @param parser is a #WPHTMLParser * @param force set to force a newline */ static void html_insert_newline(WPHTMLParser * parser, gboolean force) { if (force || (!parser->bol && !parser->is_close_tag)) { parser->space = FALSE; html_write_char(parser, "\n", NULL); html_write_text(parser); parser->bol = TRUE; } } /** * Insert a image replacement */ static void html_insert_image(WPHTMLParser *parser, const gchar *image_id) { GtkTextIter iter; html_write_text (parser); gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (parser->buffer), &iter); wp_text_buffer_insert_image_replacement (parser->buffer, &iter, image_id); } gint wp_html_parser_validate_invalid_utf8(gchar * buffer, gint chars_in_buffer, gchar * source, gint max_source_len) { gint len, clen; gchar *p = buffer; gchar *invalid_offset = NULL; if (source) { clen = MIN(6, max_source_len); memcpy(buffer + chars_in_buffer, source, clen); len = chars_in_buffer + clen; } else { len = chars_in_buffer; clen = 0; } buffer[len] = 0; while (!g_utf8_validate(p, buffer + len - p, (const gchar **) &invalid_offset)) { g_assert(buffer + len >= invalid_offset); memmove(invalid_offset, invalid_offset + 1, len - (invalid_offset + 1 - buffer)); len--; if (invalid_offset < buffer + chars_in_buffer) chars_in_buffer--; else { *invalid_offset = 0; return (invalid_offset - buffer) - chars_in_buffer; } p = invalid_offset; } return clen; } void wp_html_parser_write(WPHTMLParser * parser, gchar * buffer, gint size) { gchar *pos, *end; gchar c; /* This parser works correctly only for UTF8. To implement for other * charset, should be not so hard. Just the buffer need to be converted * to utf8 from the detected character set. */ pos = buffer; end = pos + size; if (parser->last_char_bytes) { pos += wp_html_parser_validate_invalid_utf8(parser->last_char, parser->last_char_bytes, pos, size); parser->last_char_bytes = 0; if (*parser->last_char) wp_html_parser_write(parser, parser->last_char, strlen(parser->last_char)); } while (pos < end) { switch (parser->state) { case ST_TEXT: if (*pos == '<') { pos++; html_write_text(parser); parser->state = ST_TAG; parser->is_close_tag = FALSE; } else pos += html_write_char(parser, pos, end); break; case ST_TAG: if (*pos == '!') { pos++; parser->state = ST_COMMENT; parser->last_quote_mark = 0; } else { if (*pos == '/') { pos++; parser->is_close_tag = TRUE; } parser->state = ST_TAGNAME; } break; case ST_TAGNAME: if (isspace(*pos)) pos++; else { parser->state = ST_TAGNAMETXT; parser->last_tag_pos = NULL; parser->last_tag_attr_pos = NULL; parser->is_first_attr = TRUE; } break; case ST_TAGNAMETXT: if (html_is_tag_close(parser, pos)) pos++; else if (isspace(*pos)) { pos++; if (parser->last_tag_pos) parser->state = ST_TAGATTRNAME; } else if (!parser->is_close_tag && tolower(*pos) == 's') { // Handle the case when special tag script is found // Skip the entire text between these, because script // can_redo // contain very ugly tags, and fool the parser! parser->state = ST_SCRIPT; parser->last_quote_mark = 0; parser->script_pos = 3; parser->is_script = FALSE; pos++; } else { if (!parser->last_tag_pos) parser->last_tag_pos = parser->last_tag; if (parser->last_tag_pos - parser->last_tag < MAX_TAG_LENGTH) { *parser->last_tag_pos = tolower(*pos); *parser->last_tag_pos++; } pos++; } break; case ST_TAGATTRNAME: if (isspace(*pos)) pos++; else { parser->state = ST_TAGATTRNAMETXT; parser->last_tag_attr_pos = NULL; parser->last_tag_value_pos = NULL; parser->last_quote_mark = 0; } break; case ST_TAGATTRNAMETXT: if (html_is_tag_close(parser, pos)) pos++; else if (isspace(*pos)) { pos++; parser->state = ST_TAGATTRSEP; } else if (*pos == '=') { pos++; parser->state = ST_TAGVALUE; } else { if (!parser->last_tag_attr_pos) parser->last_tag_attr_pos = parser->last_tag_attr; if (parser->last_tag_attr_pos - parser->last_tag_attr < MAX_TAG_ATTR_LENGTH) { *parser->last_tag_attr_pos = tolower(*pos); parser->last_tag_attr_pos++; } pos++; } break; case ST_TAGATTRSEP: if (html_is_tag_close(parser, pos)) pos++; else if (isspace(*pos)) pos++; else if (*pos == '=') { pos++; parser->state = ST_TAGVALUE; } else { g_warning("Invalid html syntax (tagattrsep)"); parser->state = ST_TEXT; } break; case ST_TAGVALUE: if (html_is_tag_close(parser, pos)) pos++; else if (isspace(*pos)) pos++; else if (*pos == '"' || *pos == '\'') { parser->state = ST_TAGVALUETXT; parser->last_quote_mark = *pos; pos++; } else { parser->state = ST_TAGVALUETXT; parser->last_quote_mark = 0; } break; case ST_TAGVALUETXT: if (*pos == parser->last_quote_mark || (!parser->last_quote_mark && isspace(*pos))) { pos++; parser->last_quote_mark = 0; html_parse_tag(parser); parser->is_first_attr = FALSE; parser->state = ST_TAGATTRNAME; } else if (!parser->last_quote_mark && html_is_tag_close(parser, pos)) pos++; else { if (!parser->last_tag_value_pos) parser->last_tag_value_pos = parser->last_tag_value; if (parser->last_tag_value_pos - parser->last_tag_value < MAX_TAG_VALUE_LENGTH) { *parser->last_tag_value_pos = tolower(*pos); parser->last_tag_value_pos++; } pos++; } break; case ST_TAGCLOSE: if (isspace(*pos) || html_is_tag_close(parser, pos)) pos++; else { g_warning("Invalid html syntax (tagclose)"); pos++; parser->state = ST_TEXT; parser->is_close_tag = TRUE; } break; case ST_COMMENT: if (*pos == '"' || *pos == '\'') { if (!parser->last_quote_mark) parser->last_quote_mark = *pos; else if (parser->last_quote_mark == *pos) parser->last_quote_mark = 0; pos++; } else { if (!parser->last_quote_mark) html_is_tag_close(parser, pos); pos++; } break; case ST_SCRIPT: c = tolower(*pos); if (isspace(c) && parser->script_pos == 8) { if (!parser->is_script) { parser->is_script = TRUE; parser->script_pos = 0; } pos++; } else if (c == tag_script[parser->script_pos] && !parser->last_quote_mark) { parser->script_pos++; pos++; if (parser->script_pos >= SCRIPT_LEN) { if (parser->is_script) { parser->is_script = FALSE; parser->state = ST_TEXT; } else { parser->is_script = TRUE; parser->script_pos = 0; } } } else if (!parser->is_script) { memcpy(parser->last_tag, tag_script + 2, parser->script_pos - 2); parser->last_tag_pos = parser->last_tag + parser->script_pos - 2; parser->state = ST_TAGNAMETXT; } else if (c == '"' || c == '\'') { if (!parser->last_quote_mark) parser->last_quote_mark = c; else if (parser->last_quote_mark == c) parser->last_quote_mark = 0; pos++; } else pos++; default: g_assert("Should not happen!!"); } } } void wp_html_parser_begin(WPHTMLParser * parser) { WPTextBuffer *buffer = parser->buffer; WPTextBufferFormat fmt = parser->default_fmt; // WPTextBufferFormat *fmt; memset(parser, 0, sizeof(WPHTMLParser)); parser->buffer = buffer; parser->state = ST_TEXT; parser->last_text_pos = parser->last_text; parser->bol = TRUE; /* * g_object_get(buffer, "def_attr", &fmt, NULL); if (fmt) { parser->fmt = * *fmt; parser->fmt.cs.color = TRUE; parser->default_fmt = parser->fmt; * } */ parser->fmt = parser->default_fmt = fmt; parser->last_line_justification = GTK_JUSTIFY_LEFT; } gint wp_html_parser_end(WPHTMLParser * parser) { if (parser->last_char_bytes) wp_html_parser_write(parser, NULL, 0); html_write_text(parser); return parser->last_line_justification; } static void html_free_font_tags(WPHTMLParser * parser) { GSList *tmp = parser->font_tags; while (tmp) { g_free(tmp->data); tmp = tmp->next; } g_slist_free(parser->font_tags); parser->font_tags = NULL; } static void process_tag_bold(WPHTMLParser * parser) { parser->fmt.bold = !parser->is_close_tag; parser->fmt.cs.bold = !parser->is_close_tag; } static void process_tag_italic(WPHTMLParser * parser) { parser->fmt.italic = !parser->is_close_tag; parser->fmt.cs.italic = !parser->is_close_tag; } static void process_tag_underline(WPHTMLParser * parser) { parser->fmt.underline = !parser->is_close_tag; parser->fmt.cs.underline = !parser->is_close_tag; } static void process_tag_strike(WPHTMLParser * parser) { parser->fmt.strikethrough = !parser->is_close_tag; parser->fmt.cs.strikethrough = !parser->is_close_tag; } static void process_tag_sub(WPHTMLParser * parser) { parser->fmt.text_position = parser->is_close_tag ? TEXT_POSITION_NORMAL : TEXT_POSITION_SUBSCRIPT; // parser->fmt.cs.text_position = TRUE; } static void process_tag_sup(WPHTMLParser * parser) { parser->fmt.text_position = parser->is_close_tag ? TEXT_POSITION_NORMAL : TEXT_POSITION_SUPERSCRIPT; // parser->fmt.cs.text_position = TRUE; } /** * Process an align attribute * @param parser is a #WPHTMLParser */ static void process_align(WPHTMLParser * parser) { if (!parser->is_close_tag && strcmp(parser->last_tag_attr, "align") == 0) { gchar *value = parser->last_tag_value; if (strcmp(value, "center") == 0) parser->fmt.justification = GTK_JUSTIFY_CENTER; else if (strcmp(value, "right") == 0) parser->fmt.justification = GTK_JUSTIFY_RIGHT; else parser->fmt.justification = GTK_JUSTIFY_LEFT; } else parser->fmt.justification = GTK_JUSTIFY_LEFT; } static void process_tag_div(WPHTMLParser * parser) { html_insert_newline(parser, FALSE); process_align(parser); // parser->fmt.cs.justification = TRUE; } static void process_tag_ul(WPHTMLParser * parser) { html_insert_newline(parser, FALSE); parser->list_type = parser->is_close_tag ? LT_NONE : LT_BULLET; // need this, if the html syntax is not correct parser->fmt.bullet = !parser->is_close_tag; } static void process_tag_ol(WPHTMLParser * parser) { html_insert_newline(parser, FALSE); /* Numbering need to be implemented in the editor ! */ if (parser->is_first_attr && !parser->is_close_tag) { parser->list_number = 1; parser->list_type = LT_NUM; } else if (parser->is_close_tag) parser->list_type = LT_NONE; if (!parser->is_close_tag) { if (strcmp(parser->last_tag_attr, "start") == 0) { parser->list_type = atoi(parser->last_tag_value); if (parser->list_type) --parser->list_type; } else if (strcmp(parser->last_tag_attr, "type") == 0) { switch (*parser->last_tag_value) { case '1': parser->list_type = LT_NUM; break; case 'a': parser->list_type = LT_LC_ALPHA; break; case 'A': parser->list_type = LT_UC_ALPHA; break; default: parser->list_type = LT_NUM; } } } } static void process_tag_li(WPHTMLParser * parser) { if (parser->list_type != LT_NONE) { html_insert_newline(parser, FALSE); ++parser->list_number; parser->fmt.bullet = !parser->is_close_tag; parser->fmt.cs.bullet = !parser->is_close_tag; } } /** * Process a font face attribute * @param parser is a #WPHTMLParser * @param name is the name of the font */ static void process_font_face(WPHTMLParser * parser, gchar * name) { // TODO: Should we support multiple font names separated by ',' ? parser->fmt.font = wp_get_font_index(name, parser->default_fmt.font); } /** * Process a font size attribute * @param parser is a #WPHTMLParser * @param value is the size of the font */ static void process_font_size(WPHTMLParser * parser, gchar * value) { gint sign = 0; gint size; if (*value == '+') { sign = 1; value++; } else if (*value == '-') { sign = -1; value++; } if (*value >= '1' && *value <= '7') { size = *value - '0'; if (sign > 0) { size += 3; if (size >= WP_FONT_SIZE_COUNT) size = WP_FONT_SIZE_COUNT - 1; } else if (sign < 0) { size = 3 - size; if (size < 0) size = 0; } else size--; parser->fmt.font_size = size; } else g_warning("Invalid font size: %s\n", value); } /** * Process a font size attribute given in point size * @param parser is a #WPHTMLParser * @param value is the size of the font */ static void process_font_pt_size(WPHTMLParser * parser, gchar * value) { parser->fmt.font_size = wp_get_font_size_index(atoi(value), parser->default_fmt. font_size); } /** * Process a font style attribute * @param parser is a #WPHTMLParser * @param style holding the style to be parsed */ static void process_font_style(WPHTMLParser * parser, gchar * style) { gchar **list; list = g_strsplit(style, ":", 2); if (*list && list[1]) { *list = g_strstrip(*list); if (strcmp(*list, "font-family") == 0) process_font_face(parser, g_strstrip(list[1])); else if (strcmp(*list, "font-size") == 0) // not totally correct, can have sizes too process_font_pt_size(parser, g_strstrip(list[1])); } g_strfreev(list); } static void process_tag_font(WPHTMLParser * parser) { HTMLFontType *font; if (!parser->is_close_tag) { gchar *attr = parser->last_tag_attr; if (parser->is_first_attr) { font = g_new0(HTMLFontType, 1); font->font = parser->fmt.font; font->font_size = parser->fmt.font_size; font->color = parser->fmt.color; parser->font_tags = g_slist_prepend(parser->font_tags, font); } if (strcmp(attr, "face") == 0) { process_font_face(parser, g_strstrip(parser->last_tag_value)); } else if (strcmp(parser->last_tag_attr, "size") == 0) { process_font_size(parser, parser->last_tag_value); } else if (strcmp(parser->last_tag_attr, "color") == 0) { gdk_color_parse(parser->last_tag_value, &parser->fmt.color); } else if (strcmp(parser->last_tag_attr, "point-size") == 0) { process_font_pt_size(parser, parser->last_tag_value); } else if (strcmp(parser->last_tag_attr, "style") == 0) { gchar **head, **list; head = list = g_strsplit(parser->last_tag_value, ";", 5); for (; *list; list++) process_font_style(parser, *list); g_strfreev(head); } } else if (parser->font_tags) { GSList *tmp = parser->font_tags; parser->font_tags = tmp->next; tmp->next = NULL; font = tmp->data; parser->fmt.font = font->font; parser->fmt.font_size = font->font_size; parser->fmt.color = font->color; g_slist_free_1(tmp); } } static void process_tag_img (WPHTMLParser *parser) { if (!parser->is_close_tag) { gchar *attr = parser->last_tag_attr; if (strcmp (attr, "src") == 0) { gchar *src = parser->last_tag_value; if ((src != NULL)&&(g_str_has_prefix (src, "cid:"))) html_insert_image (parser, src+4); } } } static void process_skip_tag(WPHTMLParser * parser) { if (parser->is_first_attr) { if (!parser->is_close_tag) parser->skip_text++; else if (parser->skip_text > 0) parser->skip_text--; } } static void process_tag_br(WPHTMLParser * parser) { html_insert_newline(parser, TRUE); } static void process_tag_p(WPHTMLParser * parser) { gboolean bullet = parser->fmt.bullet && !parser->is_close_tag; html_insert_newline(parser, FALSE); // reset attributes to default except bullet parser->fmt = parser->default_fmt; parser->fmt.bullet = bullet; process_align(parser); html_free_font_tags(parser); } /* * int main() { gchar buffer[4096]; gint size, fd; WPHTMLParser *parser; * * parser = wp_html_parser_new(); wp_html_parser_begin(parser); fd = * open("a.note.html", O_RDONLY); while ((size = read(fd, buffer, * sizeof(buffer)))) wp_html_parser_write(parser, buffer, size); close(fd); * wp_html_parser_end(parser); wp_html_parser_free(parser); * * finalize_html_parser_library(); * * return 0; } */ wpeditor-2.18/src/gtksourceiter.c0000644000175000001440000004756010647343013016366 0ustar stevenusers/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * gtksourceiter.h Copyright (C) 2000 - 2005 Paolo Maggi Copyright (C) * 2002, 2003 Jeroen Zwartepoorte This program is free software; you can * redistribute it and/or modify it under the terms of the GNU Library * General Public License as published by the Free Software Foundation; * either version 2 of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public * License for more details. You should have received a copy of the GNU * Library General Public License along with this program; if not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ /* * Parts of this file are copied from the gedit and glimmer project. */ #ifdef HAVE_CONFIG_H #include #endif #include #include "gtksourceiter.h" #define GTK_TEXT_UNKNOWN_CHAR 0xFFFC /* this function acts like g_utf8_offset_to_pointer() except that if it finds * a decomposable character it consumes the decomposition length from the * given offset. So it's useful when the offset was calculated for the * normalized version of str, but we need a pointer to str itself. */ static const gchar * pointer_from_offset_skipping_decomp(const gchar * str, gint offset) { gchar *casefold, *normal; const gchar *p, *q; p = str; while (offset > 0) { q = g_utf8_next_char(p); casefold = g_utf8_casefold(p, q - p); normal = g_utf8_normalize(casefold, -1, G_NORMALIZE_NFD); offset -= g_utf8_strlen(normal, -1); g_free(casefold); g_free(normal); p = q; } return p; } static const gchar * g_utf8_strcasestr(const gchar * haystack, const gchar * needle) { gsize needle_len; gsize haystack_len; const gchar *ret = NULL; gchar *p; gchar *casefold; gchar *caseless_haystack; gint i; g_return_val_if_fail(haystack != NULL, NULL); g_return_val_if_fail(needle != NULL, NULL); casefold = g_utf8_casefold(haystack, -1); caseless_haystack = g_utf8_normalize(casefold, -1, G_NORMALIZE_NFD); g_free(casefold); needle_len = g_utf8_strlen(needle, -1); haystack_len = g_utf8_strlen(caseless_haystack, -1); if (needle_len == 0) { ret = (gchar *) haystack; goto finally_1; } if (haystack_len < needle_len) { ret = NULL; goto finally_1; } p = (gchar *) caseless_haystack; needle_len = strlen(needle); i = 0; while (*p) { if ((strncmp(p, needle, needle_len) == 0)) { ret = pointer_from_offset_skipping_decomp(haystack, i); goto finally_1; } p = g_utf8_next_char(p); i++; } finally_1: g_free(caseless_haystack); return ret; } static const gchar * g_utf8_strrcasestr(const gchar * haystack, const gchar * needle) { gsize needle_len; gsize haystack_len; const gchar *ret = NULL; gchar *p; gchar *casefold; gchar *caseless_haystack; gint i; g_return_val_if_fail(haystack != NULL, NULL); g_return_val_if_fail(needle != NULL, NULL); casefold = g_utf8_casefold(haystack, -1); caseless_haystack = g_utf8_normalize(casefold, -1, G_NORMALIZE_NFD); g_free(casefold); needle_len = g_utf8_strlen(needle, -1); haystack_len = g_utf8_strlen(caseless_haystack, -1); if (needle_len == 0) { ret = (gchar *) haystack; goto finally_1; } if (haystack_len < needle_len) { ret = NULL; goto finally_1; } i = haystack_len - needle_len; p = g_utf8_offset_to_pointer(caseless_haystack, i); needle_len = strlen(needle); while (p >= caseless_haystack) { if (strncmp(p, needle, needle_len) == 0) { ret = pointer_from_offset_skipping_decomp(haystack, i); goto finally_1; } p = g_utf8_prev_char(p); i--; } finally_1: g_free(caseless_haystack); return ret; } static gboolean g_utf8_caselessnmatch(const char *s1, const char *s2, gssize n1, gssize n2) { gchar *casefold; gchar *normalized_s1; gchar *normalized_s2; gint len_s1; gint len_s2; gboolean ret = FALSE; g_return_val_if_fail(s1 != NULL, FALSE); g_return_val_if_fail(s2 != NULL, FALSE); g_return_val_if_fail(n1 > 0, FALSE); g_return_val_if_fail(n2 > 0, FALSE); casefold = g_utf8_casefold(s1, n1); normalized_s1 = g_utf8_normalize(casefold, -1, G_NORMALIZE_NFD); g_free(casefold); casefold = g_utf8_casefold(s2, n2); normalized_s2 = g_utf8_normalize(casefold, -1, G_NORMALIZE_NFD); g_free(casefold); len_s1 = strlen(normalized_s1); len_s2 = strlen(normalized_s2); if (len_s1 < len_s2) goto finally_2; ret = (strncmp(normalized_s1, normalized_s2, len_s2) == 0); finally_2: g_free(normalized_s1); g_free(normalized_s2); return ret; } static void forward_chars_with_skipping(GtkTextIter * iter, gint count, gboolean skip_invisible, gboolean skip_nontext, gboolean skip_decomp) { gint i; g_return_if_fail(count >= 0); i = count; while (i > 0) { gboolean ignored = FALSE; /* minimal workaround to avoid the infinite loop of bug #168247. It * doesn't fix the problemjust the symptom... */ if (gtk_text_iter_is_end(iter)) return; if (skip_nontext && gtk_text_iter_get_char(iter) == GTK_TEXT_UNKNOWN_CHAR) ignored = TRUE; if (!ignored && skip_invisible && /* _gtk_text_btree_char_is_invisible (iter) */ FALSE) ignored = TRUE; if (!ignored && skip_decomp) { /* being UTF8 correct sucks; this accounts for extra offsets * coming from canonical decompositions of UTF8 characters (e.g. * accented characters) which g_utf8_normalize() performs */ gchar *normal; gchar buffer[6]; gint buffer_len; buffer_len = g_unichar_to_utf8(gtk_text_iter_get_char(iter), buffer); normal = g_utf8_normalize(buffer, buffer_len, G_NORMALIZE_NFD); i -= (g_utf8_strlen(normal, -1) - 1); g_free(normal); } gtk_text_iter_forward_char(iter); if (!ignored) --i; } } static gboolean lines_match(const GtkTextIter * start, const gchar ** lines, gboolean visible_only, gboolean slice, GtkTextIter * match_start, GtkTextIter * match_end) { GtkTextIter next; gchar *line_text; const gchar *found; gint offset; if (*lines == NULL || **lines == '\0') { if (match_start) *match_start = *start; if (match_end) *match_end = *start; return TRUE; } next = *start; gtk_text_iter_forward_line(&next); /* No more text in buffer, but *lines is nonempty */ if (gtk_text_iter_equal(start, &next)) return FALSE; if (slice) { if (visible_only) line_text = gtk_text_iter_get_visible_slice(start, &next); else line_text = gtk_text_iter_get_slice(start, &next); } else { if (visible_only) line_text = gtk_text_iter_get_visible_text(start, &next); else line_text = gtk_text_iter_get_text(start, &next); } if (match_start) /* if this is the first line we're matching */ { found = g_utf8_strcasestr(line_text, *lines); } else { /* If it's not the first line, we have to match from the start of the * line. */ if (g_utf8_caselessnmatch(line_text, *lines, strlen(line_text), strlen(*lines))) found = line_text; else found = NULL; } if (found == NULL) { g_free(line_text); return FALSE; } /* Get offset to start of search string */ offset = g_utf8_strlen(line_text, found - line_text); next = *start; /* If match start needs to be returned, set it to the start of the search * string. */ forward_chars_with_skipping(&next, offset, visible_only, !slice, FALSE); if (match_start) { *match_start = next; } /* Go to end of search string */ forward_chars_with_skipping(&next, g_utf8_strlen(*lines, -1), visible_only, !slice, TRUE); g_free(line_text); ++lines; if (match_end) *match_end = next; /* pass NULL for match_start, since we don't need to find the start * again. */ return lines_match(&next, lines, visible_only, slice, NULL, match_end); } static gboolean backward_lines_match(const GtkTextIter * start, const gchar ** lines, gboolean visible_only, gboolean slice, GtkTextIter * match_start, GtkTextIter * match_end) { GtkTextIter line, next; gchar *line_text; const gchar *found; gint offset; if (*lines == NULL || **lines == '\0') { if (match_start) *match_start = *start; if (match_end) *match_end = *start; return TRUE; } line = next = *start; if (gtk_text_iter_get_line_offset(&next) == 0) { if (!gtk_text_iter_backward_line(&next)) return FALSE; } else gtk_text_iter_set_line_offset(&next, 0); if (slice) { if (visible_only) line_text = gtk_text_iter_get_visible_slice(&next, &line); else line_text = gtk_text_iter_get_slice(&next, &line); } else { if (visible_only) line_text = gtk_text_iter_get_visible_text(&next, &line); else line_text = gtk_text_iter_get_text(&next, &line); } if (match_start) /* if this is the first line we're matching */ { found = g_utf8_strrcasestr(line_text, *lines); } else { /* If it's not the first line, we have to match from the start of the * line. */ if (g_utf8_caselessnmatch(line_text, *lines, strlen(line_text), strlen(*lines))) found = line_text; else found = NULL; } if (found == NULL) { g_free(line_text); return FALSE; } /* Get offset to start of search string */ offset = g_utf8_strlen(line_text, found - line_text); forward_chars_with_skipping(&next, offset, visible_only, !slice, FALSE); /* If match start needs to be returned, set it to the start of the search * string. */ if (match_start) { *match_start = next; } /* Go to end of search string */ forward_chars_with_skipping(&next, g_utf8_strlen(*lines, -1), visible_only, !slice, TRUE); g_free(line_text); ++lines; if (match_end) *match_end = next; /* try to match the rest of the lines forward, passing NULL for * match_start so lines_match will try to match the entire line */ return lines_match(&next, lines, visible_only, slice, NULL, match_end); } /* strsplit () that retains the delimiter as part of the string. */ static gchar ** strbreakup(const char *string, const char *delimiter, gint max_tokens) { GSList *string_list = NULL, *slist; gchar **str_array, *s, *casefold, *new_string; guint i, n = 1; g_return_val_if_fail(string != NULL, NULL); g_return_val_if_fail(delimiter != NULL, NULL); if (max_tokens < 1) max_tokens = G_MAXINT; s = strstr(string, delimiter); if (s) { guint delimiter_len = strlen(delimiter); do { guint len; len = s - string + delimiter_len; new_string = g_new(gchar, len + 1); strncpy(new_string, string, len); new_string[len] = 0; casefold = g_utf8_casefold(new_string, -1); g_free(new_string); new_string = g_utf8_normalize(casefold, -1, G_NORMALIZE_NFD); g_free(casefold); string_list = g_slist_prepend(string_list, new_string); n++; string = s + delimiter_len; s = strstr(string, delimiter); } while (--max_tokens && s); } if (*string) { n++; casefold = g_utf8_casefold(string, -1); new_string = g_utf8_normalize(casefold, -1, G_NORMALIZE_NFD); g_free(casefold); string_list = g_slist_prepend(string_list, new_string); } str_array = g_new(gchar *, n); i = n - 1; str_array[i--] = NULL; for (slist = string_list; slist; slist = slist->next) str_array[i--] = slist->data; g_slist_free(string_list); return str_array; } /** * gtk_source_iter_forward_search: * @iter: start of search. * @str: a search string. * @flags: flags affecting how the search is done. * @match_start: return location for start of match, or %%NULL. * @match_end: return location for end of match, or %%NULL. * @limit: bound for the search, or %%NULL for the end of the buffer. * * Searches forward for @str. Any match is returned by setting * @match_start to the first character of the match and @match_end to the * first character after the match. The search will not continue past * @limit. Note that a search is a linear or O(n) operation, so you * may wish to use @limit to avoid locking up your UI on large * buffers. * * If the #GTK_SOURCE_SEARCH_VISIBLE_ONLY flag is present, the match may * have invisible text interspersed in @str. i.e. @str will be a * possibly-noncontiguous subsequence of the matched range. similarly, * if you specify #GTK_SOURCE_SEARCH_TEXT_ONLY, the match may have * pixbufs or child widgets mixed inside the matched range. If these * flags are not given, the match must be exact; the special 0xFFFC * character in @str will match embedded pixbufs or child widgets. * If you specify the #GTK_SOURCE_SEARCH_CASE_INSENSITIVE flag, the text will * be matched regardless of what case it is in. * * Same as gtk_text_iter_forward_search(), but supports case insensitive * searching. * * Return value: whether a match was found. **/ gboolean gtk_source_iter_forward_search(const GtkTextIter * iter, const gchar * str, GtkSourceSearchFlags flags, GtkTextIter * match_start, GtkTextIter * match_end, const GtkTextIter * limit) { gchar **lines = NULL; GtkTextIter match; gboolean retval = FALSE; GtkTextIter search; gboolean visible_only; gboolean slice; g_return_val_if_fail(iter != NULL, FALSE); g_return_val_if_fail(str != NULL, FALSE); if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0) return gtk_text_iter_forward_search(iter, str, flags, match_start, match_end, limit); if (limit && gtk_text_iter_compare(iter, limit) >= 0) return FALSE; if (*str == '\0') { /* If we can move one char, return the empty string there */ match = *iter; if (gtk_text_iter_forward_char(&match)) { if (limit && gtk_text_iter_equal(&match, limit)) return FALSE; if (match_start) *match_start = match; if (match_end) *match_end = match; return TRUE; } else { return FALSE; } } visible_only = (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) != 0; slice = (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) == 0; /* locate all lines */ lines = strbreakup(str, "\n", -1); search = *iter; do { /* This loop has an inefficient worst-case, where * gtk_text_iter_get_text () is called repeatedly on a single line. */ GtkTextIter end; if (limit && gtk_text_iter_compare(&search, limit) >= 0) break; if (lines_match(&search, (const gchar **) lines, visible_only, slice, &match, &end)) { if (limit == NULL || (limit && gtk_text_iter_compare(&end, limit) < 0)) { retval = TRUE; if (match_start) *match_start = match; if (match_end) *match_end = end; } break; } } while (gtk_text_iter_forward_line(&search)); g_strfreev((gchar **) lines); return retval; } /** * gtk_source_iter_backward_search: * @iter: a #GtkTextIter where the search begins. * @str: search string. * @flags: bitmask of flags affecting the search. * @match_start: return location for start of match, or %%NULL. * @match_end: return location for end of match, or %%NULL. * @limit: location of last possible @match_start, or %%NULL for start of buffer. * * Same as gtk_text_iter_backward_search(), but supports case insensitive * searching. * * Return value: whether a match was found. **/ gboolean gtk_source_iter_backward_search(const GtkTextIter * iter, const gchar * str, GtkSourceSearchFlags flags, GtkTextIter * match_start, GtkTextIter * match_end, const GtkTextIter * limit) { gchar **lines = NULL; GtkTextIter match; gboolean retval = FALSE; GtkTextIter search; gboolean visible_only; gboolean slice; g_return_val_if_fail(iter != NULL, FALSE); g_return_val_if_fail(str != NULL, FALSE); if ((flags & GTK_SOURCE_SEARCH_CASE_INSENSITIVE) == 0) return gtk_text_iter_backward_search(iter, str, flags, match_start, match_end, limit); if (limit && gtk_text_iter_compare(iter, limit) <= 0) return FALSE; if (*str == '\0') { /* If we can move one char, return the empty string there */ match = *iter; if (gtk_text_iter_backward_char(&match)) { if (limit && gtk_text_iter_equal(&match, limit)) return FALSE; if (match_start) *match_start = match; if (match_end) *match_end = match; return TRUE; } else { return FALSE; } } visible_only = (flags & GTK_SOURCE_SEARCH_VISIBLE_ONLY) != 0; slice = (flags & GTK_SOURCE_SEARCH_TEXT_ONLY) == 0; /* locate all lines */ lines = strbreakup(str, "\n", -1); search = *iter; while (TRUE) { /* This loop has an inefficient worst-case, where * gtk_text_iter_get_text () is called repeatedly on a single line. */ GtkTextIter end; if (limit && gtk_text_iter_compare(&search, limit) <= 0) break; if (backward_lines_match(&search, (const gchar **) lines, visible_only, slice, &match, &end)) { if (limit == NULL || (limit && gtk_text_iter_compare(&end, limit) > 0)) { retval = TRUE; if (match_start) *match_start = match; if (match_end) *match_end = end; } break; } if (gtk_text_iter_get_line_offset(&search) == 0) { if (!gtk_text_iter_backward_line(&search)) break; } else { gtk_text_iter_set_line_offset(&search, 0); } } g_strfreev((gchar **) lines); return retval; } wpeditor-2.18/configure.ac0000644000175000001440000000207510640152176015020 0ustar stevenusersAC_INIT([wpeditor], [0.1]) AM_INIT_AUTOMAKE([foreign]) AM_MAINTAINER_MODE AM_CONFIG_HEADER([config.h]) AC_ENABLE_SHARED(yes) AC_ENABLE_STATIC(no) AC_ISC_POSIX AC_PROG_CC AM_PROG_CC_STDC AC_HEADER_STDC AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AM_PROG_LIBTOOL PKG_CHECK_MODULES(PACKAGE, [gtk+-2.0 >= 2.0.0 glib-2.0 >= 2.0.0]) AC_SUBST(PACKAGE_CFLAGS) AC_SUBST(PACKAGE_LIBS) PACKAGE_INC=wpeditor AC_SUBST(PACKAGE_INC) AC_DEFINE_UNQUOTED(PACKAGE_INC,"$PACKAGE_INC",[Name of the include directory]) dnl Set PACKAGE_LOCALE_DIR in config.h DATADIRNAME="share" if test "x${prefix}" = "xNONE"; then AC_DEFINE_UNQUOTED([PACKAGE_LOCALE_DIR], ["${ac_default_prefix}/${DATADIRNAME}/locale"],[Locale directory]) else AC_DEFINE_UNQUOTED([PACKAGE_LOCALE_DIR], ["${prefix}/${DATADIRNAME}/locale"], [Locale directory]) fi AC_SUBST(PACKAGE_LOCALE_DIR) AC_CHECK_LIB([gtk-x11-2.0], [hildon_gtk_im_context_filter_event], AC_DEFINE(HAVE_HILDON, 1, [Define to 1 if you have hildon_gtk_im_context_filter_event]),,) AC_OUTPUT([ Makefile src/Makefile wpeditor.pc debian/wpeditor-dev.install ]) wpeditor-2.18/wpeditor.pc.in0000644000175000001440000000046010637764143015325 0ustar stevenusersprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/@PACKAGE_INC@ Name: wpeditor Description: A library implementing WPTextBuffer and WPTextView, which can be used to easily support rich text editing. Version: @VERSION@ Libs: -L${libdir} -lwpeditor Cflags: -I${includedir}