diff --git a/MAINTAINERS b/MAINTAINERS index 90d8b8bef..64471f608 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -33,6 +33,13 @@ M: Pavel Roschin W: S: Maintained +bracketcolors +* P: Asif Amin +* g: @asifamin13 +* M: Asif Amin +* W: +* S: Maintained + codenav P: Federico Reghenzani g: diff --git a/Makefile.am b/Makefile.am index 2dc37e3a1..68693f102 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,6 +21,10 @@ if ENABLE_AUTOMARK SUBDIRS += automark endif +if ENABLE_BRACKETCOLORS +SUBDIRS += bracketcolors +endif + if ENABLE_CODENAV SUBDIRS += codenav endif diff --git a/README b/README index 3bd6bc8e2..d9d664290 100644 --- a/README +++ b/README @@ -49,6 +49,7 @@ Available plugins are: * ``addons`` -- the Addons plugin * ``autoclose`` -- the Autoclose plugin * ``automark`` -- the Automark plugin +* ``bracketcolors`` -- the BracketColors plugin * ``codenav`` -- the CodeNav plugin * ``commander`` -- the Commander plugin * ``debugger`` -- the Debugger plugin diff --git a/bracketcolors/AUTHORS b/bracketcolors/AUTHORS new file mode 100644 index 000000000..284d02da0 --- /dev/null +++ b/bracketcolors/AUTHORS @@ -0,0 +1 @@ +Asif Amin diff --git a/bracketcolors/COPYING b/bracketcolors/COPYING new file mode 100644 index 000000000..d511905c1 --- /dev/null +++ b/bracketcolors/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies 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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/bracketcolors/ChangeLog b/bracketcolors/ChangeLog new file mode 100644 index 000000000..e69de29bb diff --git a/bracketcolors/Makefile.am b/bracketcolors/Makefile.am new file mode 100644 index 000000000..8b3dd53a5 --- /dev/null +++ b/bracketcolors/Makefile.am @@ -0,0 +1,4 @@ +include $(top_srcdir)/build/vars.auxfiles.mk + +SUBDIRS = src +plugin = bracketcolors diff --git a/bracketcolors/NEWS b/bracketcolors/NEWS new file mode 100644 index 000000000..e69de29bb diff --git a/bracketcolors/README b/bracketcolors/README new file mode 100644 index 000000000..abdde7bfc --- /dev/null +++ b/bracketcolors/README @@ -0,0 +1,32 @@ +Color brackets, parenthesis, and braces +======================================= + +.. contents:: + +About +----- + +This plugin enables bracket coloring features. Brackets are colored based on +nesting level, often referred as "rainbow brackets". + +Features +-------- + +* Color brackets for: { }, [ ], ( ) + +Usage +----- + +Install the plugin (https://plugins.geany.org/install.html) then +load it in Geany's plugin manager. + +Requirements +------------ + +* Geany >= 1.38 +* C++17 + +Contact developers +------------------ + +Asif Amin diff --git a/bracketcolors/src/BracketMap.cc b/bracketcolors/src/BracketMap.cc new file mode 100644 index 000000000..e219579a2 --- /dev/null +++ b/bracketcolors/src/BracketMap.cc @@ -0,0 +1,110 @@ +/* + * BracketMap.cc + * + * Copyright 2023 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "BracketMap.h" + + +// ----------------------------------------------------------------------------- + BracketMap::BracketMap() +/* + Constructor +----------------------------------------------------------------------------- */ +{ + +} + + +// ----------------------------------------------------------------------------- + BracketMap::~BracketMap() +/* + Destructor +----------------------------------------------------------------------------- */ +{ + +} + + +// ----------------------------------------------------------------------------- + void BracketMap::Update(Index index, Length length) +/* + +----------------------------------------------------------------------------- */ +{ + auto it = mBracketMap.find(index); + if (it != mBracketMap.end()) { + auto &bracket = it->second; + GetLength(bracket) = length; + } + else { + mBracketMap.insert( + std::make_pair(index, std::make_tuple(length, 0)) + ); + } +} + + +// ----------------------------------------------------------------------------- + void BracketMap::ComputeOrder() +/* + +----------------------------------------------------------------------------- */ +{ + std::stack orderStack; + + for (auto &it : mBracketMap) { + + const Index &startIndex = it.first; + Bracket &bracket = it.second; + Length length = GetLength(bracket); + Index endPos = startIndex + length; + + if (length == UNDEFINED) { + // Invalid brackets + GetOrder(bracket) = UNDEFINED; + continue; + } + + + if (orderStack.size() == 0) { + // First bracket + orderStack.push(endPos); + } + else if (startIndex > orderStack.top()) { + // not nested + while(orderStack.size() > 0 and orderStack.top() < startIndex) { + orderStack.pop(); + } + orderStack.push(endPos); + } + else { + // nested bracket + orderStack.push(endPos); + } + + GetOrder(bracket) = orderStack.size() - 1; + } +} diff --git a/bracketcolors/src/BracketMap.h b/bracketcolors/src/BracketMap.h new file mode 100644 index 000000000..c87420d23 --- /dev/null +++ b/bracketcolors/src/BracketMap.h @@ -0,0 +1,63 @@ +/* + * BracketMap.h + * + * Copyright 2023 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __BRACKET_MAP_H__ +#define __BRACKET_MAP_H__ + +#include +#include + +#include + + +// ----------------------------------------------------------------------------- + struct BracketMap +/* + Purpose: data structure which stores and computes nesting order +----------------------------------------------------------------------------- */ +{ + typedef gint Length, Order, Index; + typedef std::tuple Bracket; + std::map mBracketMap; + + BracketMap(); + ~BracketMap(); + + void Update(Index index, Length length); + void ComputeOrder(); + + static const gint UNDEFINED = -1; + + static Length& GetLength(Bracket &bracket) { + return std::get<0>(bracket); + } + static Order& GetOrder(Bracket &bracket) { + return std::get<1>(bracket); + } + + static const Length& GetLength(const Bracket &bracket) { + return std::get<0>(bracket); + } + static const Order& GetOrder(const Bracket &bracket) { + return std::get<1>(bracket); + } +}; + +#endif diff --git a/bracketcolors/src/Makefile.am b/bracketcolors/src/Makefile.am new file mode 100644 index 000000000..9e9afa949 --- /dev/null +++ b/bracketcolors/src/Makefile.am @@ -0,0 +1,15 @@ +include $(top_srcdir)/build/vars.build.mk +plugin = bracketcolors + +geanyplugins_LTLIBRARIES = bracketcolors.la + +bracketcolors_srcs = \ + bracketcolors.cc \ + BracketMap.cc \ + BracketMap.h + +bracketcolors_la_SOURCES = $(bracketcolors_srcs) +bracketcolors_la_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CFLAGS) -DG_LOG_DOMAIN=\"BracketColors\" +bracketcolors_la_LIBADD = $(COMMONLIBS) + +include $(top_srcdir)/build/cppcheck.mk diff --git a/bracketcolors/src/bracketcolors.cc b/bracketcolors/src/bracketcolors.cc new file mode 100644 index 000000000..43f0fc2d7 --- /dev/null +++ b/bracketcolors/src/bracketcolors.cc @@ -0,0 +1,1292 @@ +/* + * bracketcolors.cc + * + * Copyright 2023 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +/* --------------------------------- INCLUDES ------------------------------- */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_LOCALE_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include +#include "sciwrappers.h" + +#include "BracketMap.h" + +#define BC_NUM_COLORS 3 +#define BC_NO_ARG 0 +#define BC_STOP_ACTION TRUE +#define BC_CONTINUE_ACTION FALSE + +#define SSM(s, m, w, l) scintilla_send_message(s, m, w, l) + + +/* --------------------------------- CONSTANTS ------------------------------ */ + + typedef std::array BracketColorArray; + + /* + * These were copied from VS Code + * TODO: Make this user configurable, get from theme? + */ + + static const BracketColorArray sDarkBackgroundColors = { + "#FF00FF", "#FFFF00", "#00FFFF" + }; + + static const BracketColorArray sLightBackgroundColors = { + "#008000", "#000080", "#800000" + }; + + static const gchar *sPluginName = "bracketcolors"; + + // start index of indicators our plugin will use + static const guint sIndicatorIndex = INDICATOR_IME - BC_NUM_COLORS; + +/* ----------------------------------- TYPES -------------------------------- */ + + enum BracketType { + PAREN = 0, + BRACE, + BRACKET, + ANGLE, + COUNT + }; + + struct BracketColorsData { + + /* + * Associated with every document + */ + + GeanyDocument *doc; + + guint32 backgroundColor; + BracketColorArray bracketColors; + + gboolean init; + + guint computeTimeoutID, computeInterval; + guint drawTimeoutID; + + gboolean updateUI; + std::set recomputeIndicies, redrawIndicies; + + gboolean bracketColorsEnable[BracketType::COUNT]; + BracketMap bracketMaps[BracketType::COUNT]; + + BracketColorsData() : + doc(NULL), + bracketColors(sLightBackgroundColors), + init(FALSE), + computeTimeoutID(0), + computeInterval(500), + drawTimeoutID(0), + updateUI(FALSE) + { + for (guint i = 0; i < BracketType::COUNT; i++) { + bracketColorsEnable[i] = TRUE; + } + + /* + * color matching angle brackets seems to cause + * more confusion than its worth + */ + + bracketColorsEnable[BracketType::ANGLE] = FALSE; + } + + ~BracketColorsData() {} + + void RemoveFromQueues(BracketMap::Index index); + void StartTimers(); + void StopTimers(); + }; + +/* ---------------------------------- EXTERNS ------------------------------- */ + + GeanyPlugin *geany_plugin; + GeanyData *geany_data; + +/* --------------------------------- PROTOTYPES ----------------------------- */ + + static gboolean recompute_brackets_timeout(gpointer user_data); + static gboolean render_brackets_timeout(gpointer user_data); + +/* ------------------------------ IMPLEMENTATION ---------------------------- */ + + +// ----------------------------------------------------------------------------- + void BracketColorsData::StartTimers() + +/* + +----------------------------------------------------------------------------- */ +{ + if (computeTimeoutID == 0) { + computeTimeoutID = g_timeout_add_full( + G_PRIORITY_LOW, + 20, + recompute_brackets_timeout, + this, + NULL + ); + } + + if (drawTimeoutID == 0) { + drawTimeoutID = g_timeout_add_full( + G_PRIORITY_LOW, + 100, + render_brackets_timeout, + this, + NULL + ); + } +} + + + +// ----------------------------------------------------------------------------- + void BracketColorsData::StopTimers() + +/* + +----------------------------------------------------------------------------- */ +{ + if (computeTimeoutID > 0) { + g_source_remove(computeTimeoutID); + computeTimeoutID = 0; + } + + if (drawTimeoutID > 0) { + g_source_remove(drawTimeoutID); + drawTimeoutID = 0; + } +} + + + +// ----------------------------------------------------------------------------- + void BracketColorsData::RemoveFromQueues(BracketMap::Index index) + +/* + +----------------------------------------------------------------------------- */ +{ + { + auto it = recomputeIndicies.find(index); + if (it != recomputeIndicies.end()) { + recomputeIndicies.erase(it); + } + } + { + auto it = redrawIndicies.find(index); + if (it != redrawIndicies.end()) { + redrawIndicies.erase(it); + } + } +} + + +// ----------------------------------------------------------------------------- + static gboolean utils_is_dark(guint32 color) + +/* + +----------------------------------------------------------------------------- */ +{ + guint8 b = color >> 16; + guint8 g = color >> 8; + guint8 r = color; + + // https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color + guint8 y = ((r << 1) + r + (g << 2) + b) >> 3; + + if (y < 125) { + return TRUE; + } + + return FALSE; +} + + + +// ----------------------------------------------------------------------------- + static gboolean utils_parse_color( + const gchar *spec, + GdkColor *color + ) +/* + +----------------------------------------------------------------------------- */ +{ + gchar buf[64] = {0}; + + g_return_val_if_fail(spec != NULL, -1); + + if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X')) + { + /* convert to # format for GDK to understand it */ + buf[0] = '#'; + strncpy(buf + 1, spec + 2, sizeof(buf) - 2); + spec = buf; + } + + return gdk_color_parse(spec, color); +} + + + +// ----------------------------------------------------------------------------- + static gint utils_color_to_bgr(const GdkColor *c) +/* + +----------------------------------------------------------------------------- */ +{ + g_return_val_if_fail(c != NULL, -1); + return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16); +} + + + +// ----------------------------------------------------------------------------- + static gint utils_parse_color_to_bgr(const gchar *spec) +/* + +----------------------------------------------------------------------------- */ +{ + GdkColor color; + if (utils_parse_color(spec, &color)) { + return utils_color_to_bgr(&color); + } + else { + return -1; + } +} + + + +// ----------------------------------------------------------------------------- + static void assign_indicator_colors( + BracketColorsData *data + ) +/* + +----------------------------------------------------------------------------- */ +{ + ScintillaObject *sci = data->doc->editor->sci; + + for (guint i = 0; i < data->bracketColors.size(); i++) { + guint index = sIndicatorIndex + i; + std::string spec = data->bracketColors.at(i); + gint color = utils_parse_color_to_bgr(spec.c_str()); + SSM(sci, SCI_INDICSETSTYLE, index, INDIC_TEXTFORE); + SSM(sci, SCI_INDICSETFORE, index, color); + } +} + + + +// ----------------------------------------------------------------------------- + static gboolean has_document(void) +/* + sanity check +----------------------------------------------------------------------------- */ +{ + GtkNotebook *notebook = GTK_NOTEBOOK(geany_data->main_widgets->notebook); + gint currPage = gtk_notebook_get_current_page(notebook); + return currPage >= 0 ? TRUE : FALSE; +} + + + +// ----------------------------------------------------------------------------- + static gboolean is_curr_document( + BracketColorsData *data + ) +/* + check if this document is currently opened +----------------------------------------------------------------------------- */ +{ + GtkNotebook *notebook = GTK_NOTEBOOK(geany_data->main_widgets->notebook); + gint currPage = gtk_notebook_get_current_page(notebook); + GeanyDocument *currDoc = document_get_from_page(currPage); + + if (currDoc != NULL and currDoc == data->doc) { + return TRUE; + } + + return FALSE; +} + + + +// ----------------------------------------------------------------------------- + static void bracket_colors_data_free(gpointer data) +/* + +----------------------------------------------------------------------------- */ +{ + BracketColorsData *bcd = reinterpret_cast(data); + delete bcd; +} + + + +// ----------------------------------------------------------------------------- + static BracketColorsData* bracket_colors_data_new(GeanyDocument *doc) +/* + +----------------------------------------------------------------------------- */ +{ + BracketColorsData *newBCD = new BracketColorsData(); + + plugin_set_document_data_full( + geany_plugin, + doc, + sPluginName, + newBCD, + bracket_colors_data_free + ); + + return newBCD; +} + + + +// ----------------------------------------------------------------------------- + static gboolean is_bracket_type( + gchar ch, + BracketType type + ) +/* + check if char is bracket type +----------------------------------------------------------------------------- */ +{ + static const std::set sAllBrackets { + '(', ')', + '[', ']', + '{', '}', + '<', '>', + }; + + switch (type) { + case (BracketType::PAREN): { + if (ch == '(' or ch == ')') { + return TRUE; + } + return FALSE; + } + case(BracketType::BRACE): { + if (ch == '[' or ch == ']') { + return TRUE; + } + return FALSE; + } + case(BracketType::BRACKET): { + if (ch == '{' or ch == '}') { + return TRUE; + } + return FALSE; + } + case(BracketType::ANGLE): { + if (ch == '<' or ch == '>') { + return TRUE; + } + return FALSE; + } + case(BracketType::COUNT): { + return sAllBrackets.find(ch) != sAllBrackets.end() ? TRUE : FALSE; + } + default: + return FALSE; + } + +} + + + +// ----------------------------------------------------------------------------- + static gboolean is_open_bracket( + gchar ch, + BracketType type + ) +/* + check if char is open bracket type +----------------------------------------------------------------------------- */ +{ + static const std::set sAllOpenBrackets { + '(', + '[', + '{', + '<', + }; + + switch (type) { + case (BracketType::PAREN): { + if (ch == '(') { + return TRUE; + } + return FALSE; + } + case(BracketType::BRACE): { + if (ch == '[') { + return TRUE; + } + return FALSE; + } + case(BracketType::BRACKET): { + if (ch == '{') { + return TRUE; + } + return FALSE; + } + case(BracketType::ANGLE): { + if (ch == '<') { + return TRUE; + } + return FALSE; + } + case(BracketType::COUNT): { + return sAllOpenBrackets.find(ch) != sAllOpenBrackets.end() ? TRUE : FALSE; + } + default: + return FALSE; + } + +} + + + +// ----------------------------------------------------------------------------- + static gint compute_bracket_at( + ScintillaObject *sci, + BracketMap &bracketMap, + gint position, + bool updateInvalidMapping = true + ) +/* + compute bracket at position +----------------------------------------------------------------------------- */ +{ + gint matchedBrace = SSM(sci, SCI_BRACEMATCH, position, BC_NO_ARG); + gint braceIdentity = position; + + if (matchedBrace != -1) { + + gint length = matchedBrace - position; + + if (length > 0) { + // matched from start brace + bracketMap.Update(position, length); + } + else { + // matched from end brace + length = -length; + braceIdentity = position - length; + bracketMap.Update(braceIdentity, length); + } + } + else { + // invalid mapping + + if (is_open_bracket(sci_get_char_at(sci, position), BracketType::COUNT)) { + if (updateInvalidMapping) { + bracketMap.Update(position, BracketMap::UNDEFINED); + } + } + else { + // unknown start brace + braceIdentity = -1; + } + } + + return braceIdentity; +} + + + +// ----------------------------------------------------------------------------- + static gboolean is_ignore_style( + BracketColorsData *data, + gint position + ) +/* + check if position is part of non source section +----------------------------------------------------------------------------- */ +{ + ScintillaObject *sci = data->doc->editor->sci; + + gint style = SSM(sci, SCI_GETSTYLEAT, position, BC_NO_ARG); + gint lexer = sci_get_lexer(sci); + + return not highlighting_is_code_style(lexer, style); +} + + + +// ----------------------------------------------------------------------------- + static void find_all_brackets( + BracketColorsData &data + ) +/* + brute force search for brackets +----------------------------------------------------------------------------- */ +{ + ScintillaObject *sci = data.doc->editor->sci; + + gint length = sci_get_length(sci); + for (gint i = 0; i < length; i++) { + gchar ch = sci_get_char_at(sci, i); + if (is_bracket_type(ch, BracketType::COUNT)) { + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + if (data.bracketColorsEnable[bracketType] == TRUE) { + if (is_bracket_type(ch, static_cast(bracketType))) { + data.recomputeIndicies.insert(i); + data.updateUI = TRUE; + break; + } + } + } + } + } +} + + + +// ----------------------------------------------------------------------------- + static void remove_bc_indicators( + ScintillaObject *sci + ) +/* + remove indicators associated with this plugin +----------------------------------------------------------------------------- */ +{ + gint length = sci_get_length(sci); + for (gint i = 0; i < BC_NUM_COLORS; i++) { + SSM(sci, SCI_SETINDICATORCURRENT, sIndicatorIndex + i, BC_NO_ARG); + SSM(sci, SCI_INDICATORCLEARRANGE, 0, length); + } +} + + + +// ----------------------------------------------------------------------------- + static void set_bc_indicators_at( + ScintillaObject *sci, + const BracketColorsData &data, + gint index + ) +/* + assign indicator at position, check if already correct +----------------------------------------------------------------------------- */ +{ + for (gint i = 0; i < BracketType::COUNT; i++) { + + const BracketMap &bracketMap = data.bracketMaps[i]; + + auto it = bracketMap.mBracketMap.find(index); + if (it == bracketMap.mBracketMap.end()) { + continue; + } + + auto bracket = it->second; + + if (BracketMap::GetLength(bracket) != BracketMap::UNDEFINED) { + + std::array positions { + { index, index + BracketMap::GetLength(bracket) } + }; + + for (auto position : positions) { + + unsigned correctIndicatorIndex = sIndicatorIndex + \ + ((BracketMap::GetOrder(bracket) + i) % BC_NUM_COLORS); + + gint curr = SSM(sci, SCI_INDICATORVALUEAT, correctIndicatorIndex, position); + if (not curr) { + SSM( + sci, + SCI_SETINDICATORCURRENT, + correctIndicatorIndex, + BC_NO_ARG + ); + SSM(sci, SCI_INDICATORFILLRANGE, position, 1); + } + + // make sure there arent any other indicators at position + for ( + guint indicatorIndex = sIndicatorIndex; + indicatorIndex < sIndicatorIndex + BC_NUM_COLORS; + indicatorIndex++ + ) + { + if (indicatorIndex == correctIndicatorIndex) { + continue; + } + + gint hasIndicator = SSM(sci, SCI_INDICATORVALUEAT, indicatorIndex, position); + if (hasIndicator) { + SSM( + sci, + SCI_SETINDICATORCURRENT, + indicatorIndex, + BC_NO_ARG + ); + SSM(sci, SCI_INDICATORCLEARRANGE, position, 1); + } + } + } + } + } +} + + + + +// ----------------------------------------------------------------------------- + static void clear_bc_indicators( + ScintillaObject *sci, + gint position, gint length + ) +/* + clear bracket indicators in range +----------------------------------------------------------------------------- */ +{ + for (gint i = position; i < position + length; i++) { + for ( + guint indicatorIndex = sIndicatorIndex; + indicatorIndex < sIndicatorIndex + BC_NUM_COLORS; + indicatorIndex++ + ) + { + gint hasIndicator = SSM(sci, SCI_INDICATORVALUEAT, indicatorIndex, i); + if (hasIndicator) { + SSM( + sci, + SCI_SETINDICATORCURRENT, + indicatorIndex, + BC_NO_ARG + ); + SSM(sci, SCI_INDICATORCLEARRANGE, i, 1); + } + } + } +} + + + +// ----------------------------------------------------------------------------- + static gboolean move_brackets( + ScintillaObject *sci, + BracketColorsData &bracketColorsData, + gint position, gint length, + BracketType type + ) +/* + handle when text is added +----------------------------------------------------------------------------- */ +{ + if (bracketColorsData.bracketColorsEnable[type] == FALSE) { + return FALSE; + } + + BracketMap &bracketMap = bracketColorsData.bracketMaps[type]; + + std::set indiciesToAdjust, indiciesToRecompute; + + /* + * Look through existing bracket map and check if addition of characters + * will require adjustment + */ + + for (const auto &it : bracketMap.mBracketMap) { + const auto &bracket = it.second; + gint endPos = it.first + BracketMap::GetLength(bracket); + if (it.first >= position) { + indiciesToAdjust.insert(it.first); + } + else if ( + endPos >= position or + BracketMap::GetLength(bracket) == BracketMap::UNDEFINED + ) { + indiciesToRecompute.insert(it.first); + } + } + + gboolean madeChange = FALSE; + + // Check if the new characters that are added were brackets + for (gint i = position; i < position + length; i++) { + gchar newChar = sci_get_char_at(sci, i); + if (is_bracket_type(newChar, type)) { + madeChange = TRUE; + bracketColorsData.recomputeIndicies.insert(i); + } + } + + if (not indiciesToAdjust.size() and not indiciesToRecompute.size()) { + return madeChange; + } + + for (const auto &it : indiciesToRecompute) { + bracketColorsData.recomputeIndicies.insert(it); + } + + for (const auto &it : indiciesToAdjust) { + bracketMap.mBracketMap.insert( + std::make_pair( + it + length, + bracketMap.mBracketMap.at(it) + ) + ); + + bracketMap.mBracketMap.erase(it); + bracketColorsData.RemoveFromQueues(it); + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static gboolean remove_brackets( + ScintillaObject *sci, + BracketColorsData &bracketColorsData, + gint position, gint length, + BracketType type + ) +/* + handle when text is removed +----------------------------------------------------------------------------- */ +{ + if (bracketColorsData.bracketColorsEnable[type] == FALSE) { + return FALSE; + } + + BracketMap &bracketMap = bracketColorsData.bracketMaps[type]; + + std::set indiciesToRemove, indiciesToRecompute; + + for (const auto &it : bracketMap.mBracketMap) { + const auto &bracket = it.second; + gint endPos = it.first + BracketMap::GetLength(bracket); + // start bracket was deleted + if ( (it.first >= position) and (it.first < position + length) ) { + indiciesToRemove.insert(it.first); + } + // end bracket removed or space removed + else if (it.first >= position or endPos >= position) { + indiciesToRecompute.insert(it.first); + } + } + + if ( + not indiciesToRemove.size() and + not indiciesToRecompute.size() + ) { + return FALSE; + } + + for (const auto &it : indiciesToRemove) { + bracketMap.mBracketMap.erase(it); + bracketColorsData.RemoveFromQueues(it); + } + + for (const auto &it : indiciesToRecompute) { + // first bracket was moved backwards + if (it >= position) { + bracketMap.mBracketMap.insert( + std::make_pair( + it - length, + bracketMap.mBracketMap.at(it) + ) + ); + bracketMap.mBracketMap.erase(it); + bracketColorsData.RemoveFromQueues(it); + } + // last bracket was moved + else { + bracketColorsData.recomputeIndicies.insert(it); + } + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static void render_document( + ScintillaObject *sci, + BracketColorsData *data + ) +/* + +----------------------------------------------------------------------------- */ +{ + if (data->updateUI) { + + for ( + auto position = data->redrawIndicies.begin(); + position != data->redrawIndicies.end(); + position++ + ) + { + // if this bracket has been reinserted into the work queue, ignore + if (data->recomputeIndicies.find(*position) == data->recomputeIndicies.end()) { + set_bc_indicators_at(sci, *data, *position); + } + } + + data->redrawIndicies.clear(); + data->updateUI = FALSE; + } +} + + + +// ----------------------------------------------------------------------------- + static void on_sci_notify( + ScintillaObject *sci, + gint scn, + SCNotification *nt, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + BracketColorsData *data = reinterpret_cast(user_data); + + switch(nt->nmhdr.code) { + + case(SCN_UPDATEUI): { + + if (nt->updated & SC_UPDATE_CONTENT) { + + if (is_curr_document(data)) { + render_document(sci, data); + } + } + + break; + } + + case(SCN_MODIFIED): + { + if (nt->modificationType & SC_MOD_INSERTTEXT) { + + // if we insert into position that had bracket + clear_bc_indicators(sci, nt->position, nt->length); + + /* + * Check to adjust current bracket positions + */ + + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + if ( + move_brackets( + sci, + *data, + nt->position, nt->length, + static_cast(bracketType) + ) + ) { + data->updateUI = TRUE; + } + } + } + + if (nt->modificationType & SC_MOD_DELETETEXT) { + + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + if ( + remove_brackets( + sci, + *data, + nt->position, nt->length, + static_cast(bracketType) + ) + ) { + data->updateUI = TRUE; + } + } + } + + if (nt->modificationType & SC_MOD_CHANGESTYLE) { + + if (data->init == TRUE) { + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + if (data->bracketColorsEnable[bracketType] == FALSE) { + continue; + } + for (gint i = nt->position; i < nt->position + nt->length; i++) { + gchar currChar = sci_get_char_at(sci, i); + if (is_bracket_type(currChar, static_cast(bracketType))) { + //g_debug("%s: Handling style change for bracket at %d", __FUNCTION__, i); + data->recomputeIndicies.insert(i); + } + } + } + } + } + + break; + } + } +} + + + +// ----------------------------------------------------------------------------- + gboolean render_brackets_timeout( + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + if (not has_document()) { + return FALSE; + } + + BracketColorsData *data = reinterpret_cast(user_data); + + if (not is_curr_document(data)) { + data->StopTimers(); + return FALSE; + } + + /* + * check if background color changed + */ + + ScintillaObject *sci = data->doc->editor->sci; + guint32 currBGColor = SSM(sci, SCI_STYLEGETBACK, STYLE_DEFAULT, BC_NO_ARG); + if (currBGColor != data->backgroundColor) { + g_debug("%s: background color changed: %#04x", __FUNCTION__, currBGColor); + + gboolean currDark = utils_is_dark(currBGColor); + gboolean wasDark = utils_is_dark(data->backgroundColor); + + if (currDark != wasDark) { + g_debug("%s: Need to change colors scheme!", __FUNCTION__); + data->bracketColors = currDark ? sDarkBackgroundColors : sLightBackgroundColors; + assign_indicator_colors(data); + } + + data->backgroundColor = currBGColor; + } + + if (data->updateUI) { + render_document(sci, data); + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + gboolean recompute_brackets_timeout( + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + static const unsigned sIterationLimit = 50; + + if (not has_document()) { + return FALSE; + } + + BracketColorsData *data = reinterpret_cast(user_data); + if (not is_curr_document(data)) { + data->StopTimers(); + return FALSE; + } + + if (data->init == FALSE) { + find_all_brackets(*data); + data->init = TRUE; + } + + if (data->recomputeIndicies.size()) { + + ScintillaObject *sci = data->doc->editor->sci; + + unsigned numIterations = 0; + for ( + auto position = data->recomputeIndicies.begin(); + position != data->recomputeIndicies.end(); + numIterations++ + ) + { + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + + BracketMap &bracketMap = data->bracketMaps[bracketType]; + + if ( + is_bracket_type( + sci_get_char_at(sci, *position), + static_cast(bracketType) + ) + ) { + // check if in a comment + if (is_ignore_style(data, *position)) { + bracketMap.mBracketMap.erase(*position); + clear_bc_indicators(sci, *position, 1); + } + else { + gint brace = compute_bracket_at(sci, bracketMap, *position); + if (brace >= 0) { + data->redrawIndicies.insert(brace); + } + data->updateUI = TRUE; + } + } + + bracketMap.ComputeOrder(); + } + + position = data->recomputeIndicies.erase(position); + if (numIterations >= sIterationLimit) { + break; + } + } + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static void on_document_close( + GObject *obj, + GeanyDocument *doc, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + g_return_if_fail(DOC_VALID(doc)); + gpointer pluginData = plugin_get_document_data(geany_plugin, doc, sPluginName); + if (pluginData != NULL) { + BracketColorsData *data = reinterpret_cast(pluginData); + data->StopTimers(); + } + + ScintillaObject *sci = doc->editor->sci; + remove_bc_indicators(sci); +} + + + +// ----------------------------------------------------------------------------- + static void on_document_activate( + GObject *obj, + GeanyDocument *doc, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + gpointer pluginData = plugin_get_document_data(geany_plugin, doc, sPluginName); + if (pluginData != NULL) { + BracketColorsData *data = reinterpret_cast(pluginData); + data->StartTimers(); + } +} + + + +// ----------------------------------------------------------------------------- + static void on_startup_complete( + GObject *obj, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + GeanyDocument *currDoc = document_get_current(); + if (currDoc != NULL) { + gpointer pluginData = plugin_get_document_data(geany_plugin, currDoc, sPluginName); + if (pluginData != NULL) { + BracketColorsData *data = reinterpret_cast(pluginData); + data->StartTimers(); + } + } +} + + + + +// ----------------------------------------------------------------------------- + static void on_document_open( + GObject *obj, + GeanyDocument *doc, + gpointer user_data + ) +/* + +----------------------------------------------------------------------------- */ +{ + g_return_if_fail(DOC_VALID(doc)); + + BracketColorsData *data = bracket_colors_data_new(doc); + ScintillaObject *sci = doc->editor->sci; + data->doc = doc; + + plugin_signal_connect( + geany_plugin, + G_OBJECT(sci), "sci-notify", + FALSE, + G_CALLBACK(on_sci_notify), data + ); + + /* + * Setup our bracket indicators + */ + + data->backgroundColor = SSM(sci, SCI_STYLEGETBACK, STYLE_DEFAULT, BC_NO_ARG); + if (utils_is_dark(data->backgroundColor)) { + data->bracketColors = sDarkBackgroundColors; + } + + assign_indicator_colors(data); + + if (user_data == NULL) { + data->StartTimers(); + } + +} + + + +// ----------------------------------------------------------------------------- + static gboolean plugin_bracketcolors_init ( + GeanyPlugin *plugin, + gpointer pdata + ) +/* + +----------------------------------------------------------------------------- */ +{ + geany_plugin = plugin; + geany_data = plugin->geany_data; + + gboolean inInit = TRUE; + + guint i = 0; + foreach_document(i) + { + on_document_open(NULL, documents[i], (gpointer) &inInit); + } + + plugin_signal_connect( + plugin, + NULL, "document-activate", + FALSE, + G_CALLBACK(on_document_activate), NULL + ); + + on_startup_complete(NULL, (gpointer) &inInit); + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static void plugin_bracketcolors_cleanup ( + GeanyPlugin *plugin, + gpointer pdata + ) +/* + +----------------------------------------------------------------------------- */ +{ + guint i = 0; + foreach_document(i) + { + on_document_close(NULL, documents[i], NULL); + } +} + + + +// ----------------------------------------------------------------------------- + static PluginCallback plugin_bracketcolors_callbacks[] = +/* + +----------------------------------------------------------------------------- */ +{ + { "document-open", (GCallback) &on_document_open, FALSE, NULL }, + { "document-new", (GCallback) &on_document_open, FALSE, NULL }, + { "document-close", (GCallback) &on_document_close, FALSE, NULL }, + { "geany-startup-complete", (GCallback) &on_startup_complete, FALSE, NULL }, + { NULL, NULL, FALSE, NULL } +}; + + + + + +// ----------------------------------------------------------------------------- + extern "C" void geany_load_module(GeanyPlugin *plugin) +/* + Load module +----------------------------------------------------------------------------- */ +{ + main_locale_init(LOCALEDIR, GETTEXT_PACKAGE); + + /* Set metadata */ + plugin->info->name = _("Bracket Colors"); + plugin->info->description = _("Color nested brackets, braces, parenthesis"); + plugin->info->version = "0.1"; + plugin->info->author = "Asif Amin "; + + /* Set functions */ + plugin->funcs->init = plugin_bracketcolors_init; + plugin->funcs->cleanup = plugin_bracketcolors_cleanup; + plugin->funcs->callbacks = plugin_bracketcolors_callbacks; + + /* Register! */ + GEANY_PLUGIN_REGISTER(plugin, 226); +} diff --git a/build/ax_cxx_compile_stdcxx.m4 b/build/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..9413da624 --- /dev/null +++ b/build/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,962 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for no added switch, and then for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# Copyright (c) 2019 Enji Cooper +# Copyright (c) 2020 Jason Merrill +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [], [dnl + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi]) + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual ~Base() {} + virtual void f() {} + }; + + struct Derived : public Base + { + virtual ~Derived() override {} + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201703L + +#error "This is not a C++17 compiler" + +#else + +#include +#include +#include + +namespace cxx17 +{ + + namespace test_constexpr_lambdas + { + + constexpr int foo = [](){return 42;}(); + + } + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + + namespace test_template_argument_deduction_for_class_templates + { + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + + namespace test_structured_bindings + { + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } + + namespace test_exception_spec_type_system + { + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus < 201703L + +]]) diff --git a/build/ax_cxx_compile_stdcxx_17.m4 b/build/ax_cxx_compile_stdcxx_17.m4 new file mode 100644 index 000000000..a68341717 --- /dev/null +++ b/build/ax_cxx_compile_stdcxx_17.m4 @@ -0,0 +1,35 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_17.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_17([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++17 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++17. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 2 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_17], [AX_CXX_COMPILE_STDCXX([17], [$1], [$2])]) diff --git a/build/bracketcolors.m4 b/build/bracketcolors.m4 new file mode 100644 index 000000000..61ba44ae9 --- /dev/null +++ b/build/bracketcolors.m4 @@ -0,0 +1,31 @@ +AC_DEFUN([GP_CHECK_BRACKETCOLORS], +[ + GP_ARG_DISABLE([Bracketcolors], [auto]) + GP_CHECK_PLUGIN_GTK3_ONLY([Bracketcolors]) + GP_CHECK_PLUGIN_DEPS([Bracketcolors], [BRACKETCOLORS], [geany >= 1.38]) + + dnl Check for c++17. We really only need c++11 but scintilla is + dnl compiled with c++17 so just use that + + AS_IF( + [test x$enable_bracketcolors = xauto], [ + AX_CXX_COMPILE_STDCXX_17(, optional) + AS_IF( + [test ${HAVE_CXX17} = 1], [ + enable_bracketcolors=yes + ], + [enable_bracketcolors=no] + ) + ], + [test x$enable_bracketcolors = xyes], [ + AX_CXX_COMPILE_STDCXX_17(, mandatory) + ] + ) + + GP_COMMIT_PLUGIN_STATUS([Bracketcolors]) + AC_CONFIG_FILES([ + bracketcolors/Makefile + bracketcolors/src/Makefile + ]) +]) + diff --git a/build/geany-plugins.nsi b/build/geany-plugins.nsi index 16c1d2f96..53f86b7c6 100644 --- a/build/geany-plugins.nsi +++ b/build/geany-plugins.nsi @@ -174,6 +174,7 @@ Section Uninstall Delete "$INSTDIR\lib\geany\addons.dll" Delete "$INSTDIR\lib\geany\autoclose.dll" Delete "$INSTDIR\lib\geany\automark.dll" + Delete "$INSTDIR\lib\geany\bracketcolors.dll" Delete "$INSTDIR\lib\geany\codenav.dll" Delete "$INSTDIR\lib\geany\commander.dll" Delete "$INSTDIR\lib\geany\defineformat.dll" diff --git a/build/webhelper.m4 b/build/webhelper.m4 index e4f285712..eacef95c4 100644 --- a/build/webhelper.m4 +++ b/build/webhelper.m4 @@ -19,14 +19,13 @@ AC_DEFUN([GP_CHECK_WEBHELPER], fi fi - GP_CHECK_GTK3([webkit_package=webkitgtk-3.0], - [webkit_package=webkit-1.0]) + GP_CHECK_PLUGIN_GTK3_ONLY([webhelper]) GP_CHECK_PLUGIN_DEPS([WebHelper], [WEBHELPER], [$GP_GTK_PACKAGE >= ${GTK_VERSION} glib-2.0 >= ${GLIB_VERSION} gio-2.0 >= ${GIO_VERSION} gdk-pixbuf-2.0 >= ${GDK_PIXBUF_VERSION} - $webkit_package >= ${WEBKIT_VERSION} + webkit2gtk-4.0 >= ${WEBKIT_VERSION} gthread-2.0]) diff --git a/configure.ac b/configure.ac index d9fe4be06..aeef50925 100644 --- a/configure.ac +++ b/configure.ac @@ -13,6 +13,7 @@ AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC AC_PROG_CC_C99 AM_PROG_CC_C_O +AC_PROG_CXX AC_DISABLE_STATIC AC_PROG_LIBTOOL @@ -33,6 +34,7 @@ dnl plugin checks GP_CHECK_ADDONS GP_CHECK_AUTOCLOSE GP_CHECK_AUTOMARK +GP_CHECK_BRACKETCOLORS GP_CHECK_CODENAV GP_CHECK_COMMANDER GP_CHECK_DEBUGGER diff --git a/po/POTFILES.in b/po/POTFILES.in index 6e310f9b2..b34e0014b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -19,6 +19,9 @@ autoclose/src/autoclose.c # Automark automark/src/automark.c +# bracketcolors +bracketcolors/src/bracketcolors.cc + # codenav codenav/src/codenavigation.c codenav/src/goto_file.c diff --git a/webhelper/src/Makefile.am b/webhelper/src/Makefile.am index dd14094bd..b361565fa 100644 --- a/webhelper/src/Makefile.am +++ b/webhelper/src/Makefile.am @@ -34,7 +34,8 @@ webhelper_la_CPPFLAGS = $(AM_CPPFLAGS) \ webhelper_la_CFLAGS = $(AM_CFLAGS) \ $(WEBHELPER_CFLAGS) webhelper_la_LIBADD = $(COMMONLIBS) \ - $(WEBHELPER_LIBS) + $(WEBHELPER_LIBS) \ + -lm # These are generated in $(srcdir) because they are part of the distribution, # and should anyway only be regenerated if the .tpl changes, which is a diff --git a/webhelper/src/gwh-browser.c b/webhelper/src/gwh-browser.c index 4e3c774f9..a520d2e3b 100644 --- a/webhelper/src/gwh-browser.c +++ b/webhelper/src/gwh-browser.c @@ -21,13 +21,15 @@ #include "config.h" +#include #include #include #include #include #include +#include #include -#include +#include #include "gwh-utils.h" #include "gwh-settings.h" @@ -35,50 +37,6 @@ #include "gwh-plugin.h" -#if ! GTK_CHECK_VERSION (2, 18, 0) -# ifndef gtk_widget_set_visible -# define gtk_widget_set_visible(w, v) \ - ((v) ? gtk_widget_show (w) : gtk_widget_hide (w)) -# endif /* defined (gtk_widget_set_visible) */ -# ifndef gtk_widget_get_visible -# define gtk_widget_get_visible(w) \ - (GTK_WIDGET_VISIBLE (w)) -# endif /* defined (gtk_widget_get_visible) */ -#endif /* GTK_CHECK_VERSION (2, 18, 0) */ -#if ! GTK_CHECK_VERSION (2, 20, 0) -# ifndef gtk_widget_get_mapped -# define gtk_widget_get_mapped(w) \ - (GTK_WIDGET_MAPPED ((w))) -# endif /* defined (gtk_widget_get_mapped) */ -#endif /* GTK_CHECK_VERSION (2, 20, 0) */ -#if ! GTK_CHECK_VERSION (3, 0, 0) -/* the GtkComboBoxText API is actually available in 2.24, but Geany's - * gtkcompat.h hides it on < 3.0.0 to keep ABI compatibility on all 2.x, so we - * need to use the old API there too. */ -# define gtk_combo_box_get_entry_text_column(c) \ - (gtk_combo_box_entry_get_text_column (GTK_COMBO_BOX_ENTRY (c))) -#endif /* GTK_CHECK_VERSION (3, 0, 0) */ -#if ! GTK_CHECK_VERSION (3, 0, 0) -static void -combo_box_text_remove_all (GtkComboBoxText *combo_box) -{ - GtkListStore *store; - - g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box)); - - store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box))); - gtk_list_store_clear (store); -} -# define gtk_combo_box_text_remove_all combo_box_text_remove_all -#endif /* GTK_CHECK_VERSION (3, 0, 0) */ -#if GTK_CHECK_VERSION (3, 0, 0) -/* alias GtkObject, we implement the :destroy signal */ -# define GtkObject GtkWidget -# define GtkObjectClass GtkWidgetClass -# define GTK_OBJECT_CLASS GTK_WIDGET_CLASS -#endif /* GTK_CHECK_VERSION (3, 0, 0) */ - - struct _GwhBrowserPrivate { GwhSettings *settings; @@ -86,16 +44,8 @@ struct _GwhBrowserPrivate GIcon *default_icon; GtkWidget *toolbar; - GtkWidget *paned; GtkWidget *web_view; WebKitWebInspector *inspector; - GtkWidget *inspector_view; /* the widget that should be shown to - * display the inspector, not necessarily - * a WebKitWebView */ - GtkWidget *inspector_window; - gint inspector_window_x; - gint inspector_window_y; - GtkWidget *inspector_web_view; GtkWidget *url_entry; GtkWidget *url_combo; @@ -111,8 +61,6 @@ struct _GwhBrowserPrivate enum { PROP_0, - PROP_INSPECTOR_TRANSIENT_FOR, - PROP_ORIENTATION, PROP_URI, PROP_WEB_VIEW, PROP_TOOLBAR @@ -124,33 +72,34 @@ enum { }; static guint signals[LAST_SIGNAL] = {0}; +static const gdouble zoom_in_factor = 1.2; +static const gdouble zoom_out_factor = 1.0 / 1.2; -G_DEFINE_TYPE_WITH_CODE (GwhBrowser, gwh_browser, GTK_TYPE_VBOX, - G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)) - - -static void inspector_set_detached (GwhBrowser *self, - gboolean detached); +G_DEFINE_TYPE (GwhBrowser, gwh_browser, GTK_TYPE_VBOX) static void set_location_icon (GwhBrowser *self, - const gchar *icon_uri) + cairo_surface_t *icon_surface) { gboolean success = FALSE; - - if (icon_uri) { + + if (icon_surface) { GdkPixbuf *icon; - - icon = gwh_pixbuf_new_from_uri (icon_uri, NULL); - if (icon) { - gtk_entry_set_icon_from_pixbuf (GTK_ENTRY (self->priv->url_entry), - GTK_ENTRY_ICON_PRIMARY, icon); - g_object_unref (icon); - success = TRUE; - } + + icon = gdk_pixbuf_get_from_surface ( + icon_surface, 0, 0, + cairo_image_surface_get_width (icon_surface), + cairo_image_surface_get_height (icon_surface) + ); + + gtk_entry_set_icon_from_pixbuf (GTK_ENTRY (self->priv->url_entry), + GTK_ENTRY_ICON_PRIMARY, icon); + g_object_unref (icon); + success = TRUE; } + if (! success) { if (G_UNLIKELY (self->priv->default_icon == NULL)) { gchar *ctype; @@ -165,23 +114,6 @@ set_location_icon (GwhBrowser *self, } } -static gchar * -get_web_inspector_window_geometry (GwhBrowser *self) -{ - return gwh_get_window_geometry (GTK_WINDOW (self->priv->inspector_window), - self->priv->inspector_window_x, - self->priv->inspector_window_y); -} - -static void -set_web_inspector_window_geometry (GwhBrowser *self, - const gchar *geometry) -{ - gwh_set_window_geometry (GTK_WINDOW (self->priv->inspector_window), - geometry, &self->priv->inspector_window_x, - &self->priv->inspector_window_y); -} - /* settings change notifications */ static void @@ -218,82 +150,10 @@ on_settings_browser_bookmarks_notify (GObject *object, } } -static void -on_settings_browser_orientation_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) -{ - GtkOrientation orientation; - - g_object_get (object, pspec->name, &orientation, NULL); - if (orientation != gtk_orientable_get_orientation (GTK_ORIENTABLE (self))) { - gtk_orientable_set_orientation (GTK_ORIENTABLE (self), orientation); - } -} - -static void -on_settings_inspector_window_geometry_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) -{ - gchar *geometry; - - g_object_get (object, pspec->name, &geometry, NULL); - set_web_inspector_window_geometry (self, geometry); - g_free (geometry); -} - -static void -on_settings_inspector_detached_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) -{ - gboolean detached; - - g_object_get (object, pspec->name, &detached, NULL); - inspector_set_detached (self, detached); -} - -static void -on_settings_wm_windows_skip_taskbar_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) -{ - gboolean skips_taskbar; - - g_object_get (object, pspec->name, &skips_taskbar, NULL); - gtk_window_set_skip_taskbar_hint (GTK_WINDOW (self->priv->inspector_window), - skips_taskbar); -} - -static void -on_settings_wm_windows_type_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) -{ - gint type; - gboolean remap = gtk_widget_get_mapped (self->priv->inspector_window); - - /* We need to remap the window if we change its type because it's not doable - * when mapped. So, hack around. */ - - g_object_get (object, pspec->name, &type, NULL); - if (remap) { - gtk_widget_unmap (self->priv->inspector_window); - } - gtk_window_set_type_hint (GTK_WINDOW (self->priv->inspector_window), type); - if (remap) { - gtk_widget_map (self->priv->inspector_window); - } -} - /* web inspector events handling */ -#define INSPECTOR_DETACHED(self) \ - (gtk_bin_get_child (GTK_BIN ((self)->priv->inspector_window)) != NULL) - #define INSPECTOR_VISIBLE(self) \ - (gtk_widget_get_visible ((self)->priv->inspector_view)) + (webkit_web_inspector_get_web_view ((self)->priv->inspector) != NULL) static void inspector_set_visible (GwhBrowser *self, @@ -308,117 +168,22 @@ inspector_set_visible (GwhBrowser *self, } } -static void -inspector_hide_window (GwhBrowser *self) -{ - if (gtk_widget_get_visible (self->priv->inspector_window)) { - gtk_window_get_position (GTK_WINDOW (self->priv->inspector_window), - &self->priv->inspector_window_x, - &self->priv->inspector_window_y); - gtk_widget_hide (self->priv->inspector_window); - } -} - -static void -inspector_show_window (GwhBrowser *self) -{ - if (! gtk_widget_get_visible (self->priv->inspector_window)) { - gtk_widget_show (self->priv->inspector_window); - gtk_window_move (GTK_WINDOW (self->priv->inspector_window), - self->priv->inspector_window_x, - self->priv->inspector_window_y); - } -} - -static void -inspector_set_detached (GwhBrowser *self, - gboolean detached) -{ - if (detached != INSPECTOR_DETACHED (self)) { - if (detached) { - gtk_widget_reparent (self->priv->inspector_view, - self->priv->inspector_window); - if (INSPECTOR_VISIBLE (self)) { - inspector_show_window (self); - } - } else { - gtk_widget_reparent (self->priv->inspector_view, self->priv->paned); - inspector_hide_window (self); - } - g_object_set (self->priv->settings, "inspector-detached", detached, NULL); - } -} - -static WebKitWebView * -on_inspector_inspect_web_view (WebKitWebInspector *inspector, - WebKitWebView *view, - GwhBrowser *self) -{ - if (self->priv->inspector_web_view) { - gtk_widget_destroy (self->priv->inspector_web_view); - } - - self->priv->inspector_web_view = webkit_web_view_new (); - gtk_widget_show (self->priv->inspector_web_view); - gtk_container_add (GTK_CONTAINER (self->priv->inspector_view), - self->priv->inspector_web_view); - - return WEBKIT_WEB_VIEW (self->priv->inspector_web_view); -} - static gboolean -on_inspector_show_window (WebKitWebInspector *inspector, - GwhBrowser *self) +on_inspector_closed (WebKitWebInspector *inspector, + GwhBrowser *self) { - gtk_widget_show (self->priv->inspector_view); - if (INSPECTOR_DETACHED (self)) { - inspector_show_window (self); - } - gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (self->priv->item_inspector), - TRUE); - - return TRUE; -} - -static gboolean -on_inspector_close_window (WebKitWebInspector *inspector, - GwhBrowser *self) -{ - gtk_widget_hide (self->priv->inspector_view); - inspector_hide_window (self); gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (self->priv->item_inspector), FALSE); - gtk_widget_grab_focus (gtk_widget_get_toplevel (self->priv->web_view)); - - return TRUE; -} - -static gboolean -on_inspector_detach_window (WebKitWebInspector *inspector, - GwhBrowser *self) -{ - inspector_set_detached (self, TRUE); - - return TRUE; + return FALSE; } static gboolean -on_inspector_attach_window (WebKitWebInspector *inspector, - GwhBrowser *self) -{ - inspector_set_detached (self, FALSE); - - return TRUE; -} - -static gboolean -on_inspector_window_delete_event (GtkWidget *window, - GdkEvent *event, - GwhBrowser *self) +on_inspector_opened (WebKitWebInspector *inspector, + GwhBrowser *self) { - webkit_web_inspector_close (self->priv->inspector); - - return TRUE; + gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (self->priv->item_inspector), + TRUE); + return FALSE; } /* web view events hanlding */ @@ -519,20 +284,9 @@ update_load_status (GwhBrowser *self) { gboolean loading = FALSE; WebKitWebView *web_view = WEBKIT_WEB_VIEW (self->priv->web_view); - - switch (webkit_web_view_get_load_status (web_view)) { - case WEBKIT_LOAD_PROVISIONAL: - case WEBKIT_LOAD_COMMITTED: - case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT: - loading = TRUE; - break; - - case WEBKIT_LOAD_FINISHED: - case WEBKIT_LOAD_FAILED: - loading = FALSE; - break; - } - + + loading = webkit_web_view_is_loading (web_view); + gtk_widget_set_sensitive (GTK_WIDGET (self->priv->item_reload), ! loading); gtk_widget_set_visible (GTK_WIDGET (self->priv->item_reload), ! loading); gtk_widget_set_sensitive (GTK_WIDGET (self->priv->item_cancel), loading); @@ -542,23 +296,23 @@ update_load_status (GwhBrowser *self) } static void -on_web_view_load_status_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) +on_web_view_load_changed (WebKitWebView *object, + WebKitLoadEvent load_event, + GwhBrowser *self) { update_load_status (self); } static gboolean -on_web_view_load_error (WebKitWebView *web_view, - WebKitWebFrame *web_frame, - gchar *uri, - gpointer web_error, - GwhBrowser *self) +on_web_view_load_failed (WebKitWebView *web_view, + WebKitLoadEvent load_event, + gchar *failing_uri, + GError *error, + GwhBrowser *self) { update_load_status (self); - return FALSE; /* we didn't really handled the error, so return %FALSE */ + return FALSE; /* we didn't really handle the error, so return %FALSE */ } static void @@ -575,118 +329,133 @@ on_web_view_uri_notify (GObject *object, } static void -on_web_view_icon_uri_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) +on_web_view_favicon_notify (GObject *object, + GParamSpec *pspec, + GwhBrowser *self) { - const gchar *icon_uri; + cairo_surface_t *icon_surface; - icon_uri = webkit_web_view_get_icon_uri (WEBKIT_WEB_VIEW (self->priv->web_view)); - set_location_icon (self, icon_uri); + icon_surface = webkit_web_view_get_favicon (WEBKIT_WEB_VIEW (self->priv->web_view)); + set_location_icon (self, icon_surface); } static void -on_web_view_progress_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) +on_web_view_estimated_load_progress_notify (GObject *object, + GParamSpec *pspec, + GwhBrowser *self) { gdouble value; - value = webkit_web_view_get_progress (WEBKIT_WEB_VIEW (self->priv->web_view)); + value = webkit_web_view_get_estimated_load_progress (WEBKIT_WEB_VIEW (self->priv->web_view)); if (value >= 1.0) { value = 0.0; } gtk_entry_set_progress_fraction (GTK_ENTRY (self->priv->url_entry), value); } - static void -on_item_flip_orientation_activate (GtkMenuItem *item, - GwhBrowser *self) +on_item_zoom_100_activate (WebKitWebView *view) { - gtk_orientable_set_orientation (GTK_ORIENTABLE (self), - gtk_orientable_get_orientation (GTK_ORIENTABLE (self)) == GTK_ORIENTATION_VERTICAL - ? GTK_ORIENTATION_HORIZONTAL - : GTK_ORIENTATION_VERTICAL); + webkit_web_view_set_zoom_level (view, 1.0); } static void -on_item_zoom_100_activate (GtkMenuItem *item, - GwhBrowser *self) +on_item_full_content_zoom_activate (GSimpleAction *action, + GVariant *dummy_parameter, + GwhBrowser *self) { - webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (self->priv->web_view), 1.0); + WebKitSettings *settings; + gboolean zoom_text_only; + + settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (self->priv->web_view)); + + zoom_text_only = !webkit_settings_get_zoom_text_only (settings); + webkit_settings_set_zoom_text_only (settings, zoom_text_only); + g_simple_action_set_state (action, g_variant_new_boolean (!zoom_text_only)); } -static void -on_item_full_content_zoom_activate (GtkCheckMenuItem *item, - GwhBrowser *self) +static void web_view_zoom (WebKitWebView *view, gdouble factor) { - webkit_web_view_set_full_content_zoom (WEBKIT_WEB_VIEW (self->priv->web_view), - gtk_check_menu_item_get_active (item)); + gdouble zoom_level = webkit_web_view_get_zoom_level (view); + webkit_web_view_set_zoom_level (view, zoom_level * factor); } -static void -on_web_view_populate_popup (WebKitWebView *view, - GtkMenu *menu, - GwhBrowser *self) +static void web_view_zoom_in (WebKitWebView *view) { - GtkWidget *item; - GtkWidget *submenu; - - #define ADD_SEPARATOR(menu) \ - item = gtk_separator_menu_item_new (); \ - gtk_widget_show (item); \ - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item) - - ADD_SEPARATOR (menu); - + web_view_zoom (view, zoom_in_factor); +} + +static void web_view_zoom_out (WebKitWebView *view) +{ + web_view_zoom (view, zoom_out_factor); +} + +static gboolean +on_web_view_context_menu (WebKitWebView *view, + WebKitContextMenu *context_menu, + GdkEvent *event, + WebKitHitTestResult *hit_test_result, + GwhBrowser *self) +{ + WebKitContextMenuItem *item; + WebKitContextMenu *submenu; + GSimpleAction *action; + gboolean zoom_text_only; + + webkit_context_menu_append (context_menu, + webkit_context_menu_item_new_separator ()); + /* Zoom menu */ - submenu = gtk_menu_new (); - item = gtk_menu_item_new_with_mnemonic (_("_Zoom")); - gtk_widget_show (item); - gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + submenu = webkit_context_menu_new (); + item = webkit_context_menu_item_new_with_submenu (_("_Zoom"), submenu); + webkit_context_menu_append (context_menu, item); + /* zoom in */ - item = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_IN, NULL); - g_signal_connect_swapped (item, "activate", - G_CALLBACK (webkit_web_view_zoom_in), view); - gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item); + action = g_simple_action_new ("zoom-in", NULL); + g_signal_connect_swapped (action, "activate", + G_CALLBACK (web_view_zoom_in), view); + item = webkit_context_menu_item_new_from_gaction (G_ACTION (action), + _("Zoom _In"), NULL); + webkit_context_menu_append (submenu, item); + g_object_unref (action); + /* zoom out */ - item = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_OUT, NULL); - g_signal_connect_swapped (item, "activate", - G_CALLBACK (webkit_web_view_zoom_out), view); - gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item); + action = g_simple_action_new ("zoom-out", NULL); + g_signal_connect_swapped (action, "activate", + G_CALLBACK (web_view_zoom_out), view); + item = webkit_context_menu_item_new_from_gaction (G_ACTION (action), + _("Zoom _Out"), NULL); + webkit_context_menu_append (submenu, item); + g_object_unref (action); + /* zoom 1:1 */ - ADD_SEPARATOR (submenu); - item = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_100, NULL); - g_signal_connect (item, "activate", - G_CALLBACK (on_item_zoom_100_activate), self); - gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item); + webkit_context_menu_append (submenu, + webkit_context_menu_item_new_separator ()); + action = g_simple_action_new ("zoom-reset", NULL); + g_signal_connect_swapped (action, "activate", + G_CALLBACK (on_item_zoom_100_activate), view); + item = webkit_context_menu_item_new_from_gaction (G_ACTION (action), + _("_Reset Zoom"), NULL); + webkit_context_menu_append (submenu, item); + g_object_unref (action); + /* full content zoom */ - ADD_SEPARATOR (submenu); - item = gtk_check_menu_item_new_with_mnemonic (_("Full-_content zoom")); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), - webkit_web_view_get_full_content_zoom (view)); - g_signal_connect (item, "activate", + webkit_context_menu_append (submenu, + webkit_context_menu_item_new_separator ()); + zoom_text_only = webkit_settings_get_zoom_text_only (webkit_web_view_get_settings (view)); + action = g_simple_action_new_stateful ("full-content-zoom", NULL, + g_variant_new_boolean (!zoom_text_only)); + g_signal_connect (action, "activate", G_CALLBACK (on_item_full_content_zoom_activate), self); - gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item); - /* show zoom sumbenu */ - gtk_widget_show_all (submenu); - - ADD_SEPARATOR (menu); - - item = gtk_menu_item_new_with_label (_("Flip panes orientation")); - g_signal_connect (item, "activate", - G_CALLBACK (on_item_flip_orientation_activate), self); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - if (! INSPECTOR_VISIBLE (self) || INSPECTOR_DETACHED (self)) { - gtk_widget_set_sensitive (item, FALSE); - } - - #undef ADD_SEPARATOR - - g_signal_emit (self, signals[POPULATE_POPUP], 0, menu); + item = webkit_context_menu_item_new_from_gaction (G_ACTION (action), + _("Full-_content zoom"), + NULL); + webkit_context_menu_append (submenu, item); + g_object_unref (action); + + g_signal_emit (self, signals[POPULATE_POPUP], 0, context_menu); + + return FALSE; } static gboolean @@ -696,18 +465,27 @@ on_web_view_scroll_event (GtkWidget *widget, { guint mods = event->state & gtk_accelerator_get_default_mod_mask (); gboolean handled = FALSE; + gdouble delta; + gdouble factor; if (mods == GDK_CONTROL_MASK) { handled = TRUE; switch (event->direction) { case GDK_SCROLL_DOWN: - webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (self->priv->web_view)); + web_view_zoom_out (WEBKIT_WEB_VIEW (self->priv->web_view)); break; case GDK_SCROLL_UP: - webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (self->priv->web_view)); + web_view_zoom_in (WEBKIT_WEB_VIEW (self->priv->web_view)); break; - + + case GDK_SCROLL_SMOOTH: + delta = event->delta_x + event->delta_y; + factor = pow (delta < 0 ? zoom_in_factor : zoom_out_factor, + fabs (delta)); + web_view_zoom (WEBKIT_WEB_VIEW (self->priv->web_view), factor); + break; + default: handled = FALSE; } @@ -717,27 +495,9 @@ on_web_view_scroll_event (GtkWidget *widget, } static void -on_orientation_notify (GObject *object, - GParamSpec *pspec, - GwhBrowser *self) +gwh_browser_destroy (GtkWidget *widget) { - g_object_set (G_OBJECT (self->priv->settings), "browser-orientation", - gtk_orientable_get_orientation (GTK_ORIENTABLE (self)), NULL); -} - -static void -gwh_browser_destroy (GtkObject *object) -{ - GwhBrowser *self = GWH_BROWSER (object); - gchar *geometry; - - /* save the setting now because we can't really set it at the time it changed, - * but it's not a problem, anyway probably nobody but us is interested by the - * geometry of our inspector window. */ - geometry = get_web_inspector_window_geometry (self); - g_object_set (self->priv->settings, "inspector-window-geometry", geometry, - NULL); - g_free (geometry); + GwhBrowser *self = GWH_BROWSER (widget); /* remove signal handlers that might get called during the destruction phase * but that rely on stuff that might already heave been destroyed */ @@ -750,10 +510,8 @@ gwh_browser_destroy (GtkObject *object) g_signal_handlers_disconnect_matched (self->priv->settings, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); - /* also destroy the window, since it has no parent that will tell it to die */ - gtk_widget_destroy (self->priv->inspector_window); - GTK_OBJECT_CLASS (gwh_browser_parent_class)->destroy (object); + GTK_WIDGET_CLASS (gwh_browser_parent_class)->destroy (widget); } static void @@ -783,8 +541,6 @@ gwh_browser_constructed (GObject *object) /* a bit ugly, fake notifications */ g_object_notify (G_OBJECT (self->priv->settings), "browser-last-uri"); g_object_notify (G_OBJECT (self->priv->settings), "browser-bookmarks"); - g_object_notify (G_OBJECT (self->priv->settings), "browser-orientation"); - g_object_notify (G_OBJECT (self->priv->settings), "inspector-window-geometry"); } static void @@ -794,20 +550,10 @@ gwh_browser_get_property (GObject *object, GParamSpec *pspec) { switch (prop_id) { - case PROP_INSPECTOR_TRANSIENT_FOR: - g_value_set_object (value, - gwh_browser_get_inspector_transient_for (GWH_BROWSER (object))); - break; - case PROP_URI: g_value_set_string (value, gwh_browser_get_uri (GWH_BROWSER (object))); break; - case PROP_ORIENTATION: - g_value_set_enum (value, - gtk_orientable_get_orientation (GTK_ORIENTABLE (GWH_BROWSER (object)->priv->paned))); - break; - case PROP_TOOLBAR: g_value_set_object (value, GWH_BROWSER (object)->priv->toolbar); break; @@ -828,20 +574,10 @@ gwh_browser_set_property (GObject *object, GParamSpec *pspec) { switch (prop_id) { - case PROP_INSPECTOR_TRANSIENT_FOR: - gwh_browser_set_inspector_transient_for (GWH_BROWSER (object), - g_value_get_object (value)); - break; - case PROP_URI: gwh_browser_set_uri (GWH_BROWSER (object), g_value_get_string (value)); break; - case PROP_ORIENTATION: - gtk_orientable_set_orientation (GTK_ORIENTABLE (GWH_BROWSER (object)->priv->paned), - g_value_get_enum (value)); - break; - default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -859,7 +595,6 @@ static void gwh_browser_class_init (GwhBrowserClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->finalize = gwh_browser_finalize; @@ -867,8 +602,7 @@ gwh_browser_class_init (GwhBrowserClass *klass) object_class->get_property = gwh_browser_get_property; object_class->set_property = gwh_browser_set_property; - gtkobject_class->destroy = gwh_browser_destroy; - + widget_class->destroy = gwh_browser_destroy; widget_class->show_all = gwh_browser_show_all; signals[POPULATE_POPUP] = g_signal_new ("populate-popup", @@ -877,18 +611,8 @@ gwh_browser_class_init (GwhBrowserClass *klass) G_STRUCT_OFFSET (GwhBrowserClass, populate_popup), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, GTK_TYPE_MENU); - - g_object_class_override_property (object_class, - PROP_ORIENTATION, - "orientation"); + G_TYPE_NONE, 1, WEBKIT_TYPE_CONTEXT_MENU); - g_object_class_install_property (object_class, PROP_INSPECTOR_TRANSIENT_FOR, - g_param_spec_object ("inspector-transient-for", - "Inspector transient for", - "The parent window of the inspector when detached", - GTK_TYPE_WINDOW, - G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_URI, g_param_spec_string ("uri", "URI", @@ -934,6 +658,19 @@ url_completion_match_func (GtkEntryCompletion *comp, return match; } +static GtkToolItem * +tool_button_new (const gchar *mnemonic, + const gchar *icon_name, + const gchar *tooltip) +{ + return g_object_new (GTK_TYPE_TOOL_BUTTON, + "use-underline", TRUE, + "label", mnemonic, + "icon-name", icon_name, + "tooltip-text", tooltip, + NULL); +} + static GtkWidget * create_toolbar (GwhBrowser *self) { @@ -946,20 +683,20 @@ create_toolbar (GwhBrowser *self) "toolbar-style", GTK_TOOLBAR_ICONS, NULL); - self->priv->item_prev = gtk_tool_button_new_from_stock (GTK_STOCK_GO_BACK); - gtk_tool_item_set_tooltip_text (self->priv->item_prev, _("Back")); + self->priv->item_prev = tool_button_new (_("_Back"), "go-previous", + _("Go back")); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), self->priv->item_prev, -1); gtk_widget_show (GTK_WIDGET (self->priv->item_prev)); - self->priv->item_next = gtk_tool_button_new_from_stock (GTK_STOCK_GO_FORWARD); - gtk_tool_item_set_tooltip_text (self->priv->item_next, _("Forward")); + self->priv->item_next = tool_button_new (_("_Forward"), "go-next", + _("Go forward")); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), self->priv->item_next, -1); gtk_widget_show (GTK_WIDGET (self->priv->item_next)); - self->priv->item_cancel = gtk_tool_button_new_from_stock (GTK_STOCK_CANCEL); - gtk_tool_item_set_tooltip_text (self->priv->item_cancel, _("Cancel loading")); + self->priv->item_cancel = tool_button_new (_("_Cancel"), "process-stop", + _("Cancel loading")); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), self->priv->item_cancel, -1); /* don't show cancel */ - self->priv->item_reload = gtk_tool_button_new_from_stock (GTK_STOCK_REFRESH); - gtk_tool_item_set_tooltip_text (self->priv->item_reload, _("Reload current page")); + self->priv->item_reload = tool_button_new (_("_Refresh"), "view-refresh", + _("Reload current page")); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), self->priv->item_reload, -1); gtk_widget_show (GTK_WIDGET (self->priv->item_reload)); @@ -983,9 +720,12 @@ create_toolbar (GwhBrowser *self) gtk_entry_completion_set_match_func (comp, url_completion_match_func, NULL, NULL); gtk_entry_set_completion (GTK_ENTRY (self->priv->url_entry), comp); - self->priv->item_inspector = gtk_toggle_tool_button_new_from_stock (GTK_STOCK_INFO); - gtk_tool_button_set_label (GTK_TOOL_BUTTON (self->priv->item_inspector), _("Web inspector")); - gtk_tool_item_set_tooltip_text (self->priv->item_inspector, _("Toggle web inspector")); + self->priv->item_inspector = g_object_new (GTK_TYPE_TOGGLE_TOOL_BUTTON, + "use-underline", TRUE, + "label", _("Web _Inspector"), + "tooltip-text", _("Toggle web inspector"), + "icon-name", "dialog-information", + NULL); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), self->priv->item_inspector, -1); gtk_widget_show (GTK_WIDGET (self->priv->item_inspector)); @@ -1019,33 +759,6 @@ create_toolbar (GwhBrowser *self) return toolbar; } -static GtkWidget * -create_inspector_window (GwhBrowser *self) -{ - gboolean skips_taskbar; - gboolean window_type; - - g_object_get (self->priv->settings, - "wm-secondary-windows-skip-taskbar", &skips_taskbar, - "wm-secondary-windows-type", &window_type, - NULL); - self->priv->inspector_window_x = self->priv->inspector_window_y = 0; - self->priv->inspector_window = g_object_new (GTK_TYPE_WINDOW, - "type", GTK_WINDOW_TOPLEVEL, - "skip-taskbar-hint", skips_taskbar, - "type-hint", window_type, - "title", _("Web inspector"), - NULL); - g_signal_connect (self->priv->inspector_window, "delete-event", - G_CALLBACK (on_inspector_window_delete_event), self); - g_signal_connect (self->priv->settings, "notify::wm-secondary-windows-skip-taskbar", - G_CALLBACK (on_settings_wm_windows_skip_taskbar_notify), self); - g_signal_connect (self->priv->settings, "notify::wm-secondary-windows-type", - G_CALLBACK (on_settings_wm_windows_type_notify), self); - - return self->priv->inspector_window; -} - static guint get_statusbar_context_id (GtkStatusbar *statusbar) { @@ -1059,18 +772,24 @@ get_statusbar_context_id (GtkStatusbar *statusbar) } static void -on_web_view_hovering_over_link (WebKitWebView *view, - gchar *title, - gchar *uri, - GwhBrowser *self) +on_web_view_mouse_target_changed (WebKitWebView *view, + WebKitHitTestResult *hit_test_result, + guint modifiers, + GwhBrowser *self) { GtkStatusbar *statusbar = GTK_STATUSBAR (self->priv->statusbar); - + const gchar *uri; + if (self->priv->hovered_link) { gtk_statusbar_pop (statusbar, get_statusbar_context_id (statusbar)); g_free (self->priv->hovered_link); self->priv->hovered_link = NULL; } + + if (!webkit_hit_test_result_context_is_link (hit_test_result)) + return; + + uri = webkit_hit_test_result_get_link_uri (hit_test_result); if (uri && *uri) { self->priv->hovered_link = g_strdup (uri); gtk_statusbar_push (statusbar, get_statusbar_context_id (statusbar), @@ -1114,7 +833,8 @@ static void gwh_browser_init (GwhBrowser *self) { GtkWidget *scrolled; - WebKitWebSettings *wkws; + WebKitSettings *wkws; + WebKitWebContext *wkcontext; gboolean inspector_detached; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GWH_TYPE_BROWSER, @@ -1125,6 +845,9 @@ gwh_browser_init (GwhBrowser *self) self->priv->web_view = webkit_web_view_new (); wkws = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (self->priv->web_view)); g_object_set (wkws, "enable-developer-extras", TRUE, NULL); + + wkcontext = webkit_web_view_get_context (WEBKIT_WEB_VIEW (self->priv->web_view)); + webkit_web_context_set_favicon_database_directory (wkcontext, NULL); self->priv->settings = gwh_settings_get_default (); g_object_get (self->priv->settings, @@ -1132,7 +855,6 @@ gwh_browser_init (GwhBrowser *self) NULL); self->priv->toolbar = create_toolbar (self); - self->priv->paned = gtk_vpaned_new (); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); @@ -1140,20 +862,8 @@ gwh_browser_init (GwhBrowser *self) gtk_box_pack_start (GTK_BOX (self), self->priv->toolbar, FALSE, TRUE, 0); gtk_widget_show (self->priv->toolbar); - gtk_box_pack_start (GTK_BOX (self), self->priv->paned, TRUE, TRUE, 0); - gtk_paned_pack1 (GTK_PANED (self->priv->paned), scrolled, TRUE, TRUE); - gtk_widget_show_all (self->priv->paned); - - self->priv->inspector_view = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self->priv->inspector_view), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - self->priv->inspector_web_view = NULL; - - self->priv->inspector_window = create_inspector_window (self); - gtk_container_add (GTK_CONTAINER (inspector_detached - ? self->priv->inspector_window - : self->priv->paned), - self->priv->inspector_view); + gtk_box_pack_start (GTK_BOX (self), scrolled, TRUE, TRUE, 0); + gtk_widget_show_all (scrolled); self->priv->statusbar = ui_lookup_widget (geany->main_widgets->window, "statusbar"); if (self->priv->statusbar) { @@ -1164,37 +874,28 @@ gwh_browser_init (GwhBrowser *self) } self->priv->hovered_link = NULL; - g_signal_connect (self, "notify::orientation", - G_CALLBACK (on_orientation_notify), self); - self->priv->inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (self->priv->web_view)); - g_signal_connect (self->priv->inspector, "inspect-web-view", - G_CALLBACK (on_inspector_inspect_web_view), self); - g_signal_connect (self->priv->inspector, "show-window", - G_CALLBACK (on_inspector_show_window), self); - g_signal_connect (self->priv->inspector, "close-window", - G_CALLBACK (on_inspector_close_window), self); - g_signal_connect (self->priv->inspector, "detach-window", - G_CALLBACK (on_inspector_detach_window), self); - g_signal_connect (self->priv->inspector, "attach-window", - G_CALLBACK (on_inspector_attach_window), self); - - g_signal_connect (G_OBJECT (self->priv->web_view), "notify::progress", - G_CALLBACK (on_web_view_progress_notify), self); + g_signal_connect (self->priv->inspector, "bring-to-front", + G_CALLBACK (on_inspector_opened), self); + g_signal_connect (self->priv->inspector, "closed", + G_CALLBACK (on_inspector_closed), self); + + g_signal_connect (G_OBJECT (self->priv->web_view), "notify::estimated-load-progress", + G_CALLBACK (on_web_view_estimated_load_progress_notify), self); g_signal_connect (G_OBJECT (self->priv->web_view), "notify::uri", G_CALLBACK (on_web_view_uri_notify), self); - g_signal_connect (G_OBJECT (self->priv->web_view), "notify::load-status", - G_CALLBACK (on_web_view_load_status_notify), self); - g_signal_connect (G_OBJECT (self->priv->web_view), "notify::load-error", - G_CALLBACK (on_web_view_load_error), self); - g_signal_connect (G_OBJECT (self->priv->web_view), "notify::icon-uri", - G_CALLBACK (on_web_view_icon_uri_notify), self); - g_signal_connect (G_OBJECT (self->priv->web_view), "populate-popup", - G_CALLBACK (on_web_view_populate_popup), self); + g_signal_connect (G_OBJECT (self->priv->web_view), "load-changed", + G_CALLBACK (on_web_view_load_changed), self); + g_signal_connect (G_OBJECT (self->priv->web_view), "load-failed", + G_CALLBACK (on_web_view_load_failed), self); + g_signal_connect (G_OBJECT (self->priv->web_view), "notify::favicon", + G_CALLBACK (on_web_view_favicon_notify), self); + g_signal_connect (G_OBJECT (self->priv->web_view), "context-menu", + G_CALLBACK (on_web_view_context_menu), self); g_signal_connect (G_OBJECT (self->priv->web_view), "scroll-event", G_CALLBACK (on_web_view_scroll_event), self); - g_signal_connect (G_OBJECT (self->priv->web_view), "hovering-over-link", - G_CALLBACK (on_web_view_hovering_over_link), self); + g_signal_connect (G_OBJECT (self->priv->web_view), "mouse-target-changed", + G_CALLBACK (on_web_view_mouse_target_changed), self); g_signal_connect (G_OBJECT (self->priv->web_view), "leave-notify-event", G_CALLBACK (on_web_view_leave_notify_event), self); g_signal_connect (G_OBJECT (self->priv->web_view), "enter-notify-event", @@ -1204,8 +905,6 @@ gwh_browser_init (GwhBrowser *self) g_signal_connect (self->priv->web_view, "key-press-event", G_CALLBACK (gwh_keybindings_handle_event), self); - g_signal_connect (self->priv->inspector_view, "key-press-event", - G_CALLBACK (gwh_keybindings_handle_event), self); gtk_widget_grab_focus (self->priv->url_entry); @@ -1213,12 +912,6 @@ gwh_browser_init (GwhBrowser *self) G_CALLBACK (on_settings_browser_last_uri_notify), self); g_signal_connect (self->priv->settings, "notify::browser-bookmarks", G_CALLBACK (on_settings_browser_bookmarks_notify), self); - g_signal_connect (self->priv->settings, "notify::browser-orientation", - G_CALLBACK (on_settings_browser_orientation_notify), self); - g_signal_connect (self->priv->settings, "notify::inspector-detached", - G_CALLBACK (on_settings_inspector_detached_notify), self); - g_signal_connect (self->priv->settings, "notify::inspector-window-geometry", - G_CALLBACK (on_settings_inspector_window_geometry_notify), self); } @@ -1251,7 +944,7 @@ gwh_browser_set_uri (GwhBrowser *self, } g_free (scheme); if (g_strcmp0 (real_uri, gwh_browser_get_uri (self)) != 0) { - webkit_web_view_open (WEBKIT_WEB_VIEW (self->priv->web_view), real_uri); + webkit_web_view_load_uri (WEBKIT_WEB_VIEW (self->priv->web_view), real_uri); g_object_notify (G_OBJECT (self), "uri"); } g_free (real_uri); @@ -1289,25 +982,6 @@ gwh_browser_reload (GwhBrowser *self) webkit_web_view_reload (WEBKIT_WEB_VIEW (self->priv->web_view)); } -void -gwh_browser_set_inspector_transient_for (GwhBrowser *self, - GtkWindow *window) -{ - g_return_if_fail (GWH_IS_BROWSER (self)); - g_return_if_fail (window == NULL || GTK_IS_WINDOW (window)); - - gtk_window_set_transient_for (GTK_WINDOW (self->priv->inspector_window), - window); -} - -GtkWindow * -gwh_browser_get_inspector_transient_for (GwhBrowser *self) -{ - g_return_val_if_fail (GWH_IS_BROWSER (self), NULL); - - return gtk_window_get_transient_for (GTK_WINDOW (self->priv->inspector_window)); -} - void gwh_browser_toggle_inspector (GwhBrowser *self) { diff --git a/webhelper/src/gwh-browser.h b/webhelper/src/gwh-browser.h index ecd3347d7..faa12e9b8 100644 --- a/webhelper/src/gwh-browser.h +++ b/webhelper/src/gwh-browser.h @@ -22,7 +22,7 @@ #include #include -#include +#include G_BEGIN_DECLS @@ -56,8 +56,8 @@ struct _GwhBrowserClass { GtkVBoxClass parent_class; - void (*populate_popup) (GwhBrowser *browser, - GtkMenu *menu); + void (*populate_popup) (GwhBrowser *browser, + WebKitContextMenu *menu); }; @@ -77,11 +77,6 @@ WebKitWebView *gwh_browser_get_web_view (GwhBrowser *self); G_GNUC_INTERNAL void gwh_browser_reload (GwhBrowser *self); G_GNUC_INTERNAL -void gwh_browser_set_inspector_transient_for (GwhBrowser *self, - GtkWindow *window); -G_GNUC_INTERNAL -GtkWindow *gwh_browser_get_inspector_transient_for (GwhBrowser *self); -G_GNUC_INTERNAL void gwh_browser_toggle_inspector (GwhBrowser *self); G_GNUC_INTERNAL gchar **gwh_browser_get_bookmarks (GwhBrowser *self); diff --git a/webhelper/src/gwh-plugin.c b/webhelper/src/gwh-plugin.c index 62da71e6b..68bba3fa2 100644 --- a/webhelper/src/gwh-plugin.c +++ b/webhelper/src/gwh-plugin.c @@ -105,7 +105,6 @@ static void on_separate_window_destroy (GtkWidget *widget, gpointer data) { - gwh_browser_set_inspector_transient_for (GWH_BROWSER (G_browser), NULL); gtk_container_remove (GTK_CONTAINER (G_container.widget), G_browser); } @@ -156,8 +155,6 @@ create_separate_window (void) gtk_window_set_icon_list (GTK_WINDOW (window), icons); g_list_free (icons); } - gwh_browser_set_inspector_transient_for (GWH_BROWSER (G_browser), - GTK_WINDOW (window)); return window; } @@ -183,8 +180,6 @@ attach_browser (void) } gtk_notebook_append_page (GTK_NOTEBOOK (G_container.widget), G_browser, gtk_label_new (_("Web preview"))); - gwh_browser_set_inspector_transient_for (GWH_BROWSER (G_browser), - GTK_WINDOW (geany_data->main_widgets->window)); } } @@ -240,33 +235,43 @@ on_document_save (GObject *obj, } static void -on_item_auto_reload_toggled (GtkCheckMenuItem *item, - gpointer dummy) +on_item_auto_reload_toggled (GAction *action, + GVariant *parameter, + gpointer dummy) { + gboolean browser_auto_reload; + + g_object_get (G_OBJECT (G_settings), + "browser-auto-reload", &browser_auto_reload, NULL); g_object_set (G_OBJECT (G_settings), "browser-auto-reload", - gtk_check_menu_item_get_active (item), NULL); + !browser_auto_reload, NULL); + g_simple_action_set_state (G_SIMPLE_ACTION (action), + g_variant_new_boolean (!browser_auto_reload)); } static void -on_browser_populate_popup (GwhBrowser *browser, - GtkMenu *menu, - gpointer dummy) +on_browser_populate_popup (GwhBrowser *browser, + WebKitContextMenu *menu, + gpointer dummy) { - GtkWidget *item; - gboolean auto_reload = FALSE; - - item = gtk_separator_menu_item_new (); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - + GSimpleAction *action; + gboolean auto_reload = FALSE; + WebKitContextMenuItem *item; + + webkit_context_menu_append (menu, + webkit_context_menu_item_new_separator ()); + g_object_get (G_OBJECT (G_settings), "browser-auto-reload", &auto_reload, NULL); - item = gtk_check_menu_item_new_with_mnemonic (_("Reload upon document saving")); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), auto_reload); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - g_signal_connect (item, "toggled", G_CALLBACK (on_item_auto_reload_toggled), - NULL); + action = g_simple_action_new_stateful ("browser-auto-reload", NULL, + g_variant_new_boolean (auto_reload)); + g_signal_connect (action, "activate", + G_CALLBACK (on_item_auto_reload_toggled), NULL); + item = webkit_context_menu_item_new_from_gaction (G_ACTION (action), + _("Reload upon document saving"), + NULL); + webkit_context_menu_append (menu, item); + g_object_unref (action); } static void @@ -349,12 +354,6 @@ load_config (void) _("Last geometry of the separated browser's window"), "400x300", G_PARAM_READWRITE)); - gwh_settings_install_property (G_settings, g_param_spec_string ( - "inspector-window-geometry", - _("Inspector window geometry"), - _("Last geometry of the inspector window"), - "400x300", - G_PARAM_READWRITE)); gwh_settings_install_property (G_settings, g_param_spec_boolean ( "inspector-detached", _("Inspector detached"), diff --git a/webhelper/src/gwh-settings.c b/webhelper/src/gwh-settings.c index 31f3f29cf..9e785781c 100644 --- a/webhelper/src/gwh-settings.c +++ b/webhelper/src/gwh-settings.c @@ -29,13 +29,6 @@ #include -#if ! GTK_CHECK_VERSION (3, 0, 0) -/* make gtk_adjustment_new() return a real GtkAdjustment, not a GtkObject */ -# define gtk_adjustment_new(v, l, u, si, pi, ps) \ - (GtkAdjustment *) (gtk_adjustment_new ((v), (l), (u), (si), (pi), (ps))) -#endif - - struct _GwhSettingsPrivate { GPtrArray *prop_array; @@ -800,7 +793,7 @@ gwh_settings_widget_new_full (GwhSettings *self, GtkWidget *box; gchar *label; - box = gtk_hbox_new (FALSE, 6); + box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); label = g_strdup_printf (_("%s:"), g_param_spec_get_nick (pspec)); gtk_box_pack_start (GTK_BOX (box), gtk_label_new (label), FALSE, TRUE, 0); g_free (label);