Initial commit
This commit is contained in:
commit
3bbd273a4f
37 changed files with 7883 additions and 0 deletions
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
*.o
|
||||||
|
.deps
|
||||||
|
aclocal.m4
|
||||||
|
autom4te.cache
|
||||||
|
config.*
|
||||||
|
configure
|
||||||
|
depcomp
|
||||||
|
install-sh
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
missing
|
||||||
|
stamp-h1
|
||||||
|
|
||||||
|
midori
|
8
AUTHORS
Normal file
8
AUTHORS
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Written by:
|
||||||
|
Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
Artwork by:
|
||||||
|
Nancy Runge <nancy@twotoasts.de>
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
de: Christian Dywan <christian@twotoasts.de>
|
504
COPYING
Normal file
504
COPYING
Normal file
|
@ -0,0 +1,504 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
|
|
||||||
|
|
133
ChangeLog
Normal file
133
ChangeLog
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
v0.0.14:
|
||||||
|
+ FIX Reopening a tab from the trash causes a crash
|
||||||
|
+ FIX An untitled website keeps the previous title
|
||||||
|
+ FIX When switching tabs the location/ title isn't updated correctly
|
||||||
|
+ FIX Issues with the preferences dialog
|
||||||
|
+ Disable location completion for now
|
||||||
|
+ Save tabtrash to file
|
||||||
|
+ Restructure some code
|
||||||
|
+ Remove color picker and throbber
|
||||||
|
+ Change the license to LGPL
|
||||||
|
|
||||||
|
v0.0.13:
|
||||||
|
+ Adapt WebKit api change, remove engine wrappers, remove dialog hack
|
||||||
|
+ Improve XBEL loading and saving
|
||||||
|
+ Show dialog and backup files on startup errors
|
||||||
|
+ Rearranged and removed some menus
|
||||||
|
|
||||||
|
v0.0.12:
|
||||||
|
+ FIX Improve flawed window creation
|
||||||
|
+ Build with and eliminate all compiler warnings
|
||||||
|
+ Implement clipboard handling menus
|
||||||
|
+ Allow editing of search engines
|
||||||
|
+ Update search engines properly
|
||||||
|
+ Implement bookmarks saving
|
||||||
|
+ Implement session saving and loading
|
||||||
|
+ Cleaned up and revised most code
|
||||||
|
+ Remove legacy webi code
|
||||||
|
|
||||||
|
v0.0.11:
|
||||||
|
+ FIX Back/ forward and initial check menu item states
|
||||||
|
+ Remove rather useless debugging helpers
|
||||||
|
+ Improvements on the preferences
|
||||||
|
+ First attempt at websearch
|
||||||
|
+ Preserve typed uri on tab switch
|
||||||
|
+ First attempt at bookmarks, readonly for now
|
||||||
|
+ Add an animated throbber
|
||||||
|
|
||||||
|
v0.0.10:
|
||||||
|
+ FIX Can crash on context menu or new protocol
|
||||||
|
+ FIX Location isn't updated on tab switch
|
||||||
|
+ Remember last window position and size
|
||||||
|
+ Implement Open menu item
|
||||||
|
+ Allow using location and web search if hidden
|
||||||
|
|
||||||
|
v0.0.9:
|
||||||
|
+ FIX Close tab not insensitive for only one tab
|
||||||
|
+ FIX Debug output is broken
|
||||||
|
+ Display uri when hovering a link
|
||||||
|
+ Implement link uri related part of context menu
|
||||||
|
+ Implement alt/ middle/ shift click link
|
||||||
|
+ First attempt at external protocol handlers
|
||||||
|
+ Initial download manager integration
|
||||||
|
+ Adapt WebKit api prefix change
|
||||||
|
+ More code reorganization and cleanup
|
||||||
|
|
||||||
|
v0.0.8:
|
||||||
|
+ FIX Crash when invoking document context menu via keyboard
|
||||||
|
+ FIX Can't build with debug = yes on GTK+2.12
|
||||||
|
+ Changes related to icons in the gui
|
||||||
|
+ Reorganize code by splitting into several files
|
||||||
|
+ Switch from WebkitGdk to WebkitGtk
|
||||||
|
|
||||||
|
v0.0.7:
|
||||||
|
+ FIX Make settings finally work flawlessly
|
||||||
|
+ FIX Can crash when settings are opened
|
||||||
|
+ Handle all panels in a general way
|
||||||
|
+ Install xdg compliant desktop file
|
||||||
|
+ Implement location and web search menu items
|
||||||
|
+ Display a loading icon on tabs again
|
||||||
|
+ Changed the settings dialog again
|
||||||
|
|
||||||
|
v0.0.6:
|
||||||
|
+ FIX Closing an individual tab doesn't work correctly.
|
||||||
|
+ FIX Doesn't build with gtkwebcore.
|
||||||
|
+ Reimplement menus and and navibar with GtkUIManager.
|
||||||
|
+ Improve document handling in general.
|
||||||
|
+ Finished tab trash menu.
|
||||||
|
+ Implement searchbox default text.
|
||||||
|
+ Remove some gtkwebcore code.
|
||||||
|
+ Use Xfce style dialog in Xfce.
|
||||||
|
+ Implement a few settings.
|
||||||
|
+ Change the panel's look.
|
||||||
|
+ Implement a 'pageholder' panel.
|
||||||
|
|
||||||
|
v0.0.5:
|
||||||
|
+ Implement a few more signals for WebkitGdk.
|
||||||
|
+ Add tooltips to navigation toolbar buttons.
|
||||||
|
+ First attempt on a settings dialog.
|
||||||
|
+ Reimplemented color picker.
|
||||||
|
+ Autocompletion for location and searchbox.
|
||||||
|
+ Changed menu items and incremental findbar.
|
||||||
|
+ Implement tab switching via keyboard.
|
||||||
|
|
||||||
|
v0.0.4:
|
||||||
|
+ FIX Midori segfaults when quitting.
|
||||||
|
+ FIX Config loading and saving is broken.
|
||||||
|
+ Switch WebkitGdk to gtk api and make it the build default.
|
||||||
|
+ Register custom stock icons instead of icon theme magic.
|
||||||
|
+ Implement dynamic window menu.
|
||||||
|
+ First attempt on resizable panels.
|
||||||
|
+ Add about dialog.
|
||||||
|
|
||||||
|
v0.0.3:
|
||||||
|
+ FIX Refresh via menu or shortcut crashes the browser.
|
||||||
|
+ FIX Assertions with and visibility of the progressbar.
|
||||||
|
+ FIX Tabs are not reorderable.
|
||||||
|
+ Package versions in ./configure result and --version output.
|
||||||
|
+ Improve view menu and add tools menu.
|
||||||
|
+ Replace deprecated functions and macros.
|
||||||
|
+ Implement settings saving and loading.
|
||||||
|
+ Fill the common context menu with items
|
||||||
|
+ Allow multiple homepages, seperated by '|'.
|
||||||
|
+ Make code typesafe and C++ friendly.
|
||||||
|
+ Initially support WebkitGdk directly.
|
||||||
|
+ Urlify location inputs automatically.
|
||||||
|
|
||||||
|
v0.0.2:
|
||||||
|
+ Dynamic tab trash menu.
|
||||||
|
+ Update UI when page is changed.
|
||||||
|
+ Enhanced WebkitGtk support.
|
||||||
|
+ New function sokoke_dialog_run_modeless.
|
||||||
|
+ Finished on_document_request_script_prompt.
|
||||||
|
+ One name and version, visible in the user agent.
|
||||||
|
+ Changed some accelerators and menu items.
|
||||||
|
+ Create and destroy color picker properly.
|
||||||
|
+ Ctrl + Wheel resets the zoom level.
|
||||||
|
+ Escape in the location entry resets the uri.
|
||||||
|
+ Use gtk-webcore prefix instead of osb now.
|
||||||
|
+ Save keybindings on quit.
|
||||||
|
+ Fancy autotools build setup.
|
||||||
|
|
||||||
|
v0.0.1:
|
||||||
|
+ Initial release
|
85
HACKING
Normal file
85
HACKING
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
It is 4 spaces, no tabs, preferrably at 80 columns per line.
|
||||||
|
|
||||||
|
The preferred coding style is explained by example.
|
||||||
|
|
||||||
|
Source file example:
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright
|
||||||
|
LICENSE TEXT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "foo.h"
|
||||||
|
|
||||||
|
#include "bar.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
void foobar(FooEnum bar, const gchar* foo)
|
||||||
|
{
|
||||||
|
if(!foo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef BAR_STRICT
|
||||||
|
if(bar == FOO_N)
|
||||||
|
{
|
||||||
|
g_print("illegal value for 'bar'.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// this is an example
|
||||||
|
gint n;
|
||||||
|
switch(bar)
|
||||||
|
{
|
||||||
|
case FOO_FOO:
|
||||||
|
n = bar + 1;
|
||||||
|
break;
|
||||||
|
case FOO_BAR:
|
||||||
|
n = bar + 10;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
n = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
g_print("%s\n", foo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Header file example:
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright
|
||||||
|
LICENSE TEXT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __FOO_H__
|
||||||
|
#define __FOO_H__ 1
|
||||||
|
|
||||||
|
#ifdef HAVE_BAR_H
|
||||||
|
#define BAR_STRICT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -- Types
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FOO_FOO,
|
||||||
|
FOO_BAR,
|
||||||
|
FOO_N
|
||||||
|
} FooEnum;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
FooEnum fooBar;
|
||||||
|
} FooStruct;
|
||||||
|
|
||||||
|
// -- Declarations
|
||||||
|
|
||||||
|
void
|
||||||
|
foobar(FooEnum, const gchar*);
|
||||||
|
|
||||||
|
#endif /* !__FOO_H__ */
|
18
INSTALL
Normal file
18
INSTALL
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
1. Unpack the archive.
|
||||||
|
|
||||||
|
2. Check build options with
|
||||||
|
./configure --help
|
||||||
|
|
||||||
|
3. Prepare for build with
|
||||||
|
./configure
|
||||||
|
|
||||||
|
4. Build midori with
|
||||||
|
make
|
||||||
|
|
||||||
|
5. To install midori run either
|
||||||
|
(sudo) make install
|
||||||
|
or
|
||||||
|
(sudo) checkinstall
|
||||||
|
|
||||||
|
6. For a list of command line options run
|
||||||
|
midori --help
|
8
Makefile.am
Normal file
8
Makefile.am
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
AUTOMAKE_OPTIONS = gnu
|
||||||
|
|
||||||
|
SUBDIRS = src
|
||||||
|
|
||||||
|
desktopdir = $(datadir)/applications
|
||||||
|
desktop_DATA = midori.desktop
|
||||||
|
|
||||||
|
EXTRA_DIST = HACKING TODO $(desktop_DATA)
|
0
NEWS
Normal file
0
NEWS
Normal file
20
README
Normal file
20
README
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
Midori is a lightweight web browser.
|
||||||
|
|
||||||
|
* Full integration with GTK+2.
|
||||||
|
* Fast rendering with WebKit.
|
||||||
|
* Tabs, windows and session management.
|
||||||
|
* Bookmarks are stored with XBEL.
|
||||||
|
* Searchbox based on OpenSearch.
|
||||||
|
* Custom context menu actions.
|
||||||
|
* User scripts and user styles support.
|
||||||
|
* Extensible via Lua scripts.
|
||||||
|
|
||||||
|
Requirements: GTK+ 2.6, libsexy, WebkitGtk, libXML2
|
||||||
|
|
||||||
|
For installation instructions read INSTALL.
|
||||||
|
|
||||||
|
Please report comments, suggestions and bugs to:
|
||||||
|
Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
Check for new versions at:
|
||||||
|
http://software.twotoasts.de
|
62
TODO
Normal file
62
TODO
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
TODO:
|
||||||
|
. Save files on change as opposed to on quit
|
||||||
|
. Position menus properly
|
||||||
|
. Use an animated throbber
|
||||||
|
. Add a context menu to the pageholder
|
||||||
|
. Make tabs autoshrink
|
||||||
|
. New tabs from a TabNew action should always open in the foreground
|
||||||
|
. Middle click toolbuttons or menuitems should open new tabs
|
||||||
|
. Implement userscript support
|
||||||
|
. Actual multiple window support, some things just ignore this currently
|
||||||
|
. Custom context menu actions
|
||||||
|
. custom tab names
|
||||||
|
. Open an auto-vanishing findbox with '.'
|
||||||
|
. Support gettext
|
||||||
|
. Custom panels, loaded from (x)htm(l) files or websites
|
||||||
|
. Drag tabs onto the panel to have it in the sidebar.
|
||||||
|
. Save completion stores
|
||||||
|
. analogus to blocked popups, blocked scripts moving layers on load
|
||||||
|
. per-site blocking of individual elements on a page
|
||||||
|
. statusbar icon 'cookies blocked', icon 'popups blocked'
|
||||||
|
. per-site settings accessible via statusbar icons, ie. cookies, popups, plugins
|
||||||
|
. cookieSafe like, a list of cookies, with type, block, allow
|
||||||
|
. support mouse gestures
|
||||||
|
. optional internal source view using gtksourceview
|
||||||
|
. automatic update checks (browser, extensions)?
|
||||||
|
. auto-group tabs by opener, with colors?
|
||||||
|
. mark (dogear) a selection so that it isn't cleared implicitly, multiply on one page
|
||||||
|
. have an internal uri scheme, eg. 'res:', to reference eg. themed icons
|
||||||
|
. 'about:' uris: about, blank, cache, config, plugins
|
||||||
|
. panel of open tabs (with tree-structure), optional thumbnail-view
|
||||||
|
. spell check support
|
||||||
|
. allow full page zoom (how do we incorporate it in the gui?)
|
||||||
|
. check specific bookmarks for updates automatically (as an extension?)
|
||||||
|
. mark "new" as well as "actually modified" tabs specially (even over sessions)
|
||||||
|
. customizable toolbars, custom buttons (uri, title, icon)
|
||||||
|
. searchEngine: "Show in context menu"
|
||||||
|
. use libnotify for events, e.g. download finished
|
||||||
|
. save screenshot of a document
|
||||||
|
. right-click a textbox in a search form and choose 'add to websearch'
|
||||||
|
. support extensions written in lua
|
||||||
|
. the scrollbar must be exactly at the right (left) edge if maximized: fitt's law
|
||||||
|
. detailed css element view, maybe in 'properties'?
|
||||||
|
. reuse running instance, probably via libunique
|
||||||
|
. respect design principle "no warnings but undo of backups"?
|
||||||
|
. support widgets 1.0 spec in tool windows and standalone
|
||||||
|
. blank page: several custom links, displayed as thumbnails, like Opera
|
||||||
|
. handle downloads, optionally in a downloadbar
|
||||||
|
. Implement userstyle support
|
||||||
|
. Protected tabs prompt when attempting to close them
|
||||||
|
. provide a 'sleep mode' after a crash where open documents are loaded manually
|
||||||
|
. option to run plugins or scripts only on demand, like NoScript, per-site
|
||||||
|
. optional http redirection manually or on timeout
|
||||||
|
. style: none, compatible (b/w), default, [styles], "media", ["media" styles], ...
|
||||||
|
. mouse pointer coordinates in the status bar
|
||||||
|
. draw rectangle with the mouse, x/y/x2/y2 in the statusbar
|
||||||
|
. formfill (like Opera's magic wand)
|
||||||
|
. private browsing mode (no browsing, download or search history)
|
||||||
|
. shared bookmarks and config
|
||||||
|
. custom-mode, e.g. hide menubar and use help icon to have a help viewer
|
||||||
|
. dead tabs: download, aborted page
|
||||||
|
. on url load, for big files, ask "Open or save?"
|
||||||
|
. middle-click on selection to open <selection>
|
6
autogen.sh
Executable file
6
autogen.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
aclocal
|
||||||
|
autoheader
|
||||||
|
autoconf
|
||||||
|
automake --add-missing --copy
|
101
configure.in
Normal file
101
configure.in
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
# Register ourselves to autoconf
|
||||||
|
AC_INIT([midori], [0.0.14], [christian@twotoasts.de])
|
||||||
|
AC_CONFIG_SRCDIR([src/main.h])
|
||||||
|
AC_CONFIG_HEADER([config.h])
|
||||||
|
|
||||||
|
AM_INIT_AUTOMAKE([AC_PACKAGE_TARNAME()], [AC_PACKAGE_VERSION()])
|
||||||
|
|
||||||
|
# Checks for programs
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_INSTALL
|
||||||
|
AC_PROG_MAKE_SET
|
||||||
|
|
||||||
|
# Checks for header files
|
||||||
|
AC_HEADER_STDC
|
||||||
|
|
||||||
|
# Checks for typedefs, structures, and compiler characteristics.
|
||||||
|
AC_C_CONST
|
||||||
|
|
||||||
|
# Checks if we want debugging support
|
||||||
|
AC_ARG_ENABLE([debug],
|
||||||
|
AC_HELP_STRING([--enable-debug=@<:@no/simple/yes@:>@]
|
||||||
|
, [Turn on debugging @<:@default=simple@:>@])
|
||||||
|
, [], [enable_debug=simple])
|
||||||
|
AC_MSG_CHECKING([whether to enable debugging support])
|
||||||
|
AC_MSG_RESULT([$enable_debug])
|
||||||
|
if test x"$enable_debug" = x"simple"; then
|
||||||
|
AC_DEFINE([SOKOKE_DEBUG], 1, [Level of debugging support])
|
||||||
|
fi
|
||||||
|
if test x"$enable_debug" = x"yes"; then
|
||||||
|
# Check whether the compiler accepts -Wall
|
||||||
|
save_CFLAGS="$CFLAGS"
|
||||||
|
CFLAGS="$CFLAGS -Wall"
|
||||||
|
AC_MSG_CHECKING([whether $CC accepts -Wall])
|
||||||
|
AC_COMPILE_IFELSE(AC_LANG_SOURCE([int x;]), [
|
||||||
|
AC_MSG_RESULT([yes])
|
||||||
|
], [
|
||||||
|
AC_MSG_RESULT([no])
|
||||||
|
CFLAGS="$save_CFLAGS"
|
||||||
|
])
|
||||||
|
AC_DEFINE([SOKOKE_DEBUG], 2, [Level of debugging support])
|
||||||
|
fi
|
||||||
|
AC_DEFINE_UNQUOTED([SOKOKE_DEBUG_], "$enable_debug", [Debugging?])
|
||||||
|
|
||||||
|
# Checks for GTK+2
|
||||||
|
PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.6, have_gtk=true, have_gtk=false)
|
||||||
|
if test "x${have_gtk}" = "xfalse" ; then
|
||||||
|
AC_MSG_ERROR([No GTK+2 package information found])
|
||||||
|
fi
|
||||||
|
AC_SUBST(GTK_CFLAGS)
|
||||||
|
AC_SUBST(GTK_LIBS)
|
||||||
|
GTK_VER=`pkg-config --modversion gtk+-2.0`
|
||||||
|
AC_DEFINE_UNQUOTED([GTK_VER], "$GTK_VER", [GTK+ version])
|
||||||
|
|
||||||
|
# Checks for WebKitGtk
|
||||||
|
PKG_CHECK_MODULES(WEBKIT, WebKitGtk, have_webkit=true, have_webkit=false)
|
||||||
|
if test "x${have_webkit}" = "xfalse" ; then
|
||||||
|
AC_MSG_ERROR([No WebKitGtk package information found])
|
||||||
|
fi
|
||||||
|
AC_SUBST(WEBKIT_CFLAGS)
|
||||||
|
AC_SUBST(WEBKIT_LIBS)
|
||||||
|
WEBKIT_VER=`pkg-config --modversion WebKitGtk`
|
||||||
|
AC_DEFINE_UNQUOTED([WEBKIT_VER], "$WEBKITGTK_VER", [WebKitGtk version])
|
||||||
|
|
||||||
|
# Checks for libsexy
|
||||||
|
PKG_CHECK_MODULES(LIBSEXY, libsexy, have_libsexy=true, have_libsexy=false)
|
||||||
|
if test "x${have_libsexy}" = "xfalse" ; then
|
||||||
|
AC_MSG_ERROR([No Libsexy package information found])
|
||||||
|
fi
|
||||||
|
AC_SUBST(LIBSEXY_CFLAGS)
|
||||||
|
AC_SUBST(LIBSEXY_LIBS)
|
||||||
|
LIBSEXY_VER=`pkg-config --modversion libsexy`
|
||||||
|
AC_DEFINE_UNQUOTED([LIBSEXY_VER], "$LIBSEXY_VER", [Libsexy version])
|
||||||
|
|
||||||
|
# Checks for LibXML2
|
||||||
|
PKG_CHECK_MODULES(LIBXML, libxml-2.0 >= 2.6, have_libxml=true, have_libxml=false)
|
||||||
|
if test "x${have_libxml}" = "xfalse" ; then
|
||||||
|
AC_MSG_ERROR([No libXML2 package information found])
|
||||||
|
fi
|
||||||
|
AC_SUBST(LIBXML_CFLAGS)
|
||||||
|
AC_SUBST(LIBXML_LIBS)
|
||||||
|
LIBXML_VER=`pkg-config --modversion libxml-2.0`
|
||||||
|
AC_DEFINE_UNQUOTED([LIBXML_VER], "$LIBXML_VER", [libXML2 version])
|
||||||
|
|
||||||
|
# Here we tell the configure script which files to *create*
|
||||||
|
AC_CONFIG_FILES([
|
||||||
|
Makefile \
|
||||||
|
src/Makefile
|
||||||
|
])
|
||||||
|
AC_OUTPUT
|
||||||
|
|
||||||
|
# Show us what we have
|
||||||
|
echo
|
||||||
|
echo " GTK+2 $GTK_VER"
|
||||||
|
echo " WebKit $WEBKIT_VER"
|
||||||
|
echo " Libsexy $LIBSEXY_VER"
|
||||||
|
echo " libXML2 $LIBXML_VER"
|
||||||
|
echo " GetText N/A"
|
||||||
|
echo
|
||||||
|
echo " Debugging $enable_debug"
|
||||||
|
echo
|
||||||
|
echo " Prefix $prefix"
|
14
midori.desktop
Normal file
14
midori.desktop
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Encoding=UTF-8
|
||||||
|
Type=Application
|
||||||
|
Name=Midori Web Browser
|
||||||
|
GenericName=Web Browser
|
||||||
|
Comment=Lightweight web browser
|
||||||
|
Categories=GTK;Network;
|
||||||
|
MimeType=text/html;text/xml;application/xhtml+xml;application/xml
|
||||||
|
Exec=midori %u
|
||||||
|
Icon=web-browser
|
||||||
|
Terminal=false
|
||||||
|
StartupNotify=true
|
17
src/Makefile.am
Normal file
17
src/Makefile.am
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
INCLUDES = $(GTK_CFLAGS) $(WEBKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBSEXY_CFLAGS)
|
||||||
|
LDADD = $(GTK_LIBS) $(WEBKIT_LIBS) $(LIBXML_LIBS) $(LIBSEXY_LIBS)
|
||||||
|
|
||||||
|
bin_PROGRAMS = midori
|
||||||
|
midori_SOURCES = main.c main.h \
|
||||||
|
browser.c browser.h \
|
||||||
|
prefs.c prefs.h \
|
||||||
|
webSearch.c webSearch.h \
|
||||||
|
helpers.c helpers.h \
|
||||||
|
webView.c webView.h \
|
||||||
|
sokoke.c sokoke.h \
|
||||||
|
conf.c conf.h \
|
||||||
|
search.c search.h \
|
||||||
|
xbel.c xbel.h \
|
||||||
|
global.h \
|
||||||
|
ui.h \
|
||||||
|
debug.h
|
1384
src/browser.c
Normal file
1384
src/browser.c
Normal file
File diff suppressed because it is too large
Load diff
563
src/browser.h
Normal file
563
src/browser.h
Normal file
|
@ -0,0 +1,563 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __BROWSER_H__
|
||||||
|
#define __BROWSER_H__ 1
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
// -- Types
|
||||||
|
|
||||||
|
typedef struct _CBrowser
|
||||||
|
{
|
||||||
|
// shared widgets
|
||||||
|
GtkWidget* window;
|
||||||
|
GtkActionGroup* actiongroup;
|
||||||
|
// menus
|
||||||
|
GtkWidget* menubar;
|
||||||
|
GtkWidget* menu_bookmarks;
|
||||||
|
GtkWidget* menu_window;
|
||||||
|
GtkWidget* popup_webView;
|
||||||
|
GtkWidget* popup_element;
|
||||||
|
GtkWidget* popup_editable;
|
||||||
|
GtkWidget* throbber;
|
||||||
|
// navibar
|
||||||
|
GtkWidget* navibar;
|
||||||
|
GtkWidget* newTab;
|
||||||
|
GtkWidget* location_icon;
|
||||||
|
GtkWidget* location;
|
||||||
|
GtkWidget* webSearch;
|
||||||
|
GtkWidget* closedTabs;
|
||||||
|
GtkWidget* bookmarkbar;
|
||||||
|
// panels
|
||||||
|
GtkWidget* panels;
|
||||||
|
GtkWidget* panels_notebook;
|
||||||
|
GtkWidget* panel_pageholder;
|
||||||
|
GtkWidget* webViews;
|
||||||
|
// findbox
|
||||||
|
GtkWidget* findbox;
|
||||||
|
GtkWidget* findbox_text;
|
||||||
|
GtkToolItem* findbox_case;
|
||||||
|
GtkToolItem* findbox_highlight;
|
||||||
|
GtkWidget* statusbar;
|
||||||
|
GtkWidget* progress;
|
||||||
|
GtkWidget* icon_security;
|
||||||
|
GtkWidget* icon_newsfeed;
|
||||||
|
|
||||||
|
// view specific widgets
|
||||||
|
GtkWidget* webView_menu;
|
||||||
|
GtkWidget* webView_icon;
|
||||||
|
GtkWidget* webView_name;
|
||||||
|
GtkWidget* webView_close;
|
||||||
|
GtkWidget* webView;
|
||||||
|
|
||||||
|
// view specific values
|
||||||
|
gboolean hasMenubar;
|
||||||
|
gboolean hasToolbar;
|
||||||
|
gboolean hasLocation;
|
||||||
|
gboolean hasStatusbar;
|
||||||
|
gchar* elementUri; // the element the mouse is hovering on
|
||||||
|
gint loadedPercent; // -1 means "not loading"
|
||||||
|
gint loadedBytes;
|
||||||
|
gint loadedBytesMax;
|
||||||
|
//UNDEFINED favicon;
|
||||||
|
guint security;
|
||||||
|
gchar* statusMessage; // message from a webView
|
||||||
|
XbelItem* sessionItem;
|
||||||
|
} CBrowser;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SEARCH_COL_ICON,
|
||||||
|
SEARCH_COL_TEXT,
|
||||||
|
SEARCH_COL_N
|
||||||
|
};
|
||||||
|
|
||||||
|
// -- Declarations
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_window_new_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_tab_new_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_open_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_tab_close_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_window_close_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_quit_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_edit_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_cut_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_copy_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_paste_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_delete_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_selectAll_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_find_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_find_next_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_find_previous_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_preferences_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_toolbar_navigation_activate(GtkToggleAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_toolbar_bookmarks_activate(GtkToggleAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_panels_activate(GtkToggleAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_toolbar_status_activate(GtkToggleAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_refresh_stop_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_zoom_in_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_zoom_out_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_zoom_normal_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_source_view_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_back_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_forward_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_home_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_location_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_webSearch_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_openInPanel_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_menu_tabsClosed_activate(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_menu_tabsClosed_item_activate(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_tabsClosed_clear_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_tabsClosed_undo_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_link_tab_new_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_link_tab_current_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_link_window_new_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_link_saveWith_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_link_copy_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_menu_bookmarks_item_activate(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_bookmark_new_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_manageSearchEngines_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_tab_previous_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_tab_next_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_action_about_activate(GtkAction*, CBrowser*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
on_location_key_down(GtkWidget*, GdkEventKey*, CBrowser*);
|
||||||
|
|
||||||
|
CBrowser*
|
||||||
|
browser_new(CBrowser*);
|
||||||
|
|
||||||
|
// -- Action definitions
|
||||||
|
|
||||||
|
// TODO: Fill in a good description for each 'hm?'
|
||||||
|
static const GtkActionEntry entries[] = {
|
||||||
|
{ "File", NULL, "_File" },
|
||||||
|
{ "WindowNew", STOCK_WINDOW_NEW
|
||||||
|
, NULL, "<Ctrl>n"
|
||||||
|
, "Open a new window", G_CALLBACK(on_action_window_new_activate) },
|
||||||
|
{ "TabNew", STOCK_TAB_NEW
|
||||||
|
, NULL, "<Ctrl>t"
|
||||||
|
, "Open a new tab", G_CALLBACK(on_action_tab_new_activate) },
|
||||||
|
{ "Open", GTK_STOCK_OPEN
|
||||||
|
, NULL, "<Ctrl>o"
|
||||||
|
, "Open a file", G_CALLBACK(on_action_open_activate) },
|
||||||
|
{ "SaveAs", GTK_STOCK_SAVE_AS
|
||||||
|
, NULL, "<Ctrl>s"
|
||||||
|
, "Save to a file", NULL/*G_CALLBACK(on_action_saveas_activate)*/ },
|
||||||
|
{ "TabClose", STOCK_TAB_CLOSE
|
||||||
|
, NULL, "<Ctrl>w"
|
||||||
|
, "Close the current tab", G_CALLBACK(on_action_tab_close_activate) },
|
||||||
|
{ "WindowClose", STOCK_WINDOW_CLOSE
|
||||||
|
, NULL, "<Ctrl><Shift>w"
|
||||||
|
, "Close this window", G_CALLBACK(on_action_window_close_activate) },
|
||||||
|
{ "PageSetup", GTK_STOCK_PROPERTIES
|
||||||
|
, "Pa_ge Setup", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_page_setup_activate)*/ },
|
||||||
|
{ "PrintPreview", GTK_STOCK_PRINT_PREVIEW
|
||||||
|
, NULL, ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_print_preview_activate)*/ },
|
||||||
|
{ "Print", GTK_STOCK_PRINT
|
||||||
|
, NULL, "<Ctrl>p"
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_print_activate)*/ },
|
||||||
|
{ "Quit", GTK_STOCK_QUIT
|
||||||
|
, NULL, "<Ctrl>q"
|
||||||
|
, "Quit the application", G_CALLBACK(on_action_quit_activate) },
|
||||||
|
|
||||||
|
{ "Edit", NULL, "_Edit", NULL, NULL, G_CALLBACK(on_action_edit_activate) },
|
||||||
|
{ "Undo", GTK_STOCK_UNDO
|
||||||
|
, NULL, "<Ctrl>z"
|
||||||
|
, "Undo the last modification", NULL/*G_CALLBACK(on_action_undo_activate)*/ },
|
||||||
|
{ "Redo", GTK_STOCK_REDO
|
||||||
|
, NULL, "<Ctrl><Shift>z"
|
||||||
|
, "Redo the last modification", NULL/*G_CALLBACK(on_action_redo_activate)*/ },
|
||||||
|
{ "Cut", GTK_STOCK_CUT
|
||||||
|
, NULL, "<Ctrl>x"
|
||||||
|
, "Cut the selected text", G_CALLBACK(on_action_cut_activate) },
|
||||||
|
{ "Copy", GTK_STOCK_COPY
|
||||||
|
, NULL, "<Ctrl>c"
|
||||||
|
, "Copy the selected text", G_CALLBACK(on_action_copy_activate) },
|
||||||
|
{ "Copy_", GTK_STOCK_COPY
|
||||||
|
, NULL, "<Ctrl>c"
|
||||||
|
, "Copy the selected text", G_CALLBACK(on_action_copy_activate) },
|
||||||
|
{ "Paste", GTK_STOCK_PASTE
|
||||||
|
, NULL, "<Ctrl>v"
|
||||||
|
, "Paste text from the clipboard", G_CALLBACK(on_action_paste_activate) },
|
||||||
|
{ "Delete", GTK_STOCK_DELETE
|
||||||
|
, NULL, NULL
|
||||||
|
, "Delete the selected text", G_CALLBACK(on_action_delete_activate) },
|
||||||
|
{ "SelectAll", GTK_STOCK_SELECT_ALL
|
||||||
|
, NULL, "<Ctrl>a"
|
||||||
|
, "Selected all text", G_CALLBACK(on_action_selectAll_activate) },
|
||||||
|
{ "FormFill", STOCK_FORM_FILL
|
||||||
|
, NULL, ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_formfill_activate)*/ },
|
||||||
|
{ "Find", GTK_STOCK_FIND
|
||||||
|
, NULL, "<Ctrl>f"
|
||||||
|
, "hm?", G_CALLBACK(on_action_find_activate) },
|
||||||
|
{ "FindNext", GTK_STOCK_GO_FORWARD
|
||||||
|
, "Find _Next", "<Ctrl>g"
|
||||||
|
, "hm?", G_CALLBACK(on_action_find_next_activate) },
|
||||||
|
{ "FindPrevious", GTK_STOCK_GO_BACK
|
||||||
|
, "Find _Previous", "<Ctrl><Shift>g"
|
||||||
|
, "hm?", G_CALLBACK(on_action_find_previous_activate) },
|
||||||
|
{ "FindQuick", GTK_STOCK_FIND
|
||||||
|
, "_Quick Find", "period"
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_find_quick_activate)*/ },
|
||||||
|
{ "ManageSearchEngines", GTK_STOCK_PROPERTIES
|
||||||
|
, "_Manage Search Engines", "<Ctrl><Alt>s"
|
||||||
|
, "hm?", G_CALLBACK(on_action_manageSearchEngines_activate) },
|
||||||
|
{ "Preferences", GTK_STOCK_PREFERENCES
|
||||||
|
, NULL, "<Ctrl><Alt>p"
|
||||||
|
, "hm?", G_CALLBACK(on_action_preferences_activate) },
|
||||||
|
|
||||||
|
{ "View", NULL, "_View" },
|
||||||
|
{ "Toolbars", NULL, "_Toolbars" },
|
||||||
|
{ "Refresh", GTK_STOCK_REFRESH
|
||||||
|
, NULL, "<Ctrl>r"
|
||||||
|
, "Refresh the current page", G_CALLBACK(on_action_refresh_stop_activate) },
|
||||||
|
// TODO: Is appointment-new a good choice?
|
||||||
|
// TODO: What if it isn't available?
|
||||||
|
{ "RefreshEvery", "appointment-new"
|
||||||
|
, "Refresh _Every...", ""
|
||||||
|
, "Refresh the current page", G_CALLBACK(on_action_refresh_stop_activate) },
|
||||||
|
{ "Stop", GTK_STOCK_STOP
|
||||||
|
, NULL, "Escape"
|
||||||
|
, "Stop loading of the current page", G_CALLBACK(on_action_refresh_stop_activate) },
|
||||||
|
{ "RefreshStop", GTK_STOCK_REFRESH
|
||||||
|
, NULL, ""
|
||||||
|
, NULL, G_CALLBACK(on_action_refresh_stop_activate) },
|
||||||
|
{ "ZoomIn", GTK_STOCK_ZOOM_IN
|
||||||
|
, NULL, "<Ctrl>plus"
|
||||||
|
, "hm?", G_CALLBACK(on_action_zoom_in_activate) },
|
||||||
|
{ "ZoomOut", GTK_STOCK_ZOOM_OUT
|
||||||
|
, NULL, "<Ctrl>minus"
|
||||||
|
, "hm?", G_CALLBACK(on_action_zoom_out_activate) },
|
||||||
|
{ "ZoomNormal", GTK_STOCK_ZOOM_100
|
||||||
|
, NULL, "<Ctrl>0"
|
||||||
|
, "hm?", G_CALLBACK(on_action_zoom_normal_activate) },
|
||||||
|
{ "BackgroundImage", STOCK_IMAGE
|
||||||
|
, "_Background Image", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_background_image_activate)*/ },
|
||||||
|
{ "SourceView", STOCK_SOURCE_VIEW
|
||||||
|
, NULL, ""
|
||||||
|
, "hm?", /*G_CALLBACK(on_action_source_view_activate)*/ },
|
||||||
|
{ "SelectionSourceView", STOCK_SOURCE_VIEW
|
||||||
|
, "View Selection Source", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_selection_source_view_activate)*/ },
|
||||||
|
{ "Properties", GTK_STOCK_PROPERTIES
|
||||||
|
, NULL, ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_activate)*/ },
|
||||||
|
|
||||||
|
{ "Go", NULL, "_Go" },
|
||||||
|
{ "Back", GTK_STOCK_GO_BACK
|
||||||
|
, NULL, "<Alt>Left"
|
||||||
|
, "hm?", G_CALLBACK(on_action_back_activate) },
|
||||||
|
{ "Forward", GTK_STOCK_GO_FORWARD
|
||||||
|
, NULL, "<Alt>Right"
|
||||||
|
, "hm?", G_CALLBACK(on_action_forward_activate) },
|
||||||
|
{ "Home", STOCK_HOMEPAGE
|
||||||
|
, NULL, "<Alt>Home"
|
||||||
|
, "hm?", G_CALLBACK(on_action_home_activate) },
|
||||||
|
{ "Location", GTK_STOCK_JUMP_TO
|
||||||
|
, "Location...", "<Ctrl>l"
|
||||||
|
, "hm?", G_CALLBACK(on_action_location_activate) },
|
||||||
|
{ "Websearch", GTK_STOCK_FIND
|
||||||
|
, "Websearch...", "<Ctrl><Shift>f"
|
||||||
|
, "hm?", G_CALLBACK(on_action_webSearch_activate) },
|
||||||
|
{ "OpenInPageholder", GTK_STOCK_JUMP_TO
|
||||||
|
, "Open in Page_holder...", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_openInPanel_activate) },
|
||||||
|
{ "TabsClosed", STOCK_USER_TRASH
|
||||||
|
, "Closed Tabs", ""
|
||||||
|
, "hm?", NULL },
|
||||||
|
{ "TabsClosedClear", GTK_STOCK_CLEAR
|
||||||
|
, "Clear List of Closed Tabs", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_tabsClosed_clear_activate) },
|
||||||
|
{ "UndoTabClose", GTK_STOCK_UNDELETE
|
||||||
|
, "Undo Close Tab", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_tabsClosed_undo_activate) },
|
||||||
|
{ "LinkTabNew", STOCK_TAB_NEW
|
||||||
|
, "Open Link in New Tab", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_link_tab_new_activate) },
|
||||||
|
{ "LinkTabCurrent", NULL
|
||||||
|
, "Open Link in Current Tab", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_link_tab_current_activate) },
|
||||||
|
{ "LinkWindowNew", STOCK_WINDOW_NEW
|
||||||
|
, "Open Link in New Window", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_link_window_new_activate) },
|
||||||
|
{ "LinkBookmarkNew", STOCK_BOOKMARK_NEW
|
||||||
|
, NULL, ""
|
||||||
|
, "Bookmark this link", NULL/*G_CALLBACK(on_action_link_bookmark_activate)*/ },
|
||||||
|
{ "LinkSaveAs", GTK_STOCK_SAVE
|
||||||
|
, "Save Destination as...", ""
|
||||||
|
, "Save destination to a file", NULL/*G_CALLBACK(on_action_link_saveas_activate)*/ },
|
||||||
|
{ "LinkSaveWith", STOCK_DOWNLOADS
|
||||||
|
, "Download Destination", ""
|
||||||
|
, "Save destination with the chosen download manager", G_CALLBACK(on_action_link_saveWith_activate) },
|
||||||
|
{ "LinkCopy", GTK_STOCK_COPY
|
||||||
|
, "Copy Link Address", ""
|
||||||
|
, "Copy the link address to the clipboard", G_CALLBACK(on_action_link_copy_activate) },
|
||||||
|
{ "SelectionLinksNewTabs", NULL
|
||||||
|
, "Open Selected Links in Tabs", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "SelectionTextTabNew", STOCK_TAB_NEW
|
||||||
|
, "Open <Selection> in New Tab", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "SelectionTextTabCurrent", NULL
|
||||||
|
, "Open <Selection> in Current Tab", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "SelectionTextWindowNew", STOCK_WINDOW_NEW
|
||||||
|
, "Open <Selection> in New Qindow", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "SelectionSearch", GTK_STOCK_FIND
|
||||||
|
, "Search for <Selection>", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "SelectionSearchWith", GTK_STOCK_FIND
|
||||||
|
, "Search for <Selection> with...", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "ImageViewTabNew", STOCK_TAB_NEW
|
||||||
|
, "View Image in New Tab", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "ImageViewTabCurrent", NULL
|
||||||
|
, "View image in current tab", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "ImageSaveAs", GTK_STOCK_SAVE
|
||||||
|
, "Save Image as...", ""
|
||||||
|
, "Save image to a file", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "ImageSaveWith", STOCK_DOWNLOADS
|
||||||
|
, "Download Image", ""
|
||||||
|
, "Save image with the chosen download manager", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
{ "ImageCopy", GTK_STOCK_COPY
|
||||||
|
, "Copy Image Address", ""
|
||||||
|
, "Copy the image address to the clipboard", NULL/*G_CALLBACK(on_action_properties_selection_activate)*/ },
|
||||||
|
|
||||||
|
{ "Bookmarks", NULL, "_Bookmarks" },
|
||||||
|
{ "BookmarkNew", STOCK_BOOKMARK_NEW
|
||||||
|
, NULL, "<Ctrl>d"
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_bookmark_new_activate)*/ },
|
||||||
|
{ "BookmarksManage", STOCK_BOOKMARKS
|
||||||
|
, "_Manage Bookmarks", "<Ctrl>b"
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_bookmarks_manage_activate)*/ },
|
||||||
|
|
||||||
|
{ "Tools", NULL, "_Tools" },
|
||||||
|
|
||||||
|
{ "Window", NULL, "_Window" },
|
||||||
|
{ "SessionLoad", GTK_STOCK_REVERT_TO_SAVED
|
||||||
|
, "_Load Session", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_session_load_activate)*/ },
|
||||||
|
{ "SessionSave", GTK_STOCK_SAVE_AS
|
||||||
|
, "_Save Session", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_session_save_activate)*/ },
|
||||||
|
{ "TabPrevious", GTK_STOCK_GO_BACK
|
||||||
|
, "_Previous Tab", "<Ctrl>Page_Up"
|
||||||
|
, "hm?", G_CALLBACK(on_action_tab_previous_activate) },
|
||||||
|
{ "TabNext", GTK_STOCK_GO_FORWARD
|
||||||
|
, "_Next Tab", "<Ctrl>Page_Down"
|
||||||
|
, "hm?", G_CALLBACK(on_action_tab_next_activate) },
|
||||||
|
{ "TabOverview", NULL
|
||||||
|
, "Tab _Overview", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_tab_overview_activate)*/ },
|
||||||
|
|
||||||
|
{ "Help", NULL, "_Help" },
|
||||||
|
{ "HelpContents", GTK_STOCK_HELP
|
||||||
|
, "_Contents", "F1"
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_help_contents_activate)*/ },
|
||||||
|
{ "About", GTK_STOCK_ABOUT
|
||||||
|
, NULL, ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_about_activate) },
|
||||||
|
};
|
||||||
|
static const guint entries_n = G_N_ELEMENTS(entries);
|
||||||
|
|
||||||
|
static const GtkToggleActionEntry toggle_entries[] = {
|
||||||
|
{ "PrivateBrowsing", NULL
|
||||||
|
, "P_rivate Browsing", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_private_browsing_activate)*/
|
||||||
|
, FALSE },
|
||||||
|
{ "WorkOffline", GTK_STOCK_DISCONNECT
|
||||||
|
, "_Work Offline", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_work_offline_activate)*/
|
||||||
|
, FALSE },
|
||||||
|
|
||||||
|
{ "ToolbarNavigation", NULL
|
||||||
|
, "_Navigationbar", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_toolbar_navigation_activate)
|
||||||
|
, FALSE },
|
||||||
|
{ "Panels", NULL
|
||||||
|
, "_Panels", "F9"
|
||||||
|
, "hm?", G_CALLBACK(on_action_panels_activate)
|
||||||
|
, FALSE },
|
||||||
|
{ "ToolbarBookmarks", NULL
|
||||||
|
, "_Bookmarkbar", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_toolbar_bookmarks_activate)
|
||||||
|
, FALSE },
|
||||||
|
{ "ToolbarDownloads", NULL
|
||||||
|
, "_Downloadbar", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_toolbar_downloads_activate)*/
|
||||||
|
, FALSE },
|
||||||
|
{ "ToolbarStatus", NULL
|
||||||
|
, "_Statusbar", ""
|
||||||
|
, "hm?", G_CALLBACK(on_action_toolbar_status_activate)
|
||||||
|
, FALSE },
|
||||||
|
{ "RefreshEveryEnable", NULL
|
||||||
|
, "_Enabled", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_reloadevery_enable_activate)*/
|
||||||
|
, FALSE },
|
||||||
|
{ "ReloadEveryActive", NULL
|
||||||
|
, "_Active", ""
|
||||||
|
, "hm?", NULL/*G_CALLBACK(on_action_reloadevery_active_activate)*/
|
||||||
|
, FALSE },
|
||||||
|
};
|
||||||
|
static const guint toggle_entries_n = G_N_ELEMENTS(toggle_entries);
|
||||||
|
|
||||||
|
static const GtkRadioActionEntry refreshevery_entries[] = {
|
||||||
|
{ "RefreshEvery30", NULL
|
||||||
|
, "30 seconds", ""
|
||||||
|
, "Refresh Every _30 Seconds", 30 },
|
||||||
|
{ "RefreshEvery60", NULL
|
||||||
|
, "60 seconds", ""
|
||||||
|
, "Refresh Every _60 Seconds", 60 },
|
||||||
|
{ "RefreshEvery300", NULL
|
||||||
|
, "5 minutes", ""
|
||||||
|
, "Refresh Every _5 Minutes", 300 },
|
||||||
|
{ "RefreshEvery900", NULL
|
||||||
|
, "15 minutes", ""
|
||||||
|
, "Refresh Every _15 Minutes", 900 },
|
||||||
|
{ "RefreshEvery1800", NULL
|
||||||
|
, "30 minutes", ""
|
||||||
|
, "Refresh Every 3_0 Minutes", 1800 },
|
||||||
|
{ "RefreshEveryCustom", NULL
|
||||||
|
, "Custom...", ""
|
||||||
|
, "Refresh by a _Custom Period", 0 },
|
||||||
|
};
|
||||||
|
static const guint refreshevery_entries_n = G_N_ELEMENTS(refreshevery_entries);
|
||||||
|
|
||||||
|
static const GtkRadioActionEntry panel_entries[] = {
|
||||||
|
{ "PanelDownloads", STOCK_DOWNLOADS
|
||||||
|
, NULL, ""
|
||||||
|
, "hm?", 0 },
|
||||||
|
{ "PanelBookmarks", STOCK_BOOKMARKS
|
||||||
|
, "_Bookmarks", ""
|
||||||
|
, "hm?", 1 },
|
||||||
|
{ "PanelConsole", STOCK_CONSOLE
|
||||||
|
, NULL, ""
|
||||||
|
, "hm?", 2 },
|
||||||
|
{ "PanelExtensions", STOCK_EXTENSIONS
|
||||||
|
, NULL, ""
|
||||||
|
, "hm?", 3 },
|
||||||
|
{ "PanelHistory", STOCK_HISTORY
|
||||||
|
, "_History", ""
|
||||||
|
, "hm?", 4 },
|
||||||
|
// TODO: We want a better icon here, but which one?
|
||||||
|
{ "PanelTabs", STOCK_TAB_NEW
|
||||||
|
, "_Tabs", ""
|
||||||
|
, "hm?", 5 },
|
||||||
|
// TODO: We probably want another icon here
|
||||||
|
{ "PanelPageholder", GTK_STOCK_CONVERT
|
||||||
|
, "_Pageholder", ""
|
||||||
|
, "hm?", 6 },
|
||||||
|
};
|
||||||
|
static const guint panel_entries_n = G_N_ELEMENTS(panel_entries);
|
||||||
|
|
||||||
|
#endif /* !__BROWSER_H__ */
|
173
src/conf.c
Normal file
173
src/conf.c
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
|
|
||||||
|
#include "sokoke.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
CConfig* config_new(void)
|
||||||
|
{
|
||||||
|
return g_new0(CConfig, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_free(CConfig* config)
|
||||||
|
{
|
||||||
|
g_free(config->homepage);
|
||||||
|
g_free(config->locationSearch);
|
||||||
|
g_free(config->panelPageholder);
|
||||||
|
g_datalist_clear(&config->protocols_commands);
|
||||||
|
g_ptr_array_free(config->protocols_names, TRUE);
|
||||||
|
g_free(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean config_from_file(CConfig* config, const gchar* filename, GError** error)
|
||||||
|
{
|
||||||
|
GKeyFile* keyFile = g_key_file_new();
|
||||||
|
g_key_file_load_from_file(keyFile, filename, G_KEY_FILE_KEEP_COMMENTS, error);
|
||||||
|
/*g_key_file_load_from_data_dirs(keyFile, sFilename, NULL
|
||||||
|
, G_KEY_FILE_KEEP_COMMENTS, error);*/
|
||||||
|
#define GET_INT(var, key, default) \
|
||||||
|
var = sokoke_key_file_get_integer_default( \
|
||||||
|
keyFile, "browser", key, default, NULL)
|
||||||
|
#define GET_STR(var, key, default) \
|
||||||
|
var = sokoke_key_file_get_string_default( \
|
||||||
|
keyFile, "browser", key, default, NULL)
|
||||||
|
GET_INT(config->startup, "Startup", CONFIG_STARTUP_HOMEPAGE);
|
||||||
|
GET_STR(config->homepage, "Homepage", "http://www.google.com");
|
||||||
|
GET_STR(config->locationSearch, "LocationSearch", "http://www.google.com/search?q=%s");
|
||||||
|
GET_INT(config->toolbarNavigation, "ToolbarNavigation", TRUE);
|
||||||
|
GET_INT(config->toolbarBookmarks, "ToolbarBookmarks", FALSE);
|
||||||
|
GET_INT(config->toolbarStatus, "ToolbarStatus", TRUE);
|
||||||
|
//GET_INT(config->toolbarDownloads, "ToolbarDownloads", FALSE);
|
||||||
|
GET_INT(config->toolbarStyle, "ToolbarStyle", CONFIG_TOOLBAR_DEFAULT);
|
||||||
|
GET_INT(config->toolbarSmall, "ToolbarSmall", FALSE);
|
||||||
|
GET_INT(config->toolbarWebSearch, "ToolbarWebSearch", TRUE);
|
||||||
|
GET_INT(config->toolbarNewTab, "ToolbarNewTab", TRUE);
|
||||||
|
GET_INT(config->toolbarClosedTabs, "ToolbarClosedTabs", TRUE);
|
||||||
|
GET_INT(config->panelShow, "PanelShow", FALSE);
|
||||||
|
GET_INT(config->panelActive, "PanelActive", 0);
|
||||||
|
GET_STR(config->panelPageholder, "PanelPageholder", "http://www.google.com");
|
||||||
|
GET_INT(config->tabSize, "TabSize", 10);
|
||||||
|
GET_INT(config->tabClose, "TabClose", TRUE);
|
||||||
|
GET_INT(config->newPages, "NewPages", CONFIG_NEWPAGES_TAB_NEW);
|
||||||
|
GET_INT(config->openTabsInTheBackground, "OpenTabsInTheBackground", FALSE);
|
||||||
|
GET_INT(config->openPopupsInTabs, "OpenPopupsInTabs", FALSE);
|
||||||
|
#undef GET_INT
|
||||||
|
#undef GET_STR
|
||||||
|
|
||||||
|
#define GET_INT(var, key, default) \
|
||||||
|
var = sokoke_key_file_get_integer_default( \
|
||||||
|
keyFile, "content", key, default, NULL)
|
||||||
|
#define GET_STR(var, key, default) \
|
||||||
|
var = sokoke_key_file_get_string_default( \
|
||||||
|
keyFile, "content", key, default, NULL)
|
||||||
|
GET_INT(config->loadImagesAutomatically, "LoadImagesAutomatically", TRUE);
|
||||||
|
GET_INT(config->shrinkImagesToFit, "ShrinkImagesTooFit", TRUE);
|
||||||
|
GET_INT(config->resizableTextAreas, "ResizableTextAreas", FALSE);
|
||||||
|
GET_INT(config->enableJavaScript, "EnableJavaScript", TRUE);
|
||||||
|
GET_INT(config->enablePlugins, "EnablePlugins", TRUE);
|
||||||
|
#undef GET_INT
|
||||||
|
#undef GET_STR
|
||||||
|
|
||||||
|
#define GET_INT(var, key, default) \
|
||||||
|
var = sokoke_key_file_get_integer_default( \
|
||||||
|
keyFile, "session", key, default, NULL)
|
||||||
|
#define GET_STR(var, key, default) \
|
||||||
|
var = sokoke_key_file_get_string_default( \
|
||||||
|
keyFile, "session", key, default, NULL)
|
||||||
|
GET_INT(config->rememberWinMetrics , "RememberWinMetrics", TRUE);
|
||||||
|
GET_INT(config->winLeft , "WinLeft", 0);
|
||||||
|
GET_INT(config->winTop , "WinTop", 0);
|
||||||
|
GET_INT(config->winWidth , "WinWidth", 0);
|
||||||
|
GET_INT(config->winHeight, "WinHeight", 0);
|
||||||
|
GET_INT(config->winPanelPos, "WinPanelPos", 0);
|
||||||
|
GET_INT(config->searchEngine, "SearchEngine", 0);
|
||||||
|
#undef GET_INT
|
||||||
|
#undef GET_STR
|
||||||
|
|
||||||
|
config->protocols_names = g_ptr_array_new();
|
||||||
|
g_datalist_init(&config->protocols_commands);
|
||||||
|
gchar** protocols;
|
||||||
|
if((protocols = g_key_file_get_keys(keyFile, "protocols", NULL, NULL)))
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
for(i = 0; protocols[i] != NULL; i++)
|
||||||
|
{
|
||||||
|
gchar* sCommand = g_key_file_get_string(keyFile, "protocols"
|
||||||
|
, protocols[i], NULL);
|
||||||
|
g_ptr_array_add(config->protocols_names, (gpointer)protocols[i]);
|
||||||
|
g_datalist_set_data_full(&config->protocols_commands
|
||||||
|
, protocols[i], sCommand, g_free);
|
||||||
|
}
|
||||||
|
g_free(protocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_key_file_free(keyFile);
|
||||||
|
return !(error && *error);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean config_to_file(CConfig* config, const gchar* filename, GError** error)
|
||||||
|
{
|
||||||
|
GKeyFile* keyFile = g_key_file_new();
|
||||||
|
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "Startup", config->startup);
|
||||||
|
g_key_file_set_string (keyFile, "browser", "Homepage", config->homepage);
|
||||||
|
g_key_file_set_string (keyFile, "browser", "LocationSearch", config->locationSearch);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "ToolbarNavigation", config->toolbarNavigation);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "ToolbarBookmarks", config->toolbarBookmarks);
|
||||||
|
//g_key_file_set_integer(keyFile, "browser", "ToolbarDownloads", config->toolbarDownloads);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "ToolbarStatus", config->toolbarStatus);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "ToolbarStyle", config->toolbarStyle);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "ToolbarSmall", config->toolbarSmall);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "ToolbarWebSearch", config->toolbarWebSearch);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "ToolbarNewTab", config->toolbarNewTab);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "ToolbarClosedTabs", config->toolbarClosedTabs);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "PanelShow", config->panelShow);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "PanelActive", config->panelActive);
|
||||||
|
g_key_file_set_string (keyFile, "browser", "PanelPageholder", config->panelPageholder);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "TabSize", config->tabSize);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "TabClose", config->tabClose);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "NewPages", config->newPages);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "OpenTabsInTheBackground", config->openTabsInTheBackground);
|
||||||
|
g_key_file_set_integer(keyFile, "browser", "OpenPopupsInTabs", config->openPopupsInTabs);
|
||||||
|
|
||||||
|
g_key_file_set_integer(keyFile, "content", "LoadImagesAutomatically", config->loadImagesAutomatically);
|
||||||
|
g_key_file_set_integer(keyFile, "content", "ShrinkImagesToFit", config->shrinkImagesToFit);
|
||||||
|
g_key_file_set_integer(keyFile, "content", "ResizableTextAreas", config->resizableTextAreas);
|
||||||
|
g_key_file_set_integer(keyFile, "content", "EnableJavaScript", config->enableJavaScript);
|
||||||
|
g_key_file_set_integer(keyFile, "content", "EnablePlugins", config->enablePlugins);
|
||||||
|
|
||||||
|
g_key_file_set_integer(keyFile, "session", "RememberWinMetrics", config->rememberWinMetrics);
|
||||||
|
g_key_file_set_integer(keyFile, "session", "WinLeft", config->winLeft);
|
||||||
|
g_key_file_set_integer(keyFile, "session", "WinTop", config->winTop);
|
||||||
|
g_key_file_set_integer(keyFile, "session", "WinWidth", config->winWidth);
|
||||||
|
g_key_file_set_integer(keyFile, "session", "WinHeight", config->winHeight);
|
||||||
|
g_key_file_set_integer(keyFile, "session", "WinPanelPos", config->winPanelPos);
|
||||||
|
g_key_file_set_integer(keyFile, "session", "SearchEngine", config->searchEngine);
|
||||||
|
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < config->protocols_names->len; i++)
|
||||||
|
{
|
||||||
|
gchar* protocol = (gchar*)g_ptr_array_index(config->protocols_names, i);
|
||||||
|
gchar* command = g_datalist_get_data(&config->protocols_commands, protocol);
|
||||||
|
g_key_file_set_string(keyFile, "protocols", protocol, command);
|
||||||
|
g_free(protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean saved = sokoke_key_file_save_to_file(keyFile, filename, error);
|
||||||
|
g_key_file_free(keyFile);
|
||||||
|
|
||||||
|
return saved;
|
||||||
|
}
|
94
src/conf.h
Normal file
94
src/conf.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CONF_H__
|
||||||
|
#define __CONF_H__ 1
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
typedef struct _CConfig
|
||||||
|
{
|
||||||
|
guint startup;
|
||||||
|
gchar* homepage;
|
||||||
|
gchar* locationSearch;
|
||||||
|
gboolean toolbarNavigation;
|
||||||
|
gboolean toolbarBookmarks;
|
||||||
|
//gboolean toolbarDownloads;
|
||||||
|
gboolean toolbarStatus;
|
||||||
|
guint toolbarStyle;
|
||||||
|
gboolean toolbarSmall;
|
||||||
|
gboolean panelShow;
|
||||||
|
guint panelActive;
|
||||||
|
gchar* panelPageholder;
|
||||||
|
// TODO: What about this? Support it or fix tab shrinking and drop it?
|
||||||
|
guint tabSize; // tab size in charcters
|
||||||
|
gboolean tabClose;
|
||||||
|
gboolean toolbarWebSearch;
|
||||||
|
gboolean toolbarNewTab;
|
||||||
|
gboolean toolbarClosedTabs;
|
||||||
|
guint newPages; // where to open new pages
|
||||||
|
gboolean openTabsInTheBackground;
|
||||||
|
gboolean openPopupsInTabs;
|
||||||
|
|
||||||
|
gboolean loadImagesAutomatically;
|
||||||
|
gboolean shrinkImagesToFit;
|
||||||
|
gboolean resizableTextAreas;
|
||||||
|
gboolean enableJavaScript;
|
||||||
|
gboolean enablePlugins;
|
||||||
|
|
||||||
|
gboolean rememberWinMetrics; // Restore last state upon startup?
|
||||||
|
gint winLeft;
|
||||||
|
gint winTop;
|
||||||
|
gint winWidth;
|
||||||
|
gint winHeight;
|
||||||
|
guint winPanelPos;
|
||||||
|
guint searchEngine; // last selected search engine
|
||||||
|
|
||||||
|
GPtrArray* protocols_names;
|
||||||
|
GData* protocols_commands;
|
||||||
|
} CConfig;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CONFIG_STARTUP_BLANK,
|
||||||
|
CONFIG_STARTUP_HOMEPAGE,
|
||||||
|
CONFIG_STARTUP_SESSION
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CONFIG_TOOLBAR_DEFAULT,
|
||||||
|
CONFIG_TOOLBAR_ICONS,
|
||||||
|
CONFIG_TOOLBAR_TEXT,
|
||||||
|
CONFIG_TOOLBAR_BOTH,
|
||||||
|
CONFIG_TOOLBAR_BOTH_HORIZ
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CONFIG_NEWPAGES_TAB_NEW,
|
||||||
|
CONFIG_NEWPAGES_WINDOW_NEW,
|
||||||
|
CONFIG_NEWPAGES_TAB_CURRENT
|
||||||
|
};
|
||||||
|
|
||||||
|
CConfig*
|
||||||
|
config_new(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
config_free(CConfig*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
config_from_file(CConfig*, const gchar*, GError**);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
config_to_file(CConfig*, const gchar*, GError**);
|
||||||
|
|
||||||
|
#endif /* !__CONF_H__ */
|
25
src/debug.h
Normal file
25
src/debug.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DEBUG_H__
|
||||||
|
#define __DEBUG_H__ 1
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <glib/gprintf.h>
|
||||||
|
|
||||||
|
#if SOKOKE_DEBUG > 1
|
||||||
|
#define UNIMPLEMENTED g_print(" * Unimplemented: %s\n", G_STRFUNC);
|
||||||
|
#else
|
||||||
|
#define UNIMPLEMENTED ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !__DEBUG_H__ */
|
83
src/global.h
Normal file
83
src/global.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GLOBAL_H__
|
||||||
|
#define __GLOBAL_H__ 1
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
|
#include "xbel.h"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
// -- globals
|
||||||
|
|
||||||
|
CConfig* config;
|
||||||
|
GList* searchEngines; // Items of type 'SearchEngine'
|
||||||
|
GList* browsers; // Items of type 'CBrowser'
|
||||||
|
GtkAccelGroup* accel_group;
|
||||||
|
XbelItem* bookmarks;
|
||||||
|
XbelItem* session;
|
||||||
|
XbelItem* tabtrash;
|
||||||
|
|
||||||
|
// Custom stock items
|
||||||
|
|
||||||
|
// We should distribute these
|
||||||
|
// Names should match with epiphany and/ or xdg spec
|
||||||
|
/* NOTE: Those uncommented were replaced with remotely related icons
|
||||||
|
in order to reduce the amount of warnings :D */
|
||||||
|
|
||||||
|
#define STOCK_BOOKMARK GTK_STOCK_FILE // "stock_bookmark" // "bookmark-web"
|
||||||
|
#define STOCK_BOOKMARKS "bookmark-view"
|
||||||
|
#define STOCK_DOWNLOADS "package" // "download"
|
||||||
|
#define STOCK_CONSOLE "terminal" // "console" // MISSING
|
||||||
|
#define STOCK_EXTENSIONS "extension" // MISSING
|
||||||
|
#define STOCK_FORM_FILL "insert-text" // "form-fill" // MISSING
|
||||||
|
#define STOCK_HISTORY "document-open-recent"
|
||||||
|
#define STOCK_HISTORY_ "history-view"
|
||||||
|
#define STOCK_LOCATION "location-entry"
|
||||||
|
#define STOCK_NEWSFEED "gtk-index" // "newsfeed" // MISSING
|
||||||
|
#define STOCK_PLUGINS "plugin" // MISSING
|
||||||
|
#define STOCK_POPUPS_BLOCKED "popup-hidden"
|
||||||
|
#define STOCK_SOURCE_VIEW "stock_view-html-source" // MISSING
|
||||||
|
#define STOCK_TAB_CLOSE "tab-close" // MISSING
|
||||||
|
#define STOCK_WINDOW_CLOSE "window-close" // MISSING
|
||||||
|
|
||||||
|
// We can safely use standard icons
|
||||||
|
// Assuming that we have reliable fallback icons
|
||||||
|
|
||||||
|
#define STOCK_BOOKMARK_NEW "bookmark-new"
|
||||||
|
#define STOCK_BOOKMARK_NEW_ "stock_add-bookmark"
|
||||||
|
#define STOCK_HOMEPAGE GTK_STOCK_HOME
|
||||||
|
#define STOCK_IMAGE "image-x-generic"
|
||||||
|
#define STOCK_IMAGE_ "gnome-mime-image"
|
||||||
|
#define STOCK_LOCK_OPEN "stock_lock-open"
|
||||||
|
#define STOCK_LOCK_SECURE "stock_lock"
|
||||||
|
#define STOCK_LOCK_BROKEN "stock_lock-broken"
|
||||||
|
#define STOCK_NETWORK_OFFLINE "connect_no"
|
||||||
|
#define STOCK_NETWORK_OFFLINE_ "network-offline"
|
||||||
|
#define STOCK_SCRIPT "stock_script"
|
||||||
|
#define STOCK_SEND "mail-send"
|
||||||
|
#define STOCK_SEND_ "stock_mail-send"
|
||||||
|
#define STOCK_TAB_NEW "tab-new"
|
||||||
|
#define STOCK_TAB_NEW_ "stock_new-tab"
|
||||||
|
#define STOCK_THEME "gnome-settings-theme"
|
||||||
|
#define STOCK_USER_TRASH "user-trash"
|
||||||
|
#define STOCK_USER_TRASH_ "gnome-stock-trash"
|
||||||
|
#define STOCK_WINDOW_NEW "window-new"
|
||||||
|
#define STOCK_WINDOW_NEW_ "stock_new-window"
|
||||||
|
|
||||||
|
// For backwards compatibility
|
||||||
|
|
||||||
|
#if !GTK_CHECK_VERSION(2, 10, 0)
|
||||||
|
#define GTK_STOCK_SELECT_ALL "gtk-select-all"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !__GLOBAL_H__ */
|
506
src/helpers.c
Normal file
506
src/helpers.c
Normal file
|
@ -0,0 +1,506 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
|
#include "search.h"
|
||||||
|
#include "sokoke.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <webkit.h>
|
||||||
|
|
||||||
|
GtkIconTheme* get_icon_theme(GtkWidget* widget)
|
||||||
|
{
|
||||||
|
return gtk_icon_theme_get_for_screen(gtk_widget_get_screen(widget));
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* menu_item_new(const gchar* text, const gchar* icon
|
||||||
|
, GCallback signal, gboolean sensitive, gpointer userdata)
|
||||||
|
{
|
||||||
|
GtkWidget* menuitem;
|
||||||
|
if(text)
|
||||||
|
menuitem = gtk_image_menu_item_new_with_mnemonic(text);
|
||||||
|
else
|
||||||
|
menuitem = gtk_image_menu_item_new_from_stock(icon, NULL);
|
||||||
|
if(icon)
|
||||||
|
{
|
||||||
|
GtkWidget* image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU);
|
||||||
|
if(gtk_image_get_storage_type(GTK_IMAGE(image)) == GTK_IMAGE_EMPTY)
|
||||||
|
image = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_MENU);
|
||||||
|
if(gtk_image_get_storage_type(GTK_IMAGE(image)) != GTK_IMAGE_EMPTY)
|
||||||
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
|
||||||
|
else
|
||||||
|
g_print("Note: The icon %s is not available.", icon);
|
||||||
|
}
|
||||||
|
if(signal)
|
||||||
|
g_signal_connect(menuitem, "activate", signal, userdata);
|
||||||
|
gtk_widget_set_sensitive(GTK_WIDGET(menuitem), sensitive && signal);
|
||||||
|
return menuitem;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkToolItem* tool_button_new(const gchar* text, const gchar* icon
|
||||||
|
, gboolean important, gboolean sensitive, GCallback signal
|
||||||
|
, const gchar* tooltip, gpointer userdata)
|
||||||
|
{
|
||||||
|
GtkToolItem* toolbutton = gtk_tool_button_new(NULL, NULL);
|
||||||
|
GtkStockItem stockItem;
|
||||||
|
if(gtk_stock_lookup(icon, &stockItem))
|
||||||
|
toolbutton = gtk_tool_button_new_from_stock(icon);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GtkIconTheme* iconTheme = get_icon_theme(GTK_WIDGET(toolbutton));
|
||||||
|
if(gtk_icon_theme_has_icon(iconTheme, icon))
|
||||||
|
gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolbutton), icon);
|
||||||
|
else
|
||||||
|
gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(toolbutton), GTK_STOCK_MISSING_IMAGE);
|
||||||
|
}
|
||||||
|
if(text)
|
||||||
|
gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolbutton), text);
|
||||||
|
if(important)
|
||||||
|
gtk_tool_item_set_is_important(toolbutton, TRUE);
|
||||||
|
if(signal)
|
||||||
|
g_signal_connect(toolbutton, "clicked", signal, userdata);
|
||||||
|
gtk_widget_set_sensitive(GTK_WIDGET(toolbutton), sensitive && signal);
|
||||||
|
if(tooltip)
|
||||||
|
sokoke_tool_item_set_tooltip_text(toolbutton, tooltip);
|
||||||
|
return toolbutton;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* check_menu_item_new(const gchar* text
|
||||||
|
, GCallback signal, gboolean sensitive, gboolean active, CBrowser* browser)
|
||||||
|
{
|
||||||
|
GtkWidget* menuitem = gtk_check_menu_item_new_with_mnemonic(text);
|
||||||
|
gtk_widget_set_sensitive(menuitem, sensitive && signal);
|
||||||
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), active);
|
||||||
|
if(signal)
|
||||||
|
g_signal_connect(menuitem, "activate", signal, browser);
|
||||||
|
return menuitem;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* radio_button_new(GtkRadioButton* radio_button, const gchar* label)
|
||||||
|
{
|
||||||
|
return gtk_radio_button_new_with_mnemonic_from_widget(radio_button, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_error(const gchar* text, const gchar* text2, CBrowser* browser)
|
||||||
|
{
|
||||||
|
GtkWidget* dialog = gtk_message_dialog_new(
|
||||||
|
browser ? GTK_WINDOW(browser->window) : NULL
|
||||||
|
, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, text);
|
||||||
|
if(text2)
|
||||||
|
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), text2);
|
||||||
|
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
gtk_widget_destroy(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean spawn_protocol_command(const gchar* protocol, const gchar* res)
|
||||||
|
{
|
||||||
|
const gchar* command = g_datalist_get_data(&config->protocols_commands, protocol);
|
||||||
|
if(!command)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Create an argument vector
|
||||||
|
gchar* uriEscaped = g_shell_quote(res);
|
||||||
|
gchar* commandReady;
|
||||||
|
if(strstr(command, "%s"))
|
||||||
|
commandReady = g_strdup_printf(command, uriEscaped);
|
||||||
|
else
|
||||||
|
commandReady = g_strconcat(command, " ", uriEscaped, NULL);
|
||||||
|
gchar** argv; GError* error = NULL;
|
||||||
|
if(!g_shell_parse_argv(commandReady, NULL, &argv, &error))
|
||||||
|
{
|
||||||
|
// FIXME: Should we have a more specific message?
|
||||||
|
show_error("Could not run external program.", error->message, NULL);
|
||||||
|
g_error_free(error);
|
||||||
|
g_free(commandReady); g_free(uriEscaped);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to run the command
|
||||||
|
error = NULL;
|
||||||
|
gboolean success = g_spawn_async(NULL, argv, NULL
|
||||||
|
, (GSpawnFlags)G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD
|
||||||
|
, NULL, NULL, NULL, &error);
|
||||||
|
g_strfreev(argv);
|
||||||
|
|
||||||
|
if(!success)
|
||||||
|
{
|
||||||
|
// FIXME: Should we have a more specific message?
|
||||||
|
show_error("Could not run external program.", error->message, NULL);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
g_free(commandReady); g_free(uriEscaped);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GdkPixbuf* load_web_icon(const gchar* icon, GtkIconSize size, GtkWidget* widget)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(GTK_IS_WIDGET(widget), NULL);
|
||||||
|
GdkPixbuf* pixbuf = NULL;
|
||||||
|
if(icon && *icon)
|
||||||
|
{
|
||||||
|
// TODO: We want to allow http as well, maybe also base64?
|
||||||
|
const gchar* iconReady = g_str_has_prefix(icon, "file://") ? &icon[7] : icon;
|
||||||
|
GtkStockItem stockItem;
|
||||||
|
if(gtk_stock_lookup(icon, &stockItem))
|
||||||
|
pixbuf = gtk_widget_render_icon(widget, iconReady, size, NULL);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gint width; gint height;
|
||||||
|
gtk_icon_size_lookup(size, &width, &height);
|
||||||
|
pixbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default()
|
||||||
|
, icon, MAX(width, height), GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
|
||||||
|
}
|
||||||
|
if(!pixbuf)
|
||||||
|
pixbuf = gdk_pixbuf_new_from_file_at_size(iconReady, 16, 16, NULL);
|
||||||
|
}
|
||||||
|
if(!pixbuf)
|
||||||
|
pixbuf = gtk_widget_render_icon(widget, GTK_STOCK_FIND, size, NULL);
|
||||||
|
return pixbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void entry_setup_completion(GtkEntry* entry)
|
||||||
|
{
|
||||||
|
/* TODO: The current behavior works only with the beginning of strings
|
||||||
|
But we want to match "localhost" with "loc" and "hos" */
|
||||||
|
GtkEntryCompletion* completion = gtk_entry_completion_new();
|
||||||
|
gtk_entry_completion_set_model(completion
|
||||||
|
, GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_STRING)));
|
||||||
|
gtk_entry_completion_set_text_column(completion, 0);
|
||||||
|
gtk_entry_completion_set_minimum_key_length(completion, 3);
|
||||||
|
gtk_entry_set_completion(entry, completion);
|
||||||
|
gtk_entry_completion_set_popup_completion(completion, FALSE); //...
|
||||||
|
}
|
||||||
|
|
||||||
|
void entry_completion_append(GtkEntry* entry, const gchar* text)
|
||||||
|
{
|
||||||
|
GtkEntryCompletion* completion = gtk_entry_get_completion(entry);
|
||||||
|
GtkTreeModel* completion_store = gtk_entry_completion_get_model(completion);
|
||||||
|
GtkTreeIter iter;
|
||||||
|
gtk_list_store_insert(GTK_LIST_STORE(completion_store), &iter, 0);
|
||||||
|
gtk_list_store_set(GTK_LIST_STORE(completion_store), &iter, 0, text, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* get_nth_webView(gint n, CBrowser* browser)
|
||||||
|
{
|
||||||
|
if(n < 0)
|
||||||
|
n = gtk_notebook_get_current_page(GTK_NOTEBOOK(browser->webViews));
|
||||||
|
GtkWidget* scrolled = gtk_notebook_get_nth_page(GTK_NOTEBOOK(browser->webViews), n);
|
||||||
|
return gtk_bin_get_child(GTK_BIN(scrolled));
|
||||||
|
}
|
||||||
|
|
||||||
|
gint get_webView_index(GtkWidget* webView, CBrowser* browser)
|
||||||
|
{
|
||||||
|
GtkWidget* scrolled = gtk_widget_get_parent(webView);
|
||||||
|
return gtk_notebook_page_num(GTK_NOTEBOOK(browser->webViews), scrolled);
|
||||||
|
}
|
||||||
|
|
||||||
|
CBrowser* get_browser_from_webView(GtkWidget* webView)
|
||||||
|
{
|
||||||
|
// FIXME: g_list_first
|
||||||
|
CBrowser* browser = NULL; GList* item = g_list_first(browsers);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
browser = (CBrowser*)item->data;
|
||||||
|
if(browser->webView == webView)
|
||||||
|
return browser;
|
||||||
|
}
|
||||||
|
while((item = g_list_next(item)));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_favicon(CBrowser* browser)
|
||||||
|
{
|
||||||
|
if(browser->loadedPercent == -1)
|
||||||
|
{
|
||||||
|
if(0) //browser->favicon // Has favicon?
|
||||||
|
{
|
||||||
|
// TODO: use custom icon
|
||||||
|
// gtk_image_set_from_file(GTK_IMAGE(browser->icon_page), "image");
|
||||||
|
}
|
||||||
|
else if(0) // Known mime-type?
|
||||||
|
{
|
||||||
|
// TODO: Retrieve mime type and load icon; don't forget ftp listings
|
||||||
|
}
|
||||||
|
else
|
||||||
|
gtk_image_set_from_stock(GTK_IMAGE(browser->webView_icon)
|
||||||
|
, GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gtk_image_set_from_stock(GTK_IMAGE(browser->webView_icon)
|
||||||
|
, GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_security(CBrowser* browser)
|
||||||
|
{
|
||||||
|
const gchar* uri = xbel_bookmark_get_href(browser->sessionItem);
|
||||||
|
// TODO: This check is bogus, until webkit tells us how secure a page is
|
||||||
|
if(g_str_has_prefix(uri, "https://"))
|
||||||
|
{
|
||||||
|
// TODO: highlighted entry indicates security, find an alternative
|
||||||
|
gtk_widget_modify_base(browser->location, GTK_STATE_NORMAL
|
||||||
|
, &browser->location->style->base[GTK_STATE_SELECTED]);
|
||||||
|
gtk_widget_modify_text(browser->location, GTK_STATE_NORMAL
|
||||||
|
, &browser->location->style->text[GTK_STATE_SELECTED]);
|
||||||
|
gtk_image_set_from_stock(GTK_IMAGE(browser->icon_security)
|
||||||
|
, GTK_STOCK_DIALOG_AUTHENTICATION, GTK_ICON_SIZE_MENU);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gtk_widget_modify_base(browser->location, GTK_STATE_NORMAL, NULL);
|
||||||
|
gtk_widget_modify_text(browser->location, GTK_STATE_NORMAL, NULL);
|
||||||
|
gtk_image_set_from_stock(GTK_IMAGE(browser->icon_security)
|
||||||
|
, GTK_STOCK_INFO, GTK_ICON_SIZE_MENU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_visibility(CBrowser* browser, gboolean visibility)
|
||||||
|
{
|
||||||
|
// A tabbed window shouldn't be manipulatable
|
||||||
|
if(gtk_notebook_get_n_pages(GTK_NOTEBOOK(browser->webViews)) > 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// SHOULD SCRIPTS BE ABLE TO HIDE WINDOWS AT ALL?
|
||||||
|
if(0 && !visibility)
|
||||||
|
{
|
||||||
|
gtk_widget_hide(browser->window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(!visibility)
|
||||||
|
g_print("Window was not hidden.\n");
|
||||||
|
|
||||||
|
sokoke_widget_set_visible(browser->menubar, browser->hasMenubar);
|
||||||
|
sokoke_widget_set_visible(browser->navibar, browser->hasToolbar);
|
||||||
|
sokoke_widget_set_visible(browser->location, browser->hasLocation);
|
||||||
|
sokoke_widget_set_visible(browser->webSearch, browser->hasLocation);
|
||||||
|
sokoke_widget_set_visible(browser->statusbar, browser->hasStatusbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
void action_set_active(const gchar* name, gboolean active, CBrowser* browser)
|
||||||
|
{
|
||||||
|
// This shortcut toggles activity state by an action name
|
||||||
|
GtkAction* action = gtk_action_group_get_action(browser->actiongroup, name);
|
||||||
|
g_return_if_fail(GTK_IS_ACTION(action));
|
||||||
|
gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), active);
|
||||||
|
}
|
||||||
|
|
||||||
|
void action_set_sensitive(const gchar* name, gboolean sensitive, CBrowser* browser)
|
||||||
|
{
|
||||||
|
// This shortcut toggles sensitivity by an action name
|
||||||
|
GtkAction* action = gtk_action_group_get_action(browser->actiongroup, name);
|
||||||
|
g_return_if_fail(GTK_IS_ACTION(action));
|
||||||
|
gtk_action_set_sensitive(action, sensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void action_set_visible(const gchar* name, gboolean visible, CBrowser* browser)
|
||||||
|
{
|
||||||
|
// This shortcut toggles visibility by an action name
|
||||||
|
GtkAction* action = gtk_action_group_get_action(browser->actiongroup, name);
|
||||||
|
g_return_if_fail(GTK_IS_ACTION(action));
|
||||||
|
gtk_action_set_visible(action, visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_statusbar_text(CBrowser* browser)
|
||||||
|
{
|
||||||
|
if(browser->statusMessage)
|
||||||
|
{
|
||||||
|
gtk_statusbar_pop(GTK_STATUSBAR(browser->statusbar), 1);
|
||||||
|
gtk_statusbar_push(GTK_STATUSBAR(browser->statusbar), 1
|
||||||
|
, browser->statusMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gchar* message;
|
||||||
|
if(browser->loadedPercent)
|
||||||
|
message = g_strdup_printf("%d%% loaded, %d/%d bytes"
|
||||||
|
, browser->loadedPercent, browser->loadedBytes, browser->loadedBytesMax);
|
||||||
|
else if(browser->loadedBytes)
|
||||||
|
message = g_strdup_printf("%d bytes", browser->loadedBytes);
|
||||||
|
else
|
||||||
|
message = g_strdup(" ");
|
||||||
|
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(browser->progress), message);
|
||||||
|
g_free(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_edit_items(CBrowser* browser)
|
||||||
|
{
|
||||||
|
GtkWidget* widget = gtk_window_get_focus(GTK_WINDOW(browser->window));
|
||||||
|
gboolean hasSelection = FALSE;
|
||||||
|
gboolean canCut = FALSE; gboolean canCopy = FALSE; gboolean canPaste = FALSE;
|
||||||
|
if(widget && (/*WEBKIT_IS_WEB_VIEW(widget) || */GTK_IS_EDITABLE(widget)))
|
||||||
|
{
|
||||||
|
hasSelection = /*WEBKIT_IS_WEB_VIEW(widget)
|
||||||
|
? webkit_web_view_has_selection(WEBKIT_WEB_VIEW(widget), NULL, NULL)
|
||||||
|
: */gtk_editable_get_selection_bounds(GTK_EDITABLE(widget), NULL, NULL);
|
||||||
|
canCut = /*WEBKIT_IS_WEB_VIEW(widget)
|
||||||
|
? webkit_web_view_can_cut_clipboard(WEBKIT_WEB_VIEW(widget))
|
||||||
|
: */hasSelection && gtk_editable_get_editable(GTK_EDITABLE(widget));
|
||||||
|
canCopy = /*WEBKIT_IS_WEB_VIEW(widget)
|
||||||
|
? webkit_web_view_can_copy_clipboard(WEBKIT_WEB_VIEW(widget))
|
||||||
|
: */hasSelection;
|
||||||
|
canPaste = /*WEBKIT_IS_WEB_VIEW(widget)
|
||||||
|
? webkit_web_view_can_paste_clipboard(WEBKIT_WEB_VIEW(widget))
|
||||||
|
: */gtk_editable_get_editable(GTK_EDITABLE(widget));
|
||||||
|
}
|
||||||
|
action_set_sensitive("Cut", canCut, browser);
|
||||||
|
action_set_sensitive("Copy", canCopy, browser);
|
||||||
|
action_set_sensitive("Paste", canPaste, browser);
|
||||||
|
action_set_sensitive("Delete", canCut, browser);
|
||||||
|
action_set_sensitive("SelectAll", !hasSelection, browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_gui_state(CBrowser* browser)
|
||||||
|
{
|
||||||
|
GtkWidget* webView = get_nth_webView(-1, browser);
|
||||||
|
action_set_sensitive("ZoomIn", FALSE, browser);//webkit_web_view_can_increase_text_size(WEBKIT_WEB_VIEW(webView), browser);
|
||||||
|
action_set_sensitive("ZoomOut", FALSE, browser);//webkit_web_view_can_decrease_text_size(WEBKIT_WEB_VIEW(webView)), browser);
|
||||||
|
action_set_sensitive("ZoomNormal", FALSE, browser);//webkit_web_view_get_text_size(WEBKIT_WEB_VIEW(webView)) != 1, browser);
|
||||||
|
action_set_sensitive("Back", webkit_web_view_can_go_backward(WEBKIT_WEB_VIEW(webView)), browser);
|
||||||
|
action_set_sensitive("Forward", webkit_web_view_can_go_forward(WEBKIT_WEB_VIEW(webView)), browser);
|
||||||
|
action_set_sensitive("Refresh", browser->loadedPercent == -1, browser);
|
||||||
|
action_set_sensitive("Stop", browser->loadedPercent != -1, browser);
|
||||||
|
|
||||||
|
GtkAction* action = gtk_action_group_get_action(browser->actiongroup, "RefreshStop");
|
||||||
|
if(browser->loadedPercent == -1)
|
||||||
|
{
|
||||||
|
gtk_widget_hide(browser->throbber);
|
||||||
|
g_object_set(action, "stock-id", GTK_STOCK_REFRESH, NULL);
|
||||||
|
g_object_set(action, "tooltip", "Refresh the current page", NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gtk_widget_show(browser->throbber);
|
||||||
|
g_object_set(action, "stock-id", GTK_STOCK_STOP, NULL);
|
||||||
|
g_object_set(action, "tooltip", "Stop loading the current page", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_image_set_from_stock(GTK_IMAGE(browser->location_icon), GTK_STOCK_FILE
|
||||||
|
, GTK_ICON_SIZE_MENU);
|
||||||
|
|
||||||
|
if(browser->loadedBytesMax < 1) // Skip null and negative values
|
||||||
|
{
|
||||||
|
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(browser->progress));
|
||||||
|
update_statusbar_text(browser);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(browser->loadedBytes > browser->loadedBytesMax)
|
||||||
|
browser->loadedPercent = 100;
|
||||||
|
else
|
||||||
|
browser->loadedPercent
|
||||||
|
= (browser->loadedBytes * 100) / browser->loadedBytesMax;
|
||||||
|
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(browser->progress)
|
||||||
|
, browser->loadedPercent / 100.0);
|
||||||
|
update_statusbar_text(browser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_feeds(CBrowser* browser)
|
||||||
|
{
|
||||||
|
// TODO: Look for available feeds, requires dom access
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_search_engines(CBrowser* browser)
|
||||||
|
{
|
||||||
|
// TODO: Look for available search engines, requires dom access
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_status_message(const gchar* message, CBrowser* browser)
|
||||||
|
{
|
||||||
|
g_free(browser->statusMessage);
|
||||||
|
browser->statusMessage = g_strdup(message ? message : "");
|
||||||
|
update_statusbar_text(browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_browser_actions(CBrowser* browser)
|
||||||
|
{
|
||||||
|
gboolean active = gtk_notebook_get_n_pages(GTK_NOTEBOOK(browser->webViews)) > 1;
|
||||||
|
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(browser->webViews), active);
|
||||||
|
action_set_sensitive("TabClose", active, browser);
|
||||||
|
guint n = xbel_folder_get_n_items(tabtrash);
|
||||||
|
action_set_sensitive("UndoTabClose", n, browser);
|
||||||
|
action_set_sensitive("TabsClosed", n, browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar* magic_uri(const gchar* uri, gboolean search)
|
||||||
|
{
|
||||||
|
// Add file:// if we have a local path
|
||||||
|
if(g_path_is_absolute(uri))
|
||||||
|
return g_strconcat("file://", uri, NULL);
|
||||||
|
// Do we need to add a protocol?
|
||||||
|
if(!strstr(uri, "://"))
|
||||||
|
{
|
||||||
|
// Do we have a domain, ip address or localhost?
|
||||||
|
if(strstr(uri, ".") != NULL || !strcmp(uri, "localhost"))
|
||||||
|
return g_strconcat("http://", uri, NULL);
|
||||||
|
// We don't want to search? So return early.
|
||||||
|
if(!search)
|
||||||
|
return g_strdup(uri);
|
||||||
|
gchar search[256];
|
||||||
|
const gchar* searchUrl = NULL;
|
||||||
|
// Do we have a keyword and a string?
|
||||||
|
gchar** parts = g_strsplit(uri, " ", 2);
|
||||||
|
if(parts[0] && parts[1])
|
||||||
|
{
|
||||||
|
guint n = g_list_length(searchEngines);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
SearchEngine* searchEngine = (SearchEngine*)g_list_nth_data(searchEngines, i);
|
||||||
|
if(!strcmp(search_engine_get_keyword(searchEngine), parts[0]))
|
||||||
|
searchUrl = searchEngine->url;
|
||||||
|
}
|
||||||
|
if(searchUrl != NULL)
|
||||||
|
g_snprintf(search, 255, searchUrl, parts[1]);
|
||||||
|
}
|
||||||
|
//g_strfreev(sParts);
|
||||||
|
// We only have a word or there is no matching keyowrd, so search for it
|
||||||
|
if(searchUrl == NULL)
|
||||||
|
g_snprintf(search, 255, config->locationSearch, uri);
|
||||||
|
return g_strdup(search);
|
||||||
|
}
|
||||||
|
return g_strdup(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar* get_default_font(void)
|
||||||
|
{
|
||||||
|
GtkSettings* gtksettings = gtk_settings_get_default();
|
||||||
|
gchar* defaultFont;
|
||||||
|
g_object_get(gtksettings, "gtk-font-name", &defaultFont, NULL);
|
||||||
|
return defaultFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkToolbarStyle config_to_toolbarstyle(guint toolbarStyle)
|
||||||
|
{
|
||||||
|
switch(toolbarStyle)
|
||||||
|
{
|
||||||
|
case CONFIG_TOOLBAR_ICONS:
|
||||||
|
return GTK_TOOLBAR_ICONS;
|
||||||
|
case CONFIG_TOOLBAR_TEXT:
|
||||||
|
return GTK_TOOLBAR_TEXT;
|
||||||
|
case CONFIG_TOOLBAR_BOTH:
|
||||||
|
return GTK_TOOLBAR_BOTH;
|
||||||
|
case CONFIG_TOOLBAR_BOTH_HORIZ:
|
||||||
|
return GTK_TOOLBAR_BOTH_HORIZ;
|
||||||
|
}
|
||||||
|
GtkSettings* gtkSettings = gtk_settings_get_default();
|
||||||
|
g_object_get(gtkSettings, "gtk-toolbar-style", &toolbarStyle, NULL);
|
||||||
|
return toolbarStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkToolbarStyle config_to_toolbariconsize(gboolean toolbarSmall)
|
||||||
|
{
|
||||||
|
return toolbarSmall ? GTK_ICON_SIZE_SMALL_TOOLBAR
|
||||||
|
: GTK_ICON_SIZE_LARGE_TOOLBAR;
|
||||||
|
}
|
110
src/helpers.h
Normal file
110
src/helpers.h
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __HELPERS_H__
|
||||||
|
#define __HELPERS_H__ 1
|
||||||
|
|
||||||
|
#include "browser.h"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
GtkIconTheme*
|
||||||
|
get_icon_theme(GtkWidget*);
|
||||||
|
|
||||||
|
GtkWidget*
|
||||||
|
menu_item_new(const gchar*, const gchar*, GCallback, gboolean, gpointer);
|
||||||
|
|
||||||
|
GtkToolItem*
|
||||||
|
tool_button_new(const gchar*, const gchar*
|
||||||
|
, gboolean, gboolean, GCallback, const gchar*, gpointer);
|
||||||
|
|
||||||
|
GtkWidget*
|
||||||
|
check_menu_item_new(const gchar*, GCallback, gboolean, gboolean, CBrowser*);
|
||||||
|
|
||||||
|
GtkWidget*
|
||||||
|
radio_button_new(GtkRadioButton*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
show_error(const gchar*, const gchar*, CBrowser*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
spawn_protocol_command(const gchar*, const gchar*);
|
||||||
|
|
||||||
|
GdkPixbuf*
|
||||||
|
load_web_icon(const gchar*, GtkIconSize, GtkWidget*);
|
||||||
|
|
||||||
|
void
|
||||||
|
entry_setup_completion(GtkEntry*);
|
||||||
|
|
||||||
|
void
|
||||||
|
entry_completion_append(GtkEntry*, const gchar*);
|
||||||
|
|
||||||
|
GtkWidget*
|
||||||
|
get_nth_webView(gint, CBrowser*);
|
||||||
|
|
||||||
|
gint
|
||||||
|
get_webView_index(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
CBrowser*
|
||||||
|
get_browser_from_webView(GtkWidget*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_favicon(CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_security(CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_visibility(CBrowser*, gboolean);
|
||||||
|
|
||||||
|
void
|
||||||
|
action_set_active(const gchar*, gboolean, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
action_set_sensitive(const gchar*, gboolean, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
action_set_visible(const gchar*, gboolean, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_statusbar_text(CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_edit_items(CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_gui_state(CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_feeds(CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_search_engines(CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_status_message(const gchar*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
update_browser_actions(CBrowser*);
|
||||||
|
|
||||||
|
gchar*
|
||||||
|
magic_uri(const gchar*, gboolean bSearch);
|
||||||
|
|
||||||
|
gchar*
|
||||||
|
get_default_font(void);
|
||||||
|
|
||||||
|
GtkToolbarStyle
|
||||||
|
config_to_toolbarstyle();
|
||||||
|
|
||||||
|
GtkToolbarStyle
|
||||||
|
config_to_toolbariconsize(gboolean);
|
||||||
|
|
||||||
|
#endif /* !__HELPERS_H__ */
|
317
src/main.c
Executable file
317
src/main.c
Executable file
|
@ -0,0 +1,317 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#include "browser.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "sokoke.h"
|
||||||
|
#include "search.h"
|
||||||
|
#include "webView.h"
|
||||||
|
#include "xbel.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
// -- stock icons
|
||||||
|
|
||||||
|
static void stock_items_init(void)
|
||||||
|
{
|
||||||
|
static GtkStockItem items[] =
|
||||||
|
{
|
||||||
|
{ STOCK_LOCK_OPEN },
|
||||||
|
{ STOCK_LOCK_SECURE },
|
||||||
|
{ STOCK_LOCK_BROKEN },
|
||||||
|
{ STOCK_SCRIPT },
|
||||||
|
{ STOCK_THEME },
|
||||||
|
{ STOCK_USER_TRASH },
|
||||||
|
|
||||||
|
{ STOCK_BOOKMARK, "Bookmark", 0, 0, NULL },
|
||||||
|
{ STOCK_BOOKMARK_NEW, "New Bookmark", 0, 0, NULL },
|
||||||
|
{ STOCK_BOOKMARKS, "_Bookmarks", 0, 0, NULL },
|
||||||
|
{ STOCK_DOWNLOADS, "_Downloads", 0, 0, NULL },
|
||||||
|
{ STOCK_CONSOLE, "_Console", 0, 0, NULL },
|
||||||
|
{ STOCK_EXTENSIONS, "_Extensions", 0, 0, NULL },
|
||||||
|
{ STOCK_FORM_FILL, "_Form Fill", 0, 0, NULL },
|
||||||
|
{ STOCK_HISTORY, "History", 0, 0, NULL },
|
||||||
|
{ STOCK_HOMEPAGE, "Homepage", 0, 0, NULL },
|
||||||
|
{ STOCK_LOCATION, "Location Entry", 0, 0, NULL },
|
||||||
|
{ STOCK_NEWSFEED, "Newsfeed", 0, 0, NULL },
|
||||||
|
{ STOCK_PLUGINS, "_Plugins", 0, 0, NULL },
|
||||||
|
{ STOCK_POPUPS_BLOCKED, "Blocked Popups", 0, 0, NULL },
|
||||||
|
{ STOCK_SOURCE_VIEW, "View Source", 0, 0, NULL },
|
||||||
|
{ STOCK_TAB_CLOSE, "C_lose Tab", 0, 0, NULL },
|
||||||
|
{ STOCK_TAB_NEW, "New _Tab", 0, 0, NULL },
|
||||||
|
{ STOCK_WINDOW_CLOSE, "_Close Window", 0, 0, NULL },
|
||||||
|
{ STOCK_WINDOW_NEW, "New _Window", 0, 0, NULL },
|
||||||
|
#if !GTK_CHECK_VERSION(2, 10, 0)
|
||||||
|
{ GTK_STOCK_SELECT_ALL, "Select _All", 0, 0, (gchar*)"gtk20" },
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
GtkIconFactory* factory = gtk_icon_factory_new();
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < (guint)G_N_ELEMENTS(items); i++)
|
||||||
|
{
|
||||||
|
GtkIconSource* iconSource = gtk_icon_source_new();
|
||||||
|
gtk_icon_source_set_icon_name(iconSource, items[i].stock_id);
|
||||||
|
GtkIconSet* iconSet = gtk_icon_set_new();
|
||||||
|
gtk_icon_set_add_source(iconSet, iconSource);
|
||||||
|
gtk_icon_source_free(iconSource);
|
||||||
|
gtk_icon_factory_add(factory, items[i].stock_id, iconSet);
|
||||||
|
gtk_icon_set_unref(iconSet);
|
||||||
|
}
|
||||||
|
gtk_stock_add_static(items, G_N_ELEMENTS(items));
|
||||||
|
gtk_icon_factory_add_default(factory);
|
||||||
|
g_object_unref(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- main function
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
g_set_application_name(PACKAGE_NAME);
|
||||||
|
|
||||||
|
// Parse cli options
|
||||||
|
gint repeats = 2;
|
||||||
|
gboolean version = FALSE;
|
||||||
|
GOptionEntry entries[] =
|
||||||
|
{
|
||||||
|
{ "repeats", 'r', 0, G_OPTION_ARG_INT, &repeats, "An unused value", "N" },
|
||||||
|
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Display program version", NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
GError* error = NULL;
|
||||||
|
if(!gtk_init_with_args(&argc, &argv, "[URI]", entries, NULL/*GETTEXT_PACKAGE*/, &error))
|
||||||
|
{
|
||||||
|
g_error_free(error);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(version)
|
||||||
|
{
|
||||||
|
g_print(PACKAGE_STRING " - Copyright (c) 2007 Christian Dywan\n\n"
|
||||||
|
"GTK+2: " GTK_VER "\n"
|
||||||
|
"WebKit: " WEBKIT_VER "\n"
|
||||||
|
"Libsexy: " LIBSEXY_VER "\n"
|
||||||
|
"libXML2: " LIBXML_VER "\n"
|
||||||
|
"GetText: N/A\n"
|
||||||
|
"\n"
|
||||||
|
"Debugging: " SOKOKE_DEBUG_ "\n"
|
||||||
|
"\n"
|
||||||
|
"Please report comments, suggestions and bugs to:\n"
|
||||||
|
"\t" PACKAGE_BUGREPORT "\n"
|
||||||
|
"Check for new versions at:\n"
|
||||||
|
"\thttp://software.twotoasts.de\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load configuration files
|
||||||
|
GString* errorMessages = g_string_new(NULL);
|
||||||
|
// TODO: What about default config in a global config folder?
|
||||||
|
gchar* configPath = g_build_filename(g_get_user_config_dir(), PACKAGE_NAME, NULL);
|
||||||
|
g_mkdir_with_parents(configPath, 0755);
|
||||||
|
gchar* configFile = g_build_filename(configPath, "config", NULL);
|
||||||
|
error = NULL;
|
||||||
|
config = config_new();
|
||||||
|
if(!config_from_file(config, configFile, &error))
|
||||||
|
{
|
||||||
|
if(error->code != G_FILE_ERROR_NOENT)
|
||||||
|
g_string_append_printf(errorMessages
|
||||||
|
, "Configuration was not loaded. %s\n", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
g_free(configFile);
|
||||||
|
configFile = g_build_filename(configPath, "accels", NULL);
|
||||||
|
gtk_accel_map_load(configFile);
|
||||||
|
g_free(configFile);
|
||||||
|
configFile = g_build_filename(configPath, "search", NULL);
|
||||||
|
error = NULL;
|
||||||
|
searchEngines = search_engines_new();
|
||||||
|
if(!search_engines_from_file(&searchEngines, configFile, &error))
|
||||||
|
{
|
||||||
|
// FIXME: We may have a "file empty" error, how do we recognize that?
|
||||||
|
/*if(error->code != G_FILE_ERROR_NOENT)
|
||||||
|
g_string_append_printf(errorMessages
|
||||||
|
, "Notice: No search engines loaded. %s\n", error->message);*/
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
g_free(configFile);
|
||||||
|
configFile = g_build_filename(configPath, "bookmarks.xbel", NULL);
|
||||||
|
bookmarks = xbel_folder_new();
|
||||||
|
error = NULL;
|
||||||
|
if(!xbel_folder_from_file(bookmarks, configFile, &error))
|
||||||
|
{
|
||||||
|
if(error->code != G_FILE_ERROR_NOENT)
|
||||||
|
g_string_append_printf(errorMessages
|
||||||
|
, "Bookmarks couldn't be loaded. %s\n", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
g_free(configFile);
|
||||||
|
XbelItem* _session = xbel_folder_new();
|
||||||
|
if(config->startup == CONFIG_STARTUP_SESSION)
|
||||||
|
{
|
||||||
|
configFile = g_build_filename(configPath, "session.xbel", NULL);
|
||||||
|
error = NULL;
|
||||||
|
if(!xbel_folder_from_file(_session, configFile, &error))
|
||||||
|
{
|
||||||
|
if(error->code != G_FILE_ERROR_NOENT)
|
||||||
|
g_string_append_printf(errorMessages
|
||||||
|
, "Session couldn't be loaded. %s\n", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
g_free(configFile);
|
||||||
|
}
|
||||||
|
configFile = g_build_filename(configPath, "tabtrash.xbel", NULL);
|
||||||
|
tabtrash = xbel_folder_new();
|
||||||
|
error = NULL;
|
||||||
|
if(!xbel_folder_from_file(tabtrash, configFile, &error))
|
||||||
|
{
|
||||||
|
if(error->code != G_FILE_ERROR_NOENT)
|
||||||
|
g_string_append_printf(errorMessages
|
||||||
|
, "Tabtrash couldn't be loaded. %s\n", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
g_free(configFile);
|
||||||
|
|
||||||
|
// In case of errors
|
||||||
|
if(errorMessages->len)
|
||||||
|
{
|
||||||
|
GtkWidget* dialog = gtk_message_dialog_new(NULL
|
||||||
|
, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE
|
||||||
|
, "The following errors occured.");
|
||||||
|
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
|
||||||
|
gtk_window_set_title(GTK_WINDOW(dialog), g_get_application_name());
|
||||||
|
// FIXME: Use custom program icon
|
||||||
|
gtk_window_set_icon_name(GTK_WINDOW(dialog), "web-browser");
|
||||||
|
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog)
|
||||||
|
, "%s", errorMessages->str);
|
||||||
|
gtk_dialog_add_buttons(GTK_DIALOG(dialog)
|
||||||
|
, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL
|
||||||
|
, "_Ignore", GTK_RESPONSE_ACCEPT
|
||||||
|
, NULL);
|
||||||
|
if(gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
config_free(config);
|
||||||
|
search_engines_free(searchEngines);
|
||||||
|
xbel_item_free(bookmarks);
|
||||||
|
xbel_item_free(_session);
|
||||||
|
g_string_free(errorMessages, TRUE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
gtk_widget_destroy(dialog);
|
||||||
|
/* FIXME: Since we will overwrite files that could not be loaded
|
||||||
|
, would we want to make backups? */
|
||||||
|
}
|
||||||
|
g_string_free(errorMessages, TRUE);
|
||||||
|
|
||||||
|
if(xbel_folder_is_empty(_session))
|
||||||
|
{
|
||||||
|
XbelItem* item = xbel_bookmark_new();
|
||||||
|
if(config->startup == CONFIG_STARTUP_HOMEPAGE)
|
||||||
|
xbel_bookmark_set_href(item, config->homepage);
|
||||||
|
else
|
||||||
|
xbel_bookmark_set_href(item, "about:blank");
|
||||||
|
xbel_folder_append_item(_session, item);
|
||||||
|
}
|
||||||
|
g_free(configPath);
|
||||||
|
|
||||||
|
accel_group = gtk_accel_group_new();
|
||||||
|
stock_items_init();
|
||||||
|
browsers = NULL;
|
||||||
|
|
||||||
|
// TODO: Handle any number of separate uris from argv
|
||||||
|
// Open as many tabs as we have uris, seperated by pipes
|
||||||
|
gchar* uri = argc > 1 ? strtok(g_strdup(argv[1]), "|") : NULL;
|
||||||
|
while(uri != NULL)
|
||||||
|
{
|
||||||
|
XbelItem* item = xbel_bookmark_new();
|
||||||
|
gchar* uriReady = magic_uri(uri, FALSE);
|
||||||
|
xbel_bookmark_set_href(item, uriReady);
|
||||||
|
g_free(uriReady);
|
||||||
|
xbel_folder_append_item(_session, item);
|
||||||
|
uri = strtok(NULL, "|");
|
||||||
|
}
|
||||||
|
g_free(uri);
|
||||||
|
|
||||||
|
session = xbel_folder_new();
|
||||||
|
CBrowser* browser = NULL;
|
||||||
|
guint n = xbel_folder_get_n_items(_session);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
XbelItem* item = xbel_folder_get_nth_item(_session, i);
|
||||||
|
browser = browser_new(browser);
|
||||||
|
webView_open(browser->webView, xbel_bookmark_get_href(item));
|
||||||
|
}
|
||||||
|
xbel_item_free(_session);
|
||||||
|
|
||||||
|
gtk_main();
|
||||||
|
|
||||||
|
// Save configuration files
|
||||||
|
configPath = g_build_filename(g_get_user_config_dir(), PACKAGE_NAME, NULL);
|
||||||
|
g_mkdir_with_parents(configPath, 0755);
|
||||||
|
configFile = g_build_filename(configPath, "search", NULL);
|
||||||
|
error = NULL;
|
||||||
|
if(!search_engines_to_file(searchEngines, configFile, &error))
|
||||||
|
{
|
||||||
|
g_warning("Search engines couldn't be saved. %s", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
search_engines_free(searchEngines);
|
||||||
|
g_free(configFile);
|
||||||
|
configFile = g_build_filename(configPath, "bookmarks.xbel", NULL);
|
||||||
|
error = NULL;
|
||||||
|
if(!xbel_folder_to_file(bookmarks, configFile, &error))
|
||||||
|
{
|
||||||
|
g_warning("Bookmarks couldn't be saved. %s", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
xbel_item_free(bookmarks);
|
||||||
|
g_free(configFile);
|
||||||
|
configFile = g_build_filename(configPath, "tabtrash.xbel", NULL);
|
||||||
|
error = NULL;
|
||||||
|
if(!xbel_folder_to_file(tabtrash, configFile, &error))
|
||||||
|
{
|
||||||
|
g_warning("Tabtrash couldn't be saved. %s", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
xbel_item_free(tabtrash);
|
||||||
|
g_free(configFile);
|
||||||
|
if(config->startup == CONFIG_STARTUP_SESSION)
|
||||||
|
{
|
||||||
|
configFile = g_build_filename(configPath, "session.xbel", NULL);
|
||||||
|
error = NULL;
|
||||||
|
if(!xbel_folder_to_file(session, configFile, &error))
|
||||||
|
{
|
||||||
|
g_warning("Session couldn't be saved. %s", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
g_free(configFile);
|
||||||
|
}
|
||||||
|
xbel_item_free(session);
|
||||||
|
configFile = g_build_filename(configPath, "config", NULL);
|
||||||
|
error = NULL;
|
||||||
|
if(!config_to_file(config, configFile, &error))
|
||||||
|
{
|
||||||
|
g_warning("Configuration couldn't be saved. %s", error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
}
|
||||||
|
config_free(config);
|
||||||
|
g_free(configFile);
|
||||||
|
configFile = g_build_filename(configPath, "accels", NULL);
|
||||||
|
gtk_accel_map_save(configFile);
|
||||||
|
g_free(configFile);
|
||||||
|
g_free(configPath);
|
||||||
|
return 0;
|
||||||
|
}
|
15
src/main.h
Normal file
15
src/main.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __MIDORI_H__
|
||||||
|
#define __MIDORI_H__ 1
|
||||||
|
|
||||||
|
#endif /* !__MIDORI_H__ */
|
669
src/prefs.c
Normal file
669
src/prefs.c
Normal file
|
@ -0,0 +1,669 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "prefs.h"
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "sokoke.h"
|
||||||
|
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
static gboolean on_prefs_homepage_focus_out(GtkWidget* widget
|
||||||
|
, GdkEventFocus event, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
g_free(config->homepage);
|
||||||
|
config->homepage = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_loadonstartup_changed(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->startup = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_newpages_changed(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->newPages = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_prefs_openTabsInTheBackground_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->openTabsInTheBackground = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_openPopupsInTabs_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->openPopupsInTabs = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_loadImagesAutomatically_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->loadImagesAutomatically = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
// FIXME: Apply the change to all open webViews
|
||||||
|
g_object_set(get_nth_webView(-1, prefs->browser)
|
||||||
|
, "loads-images-automatically", config->loadImagesAutomatically, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_shrinkImagesToFit_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->shrinkImagesToFit = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
// FIXME: Apply the change to all open webViews
|
||||||
|
g_object_set(get_nth_webView(-1, prefs->browser)
|
||||||
|
, "shrinks-standalone-images-to-fit", config->shrinkImagesToFit, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_resizableTextAreas_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->resizableTextAreas = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
// FIXME: Apply the change to all open webViews
|
||||||
|
g_object_set(get_nth_webView(-1, prefs->browser)
|
||||||
|
, "text-areas-are-resizable", config->resizableTextAreas, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_enableJavaScript_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->enableJavaScript = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
// FIXME: Apply the change to all open webViews
|
||||||
|
g_object_set(get_nth_webView(-1, prefs->browser)
|
||||||
|
, "java-script-enabled", config->enableJavaScript, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_enablePlugins_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->enablePlugins = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
// FIXME: Apply the change to all open webViews
|
||||||
|
g_object_set(get_nth_webView(-1, prefs->browser)
|
||||||
|
, "plugins-enabled", config->enablePlugins, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_toolbarstyle_changed(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->toolbarStyle = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
|
||||||
|
gtk_toolbar_set_style(GTK_TOOLBAR(prefs->browser->navibar)
|
||||||
|
, config_to_toolbarstyle(config->toolbarStyle));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_toolbarSmall_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->toolbarSmall = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
gtk_toolbar_set_icon_size(GTK_TOOLBAR(prefs->browser->navibar)
|
||||||
|
, config_to_toolbariconsize(config->toolbarSmall));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_closeButtonsOnTabs_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->tabClose = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
GList* items = browsers;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
CBrowser* browser = (CBrowser*)items->data;
|
||||||
|
sokoke_widget_set_visible(browser->webView_close, config->tabClose);
|
||||||
|
}
|
||||||
|
while((items = g_list_next(items)));
|
||||||
|
g_list_free(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_toolbarWebSearch_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->toolbarWebSearch = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
sokoke_widget_set_visible(prefs->browser->webSearch, config->toolbarWebSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_toolbarNewTab_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->toolbarNewTab = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
sokoke_widget_set_visible(prefs->browser->newTab, config->toolbarNewTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_toolbarClosedTabs_toggled(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
config->toolbarClosedTabs = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
|
||||||
|
sokoke_widget_set_visible(prefs->browser->closedTabs, config->toolbarClosedTabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean on_prefs_locationsearch_focus_out(GtkWidget* widget
|
||||||
|
, GdkEventFocus event, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
g_free(config->locationSearch);
|
||||||
|
config->locationSearch = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget)));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_protocols_render_icon(GtkTreeViewColumn* column
|
||||||
|
, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
gchar* command;
|
||||||
|
gtk_tree_model_get(model, iter, PROTOCOLS_COL_COMMAND, &command, -1);
|
||||||
|
|
||||||
|
// TODO: Would it be better, to not do this on every redraw?
|
||||||
|
// Determine the actual binary to be able to display an icon
|
||||||
|
gchar* binary = NULL;
|
||||||
|
if(command)
|
||||||
|
binary = strtok(command, " ");
|
||||||
|
if(binary)
|
||||||
|
{
|
||||||
|
gchar* path;
|
||||||
|
if((path = g_find_program_in_path(binary)))
|
||||||
|
{
|
||||||
|
GtkIconTheme* icon_theme = get_icon_theme(prefs->treeview);
|
||||||
|
if(g_path_is_absolute(binary))
|
||||||
|
{
|
||||||
|
g_free(path); path = g_path_get_basename(binary);
|
||||||
|
}
|
||||||
|
// TODO: Is it good to just display nothing if there is no icon?
|
||||||
|
if(!gtk_icon_theme_has_icon(icon_theme, binary))
|
||||||
|
binary = NULL;
|
||||||
|
#if GTK_CHECK_VERSION(2, 10, 0)
|
||||||
|
g_object_set(renderer, "icon-name", binary, NULL);
|
||||||
|
#else
|
||||||
|
GdkPixbuf* icon = binary != NULL
|
||||||
|
? gtk_icon_theme_load_icon(gtk_icon_theme_get_default()
|
||||||
|
, binary, GTK_ICON_SIZE_MENU, 0, NULL) : NULL;
|
||||||
|
g_object_set(renderer, "pixbuf", icon, NULL);
|
||||||
|
if(icon)
|
||||||
|
g_object_unref(icon);
|
||||||
|
#endif
|
||||||
|
g_free(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if GTK_CHECK_VERSION(2, 10, 0)
|
||||||
|
g_object_set(renderer, "icon-name", NULL, NULL);
|
||||||
|
#endif
|
||||||
|
g_object_set(renderer, "stock-id", GTK_STOCK_DIALOG_ERROR, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We need to reset the icon
|
||||||
|
#if GTK_CHECK_VERSION(2, 10, 0)
|
||||||
|
g_object_set(renderer, "icon-name", NULL, NULL);
|
||||||
|
#else
|
||||||
|
g_object_set(renderer, "stock-id", NULL, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
g_free(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_protocols_edited(GtkCellRendererText* renderer
|
||||||
|
, gchar* path, gchar* textNew, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(prefs->treeview));
|
||||||
|
GtkTreeIter iter;
|
||||||
|
gtk_tree_model_get_iter_from_string(model, &iter, path);
|
||||||
|
gtk_list_store_set(GTK_LIST_STORE(model), &iter
|
||||||
|
, PROTOCOLS_COL_COMMAND, textNew, -1);
|
||||||
|
gchar* protocol;
|
||||||
|
gtk_tree_model_get(model, &iter, PROTOCOLS_COL_NAME, &protocol, -1);
|
||||||
|
g_datalist_set_data_full(&config->protocols_commands
|
||||||
|
, protocol, g_strdup(textNew), g_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_protocols_add_clicked(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
gchar* protocol = gtk_combo_box_get_active_text(GTK_COMBO_BOX(prefs->combobox));
|
||||||
|
GtkTreeModel* liststore = gtk_tree_view_get_model(GTK_TREE_VIEW(prefs->treeview));
|
||||||
|
gtk_list_store_insert_with_values(GTK_LIST_STORE(liststore), NULL, G_MAXINT
|
||||||
|
, PROTOCOLS_COL_NAME, protocol
|
||||||
|
, PROTOCOLS_COL_COMMAND, "", -1);
|
||||||
|
g_ptr_array_add(config->protocols_names, (gpointer)protocol);
|
||||||
|
g_datalist_set_data_full(&config->protocols_commands
|
||||||
|
, protocol, g_strdup(""), g_free);
|
||||||
|
gtk_widget_set_sensitive(prefs->add, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_prefs_protocols_combobox_changed(GtkWidget* widget, CPrefs* prefs)
|
||||||
|
{
|
||||||
|
gchar* protocol = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget));
|
||||||
|
gchar* command = (gchar*)g_datalist_get_data(&config->protocols_commands, protocol);
|
||||||
|
g_free(protocol);
|
||||||
|
gtk_widget_set_sensitive(prefs->add, command == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* prefs_preferences_dialog_new(CBrowser* browser)
|
||||||
|
{
|
||||||
|
gchar* dialogTitle = g_strdup_printf("%s Preferences", g_get_application_name());
|
||||||
|
GtkWidget* dialog = gtk_dialog_new_with_buttons(dialogTitle
|
||||||
|
, GTK_WINDOW(browser->window)
|
||||||
|
, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR
|
||||||
|
, GTK_STOCK_HELP
|
||||||
|
, GTK_RESPONSE_HELP
|
||||||
|
, GTK_STOCK_CLOSE
|
||||||
|
, GTK_RESPONSE_CLOSE
|
||||||
|
, NULL);
|
||||||
|
gtk_window_set_icon_name(GTK_WINDOW(dialog), GTK_STOCK_PREFERENCES);
|
||||||
|
// TODO: Implement some kind of help function
|
||||||
|
gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_HELP, FALSE); //...
|
||||||
|
g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog);
|
||||||
|
|
||||||
|
CPrefs* prefs = g_new0(CPrefs, 1);
|
||||||
|
prefs->browser = browser;
|
||||||
|
//prefs->window = dialog;
|
||||||
|
g_signal_connect(dialog, "response", G_CALLBACK(g_free), prefs);
|
||||||
|
|
||||||
|
// TODO: Do we want tooltips for explainations or can we omit that?
|
||||||
|
// TODO: We need mnemonics
|
||||||
|
// TODO: Take multiple windows into account when applying changes
|
||||||
|
GtkWidget* xfce_heading;
|
||||||
|
if((xfce_heading = sokoke_xfce_header_new(
|
||||||
|
gtk_window_get_icon_name(GTK_WINDOW(browser->window)), dialogTitle)))
|
||||||
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox)
|
||||||
|
, xfce_heading, FALSE, FALSE, 0);
|
||||||
|
g_free(dialogTitle);
|
||||||
|
GtkWidget* notebook = gtk_notebook_new();
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(notebook), 6);
|
||||||
|
GtkSizeGroup* sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||||
|
GtkSizeGroup* sizegroup2 = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||||
|
GtkWidget* page; GtkWidget* frame; GtkWidget* table; GtkWidget* align;
|
||||||
|
GtkWidget* button; GtkWidget* checkbutton; GtkWidget* colorbutton;
|
||||||
|
GtkWidget* combobox; GtkWidget* entry; GtkWidget* hbox; GtkWidget* spinbutton;
|
||||||
|
#define PAGE_NEW(__label) page = gtk_vbox_new(FALSE, 0);\
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(page), 5);\
|
||||||
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, gtk_label_new(__label))
|
||||||
|
#define FRAME_NEW(__label) frame = sokoke_hig_frame_new(__label);\
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(frame), 5);\
|
||||||
|
gtk_box_pack_start(GTK_BOX(page), frame, FALSE, FALSE, 0);
|
||||||
|
#define TABLE_NEW(__rows, __cols) table = gtk_table_new(__rows, __cols, FALSE);\
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(table), 5);\
|
||||||
|
gtk_container_add(GTK_CONTAINER(frame), table);
|
||||||
|
#define WIDGET_ADD(__widget, __left, __right, __top, __bottom)\
|
||||||
|
gtk_table_attach(GTK_TABLE(table), __widget\
|
||||||
|
, __left, __right, __top, __bottom\
|
||||||
|
, 0, GTK_FILL, 8, 2)
|
||||||
|
#define FILLED_ADD(__widget, __left, __right, __top, __bottom)\
|
||||||
|
gtk_table_attach(GTK_TABLE(table), __widget\
|
||||||
|
, __left, __right, __top, __bottom\
|
||||||
|
, GTK_EXPAND | GTK_FILL, GTK_FILL, 8, 2)
|
||||||
|
#define INDENTED_ADD(__widget, __left, __right, __top, __bottom)\
|
||||||
|
align = gtk_alignment_new(0, 0.5, 0, 0);\
|
||||||
|
gtk_container_add(GTK_CONTAINER(align), __widget);\
|
||||||
|
gtk_size_group_add_widget(sizegroup, align);\
|
||||||
|
WIDGET_ADD(align, __left, __right, __top, __bottom)
|
||||||
|
#define SEMI_INDENTED_ADD(__widget, __left, __right, __top, __bottom)\
|
||||||
|
align = gtk_alignment_new(0, 0.5, 0, 0);\
|
||||||
|
gtk_container_add(GTK_CONTAINER(align), __widget);\
|
||||||
|
gtk_size_group_add_widget(sizegroup2, align);\
|
||||||
|
WIDGET_ADD(align, __left, __right, __top, __bottom)
|
||||||
|
#define SPANNED_ADD(__widget, __left, __right, __top, __bottom)\
|
||||||
|
align = gtk_alignment_new(0, 0.5, 0, 0);\
|
||||||
|
gtk_container_add(GTK_CONTAINER(align), __widget);\
|
||||||
|
FILLED_ADD(align, __left, __right, __top, __bottom)
|
||||||
|
// Page "General"
|
||||||
|
PAGE_NEW("General");
|
||||||
|
FRAME_NEW("Startup");
|
||||||
|
TABLE_NEW(2, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new("Load on startup"), 0, 1, 0, 1);
|
||||||
|
combobox = gtk_combo_box_new_text();
|
||||||
|
sokoke_combo_box_add_strings(GTK_COMBO_BOX(combobox)
|
||||||
|
, "Blank page", "Homepage", "Last open pages", NULL);
|
||||||
|
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), config->startup);
|
||||||
|
g_signal_connect(combobox, "changed"
|
||||||
|
, G_CALLBACK(on_prefs_loadonstartup_changed), prefs);
|
||||||
|
FILLED_ADD(combobox, 1, 2, 0, 1);
|
||||||
|
INDENTED_ADD(gtk_label_new("Homepage"), 0, 1, 1, 2);
|
||||||
|
entry = gtk_entry_new();
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry), config->homepage);
|
||||||
|
g_signal_connect(entry, "focus-out-event"
|
||||||
|
, G_CALLBACK(on_prefs_homepage_focus_out), prefs);
|
||||||
|
FILLED_ADD(entry, 1, 2, 1, 2);
|
||||||
|
// TODO: We need something like "use current website"
|
||||||
|
FRAME_NEW("Downloads");
|
||||||
|
TABLE_NEW(1, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new("Download folder"), 0, 1, 0, 1);
|
||||||
|
GtkWidget* filebutton = gtk_file_chooser_button_new(
|
||||||
|
"Choose download folder", GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
|
||||||
|
// FIXME: The default should probably be ~/Desktop
|
||||||
|
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filebutton)
|
||||||
|
, g_get_home_dir()); //...
|
||||||
|
gtk_widget_set_sensitive(filebutton, FALSE); //...
|
||||||
|
FILLED_ADD(filebutton, 1, 2, 0, 1);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic
|
||||||
|
("Show a notification window for finished downloads");
|
||||||
|
gtk_widget_set_sensitive(checkbutton, FALSE); //...
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 1, 2);
|
||||||
|
FRAME_NEW("Languages");
|
||||||
|
TABLE_NEW(1, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new("Preferred languages"), 0, 1, 0, 1);
|
||||||
|
entry = gtk_entry_new();
|
||||||
|
// TODO: Make sth like get_browser_languages_default filtering encodings and C out
|
||||||
|
// TODO: Provide a real ui with real language names (iso-codes)
|
||||||
|
const gchar* const* sLanguages = g_get_language_names();
|
||||||
|
gchar* sLanguagesPreferred = g_strjoinv(",", (gchar**)sLanguages);
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry), sLanguagesPreferred/*config->sLanguagesPreferred*/);
|
||||||
|
g_free(sLanguagesPreferred);
|
||||||
|
gtk_widget_set_sensitive(entry, FALSE); //...
|
||||||
|
FILLED_ADD(entry, 1, 2, 0, 1);
|
||||||
|
|
||||||
|
// Page "Appearance"
|
||||||
|
PAGE_NEW("Appearance");
|
||||||
|
FRAME_NEW("Font settings");
|
||||||
|
TABLE_NEW(5, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new("Standard font"), 0, 1, 0, 1);
|
||||||
|
button = gtk_font_button_new_with_font("Sans 10"/*config->sFontStandard*/);
|
||||||
|
gtk_widget_set_sensitive(button, FALSE); //...
|
||||||
|
FILLED_ADD(button, 1, 2, 0, 1);
|
||||||
|
INDENTED_ADD(gtk_label_new("Minimum font size"), 0, 1, 1, 2);
|
||||||
|
hbox = gtk_hbox_new(FALSE, 4);
|
||||||
|
spinbutton = gtk_spin_button_new_with_range(5, 12, 1);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton), 5/*config->iFontSizeMin*/);
|
||||||
|
gtk_widget_set_sensitive(spinbutton, FALSE); //...
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), spinbutton, FALSE, FALSE, 0);
|
||||||
|
button = gtk_button_new_with_mnemonic("_Advanced");
|
||||||
|
gtk_widget_set_sensitive(button, FALSE); //...
|
||||||
|
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 4);
|
||||||
|
FILLED_ADD(hbox, 1, 2, 1, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new("Default encoding"), 0, 1, 2, 3);
|
||||||
|
combobox = gtk_combo_box_new_text();
|
||||||
|
const gchar* encoding = NULL; g_get_charset(&encoding);
|
||||||
|
// TODO: Fallback to utf-8 if the encoding is not sane (e.g. when lang=C)
|
||||||
|
gchar* sEncodingDefault = g_strdup_printf("System (%s)", encoding);
|
||||||
|
sokoke_combo_box_add_strings(GTK_COMBO_BOX(combobox)
|
||||||
|
, sEncodingDefault, "Chinese", "Greek", "Japanese (SHIFT_JIS)"
|
||||||
|
, "Korean", "Russian", "Unicode (UTF-8)", "Western (ISO-8859-1)", NULL);
|
||||||
|
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); //...
|
||||||
|
gtk_widget_set_sensitive(combobox, FALSE); //...
|
||||||
|
FILLED_ADD(combobox, 1, 2, 2, 3);
|
||||||
|
button = gtk_button_new_with_label("Advanced settings");
|
||||||
|
gtk_widget_set_sensitive(button, FALSE); //...
|
||||||
|
WIDGET_ADD(button, 1, 2, 2, 3);
|
||||||
|
FRAME_NEW("Default colors");
|
||||||
|
TABLE_NEW(2, 4);
|
||||||
|
SEMI_INDENTED_ADD(gtk_label_new("Text color"), 0, 1, 0, 1);
|
||||||
|
colorbutton = gtk_color_button_new();
|
||||||
|
gtk_widget_set_sensitive(colorbutton, FALSE); //...
|
||||||
|
WIDGET_ADD(colorbutton, 1, 2, 0, 1);
|
||||||
|
SEMI_INDENTED_ADD(gtk_label_new("Background color"), 2, 3, 0, 1);
|
||||||
|
colorbutton = gtk_color_button_new();
|
||||||
|
gtk_widget_set_sensitive(colorbutton, FALSE); //...
|
||||||
|
WIDGET_ADD(colorbutton, 3, 4, 0, 1);
|
||||||
|
SEMI_INDENTED_ADD(gtk_label_new("Normal link color"), 0, 1, 1, 2);
|
||||||
|
colorbutton = gtk_color_button_new();
|
||||||
|
gtk_widget_set_sensitive(colorbutton, FALSE); //...
|
||||||
|
WIDGET_ADD(colorbutton, 1, 2, 1, 2);
|
||||||
|
SEMI_INDENTED_ADD(gtk_label_new("Visited link color"), 2, 3, 1, 2);
|
||||||
|
colorbutton = gtk_color_button_new();
|
||||||
|
gtk_widget_set_sensitive(colorbutton, FALSE); //...
|
||||||
|
WIDGET_ADD(colorbutton, 3, 4, 1, 2);
|
||||||
|
|
||||||
|
// Page "Behavior"
|
||||||
|
PAGE_NEW("Behavior");
|
||||||
|
FRAME_NEW("Browsing");
|
||||||
|
TABLE_NEW(3, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new_with_mnemonic("Open _new pages in"), 0, 1, 0, 1);
|
||||||
|
combobox = gtk_combo_box_new_text();
|
||||||
|
sokoke_combo_box_add_strings(GTK_COMBO_BOX(combobox)
|
||||||
|
, "New tab", "New window", "Current tab", NULL);
|
||||||
|
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), config->newPages);
|
||||||
|
g_signal_connect(combobox, "changed"
|
||||||
|
, G_CALLBACK(on_prefs_newpages_changed), prefs);
|
||||||
|
gtk_widget_set_sensitive(combobox, FALSE); //...
|
||||||
|
FILLED_ADD(combobox, 1, 2, 0, 1);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Open tabs in the _background");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->openTabsInTheBackground);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_openTabsInTheBackground_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 1, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Open _popups in tabs");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->openPopupsInTabs);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_openPopupsInTabs_toggled), prefs);
|
||||||
|
gtk_widget_set_sensitive(checkbutton, FALSE); //...
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 2, 3);
|
||||||
|
FRAME_NEW("Features");
|
||||||
|
TABLE_NEW(3, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Load _images automatically");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->loadImagesAutomatically);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_loadImagesAutomatically_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 0, 1, 0, 1);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("_Shrink images to fit");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->shrinkImagesToFit);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_shrinkImagesToFit_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 1, 2, 0, 1);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("_Resizable textareas");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->resizableTextAreas);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_resizableTextAreas_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 0, 1, 1, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Enable java_script");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->enableJavaScript);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_enableJavaScript_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 1, 2, 1, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Enable _plugins");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->enablePlugins);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_enablePlugins_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 0, 1, 2, 3);
|
||||||
|
// For now we check for "plugins-enabled", in case this build has no properties
|
||||||
|
if(!g_object_class_find_property(G_OBJECT_GET_CLASS(browser->webView), "plugins-enabled"))
|
||||||
|
gtk_widget_set_sensitive(frame, FALSE);
|
||||||
|
|
||||||
|
// Page "Interface"
|
||||||
|
PAGE_NEW("Interface");
|
||||||
|
FRAME_NEW("Navigationbar");
|
||||||
|
TABLE_NEW(3, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new_with_mnemonic("_Toolbar style"), 0, 1, 0, 1);
|
||||||
|
combobox = gtk_combo_box_new_text();
|
||||||
|
sokoke_combo_box_add_strings(GTK_COMBO_BOX(combobox)
|
||||||
|
, "Default", "Icons", "Text", "Both", "Both horizontal", NULL);
|
||||||
|
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), config->toolbarStyle);
|
||||||
|
g_signal_connect(combobox, "changed"
|
||||||
|
, G_CALLBACK(on_prefs_toolbarstyle_changed), prefs);
|
||||||
|
FILLED_ADD(combobox, 1, 2, 0, 1);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Show small _icons");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->toolbarSmall);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_toolbarSmall_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 0, 1, 1, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Show web_search");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->toolbarWebSearch);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_toolbarWebSearch_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 1, 2, 1, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Show _New Tab Button");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->toolbarNewTab);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_toolbarNewTab_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 0, 1, 2, 3);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Show _closed tabs button");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->toolbarClosedTabs);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_toolbarClosedTabs_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 1, 2, 2, 3);
|
||||||
|
FRAME_NEW("Miscellaneous");
|
||||||
|
TABLE_NEW(3, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic
|
||||||
|
("Show close _buttons on tabs");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), config->tabClose);
|
||||||
|
g_signal_connect(checkbutton, "toggled"
|
||||||
|
, G_CALLBACK(on_prefs_closeButtonsOnTabs_toggled), prefs);
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 0, 1);
|
||||||
|
INDENTED_ADD(gtk_label_new_with_mnemonic("Tabbar _placement"), 0, 1, 1, 2);
|
||||||
|
combobox = gtk_combo_box_new_text();
|
||||||
|
sokoke_combo_box_add_strings(GTK_COMBO_BOX(combobox)
|
||||||
|
, "Left", "Top", "Right", "Bottom", NULL);
|
||||||
|
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 1); //...
|
||||||
|
gtk_widget_set_sensitive(combobox, FALSE); //...
|
||||||
|
FILLED_ADD(combobox, 1, 2, 1, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new_with_mnemonic("_Location search engine"), 0, 1, 2, 3);
|
||||||
|
entry = gtk_entry_new();
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry), config->locationSearch);
|
||||||
|
g_signal_connect(entry, "focus-out-event"
|
||||||
|
, G_CALLBACK(on_prefs_locationsearch_focus_out), prefs);
|
||||||
|
FILLED_ADD(entry, 1, 2, 2, 3);
|
||||||
|
|
||||||
|
// Page "Network"
|
||||||
|
PAGE_NEW("Network");
|
||||||
|
FRAME_NEW("Proxy Server");
|
||||||
|
TABLE_NEW(5, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("_Custom proxy server");
|
||||||
|
gtk_widget_set_sensitive(checkbutton, FALSE); //...
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 0, 1);
|
||||||
|
hbox = gtk_hbox_new(FALSE, 4);
|
||||||
|
INDENTED_ADD(gtk_label_new_with_mnemonic("_Host/ Port"), 0, 1, 1, 2);
|
||||||
|
entry = gtk_entry_new();
|
||||||
|
gtk_widget_set_sensitive(entry, FALSE); //...
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
|
||||||
|
spinbutton = gtk_spin_button_new_with_range(0, 65535, 1);
|
||||||
|
gtk_widget_set_sensitive(spinbutton, FALSE); //...
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), spinbutton, FALSE, FALSE, 0);
|
||||||
|
FILLED_ADD(hbox, 1, 2, 1, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic
|
||||||
|
("Proxy requires authentication");
|
||||||
|
gtk_widget_set_sensitive(checkbutton, FALSE); //...
|
||||||
|
// TODO: The proxy user and pass need to be indented further
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 2, 3);
|
||||||
|
INDENTED_ADD(gtk_label_new("Username"), 0, 1, 3, 4);
|
||||||
|
entry = gtk_entry_new();
|
||||||
|
gtk_widget_set_sensitive(entry, FALSE); //...
|
||||||
|
FILLED_ADD(entry, 1, 2, 3, 4);
|
||||||
|
INDENTED_ADD(gtk_label_new("Password"), 0, 1, 4, 5);
|
||||||
|
entry = gtk_entry_new();
|
||||||
|
gtk_widget_set_sensitive(entry, FALSE); //...
|
||||||
|
FILLED_ADD(entry, 1, 2, 4, 5);
|
||||||
|
FRAME_NEW("Cache");
|
||||||
|
TABLE_NEW(1, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new("Cache size"), 0, 1, 0, 1);
|
||||||
|
hbox = gtk_hbox_new(FALSE, 4);
|
||||||
|
spinbutton = gtk_spin_button_new_with_range(0, 10000, 10);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton), 100/*config->iCacheSize*/);
|
||||||
|
gtk_widget_set_sensitive(spinbutton, FALSE); //...
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), spinbutton, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("MB"), FALSE, FALSE, 0);
|
||||||
|
button = gtk_button_new_with_label("Clear cache");
|
||||||
|
gtk_widget_set_sensitive(button, FALSE); //...
|
||||||
|
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 4);
|
||||||
|
FILLED_ADD(hbox, 1, 2, 0, 1);
|
||||||
|
|
||||||
|
// Page "Privacy"
|
||||||
|
PAGE_NEW("Privacy");
|
||||||
|
FRAME_NEW("Cookies");
|
||||||
|
TABLE_NEW(3, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new("Accept cookies"), 0, 1, 0, 1);
|
||||||
|
combobox = gtk_combo_box_new_text();
|
||||||
|
sokoke_combo_box_add_strings(GTK_COMBO_BOX(combobox)
|
||||||
|
, "All cookies", "Session cookies", "None", NULL);
|
||||||
|
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); //...
|
||||||
|
gtk_widget_set_sensitive(combobox, FALSE); //...
|
||||||
|
FILLED_ADD(combobox, 1, 2, 0, 1);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic
|
||||||
|
("Allow cookies from the original website only");
|
||||||
|
gtk_widget_set_sensitive(checkbutton, FALSE); //...
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 1, 2);
|
||||||
|
INDENTED_ADD(gtk_label_new("Maximum cookie age"), 0, 1, 2, 3);
|
||||||
|
hbox = gtk_hbox_new(FALSE, 4);
|
||||||
|
spinbutton = gtk_spin_button_new_with_range(0, 360, 1);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton), 30/*config->iCookieAgeMax*/);
|
||||||
|
gtk_widget_set_sensitive(spinbutton, FALSE); //...
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), spinbutton, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("days"), FALSE, FALSE, 0);
|
||||||
|
button = gtk_button_new_with_label("View cookies");
|
||||||
|
gtk_widget_set_sensitive(button, FALSE); //...
|
||||||
|
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 4);
|
||||||
|
FILLED_ADD(hbox, 1, 2, 2, 3);
|
||||||
|
FRAME_NEW("History");
|
||||||
|
TABLE_NEW(3, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic("Remember my visited pages");
|
||||||
|
gtk_widget_set_sensitive(checkbutton, FALSE); //...
|
||||||
|
SPANNED_ADD(checkbutton, 0, 1, 0, 1);
|
||||||
|
hbox = gtk_hbox_new(FALSE, 4);
|
||||||
|
spinbutton = gtk_spin_button_new_with_range(0, 360, 1);
|
||||||
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton), 30/*config->iHistoryAgeMax*/);
|
||||||
|
gtk_widget_set_sensitive(spinbutton, FALSE); //...
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), spinbutton, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("days"), FALSE, FALSE, 0);
|
||||||
|
SPANNED_ADD(hbox, 1, 2, 0, 1);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic
|
||||||
|
("Remember my form inputs");
|
||||||
|
gtk_widget_set_sensitive(checkbutton, FALSE); //...
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 1, 2);
|
||||||
|
checkbutton = gtk_check_button_new_with_mnemonic
|
||||||
|
("Remember my downloaded files");
|
||||||
|
gtk_widget_set_sensitive(checkbutton, FALSE); //...
|
||||||
|
SPANNED_ADD(checkbutton, 0, 2, 2, 3);
|
||||||
|
|
||||||
|
// Page "Programs"
|
||||||
|
PAGE_NEW("Programs");
|
||||||
|
FRAME_NEW("External programs");
|
||||||
|
TABLE_NEW(3, 2);
|
||||||
|
GtkWidget* treeview; GtkTreeViewColumn* column;
|
||||||
|
GtkCellRenderer* renderer_text; GtkCellRenderer* renderer_pixbuf;
|
||||||
|
GtkListStore* liststore = gtk_list_store_new(PROTOCOLS_COL_N
|
||||||
|
, G_TYPE_STRING, G_TYPE_STRING);
|
||||||
|
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore));
|
||||||
|
prefs->treeview = treeview;
|
||||||
|
renderer_text = gtk_cell_renderer_text_new();
|
||||||
|
renderer_pixbuf = gtk_cell_renderer_pixbuf_new();
|
||||||
|
column = gtk_tree_view_column_new_with_attributes(
|
||||||
|
"Protocol", renderer_text, "text", PROTOCOLS_COL_NAME, NULL);
|
||||||
|
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
|
||||||
|
column = gtk_tree_view_column_new();
|
||||||
|
gtk_tree_view_column_set_title(column, "Command");
|
||||||
|
gtk_tree_view_column_pack_start(column, renderer_pixbuf, FALSE);
|
||||||
|
gtk_tree_view_column_set_cell_data_func(column, renderer_pixbuf
|
||||||
|
, (GtkTreeCellDataFunc)on_prefs_protocols_render_icon, prefs, NULL);
|
||||||
|
renderer_text = gtk_cell_renderer_text_new();
|
||||||
|
g_object_set(G_OBJECT(renderer_text), "editable", TRUE, NULL);
|
||||||
|
g_signal_connect(GTK_OBJECT(renderer_text), "edited"
|
||||||
|
, G_CALLBACK(on_prefs_protocols_edited), prefs);
|
||||||
|
gtk_tree_view_column_pack_start(column, renderer_text, TRUE);
|
||||||
|
gtk_tree_view_column_add_attribute(column, renderer_text, "text", PROTOCOLS_COL_COMMAND);
|
||||||
|
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
|
||||||
|
GtkWidget* scrolled = gtk_scrolled_window_new(NULL, NULL);
|
||||||
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled)
|
||||||
|
, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||||
|
gtk_container_add(GTK_CONTAINER(scrolled), treeview);
|
||||||
|
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
|
||||||
|
hbox = gtk_hbox_new(FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 5);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < config->protocols_names->len; i++)
|
||||||
|
{
|
||||||
|
gchar* protocol = (gchar*)g_ptr_array_index(config->protocols_names, i);
|
||||||
|
// TODO: We might want to determine 'default' programs somehow
|
||||||
|
// TODO: Any way to make it easier to add eg. only a mail client? O_o
|
||||||
|
const gchar* command = g_datalist_get_data(&config->protocols_commands, protocol);
|
||||||
|
gtk_list_store_insert_with_values(GTK_LIST_STORE(liststore), NULL, i
|
||||||
|
, PROTOCOLS_COL_NAME , protocol
|
||||||
|
, PROTOCOLS_COL_COMMAND, command
|
||||||
|
, -1);
|
||||||
|
}
|
||||||
|
g_object_unref(liststore);
|
||||||
|
GtkWidget* vbox = gtk_vbox_new(FALSE, 4);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 4);
|
||||||
|
combobox = gtk_combo_box_new_text();
|
||||||
|
prefs->combobox = combobox;
|
||||||
|
sokoke_combo_box_add_strings(GTK_COMBO_BOX(combobox)
|
||||||
|
, "download", "ed2k", "feed", "ftp", "irc", "mailto"
|
||||||
|
, "news", "tel", "torrent", "view-source", NULL);
|
||||||
|
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 1);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), combobox, FALSE, FALSE, 0);
|
||||||
|
button = gtk_button_new_from_stock(GTK_STOCK_ADD);
|
||||||
|
prefs->add = button;
|
||||||
|
g_signal_connect(combobox, "changed"
|
||||||
|
, G_CALLBACK(on_prefs_protocols_combobox_changed), prefs);
|
||||||
|
g_signal_connect(button, "clicked"
|
||||||
|
, G_CALLBACK(on_prefs_protocols_add_clicked), prefs);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
|
||||||
|
button = gtk_label_new(""); // This is an invisible separator
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 12);
|
||||||
|
button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
|
||||||
|
gtk_widget_set_sensitive(button, FALSE); //...
|
||||||
|
gtk_box_pack_end(GTK_BOX(vbox), button, FALSE, FALSE, 0);
|
||||||
|
FILLED_ADD(hbox, 0, 2, 0, 2);
|
||||||
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox)
|
||||||
|
, notebook, FALSE, FALSE, 4);
|
||||||
|
gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
|
||||||
|
return dialog;
|
||||||
|
}
|
42
src/prefs.h
Normal file
42
src/prefs.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PREFS_H__
|
||||||
|
#define __PREFS_H__ 1
|
||||||
|
|
||||||
|
#include "browser.h"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
// -- Types
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
CBrowser* browser;
|
||||||
|
//GtkWidget* window;
|
||||||
|
GtkWidget* treeview;
|
||||||
|
GtkWidget* combobox;
|
||||||
|
GtkWidget* add;
|
||||||
|
} CPrefs;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROTOCOLS_COL_NAME,
|
||||||
|
PROTOCOLS_COL_COMMAND,
|
||||||
|
PROTOCOLS_COL_N
|
||||||
|
};
|
||||||
|
|
||||||
|
// -- Declarations
|
||||||
|
|
||||||
|
GtkWidget*
|
||||||
|
prefs_preferences_dialog_new(CBrowser*);
|
||||||
|
|
||||||
|
#endif /* !__PREFS_H__ */
|
209
src/search.c
Normal file
209
src/search.c
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "search.h"
|
||||||
|
|
||||||
|
#include "sokoke.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
GList* search_engines_new(void)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_engines_free(GList* searchEngines)
|
||||||
|
{
|
||||||
|
g_list_foreach(searchEngines, (GFunc)search_engine_free, NULL);
|
||||||
|
g_list_free(searchEngines);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean search_engines_from_file(GList** searchEngines, const gchar* filename
|
||||||
|
, GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(!g_list_nth(*searchEngines, 0), FALSE);
|
||||||
|
GKeyFile* keyFile = g_key_file_new();
|
||||||
|
g_key_file_load_from_file(keyFile, filename, G_KEY_FILE_KEEP_COMMENTS, error);
|
||||||
|
/*g_key_file_load_from_data_dirs(keyFile, sFilename, NULL
|
||||||
|
, G_KEY_FILE_KEEP_COMMENTS, error);*/
|
||||||
|
gchar** engines = g_key_file_get_groups(keyFile, NULL);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; engines[i] != NULL; i++)
|
||||||
|
{
|
||||||
|
SearchEngine* engine = search_engine_new();
|
||||||
|
search_engine_set_short_name(engine, engines[i]);
|
||||||
|
engine->description = g_key_file_get_string(keyFile, engines[i], "description", NULL);
|
||||||
|
engine->url = g_key_file_get_string(keyFile, engines[i], "url", NULL);
|
||||||
|
engine->inputEncoding = g_key_file_get_string(keyFile, engines[i], "input-encoding", NULL);
|
||||||
|
engine->icon = g_key_file_get_string(keyFile, engines[i], "icon", NULL);
|
||||||
|
engine->keyword = g_key_file_get_string(keyFile, engines[i], "keyword", NULL);
|
||||||
|
*searchEngines = g_list_prepend(*searchEngines, engine);
|
||||||
|
}
|
||||||
|
*searchEngines = g_list_reverse(*searchEngines);
|
||||||
|
g_strfreev(engines);
|
||||||
|
g_key_file_free(keyFile);
|
||||||
|
return !(error && *error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void key_file_set_string(GKeyFile* keyFile, const gchar* group
|
||||||
|
, const gchar* key, const gchar* string)
|
||||||
|
{
|
||||||
|
g_return_if_fail(group);
|
||||||
|
if(string)
|
||||||
|
g_key_file_set_string(keyFile, group, key, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean search_engines_to_file(GList* searchEngines, const gchar* filename
|
||||||
|
, GError** error)
|
||||||
|
{
|
||||||
|
GKeyFile* keyFile = g_key_file_new();
|
||||||
|
guint n = g_list_length(searchEngines);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
SearchEngine* engine = (SearchEngine*)g_list_nth_data(searchEngines, i);
|
||||||
|
const gchar* name = search_engine_get_short_name(engine);
|
||||||
|
key_file_set_string(keyFile, name, "description", engine->description);
|
||||||
|
key_file_set_string(keyFile, name, "url", engine->url);
|
||||||
|
key_file_set_string(keyFile, name, "input-encoding", engine->inputEncoding);
|
||||||
|
key_file_set_string(keyFile, name, "icon", engine->icon);
|
||||||
|
key_file_set_string(keyFile, name, "keyword", engine->keyword);
|
||||||
|
}
|
||||||
|
gboolean bSaved = sokoke_key_file_save_to_file(keyFile, filename, error);
|
||||||
|
g_key_file_free(keyFile);
|
||||||
|
|
||||||
|
return bSaved;
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchEngine* search_engine_new()
|
||||||
|
{
|
||||||
|
SearchEngine* engine = g_new(SearchEngine, 1);
|
||||||
|
engine->shortName = g_strdup("");
|
||||||
|
engine->description = NULL;
|
||||||
|
engine->url = NULL;
|
||||||
|
engine->inputEncoding = NULL;
|
||||||
|
engine->icon = NULL;
|
||||||
|
engine->keyword = NULL;
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_engine_free(SearchEngine* engine)
|
||||||
|
{
|
||||||
|
g_return_if_fail(engine);
|
||||||
|
g_free(engine->shortName);
|
||||||
|
g_free(engine->description);
|
||||||
|
g_free(engine->url);
|
||||||
|
g_free(engine->inputEncoding);
|
||||||
|
g_free(engine->icon);
|
||||||
|
g_free(engine->keyword);
|
||||||
|
g_free(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchEngine* search_engine_copy(SearchEngine* engine)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(engine, NULL);
|
||||||
|
SearchEngine* copy = search_engine_new();
|
||||||
|
search_engine_set_short_name(copy, engine->shortName);
|
||||||
|
search_engine_set_description(copy, engine->description);
|
||||||
|
search_engine_set_url(copy, engine->url);
|
||||||
|
search_engine_set_input_encoding(copy, engine->inputEncoding);
|
||||||
|
search_engine_set_icon(copy, engine->icon);
|
||||||
|
search_engine_set_keyword(copy, engine->keyword);
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
GType search_engine_get_type()
|
||||||
|
{
|
||||||
|
static GType type = 0;
|
||||||
|
if(!type)
|
||||||
|
type = g_pointer_type_register_static("search_engine");
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar* search_engine_get_short_name(SearchEngine* engine)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(engine, NULL);
|
||||||
|
return engine->shortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar* search_engine_get_description(SearchEngine* engine)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(engine, NULL);
|
||||||
|
return engine->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar* search_engine_get_url(SearchEngine* engine)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(engine, NULL);
|
||||||
|
return engine->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar* search_engine_get_input_encoding(SearchEngine* engine)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(engine, NULL);
|
||||||
|
return engine->inputEncoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar* search_engine_get_icon(SearchEngine* engine)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(engine, NULL);
|
||||||
|
return engine->icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar* search_engine_get_keyword(SearchEngine* engine)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(engine, NULL);
|
||||||
|
return engine->keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_engine_set_short_name(SearchEngine* engine, const gchar* shortName)
|
||||||
|
{
|
||||||
|
g_return_if_fail(engine);
|
||||||
|
g_return_if_fail(shortName);
|
||||||
|
g_free(engine->shortName);
|
||||||
|
engine->shortName = g_strdup(shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_engine_set_description(SearchEngine* engine, const gchar* description)
|
||||||
|
{
|
||||||
|
g_return_if_fail(engine);
|
||||||
|
g_free(engine->description);
|
||||||
|
engine->description = g_strdup(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_engine_set_url(SearchEngine* engine, const gchar* url)
|
||||||
|
{
|
||||||
|
g_return_if_fail(engine);
|
||||||
|
g_free(engine->url);
|
||||||
|
engine->url = g_strdup(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_engine_set_input_encoding(SearchEngine* engine, const gchar* inputEncoding)
|
||||||
|
{
|
||||||
|
g_return_if_fail(engine);
|
||||||
|
g_free(engine->inputEncoding);
|
||||||
|
engine->inputEncoding = g_strdup(inputEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_engine_set_icon(SearchEngine* engine, const gchar* icon)
|
||||||
|
{
|
||||||
|
g_return_if_fail(engine);
|
||||||
|
g_free(engine->icon);
|
||||||
|
engine->icon = g_strdup(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_engine_set_keyword(SearchEngine* engine, const gchar* keyword)
|
||||||
|
{
|
||||||
|
g_return_if_fail(engine);
|
||||||
|
g_free(engine->keyword);
|
||||||
|
engine->keyword = g_strdup(keyword);
|
||||||
|
}
|
91
src/search.h
Normal file
91
src/search.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SEARCH_H__
|
||||||
|
#define __SEARCH_H__ 1
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
// Note: This structure is entirely private.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gchar* shortName;
|
||||||
|
gchar* description;
|
||||||
|
gchar* url;
|
||||||
|
gchar* inputEncoding;
|
||||||
|
gchar* icon;
|
||||||
|
gchar* keyword;
|
||||||
|
} SearchEngine;
|
||||||
|
|
||||||
|
GList*
|
||||||
|
search_engines_new(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
search_engines_free(GList*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
search_engines_from_file(GList**, const gchar*, GError**);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
search_engines_to_file(GList*, const gchar*, GError**);
|
||||||
|
|
||||||
|
SearchEngine*
|
||||||
|
search_engine_new(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
search_engine_free(SearchEngine*);
|
||||||
|
|
||||||
|
SearchEngine*
|
||||||
|
search_engine_copy(SearchEngine*);
|
||||||
|
|
||||||
|
GType
|
||||||
|
search_engine_get_type();
|
||||||
|
|
||||||
|
#define G_TYPE_SEARCH_ENGINE search_engine_get_type()
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
search_engine_get_short_name(SearchEngine*);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
search_engine_get_description(SearchEngine*);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
search_engine_get_url(SearchEngine*);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
search_engine_get_input_encoding(SearchEngine*);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
search_engine_get_icon(SearchEngine*);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
search_engine_get_keyword(SearchEngine*);
|
||||||
|
|
||||||
|
void
|
||||||
|
search_engine_set_short_name(SearchEngine*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
search_engine_set_description(SearchEngine*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
search_engine_set_url(SearchEngine*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
search_engine_set_input_encoding(SearchEngine*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
search_engine_set_icon(SearchEngine*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
search_engine_set_keyword(SearchEngine*, const gchar*);
|
||||||
|
|
||||||
|
#endif /* !__SEARCH_H__ */
|
381
src/sokoke.c
Normal file
381
src/sokoke.c
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sokoke.h"
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include <gdk/gdkkeysyms.h>
|
||||||
|
|
||||||
|
void sokoke_combo_box_add_strings(GtkComboBox* combobox
|
||||||
|
, const gchar* sLabelFirst, ...)
|
||||||
|
{
|
||||||
|
// Add a number of strings to a combobox, terminated with NULL
|
||||||
|
// This works only for text comboboxes
|
||||||
|
va_list args;
|
||||||
|
va_start(args, sLabelFirst);
|
||||||
|
|
||||||
|
const gchar* sLabel;
|
||||||
|
for(sLabel = sLabelFirst; sLabel; sLabel = va_arg(args, const gchar*))
|
||||||
|
gtk_combo_box_append_text(combobox, sLabel);
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_radio_action_set_current_value(GtkRadioAction* action
|
||||||
|
, gint current_value)
|
||||||
|
{
|
||||||
|
// Activates the group member with the given value
|
||||||
|
#if GTK_CHECK_VERSION(2, 10, 0)
|
||||||
|
gtk_radio_action_set_current_value(action, current_value);
|
||||||
|
#else
|
||||||
|
// TODO: Implement this for older gtk
|
||||||
|
UNIMPLEMENTED
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_widget_set_visible(GtkWidget* widget, gboolean bVisibility)
|
||||||
|
{
|
||||||
|
// Show or hide the widget
|
||||||
|
if(bVisibility)
|
||||||
|
gtk_widget_show(widget);
|
||||||
|
else
|
||||||
|
gtk_widget_hide(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_container_show_children(GtkContainer* container)
|
||||||
|
{
|
||||||
|
// Show every child but not the container itself
|
||||||
|
gtk_container_foreach(container, (GtkCallback)(gtk_widget_show_all), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_widget_set_tooltip_text(GtkWidget* widget, const gchar* sText)
|
||||||
|
{
|
||||||
|
#if GTK_CHECK_VERSION(2, 12, 0)
|
||||||
|
gtk_widget_set_tooltip_text(widget, sText);
|
||||||
|
#else
|
||||||
|
static GtkTooltips* tooltips;
|
||||||
|
if(!tooltips)
|
||||||
|
tooltips = gtk_tooltips_new();
|
||||||
|
gtk_tooltips_set_tip(tooltips, widget, sText, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_tool_item_set_tooltip_text(GtkToolItem* toolitem, const gchar* sText)
|
||||||
|
{
|
||||||
|
// TODO: Use 2.12 api if available
|
||||||
|
GtkTooltips* tooltips = gtk_tooltips_new();
|
||||||
|
gtk_tool_item_set_tooltip(toolitem, tooltips, sText, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_widget_popup(GtkWidget* widget, GtkMenu* menu
|
||||||
|
, GdkEventButton* event)
|
||||||
|
{
|
||||||
|
// TODO: Provide a GtkMenuPositionFunc in case a keyboard invoked this
|
||||||
|
int button, event_time;
|
||||||
|
if(event)
|
||||||
|
{
|
||||||
|
button = event->button;
|
||||||
|
event_time = event->time;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
button = 0;
|
||||||
|
event_time = gtk_get_current_event_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!gtk_menu_get_attach_widget(menu))
|
||||||
|
gtk_menu_attach_to_widget(menu, widget, NULL);
|
||||||
|
gtk_menu_popup(menu, NULL, NULL, NULL, NULL, button, event_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SOKOKE_DESKTOP_UNKNOWN,
|
||||||
|
SOKOKE_DESKTOP_XFCE
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint sokoke_get_desktop(void)
|
||||||
|
{
|
||||||
|
// Are we running in Xfce?
|
||||||
|
gint iResult; gchar* stdout; gchar* stderr;
|
||||||
|
gboolean bSuccess = g_spawn_command_line_sync(
|
||||||
|
"xprop -root _DT_SAVE_MODE | grep -q xfce4"
|
||||||
|
, &stdout, &stderr, &iResult, NULL);
|
||||||
|
if(bSuccess && !iResult)
|
||||||
|
return SOKOKE_DESKTOP_XFCE;
|
||||||
|
|
||||||
|
return SOKOKE_DESKTOP_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpointer sokoke_xfce_header_new(const gchar* sIcon, const gchar* sTitle)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Create an xfce header with icon and title
|
||||||
|
// This returns NULL if the desktop is not xfce
|
||||||
|
if(sokoke_get_desktop() == SOKOKE_DESKTOP_XFCE)
|
||||||
|
{
|
||||||
|
GtkWidget* entry = gtk_entry_new();
|
||||||
|
gchar* sMarkup;
|
||||||
|
GtkWidget* xfce_heading = gtk_event_box_new();
|
||||||
|
gtk_widget_modify_bg(xfce_heading, GTK_STATE_NORMAL
|
||||||
|
, &entry->style->base[GTK_STATE_NORMAL]);
|
||||||
|
GtkWidget* hbox = gtk_hbox_new(FALSE, 12);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 6);
|
||||||
|
GtkWidget* icon = gtk_image_new_from_icon_name(sIcon, GTK_ICON_SIZE_DIALOG);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0);
|
||||||
|
GtkWidget* label = gtk_label_new(NULL);
|
||||||
|
gtk_widget_modify_fg(label, GTK_STATE_NORMAL
|
||||||
|
, &entry->style->text[GTK_STATE_NORMAL]);
|
||||||
|
sMarkup = g_strdup_printf("<span size='large' weight='bold'>%s</span>", sTitle);
|
||||||
|
gtk_label_set_markup(GTK_LABEL(label), sMarkup);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
|
||||||
|
gtk_container_add(GTK_CONTAINER(xfce_heading), hbox);
|
||||||
|
g_free(sMarkup);
|
||||||
|
return xfce_heading;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpointer sokoke_superuser_warning_new(void)
|
||||||
|
{
|
||||||
|
// Create a horizontal bar with a security warning
|
||||||
|
// This returns NULL if the user is no superuser
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
if(G_UNLIKELY(!geteuid())) // effective superuser?
|
||||||
|
{
|
||||||
|
GtkWidget* hbox = gtk_event_box_new();
|
||||||
|
gtk_widget_modify_bg(hbox, GTK_STATE_NORMAL
|
||||||
|
, &hbox->style->bg[GTK_STATE_SELECTED]);
|
||||||
|
GtkWidget* label = gtk_label_new("Warning: You are using the superuser account!");
|
||||||
|
gtk_misc_set_padding(GTK_MISC(label), 0, 2);
|
||||||
|
gtk_widget_modify_fg(GTK_WIDGET(label), GTK_STATE_NORMAL
|
||||||
|
, >K_WIDGET(label)->style->fg[GTK_STATE_SELECTED]);
|
||||||
|
gtk_container_add(GTK_CONTAINER(hbox), GTK_WIDGET(label));
|
||||||
|
return hbox;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* sokoke_hig_frame_new(const gchar* sLabel)
|
||||||
|
{
|
||||||
|
// Create a frame with no actual frame but a bold label and indentation
|
||||||
|
GtkWidget* frame = gtk_frame_new(NULL);
|
||||||
|
gchar* sLabelBold = g_strdup_printf("<b>%s</b>", sLabel);
|
||||||
|
GtkWidget* label = gtk_label_new(NULL);
|
||||||
|
gtk_label_set_markup(GTK_LABEL(label), sLabelBold);
|
||||||
|
g_free(sLabelBold);
|
||||||
|
gtk_frame_set_label_widget(GTK_FRAME(frame), label);
|
||||||
|
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_widget_set_pango_font_style(GtkWidget* widget, PangoStyle style)
|
||||||
|
{
|
||||||
|
// Conveniently change the pango font style
|
||||||
|
// For some reason we need to reset if we actually want the normal style
|
||||||
|
if(style == PANGO_STYLE_NORMAL)
|
||||||
|
gtk_widget_modify_font(widget, NULL);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PangoFontDescription* pangofontdesc = pango_font_description_new();
|
||||||
|
pango_font_description_set_style(pangofontdesc, PANGO_STYLE_ITALIC);
|
||||||
|
gtk_widget_modify_font(widget, pangofontdesc);
|
||||||
|
pango_font_description_free(pangofontdesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean sokoke_on_entry_focus_in_event(GtkEntry* entry, GdkEventFocus *event
|
||||||
|
, gpointer userdata)
|
||||||
|
{
|
||||||
|
gboolean bDefaultText = (gboolean)g_object_get_data(G_OBJECT(entry)
|
||||||
|
, "sokoke_bDefaultText");
|
||||||
|
if(bDefaultText)
|
||||||
|
{
|
||||||
|
gtk_entry_set_text(entry, "");
|
||||||
|
g_object_set_data(G_OBJECT(entry), "sokoke_bDefaultText", (gpointer)FALSE);
|
||||||
|
sokoke_widget_set_pango_font_style(GTK_WIDGET(entry), PANGO_STYLE_NORMAL);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean sokoke_on_entry_focus_out_event(GtkEntry* entry, GdkEventFocus* event
|
||||||
|
, gpointer userdata)
|
||||||
|
{
|
||||||
|
const gchar* sText = gtk_entry_get_text(entry);
|
||||||
|
if(sText[0] == '\0')
|
||||||
|
{
|
||||||
|
const gchar* sDefaultText = (const gchar*)g_object_get_data(
|
||||||
|
G_OBJECT(entry), "sokoke_sDefaultText");
|
||||||
|
gtk_entry_set_text(entry, sDefaultText);
|
||||||
|
g_object_set_data(G_OBJECT(entry), "sokoke_bDefaultText", (gpointer)TRUE);
|
||||||
|
sokoke_widget_set_pango_font_style(GTK_WIDGET(entry), PANGO_STYLE_ITALIC);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_entry_set_default_text(GtkEntry* entry, const gchar* sDefaultText)
|
||||||
|
{
|
||||||
|
// Note: The default text initially overwrites any previous text
|
||||||
|
gchar* sOldValue = g_object_get_data(G_OBJECT(entry), "sokoke_sDefaultText");
|
||||||
|
if(!sOldValue)
|
||||||
|
{
|
||||||
|
g_object_set_data(G_OBJECT(entry), "sokoke_bDefaultText", (gpointer)TRUE);
|
||||||
|
sokoke_widget_set_pango_font_style(GTK_WIDGET(entry), PANGO_STYLE_ITALIC);
|
||||||
|
gtk_entry_set_text(entry, sDefaultText);
|
||||||
|
}
|
||||||
|
g_object_set_data(G_OBJECT(entry), "sokoke_sDefaultText", (gpointer)sDefaultText);
|
||||||
|
g_signal_connect(entry, "focus-in-event"
|
||||||
|
, G_CALLBACK(sokoke_on_entry_focus_in_event), NULL);
|
||||||
|
g_signal_connect(entry, "focus-out-event"
|
||||||
|
, G_CALLBACK(sokoke_on_entry_focus_out_event), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar* sokoke_key_file_get_string_default(GKeyFile* key_file
|
||||||
|
, const gchar* group_name, const gchar* key, const gchar* def, GError* *error)
|
||||||
|
{
|
||||||
|
gchar* value = g_key_file_get_string(key_file, group_name, key, error);
|
||||||
|
return value == NULL ? g_strdup(def) : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
gint sokoke_key_file_get_integer_default(GKeyFile* key_file
|
||||||
|
, const gchar* group_name, const gchar* key, const gint def, GError** error)
|
||||||
|
{
|
||||||
|
if(!g_key_file_has_key(key_file, group_name, key, NULL))
|
||||||
|
return def;
|
||||||
|
return g_key_file_get_integer(key_file, group_name, key, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean sokoke_key_file_save_to_file(GKeyFile* key_file
|
||||||
|
, const gchar* sFilename, GError** error)
|
||||||
|
{
|
||||||
|
gchar* sData = g_key_file_to_data(key_file, NULL, error);
|
||||||
|
if(!sData)
|
||||||
|
return FALSE;
|
||||||
|
FILE* fp;
|
||||||
|
if(!(fp = fopen(sFilename, "w")))
|
||||||
|
{
|
||||||
|
*error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_ACCES
|
||||||
|
, "Writing failed.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
fputs(sData, fp);
|
||||||
|
fclose(fp);
|
||||||
|
g_free(sData);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_widget_get_text_size(GtkWidget* widget, const gchar* sText
|
||||||
|
, gint* w, gint* h)
|
||||||
|
{
|
||||||
|
PangoLayout* layout = gtk_widget_create_pango_layout(widget, sText);
|
||||||
|
pango_layout_get_pixel_size(layout, w, h);
|
||||||
|
g_object_unref(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_menu_item_set_accel(GtkMenuItem* menuitem, const gchar* sPath
|
||||||
|
, const gchar* sKey, GdkModifierType accel_mods)
|
||||||
|
{
|
||||||
|
if(sPath && *sPath)
|
||||||
|
{
|
||||||
|
gchar* path = g_strconcat("<", g_get_prgname(), ">/", sPath, NULL);
|
||||||
|
gtk_menu_item_set_accel_path(GTK_MENU_ITEM(menuitem), path);
|
||||||
|
guint keyVal = sKey ? gdk_keyval_from_name(sKey) : 0;
|
||||||
|
gtk_accel_map_add_entry(path, keyVal, accel_mods);
|
||||||
|
g_free(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean sokoke_entry_can_undo(GtkEntry* entry)
|
||||||
|
{
|
||||||
|
// TODO: Can we undo the last input?
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean sokoke_entry_can_redo(GtkEntry* entry)
|
||||||
|
{
|
||||||
|
// TODO: Can we redo the last input?
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_entry_undo(GtkEntry* entry)
|
||||||
|
{
|
||||||
|
// TODO: Implement undo
|
||||||
|
UNIMPLEMENTED
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_entry_redo(GtkEntry* entry)
|
||||||
|
{
|
||||||
|
// TODO: Implement redo
|
||||||
|
UNIMPLEMENTED
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean sokoke_on_undo_entry_key_down(GtkEntry* widget, GdkEventKey* event
|
||||||
|
, gpointer userdata)
|
||||||
|
{
|
||||||
|
switch(event->keyval)
|
||||||
|
{
|
||||||
|
case GDK_Undo:
|
||||||
|
sokoke_entry_undo(widget);
|
||||||
|
return FALSE;
|
||||||
|
case GDK_Redo:
|
||||||
|
sokoke_entry_redo(widget);
|
||||||
|
return FALSE;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sokoke_on_undo_entry_populate_popup(GtkEntry* entry, GtkMenu* menu
|
||||||
|
, gpointer userdata)
|
||||||
|
{
|
||||||
|
// Enhance the entry's menu with undo and redo items.
|
||||||
|
GtkWidget* menuitem = gtk_separator_menu_item_new();
|
||||||
|
gtk_menu_shell_prepend((GtkMenuShell*)menu, menuitem);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_REDO, NULL);
|
||||||
|
g_signal_connect(menuitem, "activate", G_CALLBACK(sokoke_entry_redo), userdata);
|
||||||
|
gtk_widget_set_sensitive(menuitem, sokoke_entry_can_redo(entry));
|
||||||
|
gtk_menu_shell_prepend((GtkMenuShell*)menu, menuitem);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_UNDO, NULL);
|
||||||
|
g_signal_connect(menuitem, "activate", G_CALLBACK(sokoke_entry_undo), userdata);
|
||||||
|
gtk_widget_set_sensitive(menuitem, sokoke_entry_can_undo(entry));
|
||||||
|
gtk_menu_shell_prepend((GtkMenuShell*)menu, menuitem);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean sokoke_entry_get_can_undo(GtkEntry* entry)
|
||||||
|
{
|
||||||
|
// TODO: Is this entry undo enabled?
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sokoke_entry_set_can_undo(GtkEntry* entry, gboolean bCanUndo)
|
||||||
|
{
|
||||||
|
if(bCanUndo)
|
||||||
|
{
|
||||||
|
g_signal_connect(entry, "key-press-event"
|
||||||
|
, G_CALLBACK(sokoke_on_undo_entry_key_down), NULL);
|
||||||
|
g_signal_connect(entry, "populate-popup"
|
||||||
|
, G_CALLBACK(sokoke_on_undo_entry_populate_popup), NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
; // TODO: disconnect signal
|
||||||
|
UNIMPLEMENTED
|
||||||
|
}
|
||||||
|
}
|
94
src/sokoke.h
Normal file
94
src/sokoke.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SOKOKE_H__
|
||||||
|
#define __SOKOKE_H__ 1
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
// Many themes need this hack for small toolbars to work
|
||||||
|
#define GTK_ICON_SIZE_SMALL_TOOLBAR GTK_ICON_SIZE_BUTTON
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_combo_box_add_strings(GtkComboBox* combobox, const gchar* sLabelFirst, ...);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_radio_action_set_current_value(GtkRadioAction* action, gint current_value);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_widget_set_visible(GtkWidget* widget, gboolean bVisibility);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_container_show_children(GtkContainer* container);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_widget_set_tooltip_text(GtkWidget* widget, const gchar* sText);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_tool_item_set_tooltip_text(GtkToolItem* toolitem, const gchar* sText);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_widget_popup(GtkWidget* widget, GtkMenu* menu, GdkEventButton* event);
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
sokoke_xfce_header_new(const gchar* sIcon, const gchar* sTitle);
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
sokoke_superuser_warning_new(void);
|
||||||
|
|
||||||
|
GtkWidget*
|
||||||
|
sokoke_hig_frame_new(const gchar* sLabel);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_widget_set_pango_font_style(GtkWidget* widget, PangoStyle style);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_entry_set_default_text(GtkEntry* entry, const gchar* sDefaultText);
|
||||||
|
|
||||||
|
gchar*
|
||||||
|
sokoke_key_file_get_string_default(GKeyFile* key_file
|
||||||
|
, const gchar* group_name, const gchar* key, const gchar* def, GError* *error);
|
||||||
|
|
||||||
|
gint
|
||||||
|
sokoke_key_file_get_integer_default(GKeyFile* key_file
|
||||||
|
, const gchar* group_name, const gchar* key, const gint def, GError* *error);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sokoke_key_file_save_to_file(GKeyFile* key_file
|
||||||
|
, const gchar* file, GError* *error);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_widget_get_text_size(GtkWidget* widget, const gchar* sText
|
||||||
|
, gint* w, gint* h);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_menu_item_set_accel(GtkMenuItem* menuitem, const gchar* sPath
|
||||||
|
, const gchar* sKey, GdkModifierType accel_mods);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sokoke_entry_can_undo(GtkEntry* entry);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sokoke_entry_can_redo(GtkEntry* entry);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_entry_undo(GtkEntry* entry);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_entry_redo(GtkEntry* entry);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sokoke_entry_get_can_undo(GtkEntry* entry);
|
||||||
|
|
||||||
|
void
|
||||||
|
sokoke_entry_set_can_undo(GtkEntry* entry, gboolean bCanUndo);
|
||||||
|
|
||||||
|
#endif /* !__SOKOKE_H__ */
|
245
src/ui.h
Normal file
245
src/ui.h
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __UI_H__
|
||||||
|
#define __UI_H__ 1
|
||||||
|
|
||||||
|
// -- Credits
|
||||||
|
|
||||||
|
static const gchar* credits_authors[] = { "Christian Dywan <christian@twotoasts.de>", NULL };
|
||||||
|
static const gchar* credits_documenters/*[]*/ = /*{ */NULL/* }*/;
|
||||||
|
static const gchar* credits_artists[] = { "Nancy Runge <nancy@twotoasts.de>", NULL };
|
||||||
|
|
||||||
|
// -- Licenses
|
||||||
|
|
||||||
|
static const gchar* license =
|
||||||
|
"This library is free software; you can redistribute it and/or\n"
|
||||||
|
"modify it under the terms of the GNU Lesser General Public\n"
|
||||||
|
"License as published by the Free Software Foundation; either\n"
|
||||||
|
"version 2.1 of the License, or (at your option) any later version.\n";
|
||||||
|
|
||||||
|
// -- User interface description
|
||||||
|
|
||||||
|
static const gchar* ui_markup =
|
||||||
|
"<ui>"
|
||||||
|
"<menubar>"
|
||||||
|
"<menu action='File'>"
|
||||||
|
"<menuitem action='WindowNew'/>"
|
||||||
|
"<menuitem action='TabNew'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='Open'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='SaveAs'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='TabClose'/>"
|
||||||
|
"<menuitem action='WindowClose'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='PageSetup'/>"
|
||||||
|
"<menuitem action='PrintPreview'/>"
|
||||||
|
"<menuitem action='Print'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='PrivateBrowsing'/>"
|
||||||
|
"<menuitem action='WorkOffline'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='Quit'/>"
|
||||||
|
"</menu>"
|
||||||
|
"<menu action='Edit'>"
|
||||||
|
"<menuitem action='Undo'/>"
|
||||||
|
"<menuitem action='Redo'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='Cut'/>"
|
||||||
|
"<menuitem action='Copy'/>"
|
||||||
|
"<menuitem action='Paste'/>"
|
||||||
|
"<menuitem action='Delete'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='SelectAll'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='ManageSearchEngines'/>"
|
||||||
|
"<menuitem action='Preferences'/>"
|
||||||
|
"</menu>"
|
||||||
|
"<menu action='View'>"
|
||||||
|
"<menu action='Toolbars'>"
|
||||||
|
"<menuitem action='ToolbarNavigation'/>"
|
||||||
|
"<menuitem action='ToolbarBookmarks'/>"
|
||||||
|
"<menuitem action='ToolbarDownloads'/>"
|
||||||
|
"<menuitem action='ToolbarStatus'/>"
|
||||||
|
"</menu>"
|
||||||
|
"<menuitem action='Panels'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='Refresh'/>"
|
||||||
|
"<menuitem action='Stop'/>"
|
||||||
|
"<menu action='RefreshEvery'>"
|
||||||
|
"<menuitem action='RefreshEveryEnable'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='RefreshEvery30'/>"
|
||||||
|
"<menuitem action='RefreshEvery60'/>"
|
||||||
|
"<menuitem action='RefreshEvery300'/>"
|
||||||
|
"<menuitem action='RefreshEvery900'/>"
|
||||||
|
"<menuitem action='RefreshEvery1800'/>"
|
||||||
|
"<menuitem action='RefreshEveryCustom'/>"
|
||||||
|
"</menu>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='ZoomIn'/>"
|
||||||
|
"<menuitem action='ZoomOut'/>"
|
||||||
|
"<menuitem action='ZoomNormal'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='BackgroundImage'/>"
|
||||||
|
"<menuitem action='SourceView'/>"
|
||||||
|
"<menuitem action='Properties'/>"
|
||||||
|
"</menu>"
|
||||||
|
"<menu action='Go'>"
|
||||||
|
"<menuitem action='Back'/>"
|
||||||
|
"<menuitem action='Forward'/>"
|
||||||
|
"<menuitem action='Home'/>"
|
||||||
|
"<menuitem action='Location'/>"
|
||||||
|
"<menuitem action='Websearch'/>"
|
||||||
|
"<menuitem action='OpenInPageholder'/>"
|
||||||
|
"<menu action='TabsClosed'>"
|
||||||
|
// Closed tabs shall be prepended here
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='TabsClosedClear'/>"
|
||||||
|
"</menu>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='Find'/>"
|
||||||
|
"<menuitem action='FindNext'/>"
|
||||||
|
"<menuitem action='FindPrevious'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='FormFill'/>"
|
||||||
|
"</menu>"
|
||||||
|
"<menu action='Bookmarks'>"
|
||||||
|
"<menuitem action='BookmarkNew'/>"
|
||||||
|
"<menuitem action='BookmarksManage'/>"
|
||||||
|
"<separator/>"
|
||||||
|
// Bookmarks shall be appended here
|
||||||
|
"</menu>"
|
||||||
|
"<menu action='Tools'>"
|
||||||
|
"<menuitem action='PanelDownloads'/>"
|
||||||
|
"<menuitem action='PanelBookmarks'/>"
|
||||||
|
"<menuitem action='PanelHistory'/>"
|
||||||
|
"<menuitem action='PanelTabs'/>"
|
||||||
|
"<menuitem action='PanelPageholder'/>"
|
||||||
|
"<menuitem action='PanelExtensions'/>"
|
||||||
|
"<menuitem action='PanelConsole'/>"
|
||||||
|
"<separator/>"
|
||||||
|
// TODO: Insert widgets and custom tools here
|
||||||
|
"</menu>"
|
||||||
|
"<menu action='Window'>"
|
||||||
|
"<menuitem action='SessionLoad'/>"
|
||||||
|
"<menuitem action='SessionSave'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='TabPrevious'/>"
|
||||||
|
"<menuitem action='TabNext'/>"
|
||||||
|
"<menuitem action='TabOverview'/>"
|
||||||
|
"<separator/>"
|
||||||
|
// All open tabs shall be appended here
|
||||||
|
"</menu>"
|
||||||
|
"<menu action='Help'>"
|
||||||
|
"<menuitem action='HelpContents'/>"
|
||||||
|
"<menuitem action='About'/>"
|
||||||
|
"</menu>"
|
||||||
|
"</menubar>"
|
||||||
|
"<toolbar name='toolbar_navigation'>"
|
||||||
|
"<toolitem action='TabNew'/>"
|
||||||
|
"<toolitem action='Back'/>"
|
||||||
|
"<toolitem action='Forward'/>"
|
||||||
|
"<toolitem action='RefreshStop'/>"
|
||||||
|
"<toolitem action='Home'/>"
|
||||||
|
"<toolitem action='FormFill'/>"
|
||||||
|
"<placeholder name='Location'/>"
|
||||||
|
"<placeholder name='WebSearch'/>"
|
||||||
|
"<placeholder name='TabTrash'/>"
|
||||||
|
"</toolbar>"
|
||||||
|
"<toolbar name='toolbar_panels'>"
|
||||||
|
"<toolitem action='PanelDownloads'/>"
|
||||||
|
"<toolitem action='PanelBookmarks'/>"
|
||||||
|
"<toolitem action='PanelHistory'/>"
|
||||||
|
"<toolitem action='PanelTabs'/>"
|
||||||
|
"<toolitem action='PanelPageholder'/>"
|
||||||
|
"<toolitem action='PanelExtensions'/>"
|
||||||
|
"<toolitem action='PanelConsole'/>"
|
||||||
|
"</toolbar>"
|
||||||
|
"<popup name='popup_webView'>"
|
||||||
|
"<menuitem action='Back'/>"
|
||||||
|
"<menuitem action='Forward'/>"
|
||||||
|
"<menuitem action='Refresh'/>"
|
||||||
|
"<menuitem action='Stop'/>"
|
||||||
|
"<menu action='RefreshEvery'>"
|
||||||
|
"<menuitem action='RefreshEveryEnable'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='RefreshEvery30'/>"
|
||||||
|
"<menuitem action='RefreshEvery60'/>"
|
||||||
|
"<menuitem action='RefreshEvery300'/>"
|
||||||
|
"<menuitem action='RefreshEvery900'/>"
|
||||||
|
"<menuitem action='RefreshEvery1800'/>"
|
||||||
|
"<menuitem action='RefreshEveryCustom'/>"
|
||||||
|
"</menu>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='SelectionLinksNewTabs'/>"
|
||||||
|
"<menuitem action='SelectionTextTabNew'/>"
|
||||||
|
"<menuitem action='SelectionTextTabCurrent'/>"
|
||||||
|
"<menuitem action='SelectionTextWindowNew'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='UndoTabClose'/>"
|
||||||
|
"<menuitem action='SaveAs'/>"
|
||||||
|
"<menuitem action='BookmarkNew'/>"
|
||||||
|
"<menuitem action='Print'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='SelectAll'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='BackgroundImage'/>"
|
||||||
|
"<menuitem action='SourceView'/>"
|
||||||
|
"<menuitem action='Properties'/>"
|
||||||
|
"</popup>"
|
||||||
|
"<popup name='popup_element'>"
|
||||||
|
"<menuitem action='LinkTabNew'/>"
|
||||||
|
"<menuitem action='LinkTabCurrent'/>"
|
||||||
|
"<menuitem action='LinkWindowNew'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='LinkSaveAs'/>"
|
||||||
|
"<menuitem action='LinkSaveWith'/>"
|
||||||
|
"<menuitem action='LinkCopy'/>"
|
||||||
|
"<menuitem action='LinkBookmarkNew'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='SelectionLinksNewTabs'/>"
|
||||||
|
"<menuitem action='SelectionTextTabNew'/>"
|
||||||
|
"<menuitem action='SelectionTextTabCurrent'/>"
|
||||||
|
"<menuitem action='SelectionTextWindowNew'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='ImageViewTabNew'/>"
|
||||||
|
"<menuitem action='ImageViewTabCurrent'/>"
|
||||||
|
"<menuitem action='ImageSaveAs'/>"
|
||||||
|
"<menuitem action='ImageSaveWith'/>"
|
||||||
|
"<menuitem action='ImageCopy'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='ImageViewTabNew'/>"
|
||||||
|
"<menuitem action='ImageViewTabCurrent'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='Copy_'/>"
|
||||||
|
"<menuitem action='SelectionSearch'/>"
|
||||||
|
"<menuitem action='SelectionSearchWith'/>"
|
||||||
|
"<menuitem action='SelectionSourceView'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='Properties'/>"
|
||||||
|
"</popup>"
|
||||||
|
"<popup name='popup_editable'>"
|
||||||
|
"<menuitem action='Undo'/>"
|
||||||
|
"<menuitem action='Redo'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='Cut'/>"
|
||||||
|
"<menuitem action='Copy'/>"
|
||||||
|
"<menuitem action='Paste'/>"
|
||||||
|
"<menuitem action='Delete'/>"
|
||||||
|
"<separator/>"
|
||||||
|
"<menuitem action='SelectAll'/>"
|
||||||
|
"</popup>"
|
||||||
|
"</ui>";
|
||||||
|
|
||||||
|
#endif /* !__UI_H__ */
|
441
src/webSearch.c
Normal file
441
src/webSearch.c
Normal file
|
@ -0,0 +1,441 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "webSearch.h"
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "search.h"
|
||||||
|
#include "sokoke.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <gdk/gdkkeysyms.h>
|
||||||
|
|
||||||
|
void update_searchEngine(guint index, CBrowser* browser)
|
||||||
|
{
|
||||||
|
guint n = g_list_length(searchEngines);
|
||||||
|
// Display a default icon in case we have no engines
|
||||||
|
if(!n)
|
||||||
|
sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(browser->webSearch), SEXY_ICON_ENTRY_PRIMARY
|
||||||
|
, GTK_IMAGE(gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU)));
|
||||||
|
// Change the icon and default text according to the chosen engine
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reset in case the index is out of range
|
||||||
|
if(index >= n)
|
||||||
|
index = 0;
|
||||||
|
SearchEngine* engine = (SearchEngine*)g_list_nth_data(searchEngines, index);
|
||||||
|
GdkPixbuf* pixbuf = load_web_icon(search_engine_get_icon(engine)
|
||||||
|
, GTK_ICON_SIZE_MENU, browser->navibar);
|
||||||
|
sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(browser->webSearch)
|
||||||
|
, SEXY_ICON_ENTRY_PRIMARY, GTK_IMAGE(gtk_image_new_from_pixbuf(pixbuf)));
|
||||||
|
g_object_unref(pixbuf);
|
||||||
|
sokoke_entry_set_default_text(GTK_ENTRY(browser->webSearch)
|
||||||
|
, search_engine_get_short_name(engine));
|
||||||
|
config->searchEngine = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webSearch_engine_activate(GtkWidget* widget, CBrowser* browser)
|
||||||
|
{
|
||||||
|
guint index = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "engine"));
|
||||||
|
update_searchEngine(index, browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webSearch_icon_released(GtkWidget* widget, SexyIconEntryPosition* pos
|
||||||
|
, gint button, CBrowser* browser)
|
||||||
|
{
|
||||||
|
GtkWidget* menu = gtk_menu_new();
|
||||||
|
guint n = g_list_length(searchEngines);
|
||||||
|
GtkWidget* menuitem;
|
||||||
|
if(n)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
SearchEngine* engine = (SearchEngine*)g_list_nth_data(searchEngines, i);
|
||||||
|
menuitem = gtk_image_menu_item_new_with_label(
|
||||||
|
search_engine_get_short_name(engine));
|
||||||
|
GdkPixbuf* pixbuf = load_web_icon(search_engine_get_icon(engine)
|
||||||
|
, GTK_ICON_SIZE_MENU, menuitem);
|
||||||
|
GtkWidget* icon = gtk_image_new_from_pixbuf(pixbuf);
|
||||||
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), icon);
|
||||||
|
g_object_unref(pixbuf);
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
|
||||||
|
g_object_set_data(G_OBJECT(menuitem), "engine", GUINT_TO_POINTER(i));
|
||||||
|
g_signal_connect(menuitem, "activate"
|
||||||
|
, G_CALLBACK(on_webSearch_engine_activate), browser);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
menuitem = gtk_image_menu_item_new_with_label("Empty");
|
||||||
|
gtk_widget_set_sensitive(menuitem, FALSE);
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
}
|
||||||
|
|
||||||
|
menuitem = gtk_separator_menu_item_new();
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
GtkAction* action = gtk_action_group_get_action(
|
||||||
|
browser->actiongroup, "ManageSearchEngines");
|
||||||
|
menuitem = gtk_action_create_menu_item(action);
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
sokoke_widget_popup(widget, GTK_MENU(menu), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_webSearch_engines_render_icon(GtkTreeViewColumn* column
|
||||||
|
, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter
|
||||||
|
, GtkWidget* treeview)
|
||||||
|
{
|
||||||
|
SearchEngine* searchEngine;
|
||||||
|
gtk_tree_model_get(model, iter, ENGINES_COL_ENGINE, &searchEngine, -1);
|
||||||
|
|
||||||
|
// TODO: Would it be better to not do this on every redraw?
|
||||||
|
const gchar* icon = search_engine_get_icon(searchEngine);
|
||||||
|
if(icon)
|
||||||
|
{
|
||||||
|
GdkPixbuf* pixbuf = load_web_icon(icon, GTK_ICON_SIZE_DND, treeview);
|
||||||
|
g_object_set(renderer, "pixbuf", pixbuf, NULL);
|
||||||
|
if(pixbuf)
|
||||||
|
g_object_unref(pixbuf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_object_set(renderer, "pixbuf", NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_webSearch_engines_render_text(GtkTreeViewColumn* column
|
||||||
|
, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter
|
||||||
|
, GtkWidget* treeview)
|
||||||
|
{
|
||||||
|
SearchEngine* searchEngine;
|
||||||
|
gtk_tree_model_get(model, iter, ENGINES_COL_ENGINE, &searchEngine, -1);
|
||||||
|
const gchar* name = search_engine_get_short_name(searchEngine);
|
||||||
|
const gchar* description = search_engine_get_description(searchEngine);
|
||||||
|
gchar* markup = g_strdup_printf("<b>%s</b>\n%s", name, description);
|
||||||
|
g_object_set(renderer, "markup", markup, NULL);
|
||||||
|
g_free(markup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void webSearch_toggle_edit_buttons(gboolean sensitive, CWebSearch* webSearch)
|
||||||
|
{
|
||||||
|
gtk_widget_set_sensitive(webSearch->edit, sensitive);
|
||||||
|
gtk_widget_set_sensitive(webSearch->remove, sensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_webSearch_shortName_changed(GtkWidget* widget, GtkWidget* dialog)
|
||||||
|
{
|
||||||
|
const gchar* text = gtk_entry_get_text(GTK_ENTRY(widget));
|
||||||
|
gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog)
|
||||||
|
, GTK_RESPONSE_ACCEPT, text && *text);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gchar* STR_NON_NULL(const gchar* string)
|
||||||
|
{
|
||||||
|
return string ? string : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void webSearch_editEngine_dialog_new(gboolean newEngine, CWebSearch* webSearch)
|
||||||
|
{
|
||||||
|
GtkWidget* dialog = gtk_dialog_new_with_buttons(
|
||||||
|
newEngine ? "Add search engine" : "Edit search engine"
|
||||||
|
, GTK_WINDOW(webSearch->window)
|
||||||
|
, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR
|
||||||
|
, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL
|
||||||
|
, newEngine ? GTK_STOCK_ADD : GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT
|
||||||
|
, NULL);
|
||||||
|
gtk_window_set_icon_name(GTK_WINDOW(dialog)
|
||||||
|
, newEngine ? GTK_STOCK_ADD : GTK_STOCK_REMOVE);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 5);
|
||||||
|
GtkSizeGroup* sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||||
|
|
||||||
|
SearchEngine* searchEngine;
|
||||||
|
GtkTreeModel* liststore;
|
||||||
|
GtkTreeIter iter;
|
||||||
|
if(newEngine)
|
||||||
|
{
|
||||||
|
searchEngine = search_engine_new();
|
||||||
|
gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog)
|
||||||
|
, GTK_RESPONSE_ACCEPT, FALSE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(webSearch->treeview));
|
||||||
|
gtk_tree_selection_get_selected(selection, &liststore, &iter);
|
||||||
|
gtk_tree_model_get(liststore, &iter, ENGINES_COL_ENGINE, &searchEngine, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* hbox = gtk_hbox_new(FALSE, 8);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
||||||
|
GtkWidget* label = gtk_label_new_with_mnemonic("_Name:");
|
||||||
|
gtk_size_group_add_widget(sizegroup, label);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
|
||||||
|
GtkWidget* entry_shortName = gtk_entry_new();
|
||||||
|
g_signal_connect(entry_shortName, "changed"
|
||||||
|
, G_CALLBACK(on_webSearch_shortName_changed), dialog);
|
||||||
|
gtk_entry_set_activates_default(GTK_ENTRY(entry_shortName), TRUE);
|
||||||
|
if(!newEngine)
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry_shortName)
|
||||||
|
, search_engine_get_short_name(searchEngine));
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), entry_shortName, TRUE, TRUE, 0);
|
||||||
|
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
|
||||||
|
gtk_widget_show_all(hbox);
|
||||||
|
|
||||||
|
hbox = gtk_hbox_new(FALSE, 8);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
||||||
|
label = gtk_label_new_with_mnemonic("_Description:");
|
||||||
|
gtk_size_group_add_widget(sizegroup, label);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
|
||||||
|
GtkWidget* entry_description = gtk_entry_new();
|
||||||
|
gtk_entry_set_activates_default(GTK_ENTRY(entry_description), TRUE);
|
||||||
|
if(!newEngine)
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry_description)
|
||||||
|
, STR_NON_NULL(search_engine_get_description(searchEngine)));
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), entry_description, TRUE, TRUE, 0);
|
||||||
|
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
|
||||||
|
gtk_widget_show_all(hbox);
|
||||||
|
|
||||||
|
hbox = gtk_hbox_new(FALSE, 8);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
||||||
|
label = gtk_label_new_with_mnemonic("_Url:");
|
||||||
|
gtk_size_group_add_widget(sizegroup, label);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
|
||||||
|
GtkWidget* entry_url = gtk_entry_new();
|
||||||
|
gtk_entry_set_activates_default(GTK_ENTRY(entry_url), TRUE);
|
||||||
|
if(!newEngine)
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry_url)
|
||||||
|
, STR_NON_NULL(search_engine_get_url(searchEngine)));
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), entry_url, TRUE, TRUE, 0);
|
||||||
|
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
|
||||||
|
gtk_widget_show_all(hbox);
|
||||||
|
|
||||||
|
hbox = gtk_hbox_new(FALSE, 8);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
||||||
|
label = gtk_label_new_with_mnemonic("_Icon (name or file):");
|
||||||
|
gtk_size_group_add_widget(sizegroup, label);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
|
||||||
|
GtkWidget* entry_icon = gtk_entry_new();
|
||||||
|
gtk_entry_set_activates_default(GTK_ENTRY(entry_icon), TRUE);
|
||||||
|
if(!newEngine)
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry_icon)
|
||||||
|
, STR_NON_NULL(search_engine_get_icon(searchEngine)));
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), entry_icon, TRUE, TRUE, 0);
|
||||||
|
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
|
||||||
|
gtk_widget_show_all(hbox);
|
||||||
|
|
||||||
|
hbox = gtk_hbox_new(FALSE, 8);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
||||||
|
label = gtk_label_new_with_mnemonic("_Keyword:");
|
||||||
|
gtk_size_group_add_widget(sizegroup, label);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
|
||||||
|
GtkWidget* entry_keyword = gtk_entry_new();
|
||||||
|
gtk_entry_set_activates_default(GTK_ENTRY(entry_keyword), TRUE);
|
||||||
|
if(!newEngine)
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry_keyword)
|
||||||
|
, STR_NON_NULL(search_engine_get_keyword(searchEngine)));
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), entry_keyword, TRUE, TRUE, 0);
|
||||||
|
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
|
||||||
|
gtk_widget_show_all(hbox);
|
||||||
|
|
||||||
|
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
||||||
|
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
search_engine_set_short_name(searchEngine
|
||||||
|
, gtk_entry_get_text(GTK_ENTRY(entry_shortName)));
|
||||||
|
search_engine_set_description(searchEngine
|
||||||
|
, gtk_entry_get_text(GTK_ENTRY(entry_description)));
|
||||||
|
search_engine_set_url(searchEngine
|
||||||
|
, gtk_entry_get_text(GTK_ENTRY(entry_url)));
|
||||||
|
/*search_engine_set_input_encoding(searchEngine
|
||||||
|
, gtk_entry_get_text(GTK_ENTRY(entry_inputEncoding)));*/
|
||||||
|
search_engine_set_icon(searchEngine
|
||||||
|
, gtk_entry_get_text(GTK_ENTRY(entry_icon)));
|
||||||
|
search_engine_set_keyword(searchEngine
|
||||||
|
, gtk_entry_get_text(GTK_ENTRY(entry_keyword)));
|
||||||
|
|
||||||
|
if(newEngine)
|
||||||
|
{
|
||||||
|
searchEngines = g_list_append(searchEngines, searchEngine);
|
||||||
|
liststore = gtk_tree_view_get_model(GTK_TREE_VIEW(webSearch->treeview));
|
||||||
|
gtk_list_store_append(GTK_LIST_STORE(liststore), &iter);
|
||||||
|
}
|
||||||
|
gtk_list_store_set(GTK_LIST_STORE(liststore), &iter
|
||||||
|
, ENGINES_COL_ENGINE, searchEngine, -1);
|
||||||
|
webSearch_toggle_edit_buttons(TRUE, webSearch);
|
||||||
|
}
|
||||||
|
gtk_widget_destroy(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_webSearch_add(GtkWidget* widget, CWebSearch* webSearch)
|
||||||
|
{
|
||||||
|
webSearch_editEngine_dialog_new(TRUE, webSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_webSearch_edit(GtkWidget* widget, CWebSearch* webSearch)
|
||||||
|
{
|
||||||
|
webSearch_editEngine_dialog_new(FALSE, webSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_webSearch_remove(GtkWidget* widget, CWebSearch* webSearch)
|
||||||
|
{
|
||||||
|
GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(webSearch->treeview));
|
||||||
|
GtkTreeModel* liststore;
|
||||||
|
GtkTreeIter iter;
|
||||||
|
gtk_tree_selection_get_selected(selection, &liststore, &iter);
|
||||||
|
SearchEngine* searchEngine;
|
||||||
|
gtk_tree_model_get(liststore, &iter, ENGINES_COL_ENGINE, &searchEngine, -1);
|
||||||
|
gtk_list_store_remove(GTK_LIST_STORE(liststore), &iter);
|
||||||
|
search_engine_free(searchEngine);
|
||||||
|
searchEngines = g_list_remove(searchEngines, searchEngine);
|
||||||
|
update_searchEngine(config->searchEngine, webSearch->browser);
|
||||||
|
webSearch_toggle_edit_buttons(g_list_nth(searchEngines, 0) != NULL, webSearch);
|
||||||
|
// FIXME: we want to allow undo of some kind
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* webSearch_manageSearchEngines_dialog_new(CBrowser* browser)
|
||||||
|
{
|
||||||
|
const gchar* dialogTitle = "Manage search engines";
|
||||||
|
GtkWidget* dialog = gtk_dialog_new_with_buttons(dialogTitle
|
||||||
|
, GTK_WINDOW(browser->window)
|
||||||
|
, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR
|
||||||
|
, GTK_STOCK_HELP
|
||||||
|
, GTK_RESPONSE_HELP
|
||||||
|
, GTK_STOCK_CLOSE
|
||||||
|
, GTK_RESPONSE_CLOSE
|
||||||
|
, NULL);
|
||||||
|
gtk_window_set_icon_name(GTK_WINDOW(dialog), GTK_STOCK_PROPERTIES);
|
||||||
|
// TODO: Implement some kind of help function
|
||||||
|
gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog)
|
||||||
|
, GTK_RESPONSE_HELP, FALSE); //...
|
||||||
|
gint iWidth, iHeight;
|
||||||
|
sokoke_widget_get_text_size(dialog, "M", &iWidth, &iHeight);
|
||||||
|
gtk_window_set_default_size(GTK_WINDOW(dialog), iWidth * 45, -1);
|
||||||
|
g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog);
|
||||||
|
// TODO: Do we want tooltips for explainations or can we omit that?
|
||||||
|
// TODO: We need mnemonics
|
||||||
|
// TODO: Take multiple windows into account when applying changes
|
||||||
|
GtkWidget* xfce_heading;
|
||||||
|
if((xfce_heading = sokoke_xfce_header_new(
|
||||||
|
gtk_window_get_icon_name(GTK_WINDOW(dialog)), dialogTitle)))
|
||||||
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), xfce_heading, FALSE, FALSE, 0);
|
||||||
|
GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 12);
|
||||||
|
GtkTreeViewColumn* column;
|
||||||
|
GtkCellRenderer* renderer_text; GtkCellRenderer* renderer_pixbuf;
|
||||||
|
GtkListStore* liststore = gtk_list_store_new(ENGINES_COL_N
|
||||||
|
, G_TYPE_SEARCH_ENGINE);
|
||||||
|
GtkWidget* treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore));
|
||||||
|
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
|
||||||
|
column = gtk_tree_view_column_new();
|
||||||
|
renderer_pixbuf = gtk_cell_renderer_pixbuf_new();
|
||||||
|
gtk_tree_view_column_pack_start(column, renderer_pixbuf, FALSE);
|
||||||
|
gtk_tree_view_column_set_cell_data_func(column, renderer_pixbuf
|
||||||
|
, (GtkTreeCellDataFunc)on_webSearch_engines_render_icon, treeview, NULL);
|
||||||
|
renderer_text = gtk_cell_renderer_text_new();
|
||||||
|
gtk_tree_view_column_pack_start(column, renderer_text, TRUE);
|
||||||
|
gtk_tree_view_column_set_cell_data_func(column, renderer_text
|
||||||
|
, (GtkTreeCellDataFunc)on_webSearch_engines_render_text, treeview, NULL);
|
||||||
|
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
|
||||||
|
GtkWidget* scrolled = gtk_scrolled_window_new(NULL, NULL);
|
||||||
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled)
|
||||||
|
, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||||
|
gtk_container_add(GTK_CONTAINER(scrolled), treeview);
|
||||||
|
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 5);
|
||||||
|
guint n = g_list_length(searchEngines);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
SearchEngine* searchEngine = (SearchEngine*)g_list_nth_data(searchEngines, i);
|
||||||
|
gtk_list_store_insert_with_values(GTK_LIST_STORE(liststore), NULL, i
|
||||||
|
, ENGINES_COL_ENGINE, searchEngine, -1);
|
||||||
|
}
|
||||||
|
g_object_unref(liststore);
|
||||||
|
CWebSearch* webSearch = g_new0(CWebSearch, 1);
|
||||||
|
webSearch->browser = browser;
|
||||||
|
webSearch->window = dialog;
|
||||||
|
webSearch->treeview = treeview;
|
||||||
|
g_signal_connect(dialog, "response", G_CALLBACK(g_free), webSearch);
|
||||||
|
GtkWidget* vbox = gtk_vbox_new(FALSE, 4);
|
||||||
|
gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 4);
|
||||||
|
GtkWidget* button = gtk_button_new_from_stock(GTK_STOCK_ADD);
|
||||||
|
g_signal_connect(button, "clicked", G_CALLBACK(on_webSearch_add), webSearch);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
|
||||||
|
button = gtk_button_new_from_stock(GTK_STOCK_EDIT);
|
||||||
|
g_signal_connect(button, "clicked", G_CALLBACK(on_webSearch_edit), webSearch);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
|
||||||
|
webSearch->edit = button;
|
||||||
|
button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
|
||||||
|
g_signal_connect(button, "clicked", G_CALLBACK(on_webSearch_remove), webSearch);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
|
||||||
|
webSearch->remove = button;
|
||||||
|
button = gtk_label_new(""); // This is an invisible separator
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 12);
|
||||||
|
button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
|
||||||
|
gtk_widget_set_sensitive(button, FALSE); //...
|
||||||
|
gtk_box_pack_end(GTK_BOX(vbox), button, FALSE, FALSE, 0);
|
||||||
|
button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
|
||||||
|
gtk_widget_set_sensitive(button, FALSE); //...
|
||||||
|
gtk_box_pack_end(GTK_BOX(vbox), button, FALSE, FALSE, 0);
|
||||||
|
webSearch_toggle_edit_buttons(n > 0, webSearch);
|
||||||
|
gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean on_webSearch_key_down(GtkWidget* widget, GdkEventKey* event, CBrowser* browser)
|
||||||
|
{
|
||||||
|
GdkModifierType state = (GdkModifierType)0;
|
||||||
|
gint x, y; gdk_window_get_pointer(NULL, &x, &y, &state);
|
||||||
|
if(!(state & GDK_CONTROL_MASK))
|
||||||
|
return FALSE;
|
||||||
|
switch(event->keyval)
|
||||||
|
{
|
||||||
|
case GDK_Up:
|
||||||
|
update_searchEngine(config->searchEngine - 1, browser);
|
||||||
|
return TRUE;
|
||||||
|
case GDK_Down:
|
||||||
|
update_searchEngine(config->searchEngine + 1, browser);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean on_webSearch_scroll(GtkWidget* webView, GdkEventScroll* event, CBrowser* browser)
|
||||||
|
{
|
||||||
|
if(event->direction == GDK_SCROLL_DOWN)
|
||||||
|
update_searchEngine(config->searchEngine + 1, browser);
|
||||||
|
else if(event->direction == GDK_SCROLL_UP)
|
||||||
|
update_searchEngine(config->searchEngine - 1, browser);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webSearch_activate(GtkWidget* widget, CBrowser* browser)
|
||||||
|
{
|
||||||
|
const gchar* keywords = gtk_entry_get_text(GTK_ENTRY(widget));
|
||||||
|
gchar* url;
|
||||||
|
SearchEngine* searchEngine = (SearchEngine*)g_list_nth_data(searchEngines, config->searchEngine);
|
||||||
|
if(searchEngine)
|
||||||
|
url = searchEngine->url;
|
||||||
|
else // The location search is our fallback
|
||||||
|
url = config->locationSearch;
|
||||||
|
gchar* search;
|
||||||
|
if(strstr(url, "%s"))
|
||||||
|
search = g_strdup_printf(url, keywords);
|
||||||
|
else
|
||||||
|
search = g_strconcat(url, " ", keywords, NULL);
|
||||||
|
entry_completion_append(GTK_ENTRY(widget), keywords);
|
||||||
|
webkit_web_view_open(WEBKIT_WEB_VIEW(get_nth_webView(-1, browser)), search);
|
||||||
|
g_free(search);
|
||||||
|
}
|
61
src/webSearch.h
Normal file
61
src/webSearch.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __WEBSEARCH_H__
|
||||||
|
#define __WEBSEARCH_H__ 1
|
||||||
|
|
||||||
|
#include "browser.h"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <libsexy/sexy.h>
|
||||||
|
#include <webkit.h>
|
||||||
|
|
||||||
|
// -- Types
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
CBrowser* browser;
|
||||||
|
GtkWidget* window;
|
||||||
|
GtkWidget* treeview;
|
||||||
|
GtkWidget* edit;
|
||||||
|
GtkWidget* remove;
|
||||||
|
} CWebSearch;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ENGINES_COL_ENGINE,
|
||||||
|
ENGINES_COL_N
|
||||||
|
};
|
||||||
|
|
||||||
|
// -- Declarations
|
||||||
|
|
||||||
|
void
|
||||||
|
update_searchEngine(guint, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webSearch_icon_released(GtkWidget*, SexyIconEntryPosition*, gint, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webSearch_engine_activate(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webSearch_activate(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
GtkWidget*
|
||||||
|
webSearch_manageSearchEngines_dialog_new(CBrowser*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
on_webSearch_key_down(GtkWidget*, GdkEventKey*, CBrowser*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
on_webSearch_scroll(GtkWidget*, GdkEventScroll*, CBrowser*);
|
||||||
|
|
||||||
|
#endif /* !__WEBSEARCH_H__ */
|
351
src/webView.c
Normal file
351
src/webView.c
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "webView.h"
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "sokoke.h"
|
||||||
|
#include "xbel.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
WebKitNavigationResponse on_webView_navigation_requested(GtkWidget* webView
|
||||||
|
, WebKitWebFrame* frame, WebKitNetworkRequest* networkRequest)
|
||||||
|
{
|
||||||
|
WebKitNavigationResponse response = WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
|
||||||
|
// TODO: Ask webkit wether it knows the protocol for "unknown protcol"
|
||||||
|
// TODO: This isn't the place for uri scheme handling
|
||||||
|
const gchar* uri = webkit_network_request_get_uri(networkRequest);
|
||||||
|
gchar* protocol = strtok(g_strdup(uri), ":");
|
||||||
|
if(spawn_protocol_command(protocol, uri))
|
||||||
|
response = WEBKIT_NAVIGATION_RESPONSE_IGNORE;
|
||||||
|
g_free(protocol);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_location_changed(GtkWidget* webView, const gchar* uri
|
||||||
|
, CBrowser* browser)
|
||||||
|
{
|
||||||
|
gchar* newUri = g_strdup(uri ? uri : "");
|
||||||
|
xbel_bookmark_set_href(browser->sessionItem, newUri);
|
||||||
|
if(webView == get_nth_webView(-1, browser))
|
||||||
|
{
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(browser->location), newUri);
|
||||||
|
gtk_label_set_text(GTK_LABEL(browser->webView_name), newUri);
|
||||||
|
update_status_message(NULL, browser);
|
||||||
|
update_gui_state(browser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_title_changed(GtkWidget* webView, const gchar* title
|
||||||
|
, const gchar* uri, CBrowser* browser)
|
||||||
|
{
|
||||||
|
// TODO: We emulate location_changed here for now
|
||||||
|
// Shouldn't we have separated title_changed and location_changed signals?
|
||||||
|
on_webView_location_changed(webView, uri, browser);
|
||||||
|
gchar* newTitle;
|
||||||
|
newTitle = g_strdup(title ? title : uri);
|
||||||
|
xbel_item_set_title(browser->sessionItem, newTitle);
|
||||||
|
gtk_label_set_text(GTK_LABEL(browser->webView_name), newTitle);
|
||||||
|
sokoke_widget_set_tooltip_text(gtk_widget_get_parent(
|
||||||
|
gtk_widget_get_parent(browser->webView_name)), newTitle);
|
||||||
|
gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
|
||||||
|
browser->webView_menu))), newTitle);
|
||||||
|
if(webView == get_nth_webView(-1, browser))
|
||||||
|
{
|
||||||
|
gchar* windowTitle = g_strconcat(newTitle, " - ", PACKAGE_NAME, NULL);
|
||||||
|
gtk_window_set_title(GTK_WINDOW(browser->window), windowTitle);
|
||||||
|
g_free(windowTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_icon_changed(GtkWidget* webView, WebKitWebFrame* widget
|
||||||
|
, CBrowser* browser)
|
||||||
|
{
|
||||||
|
// TODO: Implement icon updates; currently this isn't ever called anyway
|
||||||
|
const gchar* icon = NULL;
|
||||||
|
UNIMPLEMENTED
|
||||||
|
if(icon)
|
||||||
|
{
|
||||||
|
gtk_label_set_text(GTK_LABEL(browser->webView_name), "icon");
|
||||||
|
gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
|
||||||
|
browser->webView_menu))), "icon");
|
||||||
|
if(webView == get_nth_webView(-1, browser))
|
||||||
|
{
|
||||||
|
gchar* windowTitle = g_strconcat("icon", " - ", PACKAGE_NAME, NULL);
|
||||||
|
gtk_window_set_title(GTK_WINDOW(browser->window), windowTitle);
|
||||||
|
g_free(windowTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_load_started(GtkWidget* webView, WebKitWebFrame* widget
|
||||||
|
, CBrowser* browser)
|
||||||
|
{
|
||||||
|
browser->loadedPercent = 0;
|
||||||
|
browser->loadedBytes = 0;
|
||||||
|
browser->loadedBytesMax = 0;
|
||||||
|
update_favicon(browser);
|
||||||
|
if(webView == get_nth_webView(-1, browser))
|
||||||
|
update_gui_state(browser);
|
||||||
|
update_statusbar_text(browser);
|
||||||
|
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(browser->progress), 0.1);
|
||||||
|
gtk_widget_show(browser->progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_load_changed(GtkWidget* webView, gint progress, CBrowser* browser)
|
||||||
|
{
|
||||||
|
browser->loadedBytes = progress;
|
||||||
|
browser->loadedBytes = 100;
|
||||||
|
if(webView == get_nth_webView(-1, browser))
|
||||||
|
update_gui_state(browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_load_finished(GtkWidget* webView, WebKitWebFrame* widget
|
||||||
|
, CBrowser* browser)
|
||||||
|
{
|
||||||
|
browser->loadedPercent = -1;
|
||||||
|
update_favicon(browser);
|
||||||
|
if(webView == get_nth_webView(-1, browser))
|
||||||
|
update_gui_state(browser);
|
||||||
|
update_statusbar_text(browser);
|
||||||
|
gtk_widget_hide(browser->progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_status_message(GtkWidget* webView, const gchar* text, CBrowser* browser)
|
||||||
|
{
|
||||||
|
update_status_message(text, browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_selection_changed(GtkWidget* webView, CBrowser* browser)
|
||||||
|
{
|
||||||
|
UNIMPLEMENTED
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean on_webView_console_message(GtkWidget* webView
|
||||||
|
, const gchar* message, gint line, const gchar* sourceId, CBrowser* browser)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_link_hover(GtkWidget* webView, const gchar* tooltip
|
||||||
|
, const gchar* uri, CBrowser* browser)
|
||||||
|
{
|
||||||
|
update_status_message(uri, browser);
|
||||||
|
g_free(browser->elementUri);
|
||||||
|
browser->elementUri = g_strdup(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
GtkWidget* on_webView_window_open(GtkWidget* webView, const gchar* sUri
|
||||||
|
, CBrowser* browser)
|
||||||
|
{
|
||||||
|
// A window is created
|
||||||
|
// TODO: Respect config->iNewPages
|
||||||
|
// TODO: Find out if this comes from a script or a click
|
||||||
|
// TODO: Block scripted popups, return NULL and show status icon
|
||||||
|
CBrowser* newBrowser = browser_new(config->openPopupsInTabs ? browser : NULL);
|
||||||
|
return newBrowser->webView;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void webView_popup(GtkWidget* webView, GdkEventButton* event, CBrowser* browser)
|
||||||
|
{
|
||||||
|
gboolean isLink = browser->elementUri != NULL; // Did we right-click a link?
|
||||||
|
gboolean haveLinks = FALSE; // TODO: Are several links selected?
|
||||||
|
gboolean isImage = FALSE; // TODO: Did we right-click an image?
|
||||||
|
gboolean isEditable = FALSE; //webkit_web_view_can_paste_clipboard(WEBKIT_WEB_VIEW(webView)); //...
|
||||||
|
gchar* selection = NULL; //webkit_web_view_get_selected_text(WEBKIT_WEB_VIEW(webView));
|
||||||
|
|
||||||
|
update_edit_items(browser);
|
||||||
|
|
||||||
|
// Download manager available?
|
||||||
|
const gchar* downloadManager = g_datalist_get_data(&config->protocols_commands, "download");
|
||||||
|
gboolean canDownload = downloadManager && *downloadManager;
|
||||||
|
|
||||||
|
action_set_visible("LinkTabNew", isLink, browser);
|
||||||
|
action_set_visible("LinkTabCurrent", isLink, browser);
|
||||||
|
action_set_visible("LinkWindowNew", isLink, browser);
|
||||||
|
|
||||||
|
action_set_visible("LinkSaveAs", isLink, browser);
|
||||||
|
action_set_visible("LinkSaveWith", isLink && canDownload, browser);
|
||||||
|
action_set_visible("LinkCopy", isLink, browser);
|
||||||
|
action_set_visible("LinkBookmarkNew", isLink, browser);
|
||||||
|
|
||||||
|
action_set_visible("SelectionLinksNewTabs", haveLinks && selection && *selection, browser);
|
||||||
|
action_set_visible("SelectionTextTabNew", haveLinks && selection && *selection, browser);
|
||||||
|
action_set_visible("SelectionTextTabCurrent", haveLinks && selection && *selection, browser);
|
||||||
|
action_set_visible("SelectionTextWindowNew", haveLinks && selection && *selection, browser);
|
||||||
|
|
||||||
|
action_set_visible("ImageViewTabNew", isImage, browser);
|
||||||
|
action_set_visible("ImageViewTabCurrent", isImage, browser);
|
||||||
|
action_set_visible("ImageSaveAs", isImage, browser);
|
||||||
|
action_set_visible("ImageSaveWith", isImage && canDownload, browser);
|
||||||
|
action_set_visible("ImageCopy", isImage, browser);
|
||||||
|
|
||||||
|
action_set_visible("Copy_", (selection && *selection) || isEditable, browser);
|
||||||
|
action_set_visible("SelectionSearch", selection && *selection, browser);
|
||||||
|
action_set_visible("SelectionSearchWith", selection && *selection, browser);
|
||||||
|
action_set_visible("SelectionSourceView", selection && *selection, browser);
|
||||||
|
|
||||||
|
action_set_visible("SourceView", !selection, browser);
|
||||||
|
|
||||||
|
if(isEditable)
|
||||||
|
sokoke_widget_popup(webView, GTK_MENU(browser->popup_editable), event);
|
||||||
|
else if(isLink || isImage || (selection && *selection))
|
||||||
|
sokoke_widget_popup(webView, GTK_MENU(browser->popup_element), event);
|
||||||
|
else
|
||||||
|
sokoke_widget_popup(webView, GTK_MENU(browser->popup_webView), event);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean on_webView_button_release(GtkWidget* webView, GdkEventButton* event
|
||||||
|
, CBrowser* browser)
|
||||||
|
{
|
||||||
|
GdkModifierType state = (GdkModifierType)0;
|
||||||
|
gint x, y;
|
||||||
|
gdk_window_get_pointer(NULL, &x, &y, &state);
|
||||||
|
switch(event->button)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
if(!browser->elementUri) return FALSE;
|
||||||
|
if(state & GDK_SHIFT_MASK)
|
||||||
|
{
|
||||||
|
// Open link in new window
|
||||||
|
CBrowser* curBrowser = browser_new(NULL);
|
||||||
|
webkit_web_view_open(WEBKIT_WEB_VIEW(curBrowser->webView), browser->elementUri);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else if(state & GDK_MOD1_MASK)
|
||||||
|
{
|
||||||
|
// Open link in new tab
|
||||||
|
CBrowser* curBrowser = browser_new(browser);
|
||||||
|
webkit_web_view_open(WEBKIT_WEB_VIEW(curBrowser->webView), browser->elementUri);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if(state & GDK_CONTROL_MASK)
|
||||||
|
{
|
||||||
|
//webkit_web_view_set_text_size(WEBKIT_WEB_VIEW(webView), 1);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!browser->elementUri) return FALSE;
|
||||||
|
// Open link in new tab
|
||||||
|
CBrowser* curBrowser = browser_new(browser);
|
||||||
|
webkit_web_view_open(WEBKIT_WEB_VIEW(curBrowser->webView), browser->elementUri);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
webView_popup(webView, event, browser);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_popup(GtkWidget* webView, CBrowser* browser)
|
||||||
|
{
|
||||||
|
webView_popup(webView, NULL, browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean on_webView_scroll(GtkWidget* webView, GdkEventScroll* event
|
||||||
|
, CBrowser* browser)
|
||||||
|
{
|
||||||
|
GdkModifierType state = (GdkModifierType)0;
|
||||||
|
gint x, y;
|
||||||
|
gdk_window_get_pointer(NULL, &x, &y, &state);
|
||||||
|
if(state & GDK_CONTROL_MASK)
|
||||||
|
{
|
||||||
|
/*const gfloat size = webkit_web_view_get_text_size(WEBKIT_WEB_VIEW(webView));
|
||||||
|
if(event->direction == GDK_SCROLL_DOWN)
|
||||||
|
webkit_web_view_set_text_size(WEBKIT_WEB_VIEW(webView), size + 0.1);
|
||||||
|
else if(event->direction == GDK_SCROLL_UP)
|
||||||
|
webView_set_text_size(WEBKIT_WEB_VIEW(webView), size - 0.1);*/
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean on_webView_leave(GtkWidget* webView, GdkEventCrossing* event, CBrowser* browser)
|
||||||
|
{
|
||||||
|
update_status_message(NULL, browser);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_webView_destroy(GtkWidget* widget, CBrowser* browser)
|
||||||
|
{
|
||||||
|
// Update browser list, free memory and possibly quit
|
||||||
|
GList* tmp = g_list_find(browsers, browser);
|
||||||
|
browsers = g_list_delete_link(browsers, tmp);
|
||||||
|
g_free(browser->elementUri);
|
||||||
|
g_free(browser->statusMessage);
|
||||||
|
if(!g_list_length(browsers))
|
||||||
|
{
|
||||||
|
g_object_unref(browser->actiongroup);
|
||||||
|
g_object_unref(browser->popup_webView);
|
||||||
|
g_object_unref(browser->popup_element);
|
||||||
|
g_object_unref(browser->popup_editable);
|
||||||
|
gtk_main_quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// webView actions begin here
|
||||||
|
|
||||||
|
GtkWidget* webView_new(GtkWidget** scrolled)
|
||||||
|
{
|
||||||
|
*scrolled = gtk_scrolled_window_new(NULL, NULL);
|
||||||
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(*scrolled)
|
||||||
|
, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
||||||
|
GTK_WIDGET_SET_FLAGS(*scrolled, GTK_CAN_FOCUS);
|
||||||
|
GtkWidget* webView = webkit_web_view_new();
|
||||||
|
gtk_container_add(GTK_CONTAINER(*scrolled), webView);
|
||||||
|
return webView;
|
||||||
|
}
|
||||||
|
|
||||||
|
void webView_open(GtkWidget* webView, const gchar* uri)
|
||||||
|
{
|
||||||
|
webkit_web_view_open(WEBKIT_WEB_VIEW(webView), (gchar*)uri);
|
||||||
|
// We need to check the browser first
|
||||||
|
// No browser means this is a panel
|
||||||
|
CBrowser* browser = get_browser_from_webView(webView);
|
||||||
|
if(browser)
|
||||||
|
{
|
||||||
|
xbel_bookmark_set_href(browser->sessionItem, uri);
|
||||||
|
xbel_item_set_title(browser->sessionItem, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void webView_close(GtkWidget* webView, CBrowser* browser)
|
||||||
|
{
|
||||||
|
browser = get_browser_from_webView(webView);
|
||||||
|
const gchar* uri = xbel_bookmark_get_href(browser->sessionItem);
|
||||||
|
xbel_folder_remove_item(session, browser->sessionItem);
|
||||||
|
if(uri && *uri)
|
||||||
|
{
|
||||||
|
xbel_folder_prepend_item(tabtrash, browser->sessionItem);
|
||||||
|
guint n = xbel_folder_get_n_items(tabtrash);
|
||||||
|
if(n > 10)
|
||||||
|
{
|
||||||
|
XbelItem* item = xbel_folder_get_nth_item(tabtrash, n - 1);
|
||||||
|
xbel_folder_remove_item(tabtrash, item);
|
||||||
|
xbel_item_free(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
xbel_item_free(browser->sessionItem);
|
||||||
|
gtk_widget_destroy(browser->webView_menu);
|
||||||
|
gtk_notebook_remove_page(GTK_NOTEBOOK(browser->webViews)
|
||||||
|
, get_webView_index(webView, browser));
|
||||||
|
update_browser_actions(browser);
|
||||||
|
}
|
84
src/webView.h
Normal file
84
src/webView.h
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __webView_H__
|
||||||
|
#define __webView_H__ 1
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include "browser.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include <webkit.h>
|
||||||
|
|
||||||
|
WebKitNavigationResponse
|
||||||
|
on_webView_navigation_requested(GtkWidget* webView, WebKitWebFrame* frame
|
||||||
|
, WebKitNetworkRequest* networkRequest);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_location_changed(GtkWidget*, const gchar*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_title_changed(GtkWidget*, const gchar*, const gchar*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_icon_changed(GtkWidget*, WebKitWebFrame*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_load_started(GtkWidget* , WebKitWebFrame*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_load_changed(GtkWidget*, gint progress, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_load_finished(GtkWidget*, WebKitWebFrame*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_status_message(GtkWidget*, const gchar*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_selection_changed(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
on_webView_console_message(GtkWidget*, const gchar*, gint, const gchar*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_link_hover(GtkWidget*, const gchar*, const gchar*, CBrowser*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
GtkWidget*
|
||||||
|
on_webView_window_open(GtkWidget*, const gchar*, CBrowser*);
|
||||||
|
*/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
on_webView_button_release(GtkWidget*, GdkEventButton*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_popup(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
on_webView_scroll(GtkWidget*, GdkEventScroll*, CBrowser*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
on_webView_leave(GtkWidget*, GdkEventCrossing*, CBrowser*);
|
||||||
|
|
||||||
|
void
|
||||||
|
on_webView_destroy(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
GtkWidget*
|
||||||
|
webView_new(GtkWidget**);
|
||||||
|
|
||||||
|
void
|
||||||
|
webView_open(GtkWidget*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
webView_close(GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
|
#endif /* !__webView_H__ */
|
801
src/xbel.c
Normal file
801
src/xbel.c
Normal file
|
@ -0,0 +1,801 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an implementation of XBEL based on Glib and libXML2.
|
||||||
|
*
|
||||||
|
* Design Goals:
|
||||||
|
* - XbelItem is the only opaque public data structure.
|
||||||
|
* - The interface should be intuitive and familiar to Glib users.
|
||||||
|
* - There should be no public exposure of libXML2 specific code.
|
||||||
|
* - Bookmarks should actually be easily exchangeable between programs.
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - Support info > metadata, alias, added, modified, visited
|
||||||
|
* - Compatibility: The Nokia 770 *requires* metadata and folder
|
||||||
|
* - Compatibility: Kazehakase's bookmarks
|
||||||
|
* - Compatibility: Epiphany's bookmarks
|
||||||
|
* - XML Indentation
|
||||||
|
* - Export and import to other formats
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "xbel.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
// Private: Create a new item of a certain type
|
||||||
|
static XbelItem* xbel_item_new(XbelItemKind kind)
|
||||||
|
{
|
||||||
|
XbelItem* item = g_new(XbelItem, 1);
|
||||||
|
item->parent = NULL;
|
||||||
|
item->kind = kind;
|
||||||
|
if(kind == XBEL_ITEM_FOLDER)
|
||||||
|
{
|
||||||
|
item->items = NULL;
|
||||||
|
item->folded = TRUE;
|
||||||
|
}
|
||||||
|
if(kind != XBEL_ITEM_SEPARATOR)
|
||||||
|
{
|
||||||
|
item->title = NULL;
|
||||||
|
item->desc = NULL;
|
||||||
|
}
|
||||||
|
if(kind == XBEL_ITEM_BOOKMARK)
|
||||||
|
item->href = g_strdup("");
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_bookmark_new:
|
||||||
|
*
|
||||||
|
* Create a new empty bookmark.
|
||||||
|
*
|
||||||
|
* Return value: a newly allocated bookmark
|
||||||
|
**/
|
||||||
|
XbelItem* xbel_bookmark_new(void)
|
||||||
|
{
|
||||||
|
return xbel_item_new(XBEL_ITEM_BOOKMARK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static XbelItem* xbel_bookmark_from_xmlNodePtr(xmlNodePtr cur)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(cur != NULL, NULL);
|
||||||
|
XbelItem* bookmark = xbel_bookmark_new();
|
||||||
|
xmlChar* key = xmlGetProp(cur, (xmlChar*)"href");
|
||||||
|
xbel_bookmark_set_href(bookmark, (gchar*)key);
|
||||||
|
cur = cur->xmlChildrenNode;
|
||||||
|
while(cur != NULL)
|
||||||
|
{
|
||||||
|
if(!xmlStrcmp(cur->name, (const xmlChar*)"title"))
|
||||||
|
{
|
||||||
|
xmlChar* key = xmlNodeGetContent(cur);
|
||||||
|
bookmark->title = (gchar*)g_strstrip((gchar*)key);
|
||||||
|
}
|
||||||
|
else if(!xmlStrcmp(cur->name, (const xmlChar*)"desc"))
|
||||||
|
{
|
||||||
|
xmlChar* key = xmlNodeGetContent(cur);
|
||||||
|
bookmark->desc = (gchar*)g_strstrip((gchar*)key);
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_separator_new:
|
||||||
|
*
|
||||||
|
* Create a new separator.
|
||||||
|
*
|
||||||
|
* The returned item must be freed eventually.
|
||||||
|
*
|
||||||
|
* Return value: a newly allocated separator.
|
||||||
|
**/
|
||||||
|
XbelItem* xbel_separator_new(void)
|
||||||
|
{
|
||||||
|
return xbel_item_new(XBEL_ITEM_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_new:
|
||||||
|
*
|
||||||
|
* Create a new empty folder.
|
||||||
|
*
|
||||||
|
* The returned item must be freed eventually.
|
||||||
|
*
|
||||||
|
* Return value: a newly allocated folder.
|
||||||
|
**/
|
||||||
|
XbelItem* xbel_folder_new(void)
|
||||||
|
{
|
||||||
|
return xbel_item_new(XBEL_ITEM_FOLDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private: Create a folder from an xmlNodePtr
|
||||||
|
static XbelItem* xbel_folder_from_xmlNodePtr(xmlNodePtr cur)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(cur != NULL, NULL);
|
||||||
|
XbelItem* folder = xbel_folder_new();
|
||||||
|
xmlChar* key = xmlGetProp(cur, (xmlChar*)"folded");
|
||||||
|
if(key)
|
||||||
|
{
|
||||||
|
if(!g_ascii_strncasecmp((gchar*)key, "yes", 3))
|
||||||
|
folder->folded = TRUE;
|
||||||
|
else if(!g_ascii_strncasecmp((gchar*)key, "no", 2))
|
||||||
|
folder->folded = FALSE;
|
||||||
|
else
|
||||||
|
g_warning("XBEL: Unknown value for folded.");
|
||||||
|
xmlFree(key);
|
||||||
|
}
|
||||||
|
cur = cur->xmlChildrenNode;
|
||||||
|
while(cur)
|
||||||
|
{
|
||||||
|
if(!xmlStrcmp(cur->name, (const xmlChar*)"title"))
|
||||||
|
{
|
||||||
|
xmlChar* key = xmlNodeGetContent(cur);
|
||||||
|
folder->title = (gchar*)g_strstrip((gchar*)key);
|
||||||
|
}
|
||||||
|
else if(!xmlStrcmp(cur->name, (const xmlChar*)"desc"))
|
||||||
|
{
|
||||||
|
xmlChar* key = xmlNodeGetContent(cur);
|
||||||
|
folder->desc = (gchar*)g_strstrip((gchar*)key);
|
||||||
|
}
|
||||||
|
else if(!xmlStrcmp(cur->name, (const xmlChar*)"folder"))
|
||||||
|
{
|
||||||
|
XbelItem* item = xbel_folder_from_xmlNodePtr(cur);
|
||||||
|
item->parent = (struct XbelItem*)folder;
|
||||||
|
folder->items = g_list_prepend(folder->items, item);
|
||||||
|
}
|
||||||
|
else if(!xmlStrcmp(cur->name, (const xmlChar*)"bookmark"))
|
||||||
|
{
|
||||||
|
XbelItem* item = xbel_bookmark_from_xmlNodePtr(cur);
|
||||||
|
item->parent = (struct XbelItem*)folder;
|
||||||
|
folder->items = g_list_prepend(folder->items, item);
|
||||||
|
}
|
||||||
|
else if(!xmlStrcmp(cur->name, (const xmlChar*)"separator"))
|
||||||
|
{
|
||||||
|
XbelItem* item = xbel_separator_new();
|
||||||
|
item->parent = (struct XbelItem*)folder;
|
||||||
|
folder->items = g_list_prepend(folder->items, item);
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
// Prepending and reversing is faster than appending
|
||||||
|
folder->items = g_list_reverse(folder->items);
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private: Loads the contents from an xmlNodePtr into a folder.
|
||||||
|
static gboolean xbel_folder_from_xmlDocPtr(XbelItem* folder, xmlDocPtr doc)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_folder_is_empty(folder), FALSE);
|
||||||
|
g_return_val_if_fail(doc != NULL, FALSE);
|
||||||
|
xmlNodePtr cur = xmlDocGetRootElement(doc);
|
||||||
|
xmlChar* version = xmlGetProp(cur, (xmlChar*)"version");
|
||||||
|
if(xmlStrcmp(version, (xmlChar*)"1.0"))
|
||||||
|
g_warning("XBEL version is not 1.0.");
|
||||||
|
xmlFree(version);
|
||||||
|
folder->title = (gchar*)xmlGetProp(cur, (xmlChar*)"title");
|
||||||
|
folder->desc = (gchar*)xmlGetProp(cur, (xmlChar*)"desc");
|
||||||
|
if((cur = xmlDocGetRootElement(doc)) == NULL)
|
||||||
|
{
|
||||||
|
// Empty document
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if(xmlStrcmp(cur->name, (const xmlChar*)"xbel"))
|
||||||
|
{
|
||||||
|
// Wrong document kind
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cur = cur->xmlChildrenNode;
|
||||||
|
while(cur != NULL)
|
||||||
|
{
|
||||||
|
XbelItem* item = NULL;
|
||||||
|
if(!xmlStrcmp(cur->name, (const xmlChar*)"folder"))
|
||||||
|
item = xbel_folder_from_xmlNodePtr(cur);
|
||||||
|
else if(!xmlStrcmp(cur->name, (const xmlChar*)"bookmark"))
|
||||||
|
item = xbel_bookmark_from_xmlNodePtr(cur);
|
||||||
|
else if(!xmlStrcmp(cur->name, (const xmlChar*)"separator"))
|
||||||
|
item = xbel_separator_new();
|
||||||
|
/*else if(!xmlStrcmp(cur->name, (const xmlChar*)"info"))
|
||||||
|
item = xbel_parse_info(xbel, cur);*/
|
||||||
|
if(item != NULL)
|
||||||
|
{
|
||||||
|
item->parent = (struct XbelItem*)folder;
|
||||||
|
folder->items = g_list_prepend(folder->items, item);
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
// Prepending and reversing is faster than appending
|
||||||
|
folder->items = g_list_reverse(folder->items);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_free:
|
||||||
|
* @item: a valid item
|
||||||
|
*
|
||||||
|
* Free an XbelItem. If @item is a folder all of its children will also
|
||||||
|
* be freed automatically.
|
||||||
|
*
|
||||||
|
* The item must not be contained in a folder or attempting to free it will fail.
|
||||||
|
**/
|
||||||
|
void xbel_item_free(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail(item);
|
||||||
|
g_return_if_fail(!xbel_item_get_parent(item));
|
||||||
|
if(xbel_item_is_folder(item))
|
||||||
|
{
|
||||||
|
guint n = xbel_folder_get_n_items(item);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
XbelItem* _item = xbel_folder_get_nth_item(item, i);
|
||||||
|
_item->parent = NULL;
|
||||||
|
xbel_item_free(_item);
|
||||||
|
}
|
||||||
|
g_list_free(item->items);
|
||||||
|
}
|
||||||
|
if(item->kind != XBEL_ITEM_SEPARATOR)
|
||||||
|
{
|
||||||
|
g_free(item->title);
|
||||||
|
g_free(item->desc);
|
||||||
|
}
|
||||||
|
if(item->kind == XBEL_ITEM_BOOKMARK)
|
||||||
|
g_free(item->href);
|
||||||
|
g_free(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_copy:
|
||||||
|
* @item: the item to copy
|
||||||
|
*
|
||||||
|
* Copy an XbelItem.
|
||||||
|
*
|
||||||
|
* The returned item must be freed eventually.
|
||||||
|
*
|
||||||
|
* Return value: a copy of @item
|
||||||
|
**/
|
||||||
|
XbelItem* xbel_item_copy(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(item, NULL);
|
||||||
|
XbelItem* copy = xbel_folder_new();
|
||||||
|
if(xbel_item_is_folder(item))
|
||||||
|
{
|
||||||
|
guint n = xbel_folder_get_n_items(item);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
XbelItem* _item = xbel_folder_get_nth_item(item, i);
|
||||||
|
xbel_folder_append_item(copy, xbel_item_copy(_item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(item->kind != XBEL_ITEM_SEPARATOR)
|
||||||
|
{
|
||||||
|
xbel_item_set_title(copy, item->title);
|
||||||
|
xbel_item_set_desc(copy, item->desc);
|
||||||
|
}
|
||||||
|
if(item->kind == XBEL_ITEM_BOOKMARK)
|
||||||
|
xbel_bookmark_set_href(copy, item->href);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
GType xbel_item_get_type()
|
||||||
|
{
|
||||||
|
static GType type = 0;
|
||||||
|
if(!type)
|
||||||
|
type = g_pointer_type_register_static("xbel_item");
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_append_item:
|
||||||
|
* @folder: a folder
|
||||||
|
* @item: the item to append
|
||||||
|
*
|
||||||
|
* Append the given item to a folder.
|
||||||
|
*
|
||||||
|
* The item is actually moved and must not be contained in another folder.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
void xbel_folder_append_item(XbelItem* folder, XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail(!xbel_item_get_parent(item));
|
||||||
|
g_return_if_fail(xbel_item_is_folder(folder));
|
||||||
|
item->parent = (struct XbelItem*)folder;
|
||||||
|
folder->items = g_list_append(folder->items, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_prepend_item:
|
||||||
|
* @folder: a folder
|
||||||
|
* @item: the item to prepend
|
||||||
|
*
|
||||||
|
* Prepend the given item to a folder.
|
||||||
|
*
|
||||||
|
* The item is actually moved and must not be contained in another folder.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
void xbel_folder_prepend_item(XbelItem* folder, XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail(!xbel_item_get_parent(item));
|
||||||
|
g_return_if_fail(xbel_item_is_folder(folder));
|
||||||
|
item->parent = (struct XbelItem*)folder;
|
||||||
|
folder->items = g_list_prepend(folder->items, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_remove_item:
|
||||||
|
* @folder: a folder
|
||||||
|
* @item: the item to remove
|
||||||
|
*
|
||||||
|
* Remove the given @item from a @folder.
|
||||||
|
**/
|
||||||
|
void xbel_folder_remove_item(XbelItem* folder, XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail(item);
|
||||||
|
g_return_if_fail(xbel_item_get_parent(folder) != item);
|
||||||
|
item->parent = NULL;
|
||||||
|
// Fortunately we know that items are unique
|
||||||
|
folder->items = g_list_remove(folder->items, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_get_n_items:
|
||||||
|
* @folder: a folder
|
||||||
|
*
|
||||||
|
* Retrieve the number of items contained in the given @folder.
|
||||||
|
*
|
||||||
|
* Return value: number of items
|
||||||
|
**/
|
||||||
|
guint xbel_folder_get_n_items(XbelItem* folder)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_item_is_folder(folder), 0);
|
||||||
|
return g_list_length(folder->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_get_nth_item:
|
||||||
|
* @folder: a folder
|
||||||
|
* @n: the index of an item
|
||||||
|
*
|
||||||
|
* Retrieve an item contained in the given @folder by its index.
|
||||||
|
*
|
||||||
|
* Return value: the item at the given index or %NULL
|
||||||
|
**/
|
||||||
|
XbelItem* xbel_folder_get_nth_item(XbelItem* folder, guint n)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_item_is_folder(folder), NULL);
|
||||||
|
return (XbelItem*)g_list_nth_data(folder->items, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_is_empty:
|
||||||
|
* @folder: A folder.
|
||||||
|
*
|
||||||
|
* Determines wether or not a folder contains no items. This is significantly
|
||||||
|
* faster than xbel_folder_get_n_items for this particular purpose.
|
||||||
|
*
|
||||||
|
* Return value: Wether the given @folder is folded.
|
||||||
|
**/
|
||||||
|
gboolean xbel_folder_is_empty(XbelItem* folder)
|
||||||
|
{
|
||||||
|
return !xbel_folder_get_nth_item(folder, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_get_folded:
|
||||||
|
* @folder: A folder.
|
||||||
|
*
|
||||||
|
* Determines wether or not a folder is folded. If a folder is not folded
|
||||||
|
* it should not be exposed in a user interface.
|
||||||
|
*
|
||||||
|
* New folders are folded by default.
|
||||||
|
*
|
||||||
|
* Return value: Wether the given @folder is folded.
|
||||||
|
**/
|
||||||
|
gboolean xbel_folder_get_folded(XbelItem* folder)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_item_is_folder(folder), TRUE);
|
||||||
|
return folder->folded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_get_kind:
|
||||||
|
* @item: A item.
|
||||||
|
*
|
||||||
|
* Determines the kind of an item.
|
||||||
|
*
|
||||||
|
* Return value: The kind of the given @item.
|
||||||
|
**/
|
||||||
|
XbelItemKind xbel_item_get_kind(XbelItem* item)
|
||||||
|
{
|
||||||
|
return item->kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_get_parent:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Retrieves the parent folder of an item.
|
||||||
|
*
|
||||||
|
* Return value: The parent folder of the given @item or %NULL.
|
||||||
|
**/
|
||||||
|
XbelItem* xbel_item_get_parent(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(item, NULL);
|
||||||
|
return (XbelItem*)item->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_get_title:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Retrieves the title of an item.
|
||||||
|
*
|
||||||
|
* Return value: The title of the given @item or %NULL.
|
||||||
|
**/
|
||||||
|
G_CONST_RETURN gchar* xbel_item_get_title(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(!xbel_item_is_separator(item), NULL);
|
||||||
|
return item->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_get_desc:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Retrieves the description of an item.
|
||||||
|
*
|
||||||
|
* Return value: The description of the @item or %NULL.
|
||||||
|
**/
|
||||||
|
G_CONST_RETURN gchar* xbel_item_get_desc(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(!xbel_item_is_separator(item), NULL);
|
||||||
|
return item->desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_bookmark_get_href:
|
||||||
|
* @bookmark: A bookmark.
|
||||||
|
*
|
||||||
|
* Retrieves the uri of a bookmark. The value is guaranteed to not be %NULL.
|
||||||
|
*
|
||||||
|
* Return value: The uri of the @bookmark.
|
||||||
|
**/
|
||||||
|
G_CONST_RETURN gchar* xbel_bookmark_get_href(XbelItem* bookmark)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_item_is_bookmark(bookmark), NULL);
|
||||||
|
return bookmark->href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_is_bookmark:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Determines wether or not an item is a bookmark.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE if the @item is a bookmark.
|
||||||
|
**/
|
||||||
|
gboolean xbel_item_is_bookmark(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(item, FALSE);
|
||||||
|
return item->kind == XBEL_ITEM_BOOKMARK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_is_separator:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Determines wether or not an item is a separator.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE if the @item is a separator.
|
||||||
|
**/
|
||||||
|
gboolean xbel_item_is_separator(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(item, FALSE);
|
||||||
|
return item->kind == XBEL_ITEM_SEPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_is_folder:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Determines wether or not an item is a folder.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE if the item is a folder.
|
||||||
|
**/
|
||||||
|
gboolean xbel_item_is_folder(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(item, FALSE);
|
||||||
|
return item->kind == XBEL_ITEM_FOLDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_set_folded:
|
||||||
|
* @folder: A folder.
|
||||||
|
* @folded: TRUE if the folder is folded.
|
||||||
|
*
|
||||||
|
* Sets the foldedness of the @folder.
|
||||||
|
**/
|
||||||
|
void xbel_folder_set_folded(XbelItem* folder, gboolean folded)
|
||||||
|
{
|
||||||
|
g_return_if_fail(xbel_item_is_folder(folder));
|
||||||
|
folder->folded = folded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_set_title:
|
||||||
|
* @item: A valid item.
|
||||||
|
* @title: A string to use for the title.
|
||||||
|
*
|
||||||
|
* Sets the title of the @item.
|
||||||
|
**/
|
||||||
|
void xbel_item_set_title(XbelItem* item, const gchar* title)
|
||||||
|
{
|
||||||
|
g_return_if_fail(!xbel_item_is_separator(item));
|
||||||
|
g_free(item->title);
|
||||||
|
item->title = g_strdup(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_item_set_desc:
|
||||||
|
* @item: A valid item.
|
||||||
|
* @title: A string to use for the description.
|
||||||
|
*
|
||||||
|
* Sets the description of the @item.
|
||||||
|
**/
|
||||||
|
void xbel_item_set_desc(XbelItem* item, const gchar* desc)
|
||||||
|
{
|
||||||
|
g_return_if_fail(!xbel_item_is_separator(item));
|
||||||
|
g_free(item->desc);
|
||||||
|
item->desc = g_strdup(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_bookmark_set_href:
|
||||||
|
* @bookmark: A bookmark.
|
||||||
|
* @href: A string containing a valid uri.
|
||||||
|
*
|
||||||
|
* Sets the uri of the bookmark.
|
||||||
|
*
|
||||||
|
* The uri must not be %NULL.
|
||||||
|
*
|
||||||
|
* This uri is not currently validated in any way. This may change in the future.
|
||||||
|
**/
|
||||||
|
void xbel_bookmark_set_href(XbelItem* bookmark, const gchar* href)
|
||||||
|
{
|
||||||
|
g_return_if_fail(xbel_item_is_bookmark(bookmark));
|
||||||
|
g_return_if_fail(href);
|
||||||
|
g_free(bookmark->href);
|
||||||
|
bookmark->href = g_strdup(href);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean xbel_folder_from_data(XbelItem* folder, const gchar* data, GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_folder_is_empty(folder), FALSE);
|
||||||
|
g_return_val_if_fail(data, FALSE);
|
||||||
|
xmlDocPtr doc;
|
||||||
|
if((doc = xmlParseMemory(data, strlen(data))) == NULL)
|
||||||
|
{
|
||||||
|
// No valid xml or broken encoding
|
||||||
|
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
||||||
|
, "Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if(!xbel_folder_from_xmlDocPtr(folder, doc))
|
||||||
|
{
|
||||||
|
// Parsing failed
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
||||||
|
, "Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_from_file:
|
||||||
|
* @folder: An empty folder.
|
||||||
|
* @file: A relative path to a file.
|
||||||
|
* @error: return location for a GError or %NULL
|
||||||
|
*
|
||||||
|
* Tries to load @file.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE on success or %FALSE when an error occured.
|
||||||
|
**/
|
||||||
|
gboolean xbel_folder_from_file(XbelItem* folder, const gchar* file, GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_folder_is_empty(folder), FALSE);
|
||||||
|
g_return_val_if_fail(file, FALSE);
|
||||||
|
if(!g_file_test(file, G_FILE_TEST_EXISTS))
|
||||||
|
{
|
||||||
|
// File doesn't exist
|
||||||
|
*error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_NOENT
|
||||||
|
, "File not found.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
xmlDocPtr doc;
|
||||||
|
if((doc = xmlParseFile(file)) == NULL)
|
||||||
|
{
|
||||||
|
// No valid xml or broken encoding
|
||||||
|
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
||||||
|
, "Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if(!xbel_folder_from_xmlDocPtr(folder, doc))
|
||||||
|
{
|
||||||
|
// Parsing failed
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
||||||
|
, "Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_from_data_dirs:
|
||||||
|
* @folder: An empty folder.
|
||||||
|
* @file: A relative path to a file.
|
||||||
|
* @full_path: return location for the full path of the file or %NULL
|
||||||
|
* @error: return location for a GError or %NULL
|
||||||
|
*
|
||||||
|
* Tries to load @file from the user data dir or any of the system data dirs.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE on success or %FALSE when an error occured.
|
||||||
|
**/
|
||||||
|
gboolean xbel_folder_from_data_dirs(XbelItem* folder, const gchar* file
|
||||||
|
, gchar** full_path, GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_folder_is_empty(folder), FALSE);
|
||||||
|
g_return_val_if_fail(file, FALSE);
|
||||||
|
// FIXME: Essentially unimplemented
|
||||||
|
|
||||||
|
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
||||||
|
, "Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar* xbel_xml_element(const gchar* name, const gchar* value)
|
||||||
|
{
|
||||||
|
if(!value)
|
||||||
|
return g_strdup("");
|
||||||
|
gchar* valueEscaped = g_markup_escape_text(value, -1);
|
||||||
|
gchar* XML = g_strdup_printf("<%s>%s</%s>\n", name, valueEscaped, name);
|
||||||
|
g_free(valueEscaped);
|
||||||
|
return XML;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar* xbel_item_to_data(XbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(item, NULL);
|
||||||
|
gchar* XML = NULL;
|
||||||
|
switch(xbel_item_get_kind(item))
|
||||||
|
{
|
||||||
|
case XBEL_ITEM_FOLDER:
|
||||||
|
{
|
||||||
|
GString* _XML = g_string_new(NULL);
|
||||||
|
guint n = xbel_folder_get_n_items(item);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
XbelItem* _item = xbel_folder_get_nth_item(item, i);
|
||||||
|
gchar* itemXML = xbel_item_to_data(_item);
|
||||||
|
g_string_append(_XML, itemXML);
|
||||||
|
g_free(itemXML);
|
||||||
|
}
|
||||||
|
gchar* folded = item->folded ? NULL : g_strdup_printf(" folded=\"no\"");
|
||||||
|
gchar* title = xbel_xml_element("title", item->title);
|
||||||
|
gchar* desc = xbel_xml_element("desc", item->desc);
|
||||||
|
XML = g_strdup_printf("<folder%s>\n%s%s%s</folder>\n"
|
||||||
|
, folded ? folded : ""
|
||||||
|
, title
|
||||||
|
, desc
|
||||||
|
, g_string_free(_XML, FALSE));
|
||||||
|
g_free(folded);
|
||||||
|
g_free(title);
|
||||||
|
g_free(desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XBEL_ITEM_BOOKMARK:
|
||||||
|
{
|
||||||
|
gchar* hrefEscaped = g_markup_escape_text(item->href, -1);
|
||||||
|
gchar* href = g_strdup_printf(" href=\"%s\"", hrefEscaped);
|
||||||
|
g_free(hrefEscaped);
|
||||||
|
gchar* title = xbel_xml_element("title", item->title);
|
||||||
|
gchar* desc = xbel_xml_element("desc", item->desc);
|
||||||
|
XML = g_strdup_printf("<bookmark%s>\n%s%s%s</bookmark>\n"
|
||||||
|
, href
|
||||||
|
, title
|
||||||
|
, desc
|
||||||
|
, "");
|
||||||
|
g_free(href);
|
||||||
|
g_free(title);
|
||||||
|
g_free(desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XBEL_ITEM_SEPARATOR:
|
||||||
|
XML = g_strdup("<separator/>\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_warning("XBEL: Unknown item kind");
|
||||||
|
}
|
||||||
|
return XML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_to_data:
|
||||||
|
* @folder: A folder.
|
||||||
|
* @length: return location for the length of the created string or %NULL
|
||||||
|
* @error: return location for a GError or %NULL
|
||||||
|
*
|
||||||
|
* Retrieve the contents of @folder as a string.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE on success or %FALSE when an error occured.
|
||||||
|
**/
|
||||||
|
gchar* xbel_folder_to_data(XbelItem* folder, gsize* length, GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(xbel_item_is_folder(folder), FALSE);
|
||||||
|
// FIXME: length is never filled
|
||||||
|
GString* innerXML = g_string_new(NULL);
|
||||||
|
guint n = xbel_folder_get_n_items(folder);
|
||||||
|
guint i;
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
gchar* sItem = xbel_item_to_data(xbel_folder_get_nth_item(folder, i));
|
||||||
|
g_string_append(innerXML, sItem);
|
||||||
|
g_free(sItem);
|
||||||
|
}
|
||||||
|
gchar* title = xbel_xml_element("title", folder->title);
|
||||||
|
gchar* desc = xbel_xml_element("desc", folder->desc);
|
||||||
|
gchar* outerXML;
|
||||||
|
outerXML = g_strdup_printf("%s%s<xbel version=\"1.0\">\n%s%s%s</xbel>\n"
|
||||||
|
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
, "<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD XML Bookmark Exchange Language 1.0//EN//XML\" \"http://www.python.org/topics/xml/dtds/xbel-1.0.dtd\">\n"
|
||||||
|
, title
|
||||||
|
, desc
|
||||||
|
, g_string_free(innerXML, FALSE));
|
||||||
|
g_free(title);
|
||||||
|
g_free(desc);
|
||||||
|
return outerXML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xbel_folder_to_file:
|
||||||
|
* @folder: A folder.
|
||||||
|
* @file: The destination filename.
|
||||||
|
* @error: return location for a GError or %NULL
|
||||||
|
*
|
||||||
|
* Write the contents of @folder to the specified file, creating it if necessary.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE on success or %FALSE when an error occured.
|
||||||
|
**/
|
||||||
|
gboolean xbel_folder_to_file(XbelItem* folder, const gchar* file, GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(file, FALSE);
|
||||||
|
gchar* data;
|
||||||
|
if(!(data = xbel_folder_to_data(folder, NULL, error)))
|
||||||
|
return FALSE;
|
||||||
|
FILE* fp;
|
||||||
|
if(!(fp = fopen(file, "w")))
|
||||||
|
{
|
||||||
|
*error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_ACCES
|
||||||
|
, "Writing failed.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
fputs(data, fp);
|
||||||
|
fclose(fp);
|
||||||
|
g_free(data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
154
src/xbel.h
Normal file
154
src/xbel.h
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
See the file COPYING for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __XBEL_H__
|
||||||
|
#define __XBEL_H__ 1
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#define XBEL_ERROR g_quark_from_string("XBEL_ERROR")
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
XBEL_ERROR_INVALID_URI, /* Malformed uri */
|
||||||
|
XBEL_ERROR_INVALID_VALUE, /* Requested field not found */
|
||||||
|
XBEL_ERROR_URI_NOT_FOUND, /* Requested uri not found */
|
||||||
|
XBEL_ERROR_READ, /* Malformed document */
|
||||||
|
XBEL_ERROR_UNKNOWN_ENCODING, /* Parsed text was in an unknown encoding */
|
||||||
|
XBEL_ERROR_WRITE, /* Writing failed. */
|
||||||
|
} XBELError;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
XBEL_ITEM_FOLDER,
|
||||||
|
XBEL_ITEM_BOOKMARK,
|
||||||
|
XBEL_ITEM_SEPARATOR
|
||||||
|
} XbelItemKind;
|
||||||
|
|
||||||
|
// Note: This structure is entirely private.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
XbelItemKind kind;
|
||||||
|
struct XbelItem* parent;
|
||||||
|
|
||||||
|
GList* items; // folder
|
||||||
|
gboolean folded; // foolder
|
||||||
|
gchar* title; // !separator
|
||||||
|
gchar* desc; // folder and bookmark
|
||||||
|
gchar* href; // bookmark
|
||||||
|
//time_t added; // !separator
|
||||||
|
//time_t modfied; // bookmark
|
||||||
|
//time_t visited; // bookmark
|
||||||
|
} XbelItem;
|
||||||
|
|
||||||
|
XbelItem*
|
||||||
|
xbel_bookmark_new(void);
|
||||||
|
|
||||||
|
XbelItem*
|
||||||
|
xbel_separator_new(void);
|
||||||
|
|
||||||
|
XbelItem*
|
||||||
|
xbel_folder_new(void);
|
||||||
|
|
||||||
|
void
|
||||||
|
xbel_item_free(XbelItem*);
|
||||||
|
|
||||||
|
XbelItem*
|
||||||
|
xbel_item_copy(XbelItem*);
|
||||||
|
|
||||||
|
GType
|
||||||
|
xbel_item_get_type();
|
||||||
|
|
||||||
|
#define G_TYPE_XBEL_ITEM xbel_item_get_type()
|
||||||
|
|
||||||
|
void
|
||||||
|
xbel_folder_append_item(XbelItem*, XbelItem*);
|
||||||
|
|
||||||
|
void
|
||||||
|
xbel_folder_prepend_item(XbelItem*, XbelItem*);
|
||||||
|
|
||||||
|
void
|
||||||
|
xbel_folder_remove_item(XbelItem*, XbelItem*);
|
||||||
|
|
||||||
|
guint
|
||||||
|
xbel_folder_get_n_items(XbelItem*);
|
||||||
|
|
||||||
|
XbelItem*
|
||||||
|
xbel_folder_get_nth_item(XbelItem*, guint);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_folder_is_empty(XbelItem*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_folder_get_folded(XbelItem*);
|
||||||
|
|
||||||
|
XbelItemKind
|
||||||
|
xbel_item_get_kind(XbelItem*);
|
||||||
|
|
||||||
|
XbelItem*
|
||||||
|
xbel_item_get_parent(XbelItem*);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
xbel_item_get_title(XbelItem*);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
xbel_item_get_desc(XbelItem*);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
xbel_bookmark_get_href(XbelItem*);
|
||||||
|
|
||||||
|
/*time_t
|
||||||
|
xbel_bookmark_get_added(XbelItem*);
|
||||||
|
|
||||||
|
time_t
|
||||||
|
xbel_bookmark_get_modified(XbelItem*);
|
||||||
|
|
||||||
|
time_t
|
||||||
|
xbel_bookmark_get_visited(XbelItem*);*/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_item_is_bookmark(XbelItem*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_item_is_separator(XbelItem*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_item_is_folder(XbelItem*);
|
||||||
|
|
||||||
|
void
|
||||||
|
xbel_folder_set_folded(XbelItem*, gboolean);
|
||||||
|
|
||||||
|
void
|
||||||
|
xbel_item_set_title(XbelItem*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
xbel_item_set_desc(XbelItem*, const gchar*);
|
||||||
|
|
||||||
|
void
|
||||||
|
xbel_bookmark_set_href(XbelItem*, const gchar*);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_folder_from_data(XbelItem*, const gchar*, GError**);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_folder_from_file(XbelItem*, const gchar*, GError**);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_folder_from_data_dirs(XbelItem*, const gchar*, gchar**, GError**);
|
||||||
|
|
||||||
|
gchar*
|
||||||
|
xbel_folder_to_data(XbelItem*, gsize*, GError**);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
xbel_folder_to_file(XbelItem*, const gchar*, GError**);
|
||||||
|
|
||||||
|
#endif /* !__XBEL_H__ */
|
Loading…
Reference in a new issue