diff options
-rw-r--r-- | mininewt/CHANGES | 57 | ||||
-rw-r--r-- | mininewt/COPYING | 481 | ||||
-rw-r--r-- | mininewt/Makefile | 96 | ||||
-rw-r--r-- | mininewt/button.c | 190 | ||||
-rw-r--r-- | mininewt/buttonbar.c | 46 | ||||
-rw-r--r-- | mininewt/checkbox.c | 290 | ||||
-rw-r--r-- | mininewt/checkboxtree.c | 792 | ||||
-rw-r--r-- | mininewt/config.h | 12 | ||||
-rw-r--r-- | mininewt/dialogboxes.c | 422 | ||||
-rw-r--r-- | mininewt/dialogboxes.h | 30 | ||||
-rw-r--r-- | mininewt/eawidth.c | 463 | ||||
-rw-r--r-- | mininewt/entry.c | 382 | ||||
-rw-r--r-- | mininewt/form.c | 1137 | ||||
-rw-r--r-- | mininewt/grid.c | 393 | ||||
-rw-r--r-- | mininewt/label.c | 81 | ||||
-rw-r--r-- | mininewt/listbox.c | 764 | ||||
-rw-r--r-- | mininewt/newt.c | 710 | ||||
-rw-r--r-- | mininewt/newt.h | 373 | ||||
-rw-r--r-- | mininewt/newt_pr.h | 83 | ||||
-rw-r--r-- | mininewt/scale.c | 85 | ||||
-rw-r--r-- | mininewt/scrollbar.c | 124 | ||||
-rw-r--r-- | mininewt/showchars.c | 44 | ||||
-rw-r--r-- | mininewt/showkey.c | 41 | ||||
-rw-r--r-- | mininewt/snackmodule.c | 1245 | ||||
-rw-r--r-- | mininewt/textbox.c | 410 | ||||
-rw-r--r-- | mininewt/whiptail.c | 232 | ||||
-rw-r--r-- | mininewt/whiptcl.c | 296 | ||||
-rw-r--r-- | mininewt/windows.c | 275 |
28 files changed, 9554 insertions, 0 deletions
diff --git a/mininewt/CHANGES b/mininewt/CHANGES new file mode 100644 index 000000000..1b581a908 --- /dev/null +++ b/mininewt/CHANGES @@ -0,0 +1,57 @@ +*************** +THIS FILE IS OBSOLETE -- UPDATE THE SPEC FILE INSTEAD +*************** + +0.59 -> 0.50.10 + - added support for help + - added cusor on/off stuff + +0.57 -> 0.59 + - minor fixes + +0.55 -> 0.56 + - added newtCheckboxTreeSetEntry(), newtCheckboxTreeGetEntryValue() + and newtCheckboxTreeSetEntryValue() + - checkboxtree callbacks + - if collapsing branches at the end of the list and list length + is larger then height, move first visible entry accordingly + - allow selection of all checkboxes on current branch + - snack bindings for the above + +0.54 -> 0.55 + - added newtCheckboxTreeGetCurrent() and snack bindings + - updated snack stuff to allow manual placement of grid + wrapped windows + +0.53 -> 0.54 + - fix segfault in newtRadioGetCurrent + +0.52 -> 0.53 + - place cursor in checkboxtree's more carefully + +0.51 -> 0.52 + - listbox bug fixes + +0.50 -> 0.51 + - added newtFormSetTimer() (and test case, and python) + - checkboxtree's could improperly leave info from closed trees + at the end of the display + +0.40 -> 0.50 + - added CheckboxTree widget + - vastly improved python bindings + +0.30 -> 0.40 + - GPM mouse support added + +0.30 -> 0.31: + - pgdn could core dump on short textboxes + +0.25 -> 0.30: + - newtDrawRootText() didn't use the specified position properly + - removed relics of original listbox code still handing around + checkbox.c + - renamed DOBORDER flag to simply BORDER + - listboxes no longer scroll by default + - newtListboxSetEntry() uses a key, not an index + - listbox scrollbars should work properly in borders diff --git a/mininewt/COPYING b/mininewt/COPYING new file mode 100644 index 000000000..eb685a5ec --- /dev/null +++ b/mininewt/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/mininewt/Makefile b/mininewt/Makefile new file mode 100644 index 000000000..fa0c1eafe --- /dev/null +++ b/mininewt/Makefile @@ -0,0 +1,96 @@ + +include ../Makefile.inc + +LIBS = -lslang -lm #-lefence +SHLIBS = -lslang -lm -lc + +GPM_SUPPORT= + +CFLAGS = -Os -I/usr/include/slang + +VERSION = 0.51.0 +CVSTAG = r$(subst .,-,$(VERSION)) +SONAME = 0.51 + +PYTHONVERS = $(shell ls -d /usr/include/python* | sed "s|/usr/include/||g") + +OBJS = newt.o button.o form.o checkbox.o entry.o label.o listbox.o \ + scrollbar.o textbox.o scale.o grid.o windows.o buttonbar.o \ + checkboxtree.o +SOBJS = $(patsubst %.o,%.lo,$(OBJS)) +DOBJS = $(patsubst %.o,%.do,$(OBJS)) +PROGS = +LIBNEWT = libnewt.a($(OBJS)) +ifeq (i386, $(ARCH)) +LIBNEWT=libnewt-diet.a($(DOBJS)) +endif +LIBNEWTSH = libnewt.so.$(VERSION) +LIBNEWTSONAME = libnewt.so.$(SONAME) + +SHCFLAGS = -fPIC + + +#-------------------------------------- + +SOURCES = $(subst .o,.c,$(TESTOBJS) $(OBJS)) + +ifeq (.depend,$(wildcard .depend)) +TARGET=$(PROGS) +else +TARGET=depend $(PROGS) +endif + +all: $(LIBNEWTSH) $(LIBNEWT) _snackmodule.so + +_snackmodule.so: snackmodule.c $(LIBNEWTSH) + for ver in $(PYTHONVERS) ; do \ + if [ ! -f "$$ver/_snackmodule.so" -o $(LIBNEWTSH) -nt "$$ver/_snackmodule.so" ]; then \ + mkdir -p $$ver ;\ + gcc $(CFLAGS) -I/usr/include/$$ver -fPIC -c -o $$ver/snackmodule.o snackmodule.c ;\ + gcc --shared $(SHCFLAGS) -o $$ver/_snackmodule.so $$ver/snackmodule.o -L . $(LIBNEWTSH) ;\ + fi ; \ + done + +veryclean: clean + rm -f .depend + +clean: + rm -f $(PROGS) *.o *.lo *.do *.so* *.a + +depend: + $(CPP) $(CFLAGS) -M $(SOURCES) > .depend + +$(SHAREDDIR): + mkdir -p $(SHAREDDIR) + +sharedlib: $(LIBNEWTSH) + +$(LIBNEWTSH): $(SOBJS) + gcc -shared -o $(LIBNEWTSH) -Wl,-soname,$(LIBNEWTSONAME) $(SOBJS) $(SHLIBS) + +%.do: %.c + diet $(CC) -c $(CFLAGS) -o $@ $< + +%.lo: %.c + $(CC) $(SHCFLAGS) -c $(CFLAGS) -o $@ $< + +newt.o: newt.c Makefile + $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -c -o $@ $< + +newt.do: newt.c Makefile + diet $(CC) $(SHCFLAGS) $(CFLAGS) -DVERSION=\"$(VERSION)\" -c -o $@ $< + +newt.lo: newt.c Makefile + $(CC) $(SHCFLAGS) $(CFLAGS) -DVERSION=\"$(VERSION)\" -c -o $@ $< + + +install: $(LIBNEWT) $(LIBNEWTSH) _snackmodule.so + install -m 644 $(LIBNEWT) $(DESTDIR)/$(RUNTIMEDIR) + install -s -m 755 $(LIBNEWTSH) $(DESTDIR)/$(RUNTIMEDIR)/$(LIBNEWTSONAME) + for ver in $(PYTHONVERS) ; do \ + install -s -m 755 $$ver/_snackmodule.so $(DESTDIR)/$(RUNTIMEDIR) + done + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/mininewt/button.c b/mininewt/button.c new file mode 100644 index 000000000..1ff360dc5 --- /dev/null +++ b/mininewt/button.c @@ -0,0 +1,190 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct button { + char * text; + int compact; +}; + +static void buttonDrawIt(newtComponent co, int active, int pushed); +static void buttonDrawText(newtComponent co, int active, int pushed); + +static void buttonDraw(newtComponent c); +static void buttonDestroy(newtComponent co); +static struct eventResult buttonEvent(newtComponent c, + struct event ev); +static void buttonPlace(newtComponent co, int newLeft, int newTop); + +static struct componentOps buttonOps = { + buttonDraw, + buttonEvent, + buttonDestroy, + buttonPlace, + newtDefaultMappedHandler, +} ; + +static newtComponent createButton(int left, int row, const char * text, int compact) { + newtComponent co; + struct button * bu; + + co = malloc(sizeof(*co)); + bu = malloc(sizeof(struct button)); + co->data = bu; + + bu->text = strdup(text); + bu->compact = compact; + co->ops = &buttonOps; + + if (bu->compact) { + co->height = 1; + co->width = strlen(text) + 3; + } else { + co->height = 4; + co->width = strlen(text) + 5; + } + + co->top = row; + co->left = left; + co->takesFocus = 1; + co->isMapped = 0; + + newtGotorc(co->top, co->left); + + return co; +} + +newtComponent newtCompactButton(int left, int row, const char * text) { + return createButton(left, row, text, 1); +} + +newtComponent newtButton(int left, int row, const char * text) { + return createButton(left, row, text, 0); +} + +static void buttonDestroy(newtComponent co) { + struct button * bu = co->data; + + free(bu->text); + free(bu); + free(co); +} + +static void buttonPlace(newtComponent co, int newLeft, int newTop) { + co->top = newTop; + co->left = newLeft; + + newtGotorc(co->top, co->left); +} + +static void buttonDraw(newtComponent co) { + buttonDrawIt(co, 0, 0); +} + +static void buttonDrawIt(newtComponent co, int active, int pushed) { + struct button * bu = co->data; + + if (!co->isMapped) return; + + SLsmg_set_color(NEWT_COLORSET_BUTTON); + + if (bu->compact) { + if (active) + SLsmg_set_color(NEWT_COLORSET_COMPACTBUTTON); + else + SLsmg_set_color(NEWT_COLORSET_BUTTON); + newtGotorc(co->top+ pushed, co->left + 1 + pushed); + SLsmg_write_char('<'); + SLsmg_write_string(bu->text); + SLsmg_write_char('>'); + } else { + if (pushed) { + SLsmg_set_color(NEWT_COLORSET_BUTTON); + newtDrawBox(co->left + 1, co->top + 1, co->width - 1, 3, 0); + + SLsmg_set_color(NEWT_COLORSET_WINDOW); + newtClearBox(co->left, co->top, co->width, 1); + newtClearBox(co->left, co->top, 1, co->height); + } else { + newtDrawBox(co->left, co->top, co->width - 1, 3, 1); + } + + buttonDrawText(co, active, pushed); + } +} + +static void buttonDrawText(newtComponent co, int active, int pushed) { + struct button * bu = co->data; + + if (pushed) pushed = 1; + + if (active) + SLsmg_set_color(NEWT_COLORSET_ACTBUTTON); + else + SLsmg_set_color(NEWT_COLORSET_BUTTON); + + newtGotorc(co->top + 1 + pushed, co->left + 1 + pushed); + SLsmg_write_char(' '); + SLsmg_write_string(bu->text); + SLsmg_write_char(' '); +} + +static struct eventResult buttonEvent(newtComponent co, + struct event ev) { + struct eventResult er; + struct button * bu = co->data; + + if (ev.when == EV_NORMAL) { + switch (ev.event) { + case EV_FOCUS: + buttonDrawIt(co, 1, 0); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + buttonDrawIt(co, 0, 0); + er.result = ER_SWALLOWED; + break; + + case EV_KEYPRESS: + if (ev.u.key == ' ' || ev.u.key == '\r') { + if (!bu->compact) { + /* look pushed */ + buttonDrawIt(co, 1, 1); + newtRefresh(); + newtDelay(150000); + buttonDrawIt(co, 1, 0); + newtRefresh(); + newtDelay(150000); + } + + er.result = ER_EXITFORM; + } else + er.result = ER_IGNORED; + break; + case EV_MOUSE: + if (ev.u.mouse.type == MOUSE_BUTTON_DOWN && + co->top <= ev.u.mouse.y && + co->top + co->height - !bu->compact > ev.u.mouse.y && + co->left <= ev.u.mouse.x && + co->left + co->width - !bu->compact > ev.u.mouse.x) { + if (!bu->compact) { + buttonDrawIt(co, 1, 1); + newtRefresh(); + newtDelay(150000); + buttonDrawIt(co, 1, 0); + newtRefresh(); + newtDelay(150000); + } + er.result = ER_EXITFORM; + } + break; + } + } else + er.result = ER_IGNORED; + + return er; +} diff --git a/mininewt/buttonbar.c b/mininewt/buttonbar.c new file mode 100644 index 000000000..45473c9d2 --- /dev/null +++ b/mininewt/buttonbar.c @@ -0,0 +1,46 @@ +#include <stdarg.h> + +#include "newt.h" + +/* if they try and pack more then 50 buttons, screw 'em */ +newtGrid newtButtonBarv(char * button1, newtComponent * b1comp, va_list args) { + newtGrid grid; + struct buttonInfo { + char * name; + newtComponent * compPtr; + } buttons[50]; + int num; + int i; + + buttons[0].name = button1, buttons[0].compPtr = b1comp, num = 1; + while (1) { + buttons[num].name = va_arg(args, char *); + if (!buttons[num].name) break; + buttons[num].compPtr = va_arg(args, newtComponent *); + num++; + } + + grid = newtCreateGrid(num, 1); + + for (i = 0; i < num; i++) { + *buttons[i].compPtr = newtButton(-1, -1, buttons[i].name); + newtGridSetField(grid, i, 0, NEWT_GRID_COMPONENT, + *buttons[i].compPtr, + num ? 1 : 0, 0, 0, 0, 0, 0); + } + + return grid; +} + +newtGrid newtButtonBar(char * button1, newtComponent * b1comp, ...) { + va_list args; + newtGrid grid; + + va_start(args, b1comp); + + grid = newtButtonBarv(button1, b1comp, args); + + va_end(args); + + return grid; +} diff --git a/mininewt/checkbox.c b/mininewt/checkbox.c new file mode 100644 index 000000000..eee514c98 --- /dev/null +++ b/mininewt/checkbox.c @@ -0,0 +1,290 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +enum type { CHECK, RADIO }; + +struct checkbox { + char * text; + char * seq; + char * result; + newtComponent prevButton, lastButton; + enum type type; + char value; + int active, inactive; + const void * data; + int flags; + int hasFocus; +}; + +static void makeActive(newtComponent co); + +static void cbDraw(newtComponent c); +static void cbDestroy(newtComponent co); +struct eventResult cbEvent(newtComponent co, struct event ev); + +static struct componentOps cbOps = { + cbDraw, + cbEvent, + cbDestroy, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +newtComponent newtRadiobutton(int left, int top, const char * text, int isDefault, + newtComponent prevButton) { + newtComponent co; + newtComponent curr; + struct checkbox * rb; + char initialValue; + + if (isDefault) + initialValue = '*'; + else + initialValue = ' '; + + co = newtCheckbox(left, top, text, initialValue, " *", NULL); + rb = co->data; + rb->type = RADIO; + + rb->prevButton = prevButton; + + for (curr = co; curr; curr = rb->prevButton) { + rb = curr->data; + rb->lastButton = co; + } + + return co; +} + +newtComponent newtRadioGetCurrent(newtComponent setMember) { + struct checkbox * rb = setMember->data; + + setMember = rb->lastButton; + rb = setMember->data; + + while (rb && rb->value != '*') { + setMember = rb->prevButton; + if (!setMember) + return NULL; + rb = setMember->data; + } + + return setMember; +} + +char newtCheckboxGetValue(newtComponent co) { + struct checkbox * cb = co->data; + + return cb->value; +} + +void newtCheckboxSetValue(newtComponent co, char value) { + struct checkbox * cb = co->data; + + *cb->result = value; + cbDraw(co); +} + +newtComponent newtCheckbox(int left, int top, const char * text, char defValue, + const char * seq, char * result) { + newtComponent co; + struct checkbox * cb; + + if (!seq) seq = " *"; + + co = malloc(sizeof(*co)); + cb = malloc(sizeof(struct checkbox)); + co->data = cb; + cb->flags = 0; + if (result) + cb->result = result; + else + cb->result = &cb->value; + + cb->text = strdup(text); + cb->seq = strdup(seq); + cb->type = CHECK; + cb->hasFocus = 0; + cb->inactive = COLORSET_CHECKBOX; + cb->active = COLORSET_ACTCHECKBOX; + defValue ? (*cb->result = defValue) : (*cb->result = cb->seq[0]); + + co->ops = &cbOps; + + co->callback = NULL; + co->height = 1; + co->width = strlen(text) + 4; + co->top = top; + co->left = left; + co->takesFocus = 1; + + return co; +} + +void newtCheckboxSetFlags(newtComponent co, int flags, enum newtFlagsSense sense) { + struct checkbox * cb = co->data; + int row, col; + + cb->flags = newtSetFlags(cb->flags, flags, sense); + + if (!(cb->flags & NEWT_FLAG_DISABLED)) + co->takesFocus = 1; + else + co->takesFocus = 0; + + newtGetrc(&row, &col); + cbDraw(co); + newtGotorc(row, col); +} + +static void cbDraw(newtComponent c) { + struct checkbox * cb = c->data; + + if (c->top == -1 || !c->isMapped) return; + + if (cb->flags & NEWT_FLAG_DISABLED) { + cb->inactive = NEWT_COLORSET_DISENTRY; + cb->active = NEWT_COLORSET_DISENTRY; + } else { + cb->inactive = COLORSET_CHECKBOX; + cb->active = COLORSET_ACTCHECKBOX; + } + + SLsmg_set_color(cb->inactive); + + newtGotorc(c->top, c->left); + + switch (cb->type) { + case RADIO: + SLsmg_write_string("( ) "); + break; + + case CHECK: + SLsmg_write_string("[ ] "); + break; + + default: + break; + } + + SLsmg_write_string(cb->text); + + if (cb->hasFocus) + SLsmg_set_color(cb->active); + + newtGotorc(c->top, c->left + 1); + SLsmg_write_char(*cb->result); +} + +static void cbDestroy(newtComponent co) { + struct checkbox * cb = co->data; + + free(cb->text); + free(cb->seq); + free(cb); + free(co); +} + +struct eventResult cbEvent(newtComponent co, struct event ev) { + struct checkbox * cb = co->data; + struct eventResult er; + const char * cur; + + if (ev.when == EV_NORMAL) { + switch (ev.event) { + case EV_FOCUS: + cb->hasFocus = 1; + cbDraw(co); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + cb->hasFocus = 0; + cbDraw(co); + er.result = ER_SWALLOWED; + break; + + case EV_KEYPRESS: + if (ev.u.key == ' ') { + if (cb->type == RADIO) { + makeActive(co); + } else if (cb->type == CHECK) { + cur = strchr(cb->seq, *cb->result); + if (!cur) + *cb->result = *cb->seq; + else { + cur++; + if (! *cur) + *cb->result = *cb->seq; + else + *cb->result = *cur; + } + cbDraw(co); + er.result = ER_SWALLOWED; + + if (co->callback) + co->callback(co, co->callbackData); + } else { + er.result = ER_IGNORED; + } + } else if(ev.u.key == NEWT_KEY_ENTER) { + er.result = ER_IGNORED; + } else { + er.result = ER_IGNORED; + } + break; + case EV_MOUSE: + if (ev.u.mouse.type == MOUSE_BUTTON_DOWN) { + if (cb->type == RADIO) { + makeActive(co); + } else if (cb->type == CHECK) { + cur = strchr(cb->seq, *cb->result); + if (!cur) + *cb->result = *cb->seq; + else { + cur++; + if (! *cur) + *cb->result = *cb->seq; + else + *cb->result = *cur; + } + cbDraw(co); + er.result = ER_SWALLOWED; + + if (co->callback) + co->callback(co, co->callbackData); + } + } + } + } else + er.result = ER_IGNORED; + + return er; +} + +static void makeActive(newtComponent co) { + struct checkbox * cb = co->data; + struct checkbox * rb; + newtComponent curr; + + /* find the one that's turned off */ + curr = cb->lastButton; + rb = curr->data; + while (curr && rb->value == rb->seq[0]) { + curr = rb->prevButton; + if (curr) rb = curr->data; + } + if (curr) { + rb->value = rb->seq[0]; + cbDraw(curr); + } + cb->value = cb->seq[1]; + cbDraw(co); + + if (co->callback) + co->callback(co, co->callbackData); +} diff --git a/mininewt/checkboxtree.c b/mininewt/checkboxtree.c new file mode 100644 index 000000000..2c8df85cc --- /dev/null +++ b/mininewt/checkboxtree.c @@ -0,0 +1,792 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct items { + char * text; + const void *data; + unsigned char selected; + struct items *next; + struct items *prev; + struct items *branch; + int flags; + int depth; +}; + +struct CheckboxTree { + newtComponent sb; + struct items * itemlist; + struct items ** flatList, ** currItem, ** firstItem; + int flatCount; + int flags; + int sbAdjust; + int curWidth; + int userHasSetWidth; + char * seq; + char * result; +}; + +static void ctDraw(newtComponent c); +static void ctDestroy(newtComponent co); +static void ctPlace(newtComponent co, int newLeft, int newTop); +struct eventResult ctEvent(newtComponent co, struct event ev); +static void ctMapped(newtComponent co, int isMapped); +static struct items * findItem(struct items * items, const void * data); +static void buildFlatList(newtComponent co); +static void doBuildFlatList(struct CheckboxTree * ct, struct items * item); +enum countWhat { COUNT_EXPOSED=0, COUNT_SELECTED=1 }; +static int countItems(struct items * item, enum countWhat justExposed); +static inline void updateWidth(newtComponent co, struct CheckboxTree * ct, + int maxField); + +static struct componentOps ctOps = { + ctDraw, + ctEvent, + ctDestroy, + ctPlace, + ctMapped, +} ; + +static inline void updateWidth(newtComponent co, struct CheckboxTree * ct, + int maxField) { + ct->curWidth = maxField; + co->width = ct->curWidth + ct->sbAdjust; + + if (ct->sb) + ct->sb->left = co->left + co->width - 1; +} + +static int countItems(struct items * item, enum countWhat what) { + int count = 0; + + while (item) { + if ((!item->branch && item->selected == what) || (what == COUNT_EXPOSED)) + count++; + if (item->branch || (what == COUNT_EXPOSED && item->selected)) + count += countItems(item->branch, what); + item = item->next; + } + + return count; +} + +static void doBuildFlatList(struct CheckboxTree * ct, struct items * item) { + while (item) { + ct->flatList[ct->flatCount++] = item; + if (item->branch && item->selected) doBuildFlatList(ct, item->branch); + item = item->next; + } +} + +static void buildFlatList(newtComponent co) { + struct CheckboxTree * ct = co->data; + + if (ct->flatList) free(ct->flatList); + ct->flatCount = countItems(ct->itemlist, COUNT_EXPOSED); + + ct->flatList = malloc(sizeof(*ct->flatList) * (ct->flatCount+1)); + ct->flatCount = 0; + doBuildFlatList(ct, ct->itemlist); + ct->flatList[ct->flatCount] = NULL; +} + +int newtCheckboxTreeAddItem(newtComponent co, + const char * text, const void * data, + int flags, int index, ...) { + va_list argList; + int numIndexes; + int * indexes; + int i; + + va_start(argList, index); + numIndexes = 0; + i = index; + while (i != NEWT_ARG_LAST) { + numIndexes++; + i = va_arg(argList, int); + } + + va_end(argList); + + indexes = alloca(sizeof(*indexes) * (numIndexes + 1)); + va_start(argList, index); + numIndexes = 0; + i = index; + va_start(argList, index); + while (i != NEWT_ARG_LAST) { + indexes[numIndexes++] = i; + i = va_arg(argList, int); + } + va_end(argList); + + indexes[numIndexes++] = NEWT_ARG_LAST; + + return newtCheckboxTreeAddArray(co, text, data, flags, indexes); +} + +static int doFindItemPath(struct items * items, void * data, int * path, + int * len) { + int where = 0; + + while (items) { + if (items->data == data) { + if (path) path[items->depth] = where; + if (len) *len = items->depth + 1; + return 1; + } + + if (items->branch && doFindItemPath(items->branch, data, path, len)) { + if (path) path[items->depth] = where; + return 1; + } + + items = items->next; + where++; + } + + return 0; +} + +int * newtCheckboxTreeFindItem(newtComponent co, void * data) { + int len; + int * path; + struct CheckboxTree * ct = co->data; + + if (!doFindItemPath(ct->itemlist, data, NULL, &len)) return NULL; + + path = malloc(sizeof(*path) * (len + 1)); + doFindItemPath(ct->itemlist, data, path, NULL); + path[len] = NEWT_ARG_LAST; + + return path; +} + +int newtCheckboxTreeAddArray(newtComponent co, + const char * text, const void * data, + int flags, int * indexes) { + struct items * curList, * newNode, * item; + struct items ** listPtr = NULL; + int i, index, numIndexes; + struct CheckboxTree * ct = co->data; + + numIndexes = 0; + while (indexes[numIndexes] != NEWT_ARG_LAST) numIndexes++; + + if (!ct->itemlist) { + if (numIndexes > 1) return -1; + + ct->itemlist = malloc(sizeof(*ct->itemlist)); + item = ct->itemlist; + item->prev = NULL; + item->next = NULL; + } else { + curList = ct->itemlist; + listPtr = &ct->itemlist; + + i = 0; + index = indexes[i]; + while (i < numIndexes) { + item = curList; + + if (index == NEWT_ARG_APPEND) { + item = NULL; + } else { + while (index && item) + item = item->next, index--; + } + + i++; + if (i < numIndexes) { + curList = item->branch; + listPtr = &item->branch; + if (!curList && (i + 1 != numIndexes)) return -1; + + index = indexes[i]; + } + } + + if (!curList) { /* create a new branch */ + item = malloc(sizeof(*curList->prev)); + item->next = item->prev = NULL; + *listPtr = item; + } else if (!item) { /* append to end */ + item = curList; + while (item->next) item = item->next; + item->next = malloc(sizeof(*curList->prev)); + item->next->prev = item; + item = item->next; + item->next = NULL; + } else { + newNode = malloc(sizeof(*newNode)); + newNode->prev = item->prev; + newNode->next = item; + + if (item->prev) item->prev->next = newNode; + item->prev = newNode; + item = newNode; + if (!item->prev) *listPtr = item; + } + } + + item->text = strdup(text); + item->data = data; + if (flags & NEWT_FLAG_SELECTED) { + item->selected = 1; + } else { + item->selected = 0; + } + item->flags = flags; + item->branch = NULL; + item->depth = numIndexes - 1; + + i = 4 + (3 * item->depth); + + if ((ct->userHasSetWidth == 0) && ((strlen(text) + i + ct->sbAdjust) > co->width)) { + updateWidth(co, ct, strlen(text) + i); + } + + return 0; +} + +static struct items * findItem(struct items * items, const void * data) { + struct items * i; + + while (items) { + if (items->data == data) return items; + if (items->branch) { + i = findItem(items->branch, data); + if (i) return i; + } + + items = items->next; + } + + return NULL; +} + +static void listSelected(struct items * items, int * num, const void ** list, int seqindex) { + while (items) { + if ((seqindex ? items->selected==seqindex : items->selected) && !items->branch) + list[(*num)++] = (void *) items->data; + if (items->branch) + listSelected(items->branch, num, list, seqindex); + items = items->next; + } +} + +void newtCheckboxTreeSetWidth(newtComponent co, int width) { + struct CheckboxTree * ct = co->data; + + co->width = width; + ct->curWidth = co->width - ct->sbAdjust; + ct->userHasSetWidth = 1; + if (ct->sb) ct->sb->left = co->width + co->left - 1; + ctDraw(co); +} + +const void ** newtCheckboxTreeGetSelection(newtComponent co, int *numitems) +{ + return newtCheckboxTreeGetMultiSelection(co, numitems, 0); +} + +const void ** newtCheckboxTreeGetMultiSelection(newtComponent co, int *numitems, char seqnum) +{ + struct CheckboxTree * ct; + const void **retval; + int seqindex=0; + + if(!co || !numitems) return NULL; + + ct = co->data; + + if (seqnum) { + while( ct->seq[seqindex] && ( ct->seq[seqindex] != seqnum )) seqindex++; + } else { + seqindex = 0; + } + + *numitems = countItems(ct->itemlist, (seqindex ? seqindex : COUNT_SELECTED)); + if (!*numitems) return NULL; + + retval = malloc(*numitems * sizeof(void *)); + *numitems = 0; + listSelected(ct->itemlist, numitems, retval, seqindex); + + return retval; +} + +newtComponent newtCheckboxTree(int left, int top, int height, int flags) { + return newtCheckboxTreeMulti(left, top, height, NULL, flags); +} + +newtComponent newtCheckboxTreeMulti(int left, int top, int height, char *seq, int flags) { + newtComponent co; + struct CheckboxTree * ct; + + co = malloc(sizeof(*co)); + ct = malloc(sizeof(struct CheckboxTree)); + co->callback = NULL; + co->data = ct; + co->ops = &ctOps; + co->takesFocus = 1; + co->height = height; + co->width = 0; + co->isMapped = 0; + ct->curWidth = 0; + ct->userHasSetWidth = 0; + ct->itemlist = NULL; + ct->firstItem = NULL; + ct->currItem = NULL; + ct->flatList = NULL; + + ct->flags = flags; + + if (seq) + ct->seq = strdup(seq); + else + ct->seq = strdup(" *"); + if (flags & NEWT_FLAG_SCROLL) { + ct->sb = newtVerticalScrollbar(left, top, height, + COLORSET_LISTBOX, COLORSET_ACTLISTBOX); + ct->sbAdjust = 2; + } else { + ct->sb = NULL; + ct->sbAdjust = 0; + } + + return co; +} + +static void ctMapped(newtComponent co, int isMapped) { + struct CheckboxTree * ct = co->data; + + co->isMapped = isMapped; + if (ct->sb) + ct->sb->ops->mapped(ct->sb, isMapped); +} + +static void ctPlace(newtComponent co, int newLeft, int newTop) { + struct CheckboxTree * ct = co->data; + + co->top = newTop; + co->left = newLeft; + + if (ct->sb) + ct->sb->ops->place(ct->sb, co->left + co->width - 1, co->top); +} + +int ctSetItem(newtComponent co, struct items *item, enum newtFlagsSense sense) +{ + struct CheckboxTree * ct = co->data; + struct items * currItem; + struct items * firstItem; + + if (!item) + return 1; + + switch(sense) { + case NEWT_FLAGS_RESET: + item->selected = 0; + break; + case NEWT_FLAGS_SET: + item->selected = 1; + break; + case NEWT_FLAGS_TOGGLE: + if (item->branch) + item->selected = !item->selected; + else if (!(ct->flags & NEWT_CHECKBOXTREE_UNSELECTABLE)) { + item->selected++; + if (item->selected==strlen(ct->seq)) + item->selected = 0; + } + break; + } + + if (item->branch) { + currItem = *ct->currItem; + firstItem = *ct->firstItem; + + buildFlatList(co); + + ct->currItem = ct->flatList; + while (*ct->currItem != currItem) ct->currItem++; + + ct->firstItem = ct->flatList; + if (ct->flatCount > co->height) { + struct items ** last = ct->flatList + ct->flatCount - co->height; + while (*ct->firstItem != firstItem && ct->firstItem != last) + ct->firstItem++; + } + } + + return 0; +} + +static void ctSetItems(struct items *item, int selected) +{ + for (; item; item = item->next) { + if (!item->branch) + item->selected = selected; + else + ctSetItems(item->branch, selected); + } +} + +static void ctDraw(newtComponent co) { + struct CheckboxTree * ct = co->data; + struct items ** item; + int i, j; + char * spaces; + int currRow; + + if (!co->isMapped) return ; + + if (!ct->firstItem) { + buildFlatList(co); + ct->firstItem = ct->currItem = ct->flatList; + } + + item = ct->firstItem; + + i = 0; + + newtTrashScreen(); + + while (*item && i < co->height) { + newtGotorc(co->top + i, co->left); + if (*item == *ct->currItem) { + SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX); + currRow = co->top + i; + } else + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + for (j = 0; j < (*item)->depth; j++) + SLsmg_write_string(" "); + + if ((*item)->branch) { + if ((*item)->selected) + SLsmg_write_string("<-> "); + else + SLsmg_write_string("<+> "); + } else { + if (ct->flags & NEWT_CHECKBOXTREE_HIDE_BOX) { + if ((*item)->selected) + SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX); + SLsmg_write_string(" "); + } else { + char tmp[5]; + snprintf(tmp,5,"[%c] ",ct->seq[(*item)->selected]); + SLsmg_write_string(tmp); + } + } + + SLsmg_write_nstring((*item)->text, co->width - 4 - + (3 * (*item)->depth)); + + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + item++; + i++; + } + + /* There could be empty lines left (i.e. if the user closes an expanded + list which is the last thing in the tree, and whose elements are + displayed at the bottom of the screen */ + if (i < co->height) { + spaces = alloca(co->width); + memset(spaces, ' ', co->width); + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + } + while (i < co->height) { + newtGotorc(co->top + i, co->left); + SLsmg_write_nstring(spaces, co->width); + i++; + } + + if(ct->sb) { + newtScrollbarSet(ct->sb, ct->currItem - ct->flatList, + ct->flatCount - 1); + ct->sb->ops->draw(ct->sb); + } + + newtGotorc(currRow, co->left + 1); +} + +static void ctDestroy(newtComponent co) { + struct CheckboxTree * ct = co->data; + struct items * item, * nextitem; + + nextitem = item = ct->itemlist; + + while (item != NULL) { + nextitem = item->next; + free(item->text); + free(item); + item = nextitem; + } + + free(ct->seq); + free(ct); + free(co); +} + +struct eventResult ctEvent(newtComponent co, struct event ev) { + struct CheckboxTree * ct = co->data; + struct eventResult er; + struct items ** listEnd, ** lastItem; + int key, selnum = 1; + + er.result = ER_IGNORED; + + if(ev.when == EV_EARLY || ev.when == EV_LATE) { + return er; + } + + switch(ev.event) { + case EV_KEYPRESS: + key = ev.u.key; + if (key == (char) key && key != ' ') { + for (selnum = 0; ct->seq[selnum]; selnum++) + if (key == ct->seq[selnum]) + break; + if (!ct->seq[selnum]) + switch (key) { + case '-': selnum = 0; break; + case '+': + case '*': selnum = 1; break; + } + if (ct->seq[selnum]) + key = '*'; + } + switch(key) { + case ' ': + case NEWT_KEY_ENTER: + ctSetItem(co, *ct->currItem, NEWT_FLAGS_TOGGLE); + er.result = ER_SWALLOWED; + if (!(*ct->currItem)->branch || (*ct->currItem)->selected) + key = NEWT_KEY_DOWN; + else + key = '*'; + break; + case '*': + if ((*ct->currItem)->branch) { + ctSetItems((*ct->currItem)->branch, selnum); + if (!(*ct->currItem)->selected) + key = NEWT_KEY_DOWN; + } else { + (*ct->currItem)->selected = selnum; + key = NEWT_KEY_DOWN; + } + er.result = ER_SWALLOWED; + break; + } + switch (key) { + case '*': + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + return er; + case NEWT_KEY_HOME: + ct->currItem = ct->flatList; + ct->firstItem = ct->flatList; + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + case NEWT_KEY_END: + ct->currItem = ct->flatList + ct->flatCount - 1; + if (ct->flatCount <= co->height) + ct->firstItem = ct->flatList; + else + ct->firstItem = ct->flatList + ct->flatCount - co->height; + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + case NEWT_KEY_DOWN: + if (ev.u.key != NEWT_KEY_DOWN) { + if(co->callback) co->callback(co, co->callbackData); + if (strlen(ct->seq) != 2) { + ctDraw(co); + return er; + } + } + if ((ct->currItem - ct->flatList + 1) < ct->flatCount) { + ct->currItem++; + + if (ct->currItem - ct->firstItem >= co->height) + ct->firstItem++; + + ctDraw(co); + } else if (ev.u.key != NEWT_KEY_DOWN) + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + case NEWT_KEY_UP: + if (ct->currItem != ct->flatList) { + ct->currItem--; + + if (ct->currItem < ct->firstItem) + ct->firstItem = ct->currItem; + + ctDraw(co); + } + er.result = ER_SWALLOWED; + if(co->callback) co->callback(co, co->callbackData); + return er; + case NEWT_KEY_PGUP: + if (ct->firstItem - co->height < ct->flatList) { + ct->firstItem = ct->currItem = ct->flatList; + } else { + ct->currItem -= co->height; + ct->firstItem -= co->height; + } + + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + case NEWT_KEY_PGDN: + listEnd = ct->flatList + ct->flatCount - 1; + lastItem = ct->firstItem + co->height - 1; + + if (lastItem + co->height > listEnd) { + ct->firstItem = listEnd - co->height + 1; + ct->currItem = listEnd; + } else { + ct->currItem += co->height; + ct->firstItem += co->height; + } + + ctDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + return er; + } + break; + + case EV_FOCUS: + ctDraw(co); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + ctDraw(co); + er.result = ER_SWALLOWED; + break; + default: + break; + } + + return er; +} + +const void * newtCheckboxTreeGetCurrent(newtComponent co) { + struct CheckboxTree * ct = co->data; + + if (!ct->currItem) return NULL; + return (*ct->currItem)->data; +} + +void newtCheckboxTreeSetEntry(newtComponent co, const void * data, const char * text) +{ + struct CheckboxTree * ct; + struct items * item; + int i; + + if (!co) return; + ct = co->data; + item = findItem(ct->itemlist, data); + if (!item) return; + + free(item->text); + item->text = strdup(text); + + i = 4 + (3 * item->depth); + + if ((ct->userHasSetWidth == 0) && ((strlen(text) + i + ct->sbAdjust) > co->width)) { + updateWidth(co, ct, strlen(text) + i); + } + + ctDraw(co); +} + +char newtCheckboxTreeGetEntryValue(newtComponent co, const void * data) +{ + struct CheckboxTree * ct; + struct items * item; + + if (!co) return -1; + ct = co->data; + item = findItem(ct->itemlist, data); + if (!item) return -1; + if (item->branch) + return item->selected ? NEWT_CHECKBOXTREE_EXPANDED : NEWT_CHECKBOXTREE_COLLAPSED; + else + return ct->seq[item->selected]; +} + +void newtCheckboxTreeSetEntryValue(newtComponent co, const void * data, char value) +{ + struct CheckboxTree * ct; + struct items * item; + int i; + + if (!co) return; + ct = co->data; + item = findItem(ct->itemlist, data); + if (!item || item->branch) return; + + for(i = 0; ct->seq[i]; i++) + if (value == ct->seq[i]) + break; + + if (!ct->seq[i]) return; + item->selected = i; + + ctDraw(co); +} + + +void newtCheckboxTreeSetCurrent(newtComponent co, void * data) { + struct CheckboxTree * ct = co->data; + int * path; + int i, j, itemsAfter; + struct items * treeTop, * item; + + path = newtCheckboxTreeFindItem(co, data); + if (!path) return; + + /* traverse the path and turn on all of the branches to this point */ + for (i = 0, treeTop = ct->itemlist; path[i + 1] != NEWT_ARG_LAST; i++) { + for (j = 0, item = treeTop; j < path[i]; j++) + item = item->next; + + item->selected = 1; + treeTop = item->branch; + } + + buildFlatList(co); + + item = findItem(ct->itemlist, data); + + i = 0; + while (ct->flatList[i] != item) i++; + + /* choose the top item */ + j = i - (co->height / 2); + + if ((j + co->height) > ct->flatCount) + j = ct->flatCount - co->height; + + if (j < 0) + j = 0; + + ct->firstItem = ct->flatList + j; + ct->currItem = ct->flatList + i; + + ctDraw(co); +} diff --git a/mininewt/config.h b/mininewt/config.h new file mode 100644 index 000000000..8a1438c96 --- /dev/null +++ b/mininewt/config.h @@ -0,0 +1,12 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if you have the <alloca.h> header file. */ +#define HAVE_ALLOCA_H 1 + +/* Define if you have the <sys/select.h> header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if GPM support is enabled */ +/* #undef USE_GPM */ + diff --git a/mininewt/dialogboxes.c b/mininewt/dialogboxes.c new file mode 100644 index 000000000..41440094a --- /dev/null +++ b/mininewt/dialogboxes.c @@ -0,0 +1,422 @@ +/* simple dialog boxes, used by both whiptail and tcl dialog bindings */ + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "dialogboxes.h" +#include "newt.h" +#include "popt.h" + +/* globals -- ick */ +static int buttonHeight = 1; +static newtComponent (*makeButton)(int left, int right, const char * text) = + newtCompactButton; + +static void addButtons(int height, int width, newtComponent form, + newtComponent * okay, newtComponent * cancel, + int flags) { + if (flags & FLAG_NOCANCEL) { + *okay = makeButton((width - 8) / 2, height - buttonHeight - 1, "Ok"); + *cancel = NULL; + newtFormAddComponent(form, *okay); + } else { + *okay = makeButton((width - 18) / 3, height - buttonHeight - 1, "Ok"); + *cancel = makeButton(((width - 18) / 3) * 2 + 9, + height - buttonHeight - 1, "Cancel"); + newtFormAddComponents(form, *okay, *cancel, NULL); + } +} + +static newtComponent textbox(int maxHeight, int width, const char * text, + int flags, int * height) { + newtComponent tb; + int sFlag = (flags & FLAG_SCROLL_TEXT) ? NEWT_FLAG_SCROLL : 0; + int i; + char * buf, * dst; + const char * src; + + dst = buf = alloca(strlen(text) + 1); + src = text; + while (*src) { + if (*src == '\\' && *(src + 1) == 'n') { + src += 2; + *dst++ = '\n'; + } else + *dst++ = *src++; + } + *dst++ = '\0'; + + tb = newtTextbox(1, 0, width, maxHeight, NEWT_FLAG_WRAP | sFlag); + newtTextboxSetText(tb, buf); + + i = newtTextboxGetNumLines(tb); + if (i < maxHeight) { + newtTextboxSetHeight(tb, i); + maxHeight = i; + } + + *height = maxHeight; + + return tb; +} + +int gauge(const char * text, int height, int width, poptContext optCon, int fd, + int flags) { + newtComponent form, scale, tb; + int top; + const char * arg; + char * end; + int val; + FILE * f = fdopen(fd, "r"); + char buf[3000]; + char buf3[50]; + int i; + + setlinebuf(f); + + if (!(arg = poptGetArg(optCon))) return DLG_ERROR; + val = strtoul(arg, &end, 10); + if (*end) return DLG_ERROR; + + tb = textbox(height - 3, width - 2, text, flags, &top); + + form = newtForm(NULL, NULL, 0); + + scale = newtScale(2, height - 2, width - 4, 100); + newtScaleSet(scale, val); + + newtFormAddComponents(form, tb, scale, NULL); + + newtDrawForm(form); + newtRefresh(); + + while (fgets(buf, sizeof(buf) - 1, f)) { + buf[strlen(buf) - 1] = '\0'; + + if (!strcmp(buf, "XXX")) { + fgets(buf3, sizeof(buf3) - 1, f); + buf3[strlen(buf3) - 1] = '\0'; + arg = buf3; + + i = 0; + while (fgets(buf + i, sizeof(buf) - 1 - i, f)) { + buf[strlen(buf) - 1] = '\0'; + if (!strcmp(buf + i, "XXX")) { + *(buf + i) = '\0'; + break; + } + i = strlen(buf); + } + + newtTextboxSetText(tb, buf); + } else { + arg = buf; + } + + val = strtoul(buf, &end, 10); + if (!*end) { + newtScaleSet(scale, val); + newtDrawForm(form); + newtRefresh(); + } + } + + return DLG_OKAY; +} + +int inputBox(const char * text, int height, int width, poptContext optCon, + int flags, char ** result) { + newtComponent form, entry, okay, cancel, answer, tb; + char * val; + int rc = DLG_OKAY; + int top; + + val = poptGetArg(optCon); + tb = textbox(height - 3 - buttonHeight, width - 2, + text, flags, &top); + + form = newtForm(NULL, NULL, 0); + entry = newtEntry(1, top + 1, val, width - 2, &val, + NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT); + + newtFormAddComponents(form, tb, entry, NULL); + + addButtons(height, width, form, &okay, &cancel, flags); + + answer = newtRunForm(form); + if (answer == cancel) + rc = DLG_CANCEL; + + *result = val; + + return rc; +} + +int listBox(const char * text, int height, int width, poptContext optCon, + int flags, char ** result) { + newtComponent form, okay, tb, answer, listBox; + newtComponent cancel = NULL; + const char * arg; + char * end; + int listHeight; + int numItems = 0; + int allocedItems = 5; + int i, top; + int rc = DLG_OKAY; + char buf[80], format[20]; + int maxTagWidth = 0; + int maxTextWidth = 0; + int scrollFlag; + struct { + const char * text; + const char * tag; + } * itemInfo = malloc(allocedItems * sizeof(*itemInfo)); + + if (!(arg = poptGetArg(optCon))) return DLG_ERROR; + listHeight = strtoul(arg, &end, 10); + if (*end) return DLG_ERROR; + + while ((arg = poptGetArg(optCon))) { + if (allocedItems == numItems) { + allocedItems += 5; + itemInfo = realloc(itemInfo, sizeof(*itemInfo) * allocedItems); + } + + itemInfo[numItems].tag = arg; + if (!(arg = poptGetArg(optCon))) return DLG_ERROR; + + if (!(flags & FLAG_NOITEM)) { + itemInfo[numItems].text = arg; + } else + itemInfo[numItems].text = ""; + + if (strlen(itemInfo[numItems].text) > (unsigned int)maxTextWidth) + maxTextWidth = strlen(itemInfo[numItems].text); + if (strlen(itemInfo[numItems].tag) > (unsigned int)maxTagWidth) + maxTagWidth = strlen(itemInfo[numItems].tag); + + numItems++; + } + + form = newtForm(NULL, NULL, 0); + + tb = textbox(height - 4 - buttonHeight - listHeight, width - 2, + text, flags, &top); + + if (listHeight >= numItems) { + scrollFlag = 0; + i = 0; + } else { + scrollFlag = NEWT_FLAG_SCROLL; + i = 2; + } + + listBox = newtListbox(3 + ((width - 10 - maxTagWidth - maxTextWidth - i) + / 2), + top + 1, listHeight, + NEWT_FLAG_RETURNEXIT | scrollFlag); + + sprintf(format, "%%-%ds %%s", maxTagWidth); + for (i = 0; i < numItems; i++) { + sprintf(buf, format, itemInfo[i].tag, itemInfo[i].text); + newtListboxAddEntry(listBox, buf, (void *) i); + } + + newtFormAddComponents(form, tb, listBox, NULL); + + addButtons(height, width, form, &okay, &cancel, flags); + + answer = newtRunForm(form); + if (answer == cancel) + rc = DLG_CANCEL; + + i = (int) newtListboxGetCurrent(listBox); + *result = itemInfo[i].tag; + + return rc; +} + +int checkList(const char * text, int height, int width, poptContext optCon, + int useRadio, int flags, char *** selections) { + newtComponent form, okay, tb, subform, answer; + newtComponent sb = NULL, cancel = NULL; + const char * arg; + char * end; + int listHeight; + int numBoxes = 0; + int allocedBoxes = 5; + int i; + int numSelected; + int rc = DLG_OKAY; + char buf[80], format[20]; + int maxWidth = 0; + int top; + struct { + const char * text; + const char * tag; + newtComponent comp; + } * cbInfo = malloc(allocedBoxes * sizeof(*cbInfo)); + char * cbStates = malloc(allocedBoxes * sizeof(cbStates)); + + if (!(arg = poptGetArg(optCon))) return DLG_ERROR; + listHeight = strtoul(arg, &end, 10); + if (*end) return DLG_ERROR; + + while ((arg = poptGetArg(optCon))) { + if (allocedBoxes == numBoxes) { + allocedBoxes += 5; + cbInfo = realloc(cbInfo, sizeof(*cbInfo) * allocedBoxes); + cbStates = realloc(cbStates, sizeof(*cbStates) * allocedBoxes); + } + + cbInfo[numBoxes].tag = arg; + if (!(arg = poptGetArg(optCon))) return DLG_ERROR; + + if (!(flags & FLAG_NOITEM)) { + cbInfo[numBoxes].text = arg; + if (!(arg = poptGetArg(optCon))) return DLG_ERROR; + } else + cbInfo[numBoxes].text = ""; + + if (!strcmp(arg, "1") || !strcasecmp(arg, "on") || + !strcasecmp(arg, "yes")) + cbStates[numBoxes] = '*'; + else + cbStates[numBoxes] = ' '; + + if (strlen(cbInfo[numBoxes].tag) > (unsigned int)maxWidth) + maxWidth = strlen(cbInfo[numBoxes].tag); + + numBoxes++; + } + + form = newtForm(NULL, NULL, 0); + + tb = textbox(height - 3 - buttonHeight - listHeight, width - 2, + text, flags, &top); + + if (listHeight < numBoxes) { + sb = newtVerticalScrollbar(width - 4, + top + 1, + listHeight, NEWT_COLORSET_CHECKBOX, + NEWT_COLORSET_ACTCHECKBOX); + newtFormAddComponent(form, sb); + } + subform = newtForm(sb, NULL, 0); + newtFormSetBackground(subform, NEWT_COLORSET_CHECKBOX); + + sprintf(format, "%%-%ds %%s", maxWidth); + for (i = 0; i < numBoxes; i++) { + sprintf(buf, format, cbInfo[i].tag, cbInfo[i].text); + + if (useRadio) + cbInfo[i].comp = newtRadiobutton(4, top + 1 + i, buf, + cbStates[i] != ' ', + i ? cbInfo[i - 1].comp : NULL); + else + cbInfo[i].comp = newtCheckbox(4, top + 1 + i, buf, + cbStates[i], NULL, cbStates + i); + + newtFormAddComponent(subform, cbInfo[i].comp); + } + + newtFormSetHeight(subform, listHeight); + newtFormSetWidth(subform, width - 10); + + newtFormAddComponents(form, tb, subform, NULL); + + addButtons(height, width, form, &okay, &cancel, flags); + + answer = newtRunForm(form); + if (answer == cancel) + rc = DLG_CANCEL; + + if (useRadio) { + answer = newtRadioGetCurrent(cbInfo[0].comp); + for (i = 0; i < numBoxes; i++) + if (cbInfo[i].comp == answer) { + *selections = malloc(sizeof(char *) * 2); + (*selections)[0] = cbInfo[i].tag; + (*selections)[1] = NULL; + break; + } + } else { + numSelected = 0; + for (i = 0; i < numBoxes; i++) { + if (cbStates[i] != ' ') numSelected++; + } + + *selections = malloc(sizeof(char *) * (numSelected + 1)); + + numSelected = 0; + for (i = 0; i < numBoxes; i++) { + if (cbStates[i] != ' ') + (*selections)[numSelected++] = cbInfo[i].tag; + } + + (*selections)[numSelected] = NULL; + } + + return rc; +} + +int messageBox(const char * text, int height, int width, int type, int flags) { + newtComponent form, yes, tb, answer; + newtComponent no = NULL; + int tFlag = (flags & FLAG_SCROLL_TEXT) ? NEWT_FLAG_SCROLL : 0; + + form = newtForm(NULL, NULL, 0); + + tb = newtTextbox(1, 1, width - 2, height - 3 - buttonHeight, + NEWT_FLAG_WRAP | tFlag); + newtTextboxSetText(tb, text); + + newtFormAddComponent(form, tb); + + switch ( type ) { + case MSGBOX_INFO: + break; + case MSGBOX_MSG: + yes = makeButton((width - 8) / 2, height - 1 - buttonHeight, "Ok"); + newtFormAddComponent(form, yes); + break; + default: + yes = makeButton((width - 16) / 3, height - 1 - buttonHeight, "Yes"); + no = makeButton(((width - 16) / 3) * 2 + 9, height - 1 - buttonHeight, + "No"); + newtFormAddComponents(form, yes, no, NULL); + + if (flags & FLAG_DEFAULT_NO) + newtFormSetCurrent(form, no); + } + + if ( type != MSGBOX_INFO ) { + newtRunForm(form); + + answer = newtFormGetCurrent(form); + + if (answer == no) + return DLG_CANCEL; + } + else { + newtDrawForm(form); + newtRefresh(); + } + + + + return DLG_OKAY; +} + +void useFullButtons(int state) { + if (state) { + buttonHeight = 3; + makeButton = newtButton; + } else { + buttonHeight = 1; + makeButton = newtCompactButton; + } +} diff --git a/mininewt/dialogboxes.h b/mininewt/dialogboxes.h new file mode 100644 index 000000000..29dab12c7 --- /dev/null +++ b/mininewt/dialogboxes.h @@ -0,0 +1,30 @@ +#ifndef H_DIALOGBOXES +#define H_DIALOGBOXES + +#include "popt.h" + +#define MSGBOX_MSG 0 +#define MSGBOX_YESNO 1 +#define MSGBOX_INFO 2 + +#define FLAG_NOITEM (1 << 0) +#define FLAG_NOCANCEL (1 << 1) +#define FLAG_SCROLL_TEXT (1 << 2) +#define FLAG_DEFAULT_NO (1 << 3) + +#define DLG_ERROR -1 +#define DLG_OKAY 0 +#define DLG_CANCEL 1 + +int messageBox(const char * text, int height, int width, int type, int flags); +int checkList(const char * text, int height, int width, poptContext optCon, + int useRadio, int flags, char *** selections); +int listBox(const char * text, int height, int width, poptContext optCon, + int flags, char ** result); +int inputBox(const char * text, int height, int width, poptContext optCon, + int flags, char ** result); +int gauge(const char * text, int height, int width, poptContext optCon, int fd, + int flags); +void useFullButtons(int state); + +#endif diff --git a/mininewt/eawidth.c b/mininewt/eawidth.c new file mode 100644 index 000000000..7001e96c8 --- /dev/null +++ b/mininewt/eawidth.c @@ -0,0 +1,463 @@ +/* #define TEST_GET_EAST_ASIA_STR_WIDTH 1 */ + +#include <assert.h> +#include <locale.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include "eawidth.h" + +/* + * If the amount of columns the cursor advances on a TAB character depends + * on the current position, set this to a negative number (i.e. -8 for tab + * stops every eight columns. If static, set to a positive number. Zero if + * tabs are ignored. + */ +static const int tab_width = -8; + +typedef struct { + unsigned short start, end; + east_asia_type type; +} eaw_db_type; + +static const eaw_db_type eaw_db[] = { + { 0x0020,0x007E,narrow }, + { 0x00A1,0x00A1,ambiguous }, /*INVERTED EXCLAMATION MARK*/ + { 0x00A2,0x00A3,narrow }, + { 0x00A4,0x00A4,ambiguous }, /*CURRENCY SIGN*/ + { 0x00A5,0x00A6,narrow }, + { 0x00A7,0x00A8,ambiguous }, + { 0x00AA,0x00AA,ambiguous }, /*FEMININE ORDINAL INDICATOR*/ + { 0x00AC,0x00AC,narrow }, /*NOT SIGN*/ + { 0x00AD,0x00AD,ambiguous }, /*SOFT HYPHEN*/ + { 0x00AF,0x00AF,narrow }, /*MACRON*/ + { 0x00B0,0x00B4,ambiguous }, + { 0x00B6,0x00BA,ambiguous }, + { 0x00BC,0x00BF,ambiguous }, + { 0x00C6,0x00C6,ambiguous }, /*LATIN CAPITAL LETTER AE*/ + { 0x00D0,0x00D0,ambiguous }, /*LATIN CAPITAL LETTER ETH*/ + { 0x00D7,0x00D8,ambiguous }, + { 0x00DE,0x00E1,ambiguous }, + { 0x00E6,0x00E6,ambiguous }, /*LATIN SMALL LETTER AE*/ + { 0x00E8,0x00EA,ambiguous }, + { 0x00EC,0x00ED,ambiguous }, + { 0x00F0,0x00F0,ambiguous }, /*LATIN SMALL LETTER ETH*/ + { 0x00F2,0x00F3,ambiguous }, + { 0x00F7,0x00FA,ambiguous }, + { 0x00FC,0x00FC,ambiguous }, /*LATIN SMALL LETTER U WITH DIAERESIS*/ + { 0x00FE,0x00FE,ambiguous }, /*LATIN SMALL LETTER THORN*/ + { 0x0101,0x0101,ambiguous }, /*LATIN SMALL LETTER A WITH MACRON*/ + { 0x0111,0x0111,ambiguous }, /*LATIN SMALL LETTER D WITH STROKE*/ + { 0x0113,0x0113,ambiguous }, /*LATIN SMALL LETTER E WITH MACRON*/ + { 0x011B,0x011B,ambiguous }, /*LATIN SMALL LETTER E WITH CARON*/ + { 0x0126,0x0127,ambiguous }, + { 0x012B,0x012B,ambiguous }, /*LATIN SMALL LETTER I WITH MACRON*/ + { 0x0131,0x0133,ambiguous }, + { 0x0138,0x0138,ambiguous }, /*LATIN SMALL LETTER KRA*/ + { 0x013F,0x0142,ambiguous }, + { 0x0144,0x0144,ambiguous }, /*LATIN SMALL LETTER N WITH ACUTE*/ + { 0x0148,0x014A,ambiguous }, + { 0x014D,0x014D,ambiguous }, /*LATIN SMALL LETTER O WITH MACRON*/ + { 0x0152,0x0153,ambiguous }, + { 0x0166,0x0167,ambiguous }, + { 0x016B,0x016B,ambiguous }, /*LATIN SMALL LETTER U WITH MACRON*/ + { 0x01CE,0x01CE,ambiguous }, /*LATIN SMALL LETTER A WITH CARON*/ + { 0x01D0,0x01D0,ambiguous }, /*LATIN SMALL LETTER I WITH CARON*/ + { 0x01D2,0x01D2,ambiguous }, /*LATIN SMALL LETTER O WITH CARON*/ + { 0x01D4,0x01D4,ambiguous }, /*LATIN SMALL LETTER U WITH CARON*/ + { 0x01D6,0x01D6,ambiguous }, /*LATIN SMALL LETTER U W/DIAERESIS+MACRON*/ + { 0x01D8,0x01D8,ambiguous }, /*LATIN SMALL LETTER U W/DIAERESIS+ACUTE*/ + { 0x01DA,0x01DA,ambiguous }, /*LATIN SMALL LETTER U W/DIAERESIS+CARON*/ + { 0x01DC,0x01DC,ambiguous }, /*LATIN SMALL LETTER U W/DIAERESIS+GRAVE*/ + { 0x0251,0x0251,ambiguous }, /*LATIN SMALL LETTER ALPHA*/ + { 0x0261,0x0261,ambiguous }, /*LATIN SMALL LETTER SCRIPT G*/ + { 0x02C7,0x02C7,ambiguous }, /*CARON*/ + { 0x02C9,0x02CB,ambiguous }, + { 0x02CD,0x02CD,ambiguous }, /*MODIFIER LETTER LOW MACRON*/ + { 0x02D0,0x02D0,ambiguous }, /*MODIFIER LETTER TRIANGULAR COLON*/ + { 0x02D8,0x02DB,ambiguous }, + { 0x02DD,0x02DD,ambiguous }, /*DOUBLE ACUTE ACCENT*/ + { 0x0300,0x0362,ambiguous }, + { 0x0391,0x03A9,ambiguous }, + { 0x03B1,0x03C1,ambiguous }, + { 0x03C3,0x03C9,ambiguous }, + { 0x0401,0x0401,ambiguous }, /*CYRILLIC CAPITAL LETTER IO*/ + { 0x0410,0x044F,ambiguous }, + { 0x0451,0x0451,ambiguous }, /*CYRILLIC SMALL LETTER IO*/ + { 0x1100,0x115F,wide }, + { 0x2010,0x2010,ambiguous }, /*HYPHEN*/ + { 0x2013,0x2016,ambiguous }, + { 0x2018,0x2019,ambiguous }, + { 0x201C,0x201D,ambiguous }, + { 0x2020,0x2021,ambiguous }, + { 0x2025,0x2027,ambiguous }, + { 0x2030,0x2030,ambiguous }, /*PER MILLE SIGN*/ + { 0x2032,0x2033,ambiguous }, + { 0x2035,0x2035,ambiguous }, /*REVERSED PRIME*/ + { 0x203B,0x203B,ambiguous }, /*REFERENCE MARK*/ + { 0x2074,0x2074,ambiguous }, /*SUPERSCRIPT FOUR*/ + { 0x207F,0x207F,ambiguous }, /*SUPERSCRIPT LATIN SMALL LETTER N*/ + { 0x2081,0x2084,ambiguous }, + { 0x20A9,0x20A9,half_width }, /*WON SIGN*/ + { 0x20AC,0x20AC,ambiguous }, /*EURO SIGN*/ + { 0x2103,0x2103,ambiguous }, /*DEGREE CELSIUS*/ + { 0x2105,0x2105,ambiguous }, /*CARE OF*/ + { 0x2109,0x2109,ambiguous }, /*DEGREE FAHRENHEIT*/ + { 0x2113,0x2113,ambiguous }, /*SCRIPT SMALL L*/ + { 0x2121,0x2122,ambiguous }, + { 0x2126,0x2126,ambiguous }, /*OHM SIGN*/ + { 0x212B,0x212B,ambiguous }, /*ANGSTROM SIGN*/ + { 0x2154,0x2155,ambiguous }, + { 0x215B,0x215B,ambiguous }, /*VULGAR FRACTION ONE EIGHTH*/ + { 0x215E,0x215E,ambiguous }, /*VULGAR FRACTION SEVEN EIGHTHS*/ + { 0x2160,0x216B,ambiguous }, + { 0x2170,0x2179,ambiguous }, + { 0x2190,0x2199,ambiguous }, + { 0x21D2,0x21D2,ambiguous }, /*RIGHTWARDS DOUBLE ARROW*/ + { 0x21D4,0x21D4,ambiguous }, /*LEFT RIGHT DOUBLE ARROW*/ + { 0x2200,0x2200,ambiguous }, /*FOR ALL*/ + { 0x2202,0x2203,ambiguous }, + { 0x2207,0x2208,ambiguous }, + { 0x220B,0x220B,ambiguous }, /*CONTAINS AS MEMBER*/ + { 0x220F,0x220F,ambiguous }, /*N-ARY PRODUCT*/ + { 0x2211,0x2211,ambiguous }, /*N-ARY SUMMATION*/ + { 0x2215,0x2215,ambiguous }, /*DIVISION SLASH*/ + { 0x221A,0x221A,ambiguous }, /*SQUARE ROOT*/ + { 0x221D,0x2220,ambiguous }, + { 0x2223,0x2223,ambiguous }, /*DIVIDES*/ + { 0x2225,0x2225,ambiguous }, /*PARALLEL TO*/ + { 0x2227,0x222C,ambiguous }, + { 0x222E,0x222E,ambiguous }, /*CONTOUR INTEGRAL*/ + { 0x2234,0x2237,ambiguous }, + { 0x223C,0x223D,ambiguous }, + { 0x2248,0x2248,ambiguous }, /*ALMOST EQUAL TO*/ + { 0x224C,0x224C,ambiguous }, /*ALL EQUAL TO*/ + { 0x2252,0x2252,ambiguous }, /*APPROXIMATELY EQUAL TO OR THE IMAGE OF*/ + { 0x2260,0x2261,ambiguous }, + { 0x2264,0x2267,ambiguous }, + { 0x226A,0x226B,ambiguous }, + { 0x226E,0x226F,ambiguous }, + { 0x2282,0x2283,ambiguous }, + { 0x2286,0x2287,ambiguous }, + { 0x2295,0x2295,ambiguous }, /*CIRCLED PLUS*/ + { 0x2299,0x2299,ambiguous }, /*CIRCLED DOT OPERATOR*/ + { 0x22A5,0x22A5,ambiguous }, /*UP TACK*/ + { 0x22BF,0x22BF,ambiguous }, /*RIGHT TRIANGLE*/ + { 0x2312,0x2312,ambiguous }, /*ARC*/ + { 0x2460,0x24BF,ambiguous }, + { 0x24D0,0x24E9,ambiguous }, + { 0x2500,0x254B,ambiguous }, + { 0x2550,0x2574,ambiguous }, + { 0x2580,0x258F,ambiguous }, + { 0x2592,0x25A1,ambiguous }, + { 0x25A3,0x25A9,ambiguous }, + { 0x25B2,0x25B3,ambiguous }, + { 0x25B6,0x25B7,ambiguous }, + { 0x25BC,0x25BD,ambiguous }, + { 0x25C0,0x25C1,ambiguous }, + { 0x25C6,0x25C8,ambiguous }, + { 0x25CB,0x25CB,ambiguous }, /*WHITE CIRCLE*/ + { 0x25CE,0x25D1,ambiguous }, + { 0x25E2,0x25E5,ambiguous }, + { 0x25EF,0x25EF,ambiguous }, /*LARGE CIRCLE*/ + { 0x2605,0x2606,ambiguous }, + { 0x2609,0x2609,ambiguous }, /*SUN*/ + { 0x260E,0x260F,ambiguous }, + { 0x261C,0x261C,ambiguous }, /*WHITE LEFT POINTING INDEX*/ + { 0x261E,0x261E,ambiguous }, /*WHITE RIGHT POINTING INDEX*/ + { 0x2640,0x2640,ambiguous }, /*FEMALE SIGN*/ + { 0x2642,0x2642,ambiguous }, /*MALE SIGN*/ + { 0x2660,0x2661,ambiguous }, + { 0x2663,0x2665,ambiguous }, + { 0x2667,0x266A,ambiguous }, + { 0x266C,0x266D,ambiguous }, + { 0x266F,0x266F,ambiguous }, /*MUSIC SHARP SIGN*/ + { 0x2E80,0x3009,wide }, + { 0x300A,0x300B,ambiguous }, + { 0x300C,0x3019,wide }, + { 0x301A,0x301B,ambiguous }, + { 0x301C,0x303E,wide }, + { 0x3041,0xD7A3,wide }, + { 0xE000,0xF8FF,ambiguous }, + { 0xF900,0xFA2D,wide }, + { 0xFE30,0xFE6B,wide }, + { 0xFF01,0xFF5E,full_width }, + { 0xFF61,0xFFDC,half_width }, + { 0xFFE0,0xFFE6,full_width }, + { 0xFFE8,0xFFEE,half_width }, +}; + +static int +eaw_db_cmp (const void *ck, const void *ce) { + const eaw_db_type *key = ck, *element = ce; + + assert(key != NULL); + assert(element != NULL); + if (key->start < element->start) return -1; + else if (key->end > element->end) return 1; + return 0; +} + +static int +is_cjk_locale (const char *locale_name) { + static const char c[] = "zh"; /* Chinese */ + static const char j[] = "ja"; /* Japanese */ + static const char k[] = "ko"; /* Korean */ + + if (NULL == locale_name) return 0; + if (strncmp(locale_name, c, sizeof(c)) == 0) return 1; + if (strncmp(locale_name, j, sizeof(j)) == 0) return 1; + if (strncmp(locale_name, k, sizeof(k)) == 0) return 1; + return 0; +} + +east_asia_type +get_east_asia_type (wchar_t unicode) { + assert(0xFFFF != unicode && 0xFFFE != unicode); + + if (unicode > 0xFFFF) { + + /* + * Plane 2 is intended for CJK ideographs + */ + if (unicode >= 0x20000 && unicode <= 0x2FFFD) return wide; + return ambiguous; + } + else { + eaw_db_type *pos, key; + size_t n; + + n = sizeof(eaw_db) / sizeof(eaw_db_type); + key.start = key.end = (unsigned short) unicode; + pos = bsearch(&key, eaw_db, n, sizeof(eaw_db_type), eaw_db_cmp); + if (NULL != pos) return pos->type; + } + return neutral; +} + +int +east_asia_mblen (const char *locale_name, const char *s, size_t n, int x) +{ + wchar_t *wcs, *p; + int width = 0; + + if (NULL == s) s = ""; + + /* + * Getting the locale name via setlocale() is expensive, so we prefer + * to have it passed to us. + */ + if (NULL == locale_name) { + locale_name = setlocale(LC_CTYPE, NULL); + if (NULL == locale_name) return INT_MAX; + } + + wcs = (wchar_t *) calloc(n, sizeof(wchar_t)); + if (NULL == wcs) return INT_MAX; + +#if defined __GLIBC__ && !__GLIBC_PREREQ(2,2) +#warning wide character support is broken. Glibc 2.2 or better needed. +#endif + + if ((size_t) -1 == mbstowcs(wcs, s, n)) return INT_MAX; + + switch (get_east_asia_type(*wcs)) { + case neutral: + + /* + * Put characters that print nothing here. + * + * XXX: Yes, I know there are a lot more than this in ISO-10646, but + * this function is intended to calculate the width of strings for + * fixed width terminals displaying legacy CJK character sets. + * State-of-the-art Unicode handling terminals probably won't need + * this function anyway. + */ + if (0x0000 == *wcs) break; /* NULL */ + if (0x0007 == *wcs) break; /* BELL */ + + /* FIXME: there will probably be ASCII chars after the escape + * code, which will be counted as part of the width even though they + * aren't displayed. + */ + if (0x001B == *wcs) break; /* ESC */ + if (0xFEFF == *wcs) break; /* ZWNBSP aka BOM (magic, signature) */ + + /* + * Special characters go here + */ + if (0x0008 == *wcs) { /* BACKSPACE */ + width = -1; + break; + } + if (0x0009 == *wcs) { /* TAB */ + if (tab_width < 0) width = x % abs(tab_width); + else width = tab_width; + break; + } + + /*FALLTHRU*/ + case narrow: + case half_width: + width = 1; + break; + case wide: + case full_width: + width = 2; + break; + case ambiguous: + width = is_cjk_locale(locale_name) ? 2 : 1; + break; + default: + width = INT_MAX; + } + free(wcs); + return width; +} + +int +get_east_asia_str_n_width (const char *locale_name, const char *s, size_t n, int x) +{ + int total_width = 0; + wchar_t *wcs, *p; + + if (NULL == s) s = ""; + + /* + * Getting the locale name via setlocale() is expensive, so we prefer + * to have it passed to us. + */ + if (NULL == locale_name) { + locale_name = setlocale(LC_CTYPE, NULL); + if (NULL == locale_name) return INT_MAX; + } + + wcs = (wchar_t *) calloc(n, sizeof(wchar_t)); + if (NULL == wcs) return INT_MAX; + +#if defined __GLIBC__ && !__GLIBC_PREREQ(2,2) +#warning wide character support is broken. Glibc 2.2 or better needed. +#endif + + if ((size_t) -1 == mbstowcs(wcs, s, n)) return INT_MAX; + + for (p = wcs; L'\0' != *p; p++) { + int width = 0; + + switch (get_east_asia_type(*p)) { + case neutral: + + /* + * Put characters that print nothing here. + * + * XXX: Yes, I know there are a lot more than this in ISO-10646, but + * this function is intended to calculate the width of strings for + * fixed width terminals displaying legacy CJK character sets. + * State-of-the-art Unicode handling terminals probably won't need + * this function anyway. + */ + if (0x0000 == *p) break; /* NULL */ + if (0x0007 == *p) break; /* BELL */ + + /* FIXME: there will probably be ASCII chars after the escape + * code, which will be counted as part of the width even though they + * aren't displayed. + */ + if (0x001B == *p) break; /* ESC */ + if (0xFEFF == *p) break; /* ZWNBSP aka BOM (magic, signature) */ + + /* + * Special characters go here + */ + if (0x0008 == *p) { /* BACKSPACE */ + width = -1; + break; + } + if (0x0009 == *p) { /* TAB */ + if (tab_width < 0) width = x % abs(tab_width); + else width = tab_width; + break; + } + + /*FALLTHRU*/ + case narrow: + case half_width: + width = 1; + break; + case wide: + case full_width: + width = 2; + break; + case ambiguous: + width = is_cjk_locale(locale_name) ? 2 : 1; + break; + default: abort(); /* Doh! */ + } + x += width; + total_width += width; + } + free(wcs); + return total_width; +} + +int +get_east_asia_str_width (const char *locale_name, const char *s, int x) { + size_t n; + int rc; + + n = strlen(s) + 1; + rc = get_east_asia_str_n_width (locale_name, s, n, x); + if (rc == INT_MAX) + return strlen (s); + return rc; +} + +#if TEST_GET_EAST_ASIA_STR_WIDTH + +#include <stdio.h> + +int +main (int argc, char *argv[]) { + int i; + char *lc; + const char *fmt = "word #%d ('%s') length is %zu, width is %u\n"; + + lc = setlocale(LC_CTYPE, ""); + if (NULL == lc) { + fputs("couldn't set the default locale for LC_CTYPE\n", stderr); + exit(EXIT_FAILURE); + } + if (printf("character type locale is '%s'\n", lc) < 0) { + perror(NULL); + exit(EXIT_FAILURE); + } + for (i = 1; argc < 2 || i < argc; i++) { + char *s; + size_t length; + unsigned width; + + if (argc < 2) { + if (scanf("%as", &s) < 1 && ferror(stdin)) { + perror(NULL); + exit(EXIT_FAILURE); + } + else if (feof(stdin)) break; + } + else s = strdup(argv[(size_t) i]); + if (NULL == s) { + perror(NULL); + exit(EXIT_FAILURE); + } + length = strlen(s); + width = get_east_asia_str_width(lc, s, 0); + if (printf(fmt, i, s, length, width) < 0) { + perror(NULL); + exit(EXIT_FAILURE); + } + free(s); + } + return 0; +} + +#endif diff --git a/mininewt/entry.c b/mininewt/entry.c new file mode 100644 index 000000000..94c77ef5e --- /dev/null +++ b/mininewt/entry.c @@ -0,0 +1,382 @@ +#include "config.h" + +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif + +#include <ctype.h> +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct entry { + int flags; + char * buf; + char ** resultPtr; + int bufAlloced; + int bufUsed; /* amount of the buffer that's been used */ + int cursorPosition; /* cursor *in the string* on on screen */ + int firstChar; /* first character position being shown */ + newtEntryFilter filter; + void * filterData; +}; + +static void entryDraw(newtComponent co); +static void entryDestroy(newtComponent co); +static struct eventResult entryEvent(newtComponent co, + struct event ev); + +static struct eventResult entryHandleKey(newtComponent co, int key); + +static struct componentOps entryOps = { + entryDraw, + entryEvent, + entryDestroy, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd) { + struct entry * en = co->data; + + if ((strlen(value) + 1) > (unsigned int)en->bufAlloced) { + free(en->buf); + en->bufAlloced = strlen(value) + 1; + en->buf = malloc(en->bufAlloced); + if (en->resultPtr) *en->resultPtr = en->buf; + } + memset(en->buf, 0, en->bufAlloced); /* clear the buffer */ + strcpy(en->buf, value); + en->bufUsed = strlen(value); + en->firstChar = 0; + if (cursorAtEnd) + en->cursorPosition = en->bufUsed; + else + en->cursorPosition = 0; + + entryDraw(co); +} ; + +newtComponent newtEntry(int left, int top, const char * initialValue, int width, + char ** resultPtr, int flags) { + newtComponent co; + struct entry * en; + + co = malloc(sizeof(*co)); + en = malloc(sizeof(struct entry)); + co->data = en; + + co->top = top; + co->left = left; + co->height = 1; + co->width = width; + co->isMapped = 0; + co->callback = NULL; + + co->ops = &entryOps; + + en->flags = flags; + en->cursorPosition = 0; + en->firstChar = 0; + en->bufUsed = 0; + en->bufAlloced = width + 1; + en->filter = NULL; + + if (!(en->flags & NEWT_FLAG_DISABLED)) + co->takesFocus = 1; + else + co->takesFocus = 0; + + if (initialValue && strlen(initialValue) > (unsigned int)width) { + en->bufAlloced = strlen(initialValue) + 1; + } + en->buf = malloc(en->bufAlloced); + en->resultPtr = resultPtr; + if (en->resultPtr) *en->resultPtr = en->buf; + + memset(en->buf, 0, en->bufAlloced); + if (initialValue) { + strcpy(en->buf, initialValue); + en->bufUsed = strlen(initialValue); + en->cursorPosition = en->bufUsed; + } else { + *en->buf = '\0'; + en->bufUsed = 0; + en->cursorPosition = 0; + } + + return co; +} + +static void entryDraw(newtComponent co) { + struct entry * en = co->data; + int i; + char * chptr; + int len; + + if (!co->isMapped) return; + + if (en->flags & NEWT_FLAG_DISABLED) + SLsmg_set_color(NEWT_COLORSET_DISENTRY); + else + SLsmg_set_color(NEWT_COLORSET_ENTRY); + + if (en->flags & NEWT_FLAG_HIDDEN) { + newtGotorc(co->top, co->left); + for (i = 0; i < co->width; i++) + SLsmg_write_char('_'); + newtGotorc(co->top, co->left); + + return; + } + + newtGotorc(co->top, co->left); + + if (en->cursorPosition < en->firstChar) { + /* scroll to the left */ + en->firstChar = en->cursorPosition; + } else if ((en->firstChar + co->width) <= en->cursorPosition) { + /* scroll to the right */ + en->firstChar = en->cursorPosition - co->width + 1; + } + + chptr = en->buf + en->firstChar; + + if (en->flags & NEWT_FLAG_PASSWORD) { + char *tmpptr, *p; + + tmpptr = alloca(strlen(chptr)+2); + strcpy(tmpptr, chptr); + for (p = tmpptr; *p; p++) + *p = '*'; + chptr = tmpptr; + } + + len = strlen(chptr); + + if (len <= co->width) { + i = len; + SLsmg_write_string(chptr); + while (i < co->width) { + SLsmg_write_char('_'); + i++; + } + } else { + SLsmg_write_nstring(chptr, co->width); + } + + if (en->flags & NEWT_FLAG_HIDDEN) + newtGotorc(co->top, co->left); + else + newtGotorc(co->top, co->left + (en->cursorPosition - en->firstChar)); +} + +void newtEntrySetFlags(newtComponent co, int flags, enum newtFlagsSense sense) { + struct entry * en = co->data; + int row, col; + + en->flags = newtSetFlags(en->flags, flags, sense); + + if (!(en->flags & NEWT_FLAG_DISABLED)) + co->takesFocus = 1; + else + co->takesFocus = 0; + + newtGetrc(&row, &col); + entryDraw(co); + newtGotorc(row, col); +} + +static void entryDestroy(newtComponent co) { + struct entry * en = co->data; + + free(en->buf); + free(en); + free(co); +} + +static struct eventResult entryEvent(newtComponent co, + struct event ev) { + struct entry * en = co->data; + struct eventResult er; + int ch; + + if (ev.when == EV_NORMAL) { + switch (ev.event) { + case EV_FOCUS: + newtCursorOn(); + if (en->flags & NEWT_FLAG_HIDDEN) + newtGotorc(co->top, co->left); + else + newtGotorc(co->top, co->left + + (en->cursorPosition - en->firstChar)); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + newtCursorOff(); + newtGotorc(0, 0); + er.result = ER_SWALLOWED; + if (co->callback) + co->callback(co, co->callbackData); + break; + + case EV_KEYPRESS: + ch = ev.u.key; + if (en->filter) + ch = en->filter(co, en->filterData, ch, en->cursorPosition); + if (ch) er = entryHandleKey(co, ch); + break; + + case EV_MOUSE: + if ((ev.u.mouse.type == MOUSE_BUTTON_DOWN) && + (en->flags ^ NEWT_FLAG_HIDDEN)) { + if (strlen(en->buf) >= ev.u.mouse.x - co->left) { + en->cursorPosition = ev.u.mouse.x - co->left; + newtGotorc(co->top, + co->left +(en->cursorPosition - en->firstChar)); + } else { + en->cursorPosition = strlen(en->buf); + newtGotorc(co->top, + co->left +(en->cursorPosition - en->firstChar)); + } + } + break; + } + } else + er.result = ER_IGNORED; + + return er; +} + +static struct eventResult entryHandleKey(newtComponent co, int key) { + struct entry * en = co->data; + struct eventResult er; + char * chptr, * insPoint; + + er.result = ER_SWALLOWED; + switch (key) { + case '\r': /* Return */ + if (en->flags & NEWT_FLAG_RETURNEXIT) { + er.result = ER_EXITFORM; + } else { + er.result = ER_NEXTCOMP; + } + break; + + case '\001': /* ^A */ + case NEWT_KEY_HOME: + en->cursorPosition = 0; + break; + + case '\005': /* ^E */ + case NEWT_KEY_END: + en->cursorPosition = en->bufUsed; + break; + + case '\013': /* ^K */ + en->bufUsed = en->cursorPosition; + memset(en->buf + en->bufUsed, 0, en->bufAlloced - en->bufUsed); + break; + + case '\002': /* ^B */ + case NEWT_KEY_LEFT: + if (en->cursorPosition) + en->cursorPosition--; + break; + + case '\004': + case NEWT_KEY_DELETE: + chptr = en->buf + en->cursorPosition; + if (*chptr) { + chptr++; + while (*chptr) { + *(chptr - 1) = *chptr; + chptr++; + } + *(chptr - 1) = '\0'; + en->bufUsed--; + } + break; + + case NEWT_KEY_BKSPC: + if (en->cursorPosition) { + /* if this isn't true, there's nothing to erase */ + chptr = en->buf + en->cursorPosition; + en->bufUsed--; + en->cursorPosition--; + while (*chptr) { + *(chptr - 1) = *chptr; + chptr++; + } + *(chptr - 1) = '\0'; + } + break; + + case '\006': /* ^B */ + case NEWT_KEY_RIGHT: + if (en->cursorPosition < en->bufUsed) + en->cursorPosition++; + break; + + default: + if ((key >= 0x20 && key <= 0x7e) || (key >= 0xa0 && key <= 0xff)) { + if (!(en->flags & NEWT_FLAG_SCROLL) && en->bufUsed >= co->width) { + SLtt_beep(); + break; + } + + if ((en->bufUsed + 1) == en->bufAlloced) { + en->bufAlloced += 20; + en->buf = realloc(en->buf, en->bufAlloced); + if (en->resultPtr) *en->resultPtr = en->buf; + memset(en->buf + en->bufUsed + 1, 0, 20); + } + + if (en->cursorPosition == en->bufUsed) { + en->bufUsed++; + } else { + /* insert the new character */ + + /* chptr is the last character in the string */ + chptr = (en->buf + en->bufUsed) - 1; + if ((en->bufUsed + 1) == en->bufAlloced) { + /* this string fills the buffer, so clip it */ + chptr--; + } else + en->bufUsed++; + + insPoint = en->buf + en->cursorPosition; + + while (chptr >= insPoint) { + *(chptr + 1) = *chptr; + chptr--; + } + + } + + en->buf[en->cursorPosition++] = key; + } else { + er.result = ER_IGNORED; + } + } + + entryDraw(co); + + return er; +} + +char * newtEntryGetValue(newtComponent co) { + struct entry * en = co->data; + + return en->buf; +} + +void newtEntrySetFilter(newtComponent co, newtEntryFilter filter, void * data) { + struct entry * en = co->data; + en->filter = filter; + en->filterData = data; +} diff --git a/mininewt/form.c b/mininewt/form.c new file mode 100644 index 000000000..5564a7480 --- /dev/null +++ b/mininewt/form.c @@ -0,0 +1,1137 @@ +#include "config.h" + +#include <sys/types.h> + +#include <slang.h> +#include <stdarg.h> +#include <stdlib.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <sys/time.h> + +#ifdef USE_GPM +#include <ctype.h> +#include <sys/time.h> /* timeval */ +#include <sys/socket.h> /* socket() */ +#include <sys/un.h> /* struct sockaddr_un */ +#include <sys/fcntl.h> /* O_RDONLY */ +#include <sys/stat.h> /* stat() */ +#include <termios.h> /* winsize */ +#include <unistd.h> +#include <sys/kd.h> /* KDGETMODE */ +#include <signal.h> +#include <stdio.h> +#endif + +#include "newt.h" +#include "newt_pr.h" + +#ifdef USE_GPM +/*....................................... The connection data structure */ + +typedef struct Gpm_Connect { + unsigned short eventMask, defaultMask; + unsigned short minMod, maxMod; + int pid; + int vc; +} Gpm_Connect; + +/*....................................... Stack struct */ +typedef struct Gpm_Stst { + Gpm_Connect info; + struct Gpm_Stst *next; +} Gpm_Stst; + +enum Gpm_Etype { + GPM_MOVE=1, + GPM_DRAG=2, /* exactly one of the bare ones is active at a time */ + GPM_DOWN=4, + GPM_UP= 8, + +#define GPM_BARE_EVENTS(type) ((type)&(0x0f|GPM_ENTER|GPM_LEAVE)) + + GPM_SINGLE=16, /* at most one in three is set */ + GPM_DOUBLE=32, + GPM_TRIPLE=64, /* WARNING: I depend on the values */ + + GPM_MFLAG=128, /* motion during click? */ + GPM_HARD=256, /* if set in the defaultMask, force an already + used event to pass over to another handler */ + + GPM_ENTER=512, /* enter event, user in Roi's */ + GPM_LEAVE=1024 /* leave event, used in Roi's */ +}; + +/*....................................... The reported event */ + +enum Gpm_Margin {GPM_TOP=1, GPM_BOT=2, GPM_LFT=4, GPM_RGT=8}; + +typedef struct Gpm_Event { + unsigned char buttons, modifiers; /* try to be a multiple of 4 */ + unsigned short vc; + short dx, dy, x, y; + enum Gpm_Etype type; + int clicks; + enum Gpm_Margin margin; +} Gpm_Event; + +static int Gpm_Open(Gpm_Connect *conn, int flag); +static int Gpm_Close(void); + +static int gpm_fd=-1; +static int gpm_flag=0; +static int gpm_tried=0; +Gpm_Stst *gpm_stack=NULL; +static struct sigaction gpm_saved_suspend_hook; +static struct sigaction gpm_saved_winch_hook; + +#define GPM_XTERM_ON +#define GPM_XTERM_OFF +#define GPM_NODE_DEV "/dev/gpmctl" +#define GPM_NODE_CTL GPM_NODE_DEV + +static inline int putdata(int where, Gpm_Connect *what) +{ + if (write(where,what,sizeof(Gpm_Connect))!=sizeof(Gpm_Connect)) + { + return -1; + } + return 0; +} + +static void gpm_winch_hook (int signum) +{ + if (SIG_IGN != gpm_saved_winch_hook.sa_handler && + SIG_DFL != gpm_saved_winch_hook.sa_handler) { + gpm_saved_winch_hook.sa_handler(signum); + } /*if*/ +} + +static void gpm_suspend_hook (int signum) +{ + Gpm_Connect gpm_connect; + sigset_t old_sigset; + sigset_t new_sigset; + struct sigaction sa; + int success; + + sigemptyset (&new_sigset); + sigaddset (&new_sigset, SIGTSTP); + sigprocmask (SIG_BLOCK, &new_sigset, &old_sigset); + + /* Open a completely transparent gpm connection */ + gpm_connect.eventMask = 0; + gpm_connect.defaultMask = ~0; + gpm_connect.minMod = ~0; + gpm_connect.maxMod = 0; + /* cannot do this under xterm, tough */ + success = (Gpm_Open (&gpm_connect, 0) >= 0); + + /* take the default action, whatever it is (probably a stop :) */ + sigprocmask (SIG_SETMASK, &old_sigset, 0); + sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0); + kill (getpid (), SIGTSTP); + + /* in bardo here */ + + /* Reincarnation. Prepare for another death early. */ + sigemptyset(&sa.sa_mask); + sa.sa_handler = gpm_suspend_hook; + sa.sa_flags = SA_NOMASK; + sigaction (SIGTSTP, &sa, 0); + + /* Pop the gpm stack by closing the useless connection */ + /* but do it only when we know we opened one.. */ + if (success) { + Gpm_Close (); + } /*if*/ +} + +static int Gpm_Open(Gpm_Connect *conn, int flag) +{ + char tty[32]; + char *term; + int i; + struct sockaddr_un addr; + Gpm_Stst *new; + char* sock_name = 0; + + /*....................................... First of all, check xterm */ + + if ((term=(char *)getenv("TERM")) && !strncmp(term,"xterm",5)) + { + if (gpm_tried) return gpm_fd; /* no stack */ + gpm_fd=-2; + GPM_XTERM_ON; + gpm_flag=1; + return gpm_fd; + } + /*....................................... No xterm, go on */ + + + /* + * So I chose to use the current tty, instead of /dev/console, which + * has permission problems. (I am fool, and my console is + * readable/writeable by everybody. + * + * However, making this piece of code work has been a real hassle. + */ + + if (!gpm_flag && gpm_tried) return -1; + gpm_tried=1; /* do or die */ + + new=malloc(sizeof(Gpm_Stst)); + if (!new) return -1; + + new->next=gpm_stack; + gpm_stack=new; + + conn->pid=getpid(); /* fill obvious values */ + + if (new->next) + conn->vc=new->next->info.vc; /* inherit */ + else + { + conn->vc=0; /* default handler */ + if (flag>0) + { /* forced vc number */ + conn->vc=flag; + sprintf(tty,"/dev/tty%i",flag); + } + else if (flag==0) /* use your current vc */ + { + char *t = ttyname(0); /* stdin */ + if (!t) t = ttyname(1); /* stdout */ + if (!t) goto err; + strcpy(tty,t); + if (strncmp(tty,"/dev/tty",8) || !isdigit(tty[8])) + goto err; + conn->vc=atoi(tty+8); + } + else /* a default handler -- use console */ + sprintf(tty,"/dev/tty0"); + + } + + new->info=*conn; + + /*....................................... Connect to the control socket */ + + if (!(gpm_flag++)) + { + + if ( (gpm_fd=socket(AF_UNIX,SOCK_STREAM,0))<0 ) + { + goto err; + } + + bzero((char *)&addr,sizeof(addr)); + addr.sun_family=AF_UNIX; + if (!(sock_name = tempnam (0, "gpm"))) { + goto err; + } /*if*/ + strncpy (addr.sun_path, sock_name, sizeof (addr.sun_path)); + if (bind (gpm_fd, (struct sockaddr*)&addr, + sizeof (addr.sun_family) + strlen (addr.sun_path))==-1) { + goto err; + } /*if*/ + + bzero((char *)&addr,sizeof(addr)); + addr.sun_family=AF_UNIX; + strcpy(addr.sun_path, GPM_NODE_CTL); + i=sizeof(addr.sun_family)+strlen(GPM_NODE_CTL); + + if ( connect(gpm_fd,(struct sockaddr *)(&addr),i)<0 ) + { + struct stat stbuf; + + /* + * Well, try to open a chr device called /dev/gpmctl. This should + * be forward-compatible with a kernel server + */ + close(gpm_fd); /* the socket */ + if ((gpm_fd=open(GPM_NODE_DEV,O_RDWR))==-1) { + goto err; + } /*if*/ + if (fstat(gpm_fd,&stbuf)==-1 || (stbuf.st_mode&S_IFMT)!=S_IFCHR) + goto err; + } + } + /*....................................... Put your data */ + + if (putdata(gpm_fd,conn)!=-1) + { + /* itz Wed Dec 16 23:22:16 PST 1998 use sigaction, the old + code caused a signal loop under XEmacs */ + struct sigaction sa; + sigemptyset(&sa.sa_mask); + +#if (defined(SIGWINCH)) + /* And the winch hook .. */ + sa.sa_handler = gpm_winch_hook; + sa.sa_flags = 0; + sigaction(SIGWINCH, &sa, &gpm_saved_winch_hook); +#endif + +#if (defined(SIGTSTP)) + if (gpm_flag == 1) { + /* Install suspend hook */ + sa.sa_handler = SIG_IGN; + sigaction(SIGTSTP, &sa, &gpm_saved_suspend_hook); + + /* if signal was originally ignored, job control is not supported */ + if (gpm_saved_suspend_hook.sa_handler != SIG_IGN) { + sa.sa_flags = SA_NOMASK; + sa.sa_handler = gpm_suspend_hook; + sigaction(SIGTSTP, &sa, 0); + } /*if*/ + } /*if*/ +#endif + + } /*if*/ + return gpm_fd; + + /*....................................... Error: free all memory */ + err: + do + { + new=gpm_stack->next; + free(gpm_stack); + gpm_stack=new; + } + while(gpm_stack); + if (gpm_fd>=0) close(gpm_fd); + if (sock_name) { + unlink(sock_name); + free(sock_name); + sock_name = 0; + } /*if*/ + gpm_flag=0; + return -1; +} + +/*-------------------------------------------------------------------*/ +static int Gpm_Close(void) +{ + Gpm_Stst *next; + + gpm_tried=0; /* reset the error flag for next time */ + if (gpm_fd==-2) /* xterm */ + GPM_XTERM_OFF; + else /* linux */ + { + if (!gpm_flag) return 0; + next=gpm_stack->next; + free(gpm_stack); + gpm_stack=next; + if (next) + putdata(gpm_fd,&(next->info)); + + if (--gpm_flag) return -1; + } + + if (gpm_fd>=0) close(gpm_fd); + gpm_fd=-1; +#ifdef SIGTSTP + sigaction(SIGTSTP, &gpm_saved_suspend_hook, 0); +#endif +#ifdef SIGWINCH + sigaction(SIGWINCH, &gpm_saved_winch_hook, 0); +#endif + return 0; +} + +/*-------------------------------------------------------------------*/ +static int Gpm_GetEvent(Gpm_Event *event) +{ + int count; + + if (!gpm_flag) return 0; + + if ((count=read(gpm_fd,event,sizeof(Gpm_Event)))!=sizeof(Gpm_Event)) + { + if (count==0) + { + Gpm_Close(); + return 0; + } + return -1; + } + return 1; +} +#endif + +/**************************************************************************** + These forms handle vertical scrolling of components with a height of 1 + + Horizontal scrolling won't work, and scrolling large widgets will fail + miserably. It shouldn't be too hard to fix either of those if anyone + cares to. I only use scrolling for listboxes and text boxes though so + I didn't bother. +*****************************************************************************/ + +struct element { + int top, left; /* Actual, not virtual. These are translated */ + newtComponent co; /* into actual through vertOffset */ +}; + +struct fdInfo { + int fd; + int flags; +}; + +struct form { + int numCompsAlloced; + struct element * elements; + int numComps; + int currComp; + int fixedHeight; + int flags; + int vertOffset; + newtComponent vertBar, exitComp; + const char * help; + int numRows; + int * hotKeys; + int numHotKeys; + int background; + int beenSet; + int numFds; + struct fdInfo * fds; + int maxFd; + int timer; /* in milliseconds */ + struct timeval lastTimeout; + void * helpTag; + newtCallback helpCb; +}; + +static void gotoComponent(struct form * form, int newComp); +static struct eventResult formEvent(newtComponent co, struct event ev); +static struct eventResult sendEvent(newtComponent comp, struct event ev); +static void formPlace(newtComponent co, int left, int top); + +/* Global, ick */ +static newtCallback helpCallback; + +/* this isn't static as grid.c tests against it to find forms */ +struct componentOps formOps = { + newtDrawForm, + formEvent, + newtFormDestroy, + formPlace, + newtDefaultMappedHandler, +} ; + +static inline int componentFits(newtComponent co, int compNum) { + struct form * form = co->data; + struct element * el = form->elements + compNum; + + if ((co->top + form->vertOffset) > el->top) return 0; + if ((co->top + form->vertOffset + co->height) < + (el->top + el->co->height)) return 0; + + return 1; +} + +newtComponent newtForm(newtComponent vertBar, void * help, int flags) { + newtComponent co; + struct form * form; + + co = malloc(sizeof(*co)); + form = malloc(sizeof(*form)); + co->data = form; + co->width = 0; + co->height = 0; + co->top = -1; + co->left = -1; + co->isMapped = 0; + + co->takesFocus = 0; /* we may have 0 components */ + co->ops = &formOps; + + form->help = help; + form->flags = flags; + form->numCompsAlloced = 5; + form->numComps = 0; + form->currComp = -1; + form->vertOffset = 0; + form->fixedHeight = 0; + form->numRows = 0; + form->numFds = 0; + form->maxFd = 0; + form->fds = NULL; + form->beenSet = 0; + form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced); + + form->background = COLORSET_WINDOW; + form->hotKeys = malloc(sizeof(int)); + form->numHotKeys = 0; + form->timer = 0; + form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0; + if (!(form->flags & NEWT_FLAG_NOF12)) { + newtFormAddHotKey(co, NEWT_KEY_F12); + } + + if (vertBar) + form->vertBar = vertBar; + else + form->vertBar = NULL; + + form->helpTag = help; + form->helpCb = helpCallback; + + return co; +} + +newtComponent newtFormGetCurrent(newtComponent co) { + struct form * form = co->data; + + return form->elements[form->currComp].co; +} + +void newtFormSetCurrent(newtComponent co, newtComponent subco) { + struct form * form = co->data; + int i, new; + + for (i = 0; i < form->numComps; i++) { + if (form->elements[i].co == subco) break; + } + + if (form->elements[i].co != subco) return; + new = i; + + if (co->isMapped && !componentFits(co, new)) { + gotoComponent(form, -1); + form->vertOffset = form->elements[new].top - co->top - 1; + if (form->vertOffset > (form->numRows - co->height)) + form->vertOffset = form->numRows - co->height; + } + + gotoComponent(form, new); +} + +void newtFormSetTimer(newtComponent co, int millisecs) { + struct form * form = co->data; + + form->timer = millisecs; + form->lastTimeout.tv_usec = 0; + form->lastTimeout.tv_sec = 0; +} + +void newtFormSetHeight(newtComponent co, int height) { + struct form * form = co->data; + + form->fixedHeight = 1; + co->height = height; +} + +void newtFormSetWidth(newtComponent co, int width) { + co->width = width; +} + +void newtFormAddComponent(newtComponent co, newtComponent newco) { + struct form * form = co->data; + + co->takesFocus = 1; + + if (form->numCompsAlloced == form->numComps) { + form->numCompsAlloced += 5; + form->elements = realloc(form->elements, + sizeof(*(form->elements)) * form->numCompsAlloced); + } + + /* we grab real values for these a bit later */ + form->elements[form->numComps].left = -2; + form->elements[form->numComps].top = -2; + form->elements[form->numComps].co = newco; + + if (newco->takesFocus && form->currComp == -1) + form->currComp = form->numComps; + + form->numComps++; +} + +void newtFormAddComponents(newtComponent co, ...) { + va_list ap; + newtComponent subco; + + va_start(ap, co); + + while ((subco = va_arg(ap, newtComponent))) + newtFormAddComponent(co, subco); + + va_end(ap); +} + +static void formPlace(newtComponent co, int left, int top) { + struct form * form = co->data; + int vertDelta, horizDelta; + struct element * el; + int i; + + newtFormSetSize(co); + + vertDelta = top - co->top; + horizDelta = left - co->left; + co->top = top; + co->left = left; + + for (i = 0, el = form->elements; i < form->numComps; i++, el++) { + el->co->top += vertDelta; + el->top += vertDelta; + el->co->left += horizDelta; + el->left += horizDelta; + } +} + +void newtDrawForm(newtComponent co) { + struct form * form = co->data; + struct element * el; + int i; + + newtFormSetSize(co); + + SLsmg_set_color(form->background); + newtClearBox(co->left, co->top, co->width, co->height); + + for (i = 0, el = form->elements; i < form->numComps; i++, el++) { + /* the scrollbar *always* fits somewhere */ + if (el->co == form->vertBar) { + el->co->ops->mapped(el->co, 1); + el->co->ops->draw(el->co); + } else { + /* only draw it if it'll fit on the screen vertically */ + if (componentFits(co, i)) { + el->co->top = el->top - form->vertOffset; + el->co->ops->mapped(el->co, 1); + el->co->ops->draw(el->co); + } else { + el->co->ops->mapped(el->co, 0); + } + } + } + + if (form->vertBar) + newtScrollbarSet(form->vertBar, form->vertOffset, + form->numRows - co->height); +} + +static struct eventResult formEvent(newtComponent co, struct event ev) { + struct form * form = co->data; + newtComponent subco = form->elements[form->currComp].co; + int new, wrap = 0; + struct eventResult er; + int dir = 0, page = 0; + int i, num, found; + struct element * el; + + er.result = ER_IGNORED; + if (!form->numComps) return er; + + subco = form->elements[form->currComp].co; + + switch (ev.when) { + case EV_EARLY: + if (ev.event == EV_KEYPRESS) { + if (ev.u.key == NEWT_KEY_TAB) { + er.result = ER_SWALLOWED; + dir = 1; + wrap = 1; + } else if (ev.u.key == NEWT_KEY_UNTAB) { + er.result = ER_SWALLOWED; + dir = -1; + wrap = 1; + } + } + + if (form->numComps) { + i = form->currComp; + num = 0; + while (er.result == ER_IGNORED && num != form->numComps ) { + er = form->elements[i].co->ops->event(form->elements[i].co, ev); + + num++; + i++; + if (i == form->numComps) i = 0; + } + } + + break; + + case EV_NORMAL: + if (ev.event == EV_MOUSE) { + found = 0; + for (i = 0, el = form->elements; i < form->numComps; i++, el++) { + if ((el->co->top <= ev.u.mouse.y) && + (el->co->top + el->co->height > ev.u.mouse.y) && + (el->co->left <= ev.u.mouse.x) && + (el->co->left + el->co->width > ev.u.mouse.x)) { + found = 1; + if (el->co->takesFocus) { + gotoComponent(form, i); + subco = form->elements[form->currComp].co; + } + } + /* If we did not find a co to send this event to, we + should just swallow the event here. */ + } + if (!found) { + er.result = ER_SWALLOWED; + + return er; + } + } + er = subco->ops->event(subco, ev); + switch (er.result) { + case ER_NEXTCOMP: + er.result = ER_SWALLOWED; + dir = 1; + break; + + case ER_EXITFORM: + form->exitComp = subco; + break; + + default: + break; + } + break; + + case EV_LATE: + er = subco->ops->event(subco, ev); + + if (er.result == ER_IGNORED) { + switch (ev.u.key) { + case NEWT_KEY_UP: + case NEWT_KEY_LEFT: + case NEWT_KEY_BKSPC: + er.result = ER_SWALLOWED; + dir = -1; + break; + + case NEWT_KEY_DOWN: + case NEWT_KEY_RIGHT: + er.result = ER_SWALLOWED; + dir = 1; + break; + + case NEWT_KEY_PGUP: + er.result = ER_SWALLOWED; + dir = -1; + page = 1; + break; + + case NEWT_KEY_PGDN: + er.result = ER_SWALLOWED; + dir = 1; + page = 1; + break; + } + } + } + + if (dir) { + new = form->currComp; + + if (page) { + new += dir * co->height; + if (new < 0) + new = 0; + else if (new >= form->numComps) + new = (form->numComps - 1); + + while (!form->elements[new].co->takesFocus) + new = new - dir; + } else { + do { + new += dir; + + if (wrap) { + if (new < 0) + new = form->numComps - 1; + else if (new >= form->numComps) + new = 0; + } else if (new < 0 || new >= form->numComps) + return er; + } while (!form->elements[new].co->takesFocus); + } + + /* make sure this component is visible */ + if (!componentFits(co, new)) { + gotoComponent(form, -1); + + if (dir < 0) { + /* make the new component the first one */ + form->vertOffset = form->elements[new].top - co->top; + } else { + /* make the new component the last one */ + form->vertOffset = (form->elements[new].top + + form->elements[new].co->height) - + (co->top + co->height); + } + + if (form->vertOffset < 0) form->vertOffset = 0; + if (form->vertOffset > (form->numRows - co->height)) + form->vertOffset = form->numRows - co->height; + + newtDrawForm(co); + } + + gotoComponent(form, new); + er.result = ER_SWALLOWED; + } + + return er; +} + +/* this also destroys all of the components on the form */ +void newtFormDestroy(newtComponent co) { + newtComponent subco; + struct form * form = co->data; + int i; + + /* first, destroy all of the components */ + for (i = 0; i < form->numComps; i++) { + subco = form->elements[i].co; + if (subco->ops->destroy) { + subco->ops->destroy(subco); + } else { + if (subco->data) free(subco->data); + free(subco); + } + } + + if (form->hotKeys) free(form->hotKeys); + + free(form->elements); + free(form); + free(co); +} + +newtComponent newtRunForm(newtComponent co) { + struct newtExitStruct es; + + newtFormRun(co, &es); + if (es.reason == NEWT_EXIT_HOTKEY) { + if (es.u.key == NEWT_KEY_F12) { + es.reason = NEWT_EXIT_COMPONENT; + es.u.co = co; + } else { + return NULL; + } + } + + return es.u.co; +} + +void newtFormAddHotKey(newtComponent co, int key) { + struct form * form = co->data; + + form->numHotKeys++; + form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys); + form->hotKeys[form->numHotKeys - 1] = key; +} + +void newtFormSetSize(newtComponent co) { + struct form * form = co->data; + int delta, i; + struct element * el; + + if (form->beenSet) return; + + form->beenSet = 1; + + if (!form->numComps) return; + + co->width = 0; + if (!form->fixedHeight) co->height = 0; + + co->top = form->elements[0].co->top; + co->left = form->elements[0].co->left; + for (i = 0, el = form->elements; i < form->numComps; i++, el++) { + if (el->co->ops == &formOps) + newtFormSetSize(el->co); + + el->left = el->co->left; + el->top = el->co->top; + + if (co->left > el->co->left) { + delta = co->left - el->co->left; + co->left -= delta; + co->width += delta; + } + + if (co->top > el->co->top) { + delta = co->top - el->co->top; + co->top -= delta; + if (!form->fixedHeight) + co->height += delta; + } + + if ((co->left + co->width) < (el->co->left + el->co->width)) + co->width = (el->co->left + el->co->width) - co->left; + + if (!form->fixedHeight) { + if ((co->top + co->height) < (el->co->top + el->co->height)) + co->height = (el->co->top + el->co->height) - co->top; + } + + if ((el->co->top + el->co->height - co->top) > form->numRows) { + form->numRows = el->co->top + el->co->height - co->top; + } + } +} + +void newtFormRun(newtComponent co, struct newtExitStruct * es) { + struct form * form = co->data; + struct event ev; + struct eventResult er; + int key, i, max; + int done = 0; + fd_set readSet, writeSet, exceptSet; + struct timeval nextTimeout, now, timeout; +#ifdef USE_GPM + int x, y; + Gpm_Connect conn; + Gpm_Event event; + + /* Set up GPM interface */ + conn.eventMask = ~GPM_MOVE; + conn.defaultMask = GPM_MOVE; + conn.minMod = 0; + conn.maxMod = 0; + + Gpm_Open(&conn, 0); +#endif + + newtFormSetSize(co); + /* draw all of the components */ + newtDrawForm(co); + + if (form->currComp == -1) { + gotoComponent(form, 0); + } else + gotoComponent(form, form->currComp); + + while (!done) { + newtRefresh(); + + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + FD_ZERO(&exceptSet); + FD_SET(0, &readSet); +#ifdef USE_GPM + if (gpm_fd > 0) { + FD_SET(gpm_fd, &readSet); + } + max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd; +#else + max = form->maxFd; +#endif + + for (i = 0; i < form->numFds; i++) { + if (form->fds[i].flags & NEWT_FD_READ) + FD_SET(form->fds[i].fd, &readSet); + if (form->fds[i].flags & NEWT_FD_WRITE) + FD_SET(form->fds[i].fd, &writeSet); + if (form->fds[i].flags & NEWT_FD_EXCEPT) + FD_SET(form->fds[i].fd, &exceptSet); + } + + if (form->timer) { + /* Calculate when we next need to return with a timeout. Do + this inside the loop in case a callback resets the timer. */ + if (!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) + gettimeofday(&form->lastTimeout, NULL); + + nextTimeout.tv_sec = form->lastTimeout.tv_sec + + (form->timer / 1000); + nextTimeout.tv_usec = form->lastTimeout.tv_usec + + (form->timer % 1000) * 1000; + + gettimeofday(&now, 0); + + if (now.tv_sec > nextTimeout.tv_sec) { + timeout.tv_sec = timeout.tv_usec = 0; + } else if (now.tv_sec == nextTimeout.tv_sec) { + timeout.tv_sec = 0; + if (now.tv_usec > nextTimeout.tv_usec) + timeout.tv_usec = 0; + else + timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec; + } else if (now.tv_sec < nextTimeout.tv_sec) { + timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec; + if (now.tv_usec > nextTimeout.tv_usec) + timeout.tv_sec--, + timeout.tv_usec = nextTimeout.tv_usec + 1000000 - + now.tv_usec; + else + timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec; + } + } else { + timeout.tv_sec = timeout.tv_usec = 0; + } + + i = select(max + 1, &readSet, &writeSet, &exceptSet, + form->timer ? &timeout : NULL); + if (i < 0) continue; /* ?? What should we do here? */ + + if (i == 0) { + done = 1; + es->reason = NEWT_EXIT_TIMER; + gettimeofday(&form->lastTimeout, NULL); + } else +#ifdef USE_GPM + if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) { + Gpm_GetEvent(&event); + + if (event.type & GPM_DOWN) { + /* Transform coordinates to current window */ + newtGetWindowPos(&x, &y); + + ev.event = EV_MOUSE; + ev.u.mouse.type = MOUSE_BUTTON_DOWN; + ev.u.mouse.x = event.x - x - 1; + ev.u.mouse.y = event.y - y - 1; + + /* Send the form the event */ + er = sendEvent(co, ev); + + if (er.result == ER_EXITFORM) { + done = 1; + es->reason = NEWT_EXIT_COMPONENT; + es->u.co = form->exitComp; + } + + } + } else +#endif + { + if (FD_ISSET(0, &readSet)) { + + key = newtGetKey(); + + if (key == NEWT_KEY_RESIZE) { + /* newtResizeScreen(1); */ + continue; + } + + for (i = 0; i < form->numHotKeys; i++) { + if (form->hotKeys[i] == key) { + es->reason = NEWT_EXIT_HOTKEY; + es->u.key = key; + done = 1; + break; + } + } + + if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) + form->helpCb(co, form->helpTag); + + if (!done) { + ev.event = EV_KEYPRESS; + ev.u.key = key; + + er = sendEvent(co, ev); + + if (er.result == ER_EXITFORM) { + done = 1; + es->reason = NEWT_EXIT_COMPONENT; + es->u.co = form->exitComp; + } + } + } else { + for (i = 0; i < form->numFds; i++) { + if (((form->fds[i].flags & NEWT_FD_READ) + && FD_ISSET(form->fds[i].fd, &readSet)) + || ((form->fds[i].flags & NEWT_FD_WRITE) + && FD_ISSET(form->fds[i].fd, &writeSet)) + || ((form->fds[i].flags & NEWT_FD_EXCEPT) + && FD_ISSET(form->fds[i].fd, &exceptSet))) break; + } + if(i < form->numFds) + es->u.watch = form->fds[i].fd; + else + es->u.watch = -1; + + es->reason = NEWT_EXIT_FDREADY; + done = 1; + } + } + } + newtRefresh(); +#ifdef USE_GPM + Gpm_Close(); +#endif +} + +static struct eventResult sendEvent(newtComponent co, struct event ev) { + struct eventResult er; + + ev.when = EV_EARLY; + er = co->ops->event(co, ev); + + if (er.result == ER_IGNORED) { + ev.when = EV_NORMAL; + er = co->ops->event(co, ev); + } + + if (er.result == ER_IGNORED) { + ev.when = EV_LATE; + er = co->ops->event(co, ev); + } + + return er; +} + +static void gotoComponent(struct form * form, int newComp) { + struct event ev; + + if (form->currComp != -1) { + ev.event = EV_UNFOCUS; + sendEvent(form->elements[form->currComp].co, ev); + } + + form->currComp = newComp; + + if (form->currComp != -1) { + ev.event = EV_FOCUS; + ev.when = EV_NORMAL; + sendEvent(form->elements[form->currComp].co, ev); + } +} + +void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) { + co->callback = f; + co->callbackData = data; +} + +void newtComponentTakesFocus(newtComponent co, int val) { + co->takesFocus = val; +} + +void newtFormSetBackground(newtComponent co, int color) { + struct form * form = co->data; + + form->background = color; +} + +void newtFormWatchFd(newtComponent co, int fd, int fdFlags) { + struct form * form = co->data; + int i; + + for (i = 0; i < form->numFds; i++) + if (form->fds[i].fd == fd) + break; + + if(i >= form->numFds) + form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds)); + + form->fds[i].fd = fd; + form->fds[i].flags = fdFlags; + if (form->maxFd < fd) form->maxFd = fd; +} + +void newtSetHelpCallback(newtCallback cb) { + helpCallback = cb; +} diff --git a/mininewt/grid.c b/mininewt/grid.c new file mode 100644 index 000000000..038b7c172 --- /dev/null +++ b/mininewt/grid.c @@ -0,0 +1,393 @@ +#include "config.h" + +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct gridField { + enum newtGridElement type; + union { + newtGrid grid; + newtComponent co; + } u; + int padLeft, padTop, padRight, padBottom; + int anchor; + int flags; +}; + +struct grid_s { + int rows, cols; + int width, height; /* totals, -1 means unknown */ + struct gridField ** fields; +}; + +/* this is a bit of a hack */ +extern struct componentOps formOps[]; + +newtGrid newtCreateGrid(int cols, int rows) { + newtGrid grid; + + grid = malloc(sizeof(*grid)); + grid->rows = rows; + grid->cols = cols; + + grid->fields = malloc(sizeof(*grid->fields) * cols); + while (cols--) { + grid->fields[cols] = malloc(sizeof(**(grid->fields)) * rows); + memset(grid->fields[cols], 0, sizeof(**(grid->fields)) * rows); + } + + grid->width = grid->height = -1; + + return grid; +} + +void newtGridSetField(newtGrid grid, int col, int row, + enum newtGridElement type, void * val, int padLeft, + int padTop, int padRight, int padBottom, int anchor, + int flags) { + struct gridField * field = &grid->fields[col][row]; + + if (field->type == NEWT_GRID_SUBGRID) + newtGridFree(field->u.grid, 1); + + field->type = type; + field->u.co = (void *) val; + + field->padLeft = padLeft; + field->padRight = padRight; + field->padTop = padTop; + field->padBottom = padBottom; + field->anchor = anchor; + field->flags = flags; + + grid->width = grid->height = -1; +} + +static void distSpace(int extra, int items, int * list) { + int all, some, i; + + all = extra / items; + some = extra % items; + for (i = 0; i < items; i++) { + list[i] += all; + if (some) { + list[i]++; + some--; + } + } +} + +static void shuffleGrid(newtGrid grid, int left, int top, int set) { + struct gridField * field; + int row, col; + int i, j; + int minWidth, minHeight; + int * widths, * heights; + int thisLeft, thisTop; + int x, y, remx, remy; + + widths = alloca(sizeof(*widths) * grid->cols); + memset(widths, 0, sizeof(*widths) * grid->cols); + heights = alloca(sizeof(*heights) * grid->rows); + memset(heights, 0, sizeof(*heights) * grid->rows); + + minWidth = 0; + for (row = 0; row < grid->rows; row++) { + i = 0; + for (col = 0; col < grid->cols; col++) { + field = &grid->fields[col][row]; + if (field->type == NEWT_GRID_SUBGRID) { + /* we'll have to redo this later */ + if (field->u.grid->width == -1) + shuffleGrid(field->u.grid, left, top, 0); + j = field->u.grid->width; + } else if (field->type == NEWT_GRID_COMPONENT) { + if (field->u.co->ops == formOps) + newtFormSetSize(field->u.co); + j = field->u.co->width; + } else + j = 0; + + j += field->padLeft + field->padRight; + + if (j > widths[col]) widths[col] = j; + i += widths[col]; + } + + if (i > minWidth) minWidth = i; + } + + minHeight = 0; + for (col = 0; col < grid->cols; col++) { + i = 0; + for (row = 0; row < grid->rows; row++) { + field = &grid->fields[col][row]; + if (field->type == NEWT_GRID_SUBGRID) { + /* we'll have to redo this later */ + if (field->u.grid->height == -1) + shuffleGrid(field->u.grid, 0, 0, 0); + j = field->u.grid->height; + } else if (field->type == NEWT_GRID_COMPONENT){ + j = field->u.co->height; + } else + j = 0; + + j += field->padTop + field->padBottom; + + if (j > heights[row]) heights[row] = j; + i += heights[row]; + } + + if (i > minHeight) minHeight = i; + } + + /* this catches the -1 case */ + if (grid->width < minWidth) grid->width = minWidth; /* ack! */ + if (grid->height < minHeight) grid->height = minHeight; /* ditto! */ + + if (!set) return; + + distSpace(grid->width - minWidth, grid->cols, widths); + distSpace(grid->height - minHeight, grid->rows, heights); + + thisTop = top; + for (row = 0; row < grid->rows; row++) { + i = 0; + thisLeft = left; + for (col = 0; col < grid->cols; col++) { + field = &grid->fields[col][row]; + + if (field->type == NEWT_GRID_EMPTY) continue; + + x = thisLeft + field->padLeft; + remx = widths[col] - field->padLeft - field->padRight; + y = thisTop + field->padTop; + remy = heights[row] - field->padTop - field->padBottom; + + if (field->type == NEWT_GRID_SUBGRID) { + remx -= field->u.grid->width; + remy -= field->u.grid->height; + } else if (field->type == NEWT_GRID_COMPONENT) { + remx -= field->u.co->width; + remy -= field->u.co->height; + } + + if (!(field->flags & NEWT_GRID_FLAG_GROWX)) { + if (field->anchor & NEWT_ANCHOR_RIGHT) + x += remx; + else if (!(field->anchor & NEWT_ANCHOR_LEFT)) + x += (remx / 2); + } + + if (!(field->flags & NEWT_GRID_FLAG_GROWY)) { + if (field->anchor & NEWT_ANCHOR_BOTTOM) + y += remx; + else if (!(field->anchor & NEWT_ANCHOR_TOP)) + y += (remy / 2); + } + + if (field->type == NEWT_GRID_SUBGRID) { + if (field->flags & NEWT_GRID_FLAG_GROWX) + field->u.grid->width = widths[col] - field->padLeft + - field->padRight; + if (field->flags & NEWT_GRID_FLAG_GROWY) + field->u.grid->height = heights[col] - field->padTop + - field->padBottom; + + shuffleGrid(field->u.grid, x, y, 1); + } else if (field->type == NEWT_GRID_COMPONENT) { + field->u.co->ops->place(field->u.co, x, y); + } + + thisLeft += widths[col]; + } + + thisTop += heights[row]; + } +} + +void newtGridPlace(newtGrid grid, int left, int top) { + shuffleGrid(grid, left, top, 1); +} + +void newtGridFree(newtGrid grid, int recurse) { + int row, col; + + for (col = 0; col < grid->cols; col++) { + if (recurse) { + for (row = 0; row < grid->rows; row++) { + if (grid->fields[col][row].type == NEWT_GRID_SUBGRID) + newtGridFree(grid->fields[col][row].u.grid, 1); + } + } + + free(grid->fields[col]); + } + + free(grid->fields); + free(grid); +} + +void newtGridGetSize(newtGrid grid, int * width, int * height) { + if (grid->width == -1 || grid->height == -1) { + grid->width = grid->height = -1; + shuffleGrid(grid, 0, 0, 1); + } + + *width = grid->width; + *height = grid->height; +} + +void newtGridWrappedWindow(newtGrid grid, char * title) { + int width, height, offset = 0; + + newtGridGetSize(grid, &width, &height); + if (width < strlen(title) + 2) { + offset = ((strlen(title) + 2) - width) / 2; + width = strlen(title) + 2; + } + newtCenteredWindow(width + 2, height + 2, title); + newtGridPlace(grid, 1 + offset, 1); +} + +void newtGridWrappedWindowAt(newtGrid grid, char * title, int left, int top) { + int width, height; + + newtGridGetSize(grid, &width, &height); + newtOpenWindow(left, top, width + 2, height + 2, title); + newtGridPlace(grid, 1, 1); +} + +void newtGridAddComponentsToForm(newtGrid grid, newtComponent form, + int recurse) { + int row, col; + + for (col = 0; col < grid->cols; col++) { + for (row = 0; row < grid->rows; row++) { + if (grid->fields[col][row].type == NEWT_GRID_SUBGRID && recurse) + newtGridAddComponentsToForm(grid->fields[col][row].u.grid, + form, 1); + else if (grid->fields[col][row].type == NEWT_GRID_COMPONENT) + newtFormAddComponent(form, grid->fields[col][row].u.co); + } + } +} + +/* this handles up to 50 items */ +static newtGrid stackem(int isVert, enum newtGridElement type1, void * what1, + va_list args, int close) { + struct item { + enum newtGridElement type; + void * what; + } items[50]; + int i, num; + newtGrid grid; + + items[0].type = type1, items[0].what = what1, num = 1; + while (1) { + items[num].type = va_arg(args, enum newtGridElement); + if (items[num].type == NEWT_GRID_EMPTY) break; + + items[num].what = va_arg(args, void *); + num++; + } + + grid = newtCreateGrid(isVert ? 1 : num, isVert ? num : 1); + + for (i = 0; i < num; i++) { + newtGridSetField(grid, isVert ? 0 : i, isVert ? i : 0, + items[i].type, items[i].what, + close ? 0 : (i ? (isVert ? 0 : 1) : 0), + close ? 0 : (i ? (isVert ? 1 : 0) : 0), 0, 0, 0, 0); + } + + return grid; +} + +newtGrid newtGridHCloseStacked(enum newtGridElement type1, void * what1, ...) { + va_list args; + newtGrid grid; + + va_start(args, what1); + + grid = stackem(0, type1, what1, args, 1); + + va_start(args, what1); + + return grid; +} + +newtGrid newtGridVCloseStacked(enum newtGridElement type1, void * what1, ...) { + va_list args; + newtGrid grid; + + va_start(args, what1); + + grid = stackem(1, type1, what1, args, 1); + + va_start(args, what1); + + return grid; +} + +newtGrid newtGridVStacked(enum newtGridElement type1, void * what1, ...) { + va_list args; + newtGrid grid; + + va_start(args, what1); + + grid = stackem(1, type1, what1, args, 0); + + va_start(args, what1); + + return grid; +} + +newtGrid newtGridHStacked(enum newtGridElement type1, void * what1, ...) { + va_list args; + newtGrid grid; + + va_start(args, what1); + + grid = stackem(0, type1, what1, args, 0); + + va_start(args, what1); + + return grid; +} + +newtGrid newtGridBasicWindow(newtComponent text, newtGrid middle, + newtGrid buttons) { + newtGrid grid; + + grid = newtCreateGrid(1, 3); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, middle, + 0, 1, 0, 0, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, + 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + return grid; +} + +newtGrid newtGridSimpleWindow(newtComponent text, newtComponent middle, + newtGrid buttons) { + newtGrid grid; + + grid = newtCreateGrid(1, 3); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, middle, + 0, 1, 0, 0, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, + 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + return grid; +} diff --git a/mininewt/label.c b/mininewt/label.c new file mode 100644 index 000000000..f1a9cebbf --- /dev/null +++ b/mininewt/label.c @@ -0,0 +1,81 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct label { + char * text; + int length; +}; + +static void labelDraw(newtComponent co); +static void labelDestroy(newtComponent co); + +static struct componentOps labelOps = { + labelDraw, + newtDefaultEventHandler, + labelDestroy, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +newtComponent newtLabel(int left, int top, const char * text) { + newtComponent co; + struct label * la; + + co = malloc(sizeof(*co)); + la = malloc(sizeof(struct label)); + co->data = la; + + co->ops = &labelOps; + + co->height = 1; + co->width = strlen(text); + co->top = top; + co->left = left; + co->takesFocus = 0; + + la->length = strlen(text); + la->text = strdup(text); + + return co; +} + +void newtLabelSetText(newtComponent co, const char * text) { + int newLength; + struct label * la = co->data; + + newLength = strlen(text); + if (newLength <= la->length) { + memset(la->text, ' ', la->length); + memcpy(la->text, text, newLength); + } else { + free(la->text); + la->text = strdup(text); + la->length = newLength; + co->width = newLength; + } + + labelDraw(co); +} + +static void labelDraw(newtComponent co) { + struct label * la = co->data; + + if (co->isMapped == -1) return; + + SLsmg_set_color(COLORSET_LABEL); + + newtGotorc(co->top, co->left); + SLsmg_write_string(la->text); +} + +static void labelDestroy(newtComponent co) { + struct label * la = co->data; + + free(la->text); + free(la); + free(co); +} diff --git a/mininewt/listbox.c b/mininewt/listbox.c new file mode 100644 index 000000000..259c5ccfb --- /dev/null +++ b/mininewt/listbox.c @@ -0,0 +1,764 @@ +/* This goofed-up box whacked into shape by Elliot Lee <sopwith@cuc.edu> + (from the original listbox by Erik Troan <ewt@redhat.com>) + and contributed to newt for use under the LGPL license. + Copyright (C) 1996, 1997 Elliot Lee */ + +#include <slang.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "newt.h" +#include "newt_pr.h" + + +/* Linked list of items in the listbox */ +struct items { + char * text; + const void *data; + unsigned char isSelected; + struct items *next; +}; + +/* Holds all the relevant information for this listbox */ +struct listbox { + newtComponent sb; /* Scrollbar on right side of listbox */ + int curWidth; /* size of text w/o scrollbar or border*/ + int curHeight; /* size of text w/o border */ + int sbAdjust; + int bdxAdjust, bdyAdjust; + int numItems, numSelected; + int userHasSetWidth; + int currItem, startShowItem; /* startShowItem is the first item displayed + on the screen */ + int isActive; /* If we handle key events all the time, it seems + to do things even when they are supposed to be for + another button/whatever */ + struct items *boxItems; + int grow; + int flags; /* flags for this listbox, right now just + NEWT_FLAG_RETURNEXIT */ +}; + +static void listboxDraw(newtComponent co); +static void listboxDestroy(newtComponent co); +static struct eventResult listboxEvent(newtComponent co, struct event ev); +static void newtListboxRealSetCurrent(newtComponent co); +static void listboxPlace(newtComponent co, int newLeft, int newTop); +static inline void updateWidth(newtComponent co, struct listbox * li, + int maxField); +static void listboxMapped(newtComponent co, int isMapped); + +static struct componentOps listboxOps = { + listboxDraw, + listboxEvent, + listboxDestroy, + listboxPlace, + listboxMapped, +}; + +static void listboxMapped(newtComponent co, int isMapped) { + struct listbox * li = co->data; + + co->isMapped = isMapped; + if (li->sb) + li->sb->ops->mapped(li->sb, isMapped); +} + +static void listboxPlace(newtComponent co, int newLeft, int newTop) { + struct listbox * li = co->data; + + co->top = newTop; + co->left = newLeft; + + if (li->sb) + li->sb->ops->place(li->sb, co->left + co->width - li->bdxAdjust - 1, + co->top); +} + +newtComponent newtListbox(int left, int top, int height, int flags) { + newtComponent co, sb; + struct listbox * li; + + if (!(co = malloc(sizeof(*co)))) + return NULL; + + if (!(li = malloc(sizeof(struct listbox)))) { + free(co); + return NULL; + } + + li->boxItems = NULL; + li->numItems = 0; + li->currItem = 0; + li->numSelected = 0; + li->isActive = 0; + li->userHasSetWidth = 0; + li->startShowItem = 0; + li->sbAdjust = 0; + li->bdxAdjust = 0; + li->bdyAdjust = 0; + li->flags = flags & (NEWT_FLAG_RETURNEXIT | NEWT_FLAG_BORDER | + NEWT_FLAG_MULTIPLE | NEWT_FLAG_SHOWCURSOR); + + if (li->flags & NEWT_FLAG_BORDER) { + li->bdxAdjust = 2; + li->bdyAdjust = 1; + } + + co->height = height; + li->curHeight = co->height - (2 * li->bdyAdjust); + + if (height) { + li->grow = 0; + if (flags & NEWT_FLAG_SCROLL) { + sb = newtVerticalScrollbar(left, top + li->bdyAdjust, + li->curHeight, + COLORSET_LISTBOX, COLORSET_ACTLISTBOX); + li->sbAdjust = 3; + } else { + sb = NULL; + } + } else { + li->grow = 1; + sb = NULL; + } + + li->sb = sb; + co->data = li; + co->isMapped = 0; + co->left = left; + co->top = top; + co->ops = &listboxOps; + co->takesFocus = 1; + co->callback = NULL; + + updateWidth(co, li, 5); + + return co; +} + +static inline void updateWidth(newtComponent co, struct listbox * li, + int maxField) { + li->curWidth = maxField; + co->width = li->curWidth + li->sbAdjust + 2 * li->bdxAdjust; + + if (li->sb) + li->sb->left = co->left + co->width - li->bdxAdjust - 1; +} + +void newtListboxSetCurrentByKey(newtComponent co, void * key) { + struct listbox * li = co->data; + struct items * item; + int i; + + item = li->boxItems, i = 0; + while (item && item->data != key) + item = item->next, i++; + + if (item) + newtListboxSetCurrent(co, i); +} + +void newtListboxSetCurrent(newtComponent co, int num) +{ + struct listbox * li = co->data; + + if (num >= li->numItems) + li->currItem = li->numItems - 1; + else if (num < 0) + li->currItem = 0; + else + li->currItem = num; + + if (li->currItem < li->startShowItem) + li->startShowItem = li->currItem; + else if (li->currItem - li->startShowItem > li->curHeight - 1) + li->startShowItem = li->currItem - li->curHeight + 1; + if (li->startShowItem + li->curHeight > li->numItems) + li->startShowItem = li->numItems - li->curHeight; + if(li->startShowItem < 0) + li->startShowItem = 0; + + newtListboxRealSetCurrent(co); +} + +static void newtListboxRealSetCurrent(newtComponent co) +{ + struct listbox * li = co->data; + + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + if(co->callback) co->callback(co, co->callbackData); +} + +void newtListboxSetWidth(newtComponent co, int width) { + struct listbox * li = co->data; + + co->width = width; + li->curWidth = co->width - li->sbAdjust - 2 * li->bdxAdjust; + li->userHasSetWidth = 1; + if (li->sb) li->sb->left = co->width + co->left - 1; + listboxDraw(co); +} + +void * newtListboxGetCurrent(newtComponent co) { + struct listbox * li = co->data; + int i; + struct items *item; + + for(i = 0, item = li->boxItems; item != NULL && i < li->currItem; + i++, item = item->next); + + if (item) + return (void *)item->data; + else + return NULL; +} + +void newtListboxSelectItem(newtComponent co, const void * key, + enum newtFlagsSense sense) +{ + struct listbox * li = co->data; + int i; + struct items * item; + + item = li->boxItems, i = 0; + while (item && item->data != key) + item = item->next, i++; + + if (!item) return; + + if (item->isSelected) + li->numSelected--; + + switch(sense) { + case NEWT_FLAGS_RESET: + item->isSelected = 0; break; + case NEWT_FLAGS_SET: + item->isSelected = 1; break; + case NEWT_FLAGS_TOGGLE: + item->isSelected = !item->isSelected; + } + + if (item->isSelected) + li->numSelected++; + + listboxDraw(co); +} + +void newtListboxClearSelection(newtComponent co) +{ + struct items *item; + struct listbox * li = co->data; + + for(item = li->boxItems; item != NULL; + item = item->next) + item->isSelected = 0; + li->numSelected = 0; + listboxDraw(co); +} + +/* Free the returned array after use, but NOT the values in the array */ +void ** newtListboxGetSelection(newtComponent co, int *numitems) +{ + struct listbox * li; + int i; + void **retval; + struct items *item; + + if(!co || !numitems) return NULL; + + li = co->data; + if(!li || !li->numSelected) return NULL; + + retval = malloc(li->numSelected * sizeof(void *)); + for(i = 0, item = li->boxItems; item != NULL; + item = item->next) + if(item->isSelected) + retval[i++] = (void *)item->data; + *numitems = li->numSelected; + return retval; +} + +void newtListboxSetEntry(newtComponent co, int num, const char * text) { + struct listbox * li = co->data; + int i; + struct items *item; + + for(i = 0, item = li->boxItems; item != NULL && i < num; + i++, item = item->next); + + if(!item) + return; + else { + free(item->text); + item->text = strdup(text); + } + if (li->userHasSetWidth == 0 && strlen(text) > li->curWidth) { + updateWidth(co, li, strlen(text)); + } + + if (num >= li->startShowItem && num <= li->startShowItem + co->height) + listboxDraw(co); +} + +void newtListboxSetData(newtComponent co, int num, void * data) { + struct listbox * li = co->data; + int i; + struct items *item; + + for(i = 0, item = li->boxItems; item != NULL && i < num; + i++, item = item->next); + + item->data = data; +} + +int newtListboxAppendEntry(newtComponent co, const char * text, + const void * data) { + struct listbox * li = co->data; + struct items *item; + + if(li->boxItems) { + for (item = li->boxItems; item->next != NULL; item = item->next); + + item = item->next = malloc(sizeof(struct items)); + } else { + item = li->boxItems = malloc(sizeof(struct items)); + } + + if (!li->userHasSetWidth && text && (strlen(text) > li->curWidth)) + updateWidth(co, li, strlen(text)); + + item->text = strdup(text); item->data = data; item->next = NULL; + item->isSelected = 0; + + if (li->grow) + co->height++, li->curHeight++; + li->numItems++; + + return 0; +} + +int newtListboxInsertEntry(newtComponent co, const char * text, + const void * data, void * key) { + struct listbox * li = co->data; + struct items *item, *t; + + if (li->boxItems) { + if (key) { + item = li->boxItems; + while (item && item->data != key) item = item->next; + + if (!item) return 1; + + t = item->next; + item = item->next = malloc(sizeof(struct items)); + item->next = t; + } else { + t = li->boxItems; + item = li->boxItems = malloc(sizeof(struct items)); + item->next = t; + } + } else if (key) { + return 1; + } else { + item = li->boxItems = malloc(sizeof(struct items)); + item->next = NULL; + } + + if (!li->userHasSetWidth && text && (strlen(text) > li->curWidth)) + updateWidth(co, li, strlen(text)); + + item->text = strdup(text?text:"(null)"); item->data = data; + item->isSelected = 0; + + if (li->sb) + li->sb->left = co->left + co->width - li->bdxAdjust - 1; + li->numItems++; + + listboxDraw(co); + + return 0; +} + +int newtListboxDeleteEntry(newtComponent co, void * key) { + struct listbox * li = co->data; + int widest = 0, t; + struct items *item, *item2 = NULL; + int num; + + if (li->boxItems == NULL || li->numItems <= 0) + return 0; + + num = 0; + + item2 = NULL, item = li->boxItems; + while (item && item->data != key) { + item2 = item; + item = item->next; + num++; + } + + if (!item) + return -1; + + if (item2) + item2->next = item->next; + else + li->boxItems = item->next; + + free(item->text); + free(item); + li->numItems--; + + if (!li->userHasSetWidth) { + widest = 0; + for (item = li->boxItems; item != NULL; item = item->next) + if ((t = strlen(item->text)) > widest) widest = t; + } + + if (li->currItem >= num) + li->currItem--; + + if (!li->userHasSetWidth) { + updateWidth(co, li, widest); + } + + listboxDraw(co); + + return 0; +} + +void newtListboxClear(newtComponent co) +{ + struct listbox * li; + struct items *anitem, *nextitem; + if(co == NULL || (li = co->data) == NULL) + return; + for(anitem = li->boxItems; anitem != NULL; anitem = nextitem) { + nextitem = anitem->next; + free(anitem->text); + free(anitem); + } + li->numItems = li->numSelected = li->currItem = li->startShowItem = 0; + li->boxItems = NULL; + if (!li->userHasSetWidth) + updateWidth(co, li, 5); +} + +int newtListboxItemCount(newtComponent co) +{ + struct listbox *li = co->data; + return li->numItems; +} + +/* If you don't want to get back the text, pass in NULL for the ptr-ptr. Same + goes for the data. */ +void newtListboxGetEntry(newtComponent co, int num, char **text, void **data) { + struct listbox * li = co->data; + int i; + struct items *item; + + if (!li->boxItems || num >= li->numItems) { + if(text) + *text = NULL; + if(data) + *data = NULL; + return; + } + + i = 0; + item = li->boxItems; + while (item && i < num) { + i++, item = item->next; + } + + if (item) { + if (text) + *text = item->text; + if (data) + *data = (void *)item->data; + } +} + +static void listboxDraw(newtComponent co) +{ + struct listbox * li = co->data; + struct items *item; + int i, j; + + if (!co->isMapped) return ; + + newtTrashScreen(); + + if(li->flags & NEWT_FLAG_BORDER) { + if(li->isActive) + SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX); + else + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + newtDrawBox(co->left, co->top, co->width, co->height, 0); + } + + if(li->sb) + li->sb->ops->draw(li->sb); + + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + for(i = 0, item = li->boxItems; item != NULL && i < li->startShowItem; + i++, item = item->next); + + j = i; + + for (i = 0; item != NULL && i < li->curHeight; i++, item = item->next) { + if (!item->text) continue; + + newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust); + if(j + i == li->currItem) { + if(item->isSelected) + SLsmg_set_color(NEWT_COLORSET_ACTSELLISTBOX); + else + SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX); + } else if(item->isSelected) + SLsmg_set_color(NEWT_COLORSET_SELLISTBOX); + else + SLsmg_set_color(NEWT_COLORSET_LISTBOX); + + SLsmg_write_nstring(item->text, li->curWidth); + + } + newtGotorc(co->top + (li->currItem - li->startShowItem) + 1, co->left + 1); +} + +static struct eventResult listboxEvent(newtComponent co, struct event ev) { + struct eventResult er; + struct listbox * li = co->data; + struct items *item; + int i; + + er.result = ER_IGNORED; + + if(ev.when == EV_EARLY || ev.when == EV_LATE) { + return er; + } + + switch(ev.event) { + case EV_KEYPRESS: + if (!li->isActive) break; + + switch(ev.u.key) { + case ' ': + if(!(li->flags & NEWT_FLAG_MULTIPLE)) break; + newtListboxSelectItem(co, newtListboxGetCurrent(co), + NEWT_FLAGS_TOGGLE); + er.result = ER_SWALLOWED; + /* We don't break here, because it is cool to be able to + hold space to select a bunch of items in a list at once */ + + case NEWT_KEY_DOWN: + if(li->numItems <= 0) break; + if(li->currItem < li->numItems - 1) { + li->currItem++; + if(li->currItem > (li->startShowItem + li->curHeight - 1)) { + li->startShowItem = li->currItem - li->curHeight + 1; + if(li->startShowItem + li->curHeight > li->numItems) + li->startShowItem = li->numItems - li->curHeight; + } + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + } + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_ENTER: + if(li->numItems <= 0) break; + if(li->flags & NEWT_FLAG_RETURNEXIT) + er.result = ER_EXITFORM; + break; + + case NEWT_KEY_UP: + if(li->numItems <= 0) break; + if(li->currItem > 0) { + li->currItem--; + if(li->currItem < li->startShowItem) + li->startShowItem = li->currItem; + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + } + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_PGUP: + if(li->numItems <= 0) break; + li->startShowItem -= li->curHeight - 1; + if(li->startShowItem < 0) + li->startShowItem = 0; + li->currItem -= li->curHeight - 1; + if(li->currItem < 0) + li->currItem = 0; + newtListboxRealSetCurrent(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_PGDN: + if(li->numItems <= 0) break; + li->startShowItem += li->curHeight; + if(li->startShowItem > (li->numItems - li->curHeight)) { + li->startShowItem = li->numItems - li->curHeight; + } + li->currItem += li->curHeight; + if(li->currItem >= li->numItems) { + li->currItem = li->numItems - 1; + } + newtListboxRealSetCurrent(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_HOME: + if(li->numItems <= 0) break; + newtListboxSetCurrent(co, 0); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_END: + if(li->numItems <= 0) break; + li->startShowItem = li->numItems - li->curHeight; + if(li->startShowItem < 0) + li->startShowItem = 0; + li->currItem = li->numItems - 1; + newtListboxRealSetCurrent(co); + er.result = ER_SWALLOWED; + break; + default: + if (li->numItems <= 0) break; + if (ev.u.key < NEWT_KEY_EXTRA_BASE && isalpha(ev.u.key)) { + for(i = 0, item = li->boxItems; item != NULL && + i < li->currItem; i++, item = item->next); + + if (item && item->text && (toupper(*item->text) == toupper(ev.u.key))) { + item = item->next; + i++; + } else { + item = li->boxItems; + i = 0; + } + while (item && item->text && + toupper(*item->text) != toupper(ev.u.key)) { + item = item->next; + i++; + } + if (item) { + li->currItem = i; + if(li->currItem < li->startShowItem || + li->currItem > li->startShowItem) + li->startShowItem = + li->currItem > li->numItems - li->curHeight ? + li->startShowItem = li->numItems - li->curHeight : + li->currItem; + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + newtListboxRealSetCurrent(co); + er.result = ER_SWALLOWED; + } + } + } + break; + + case EV_FOCUS: + li->isActive = 1; + listboxDraw(co); + if(li->flags & NEWT_FLAG_SHOWCURSOR) + newtCursorOn(); + er.result = ER_SWALLOWED; + break; + + case EV_UNFOCUS: + li->isActive = 0; + listboxDraw(co); + if(li->flags & NEWT_FLAG_SHOWCURSOR) + newtCursorOff(); + er.result = ER_SWALLOWED; + break; + + case EV_MOUSE: + /* if this mouse click was within the listbox, make the current + item the item clicked on. */ + /* Up scroll arrow */ + if (li->sb && + ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 && + ev.u.mouse.y == co->top + li->bdyAdjust) { + if(li->numItems <= 0) break; + if(li->currItem > 0) { + li->currItem--; + if(li->currItem < li->startShowItem) + li->startShowItem = li->currItem; + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + } + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + } + /* Down scroll arrow */ + if (li->sb && + ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 && + ev.u.mouse.y == co->top + co->height - li->bdyAdjust - 1) { + if(li->numItems <= 0) break; + if(li->currItem < li->numItems - 1) { + li->currItem++; + if(li->currItem > (li->startShowItem + li->curHeight - 1)) { + li->startShowItem = li->currItem - li->curHeight + 1; + if(li->startShowItem + li->curHeight > li->numItems) + li->startShowItem = li->numItems - li->curHeight; + } + if(li->sb) + newtScrollbarSet(li->sb, li->currItem + 1, li->numItems); + listboxDraw(co); + } + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + } + if ((ev.u.mouse.y >= co->top + li->bdyAdjust) && + (ev.u.mouse.y <= co->top + co->height - (li->bdyAdjust * 2)) && + (ev.u.mouse.x >= co->left + li->bdxAdjust) && + (ev.u.mouse.x <= co->left + co->width + (li->bdxAdjust * 2))) { + li->currItem = li->startShowItem + + (ev.u.mouse.y - li->bdyAdjust - co->top); + newtListboxRealSetCurrent(co); + listboxDraw(co); + if(co->callback) co->callback(co, co->callbackData); + er.result = ER_SWALLOWED; + break; + } + } + + return er; +} + +static void listboxDestroy(newtComponent co) { + struct listbox * li = co->data; + struct items * item, * nextitem; + + nextitem = item = li->boxItems; + + while (item != NULL) { + nextitem = item->next; + free(item->text); + free(item); + item = nextitem; + } + + if (li->sb) li->sb->ops->destroy(li->sb); + + free(li); + free(co); +} diff --git a/mininewt/newt.c b/mininewt/newt.c new file mode 100644 index 000000000..56b4e1aba --- /dev/null +++ b/mininewt/newt.c @@ -0,0 +1,710 @@ +#include "config.h" + +#include <slang.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/signal.h> +#include <sys/time.h> +#include <sys/types.h> +#include <termios.h> +#include <unistd.h> + +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif + +#include "newt.h" +#include "newt_pr.h" + +struct Window { + int height, width, top, left; + SLsmg_Char_Type * buffer; + char * title; +}; + +struct keymap { + char * str; + int code; + char * tc; +}; + +static struct Window windowStack[20]; +static struct Window * currentWindow = NULL; + +static char * helplineStack[20]; +static char ** currentHelpline = NULL; + +static int cursorRow, cursorCol; +static int needResize; +static int cursorOn = 1; +static int trashScreen = 0; + +static const char * defaultHelpLine = +" <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen" +; + +const struct newtColors newtDefaultColorPalette = { + "white", "blue", /* root fg, bg */ + "black", "lightgray", /* border fg, bg */ + "black", "lightgray", /* window fg, bg */ + "white", "black", /* shadow fg, bg */ + "red", "lightgray", /* title fg, bg */ + "lightgray", "red", /* button fg, bg */ + "red", "lightgray", /* active button fg, bg */ + "yellow", "blue", /* checkbox fg, bg */ + "blue", "brown", /* active checkbox fg, bg */ + "yellow", "blue", /* entry box fg, bg */ + "blue", "lightgray", /* label fg, bg */ + "black", "lightgray", /* listbox fg, bg */ + "yellow", "blue", /* active listbox fg, bg */ + "black", "lightgray", /* textbox fg, bg */ + "lightgray", "black", /* active textbox fg, bg */ + "white", "blue", /* help line */ + "yellow", "blue", /* root text */ + "blue", /* scale full */ + "red", /* scale empty */ + "blue", "lightgray", /* disabled entry fg, bg */ + "white", "blue", /* compact button fg, bg */ + "yellow", "red", /* active & sel listbox */ + "black", "brown" /* selected listbox */ +}; + +static const struct keymap keymap[] = { + { "\033OA", NEWT_KEY_UP, "kh" }, + { "\033[A", NEWT_KEY_UP, "ku" }, + { "\033OB", NEWT_KEY_DOWN, "kd" }, + { "\033[B", NEWT_KEY_DOWN, "kd" }, + { "\033[C", NEWT_KEY_RIGHT, "kr" }, + { "\033OC", NEWT_KEY_RIGHT, "kr" }, + { "\033[D", NEWT_KEY_LEFT, "kl" }, + { "\033OD", NEWT_KEY_LEFT, "kl" }, + { "\033[H", NEWT_KEY_HOME, "kh" }, + { "\033[1~", NEWT_KEY_HOME, "kh" }, + { "\033Ow", NEWT_KEY_END, "kH" }, + { "\033[4~", NEWT_KEY_END, "kH" }, + + { "\033[3~", NEWT_KEY_DELETE, "kl" }, + { "\033[2~", NEWT_KEY_INSERT, NULL }, + + { "\033\t", NEWT_KEY_UNTAB, NULL }, + + { "\033[5~", NEWT_KEY_PGUP, NULL }, + { "\033[6~", NEWT_KEY_PGDN, NULL }, + { "\033V", NEWT_KEY_PGUP, "kH" }, + { "\033v", NEWT_KEY_PGUP, "kH" }, + + { "\033[[A", NEWT_KEY_F1, NULL }, + { "\033[[B", NEWT_KEY_F2, NULL }, + { "\033[[C", NEWT_KEY_F3, NULL }, + { "\033[[D", NEWT_KEY_F4, NULL }, + { "\033[[E", NEWT_KEY_F5, NULL }, + + { "\033OP", NEWT_KEY_F1, NULL }, + { "\033OQ", NEWT_KEY_F2, NULL }, + { "\033OR", NEWT_KEY_F3, NULL }, + { "\033OS", NEWT_KEY_F4, NULL }, + + { "\033[11~", NEWT_KEY_F1, NULL }, + { "\033[12~", NEWT_KEY_F2, NULL }, + { "\033[13~", NEWT_KEY_F3, NULL }, + { "\033[14~", NEWT_KEY_F4, NULL }, + { "\033[15~", NEWT_KEY_F5, NULL }, + { "\033[17~", NEWT_KEY_F6, NULL }, + { "\033[18~", NEWT_KEY_F7, NULL }, + { "\033[19~", NEWT_KEY_F8, NULL }, + { "\033[20~", NEWT_KEY_F9, NULL }, + { "\033[21~", NEWT_KEY_F10, NULL }, + { "\033[23~", NEWT_KEY_F11, NULL }, + { "\033[24~", NEWT_KEY_F12, NULL }, + { "\033", NEWT_KEY_ESCAPE, NULL }, + + { NULL, 0, NULL }, /* LEAVE this one */ +}; +static char keyPrefix = '\033'; + +static const char * version = "Newt windowing library version " VERSION + " - (C) 1996-2000 Red Hat Software. " + "Redistributable under the term of the Library " + "GNU Public License. " + "Written by Erik Troan\n"; + +static newtSuspendCallback suspendCallback = NULL; +static void * suspendCallbackData = NULL; + +void newtSetSuspendCallback(newtSuspendCallback cb, void * data) { + suspendCallback = cb; + suspendCallbackData = data; +} + +static void handleSigwinch(int signum) { + needResize = 1; +} + +static int getkeyInterruptHook(void) { + return -1; +} + +void newtFlushInput(void) { + while (SLang_input_pending(0)) { + SLang_getkey(); + } +} + +void newtRefresh(void) { + SLsmg_refresh(); +} + +void newtSuspend(void) { + SLtt_set_cursor_visibility (1); + SLsmg_suspend_smg(); + SLang_reset_tty(); + SLtt_set_cursor_visibility (cursorOn); +} + +void newtResume(void) { + SLsmg_resume_smg (); + SLsmg_refresh(); + SLang_init_tty(0, 0, 0); +} + +void newtCls(void) { + SLsmg_set_color(NEWT_COLORSET_ROOT); + SLsmg_gotorc(0, 0); + SLsmg_erase_eos(); + + newtRefresh(); +} + +#if defined(THIS_DOESNT_WORK) +void newtResizeScreen(int redraw) { + newtPushHelpLine(""); + + SLtt_get_screen_size(); + SLang_init_tty(0, 0, 0); + + SLsmg_touch_lines (0, SLtt_Screen_Rows - 1); + + /* I don't know why I need this */ + SLsmg_refresh(); + + newtPopHelpLine(); + + if (redraw) + SLsmg_refresh(); +} +#endif + +int newtInit(void) { + char * MonoValue, * MonoEnv = "NEWT_MONO", * lang; + + lang = getenv ("LANG"); + if (lang && !strcasecmp (lang, "ja_JP.eucJP")) + trashScreen = 1; + + /* use the version variable just to be sure it gets included */ + strlen(version); + + SLtt_get_terminfo(); + SLtt_get_screen_size(); + + MonoValue = getenv(MonoEnv); + if ( MonoValue == NULL ) { + SLtt_Use_Ansi_Colors = 1; + } else { + SLtt_Use_Ansi_Colors = 0; + } + + SLsmg_init_smg(); + SLang_init_tty(0, 0, 0); + + newtSetColors(newtDefaultColorPalette); + newtCursorOff(); + /*initKeymap();*/ + + /*memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handleSigwinch; + sigaction(SIGWINCH, &sa, NULL);*/ + + SLsignal_intr(SIGWINCH, handleSigwinch); + SLang_getkey_intr_hook = getkeyInterruptHook; + + + + return 0; +} + +int newtFinished(void) { + SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); + newtCursorOn(); + SLsmg_refresh(); + SLsmg_reset_smg(); + SLang_reset_tty(); + + return 0; +} + +void newtSetColors(struct newtColors colors) { + SLtt_set_color(NEWT_COLORSET_ROOT, "", colors.rootFg, colors.rootBg); + SLtt_set_color(NEWT_COLORSET_BORDER, "", colors.borderFg, colors.borderBg); + SLtt_set_color(NEWT_COLORSET_WINDOW, "", colors.windowFg, colors.windowBg); + SLtt_set_color(NEWT_COLORSET_SHADOW, "", colors.shadowFg, colors.shadowBg); + SLtt_set_color(NEWT_COLORSET_TITLE, "", colors.titleFg, colors.titleBg); + SLtt_set_color(NEWT_COLORSET_BUTTON, "", colors.buttonFg, colors.buttonBg); + SLtt_set_color(NEWT_COLORSET_ACTBUTTON, "", colors.actButtonFg, + colors.actButtonBg); + SLtt_set_color(NEWT_COLORSET_CHECKBOX, "", colors.checkboxFg, + colors.checkboxBg); + SLtt_set_color(NEWT_COLORSET_ACTCHECKBOX, "", colors.actCheckboxFg, + colors.actCheckboxBg); + SLtt_set_color(NEWT_COLORSET_ENTRY, "", colors.entryFg, colors.entryBg); + SLtt_set_color(NEWT_COLORSET_LABEL, "", colors.labelFg, colors.labelBg); + SLtt_set_color(NEWT_COLORSET_LISTBOX, "", colors.listboxFg, + colors.listboxBg); + SLtt_set_color(NEWT_COLORSET_ACTLISTBOX, "", colors.actListboxFg, + colors.actListboxBg); + SLtt_set_color(NEWT_COLORSET_TEXTBOX, "", colors.textboxFg, + colors.textboxBg); + SLtt_set_color(NEWT_COLORSET_ACTTEXTBOX, "", colors.actTextboxFg, + colors.actTextboxBg); + SLtt_set_color(NEWT_COLORSET_HELPLINE, "", colors.helpLineFg, + colors.helpLineBg); + SLtt_set_color(NEWT_COLORSET_ROOTTEXT, "", colors.rootTextFg, + colors.rootTextBg); + + SLtt_set_color(NEWT_COLORSET_EMPTYSCALE, "", "white", + colors.emptyScale); + SLtt_set_color(NEWT_COLORSET_FULLSCALE, "", "white", + colors.fullScale); + SLtt_set_color(NEWT_COLORSET_DISENTRY, "", colors.disabledEntryFg, + colors.disabledEntryBg); + + SLtt_set_color(NEWT_COLORSET_COMPACTBUTTON, "", colors.compactButtonFg, + colors.compactButtonBg); + + SLtt_set_color(NEWT_COLORSET_ACTSELLISTBOX, "", colors.actSelListboxFg, + colors.actSelListboxBg); + SLtt_set_color(NEWT_COLORSET_SELLISTBOX, "", colors.selListboxFg, + colors.selListboxBg); +} + +int newtGetKey(void) { + int key; + char buf[10], * chptr = buf; + const struct keymap * curr; + + do { + key = SLang_getkey(); + if (key == 0xFFFF) { + if (needResize) + return NEWT_KEY_RESIZE; + + /* ignore other signals */ + continue; + } + + if (key == NEWT_KEY_SUSPEND && suspendCallback) + suspendCallback(suspendCallbackData); + } while (key == NEWT_KEY_SUSPEND); + + switch (key) { + case 'v' | 0x80: + case 'V' | 0x80: + return NEWT_KEY_PGUP; + + case 22: + return NEWT_KEY_PGDN; + + return NEWT_KEY_BKSPC; + case 0x7f: + return NEWT_KEY_BKSPC; + + case 0x08: + return NEWT_KEY_BKSPC; + + default: + if (key != keyPrefix) return key; + } + + memset(buf, 0, sizeof(buf)); + + *chptr++ = key; + while (SLang_input_pending(5)) { + key = SLang_getkey(); + if (key == keyPrefix) { + /* he hit unknown keys too many times -- start over */ + memset(buf, 0, sizeof(buf)); + chptr = buf; + } + + *chptr++ = key; + + /* this search should use bsearch(), but when we only look through + a list of 20 (or so) keymappings, it's probably faster just to + do a inline linear search */ + + for (curr = keymap; curr->code; curr++) { + if (curr->str) { + if (!strcmp(curr->str, buf)) + return curr->code; + } + } + } + + for (curr = keymap; curr->code; curr++) { + if (curr->str) { + if (!strcmp(curr->str, buf)) + return curr->code; + } + } + + /* Looks like we were a bit overzealous in reading characters. Return + just the first character, and put everything else back in the buffer + for later */ + + chptr--; + while (chptr > buf) + SLang_ungetkey(*chptr--); + + return *chptr; +} + +void newtWaitForKey(void) { + newtRefresh(); + + SLang_getkey(); + newtClearKeyBuffer(); +} + +void newtClearKeyBuffer(void) { + while (SLang_input_pending(1)) { + SLang_getkey(); + } +} + +int newtOpenWindow(int left, int top, int width, int height, + const char * title) { + int j, row, col; + int n; + int i; + + newtFlushInput(); + + if (!currentWindow) { + currentWindow = windowStack; + } else { + currentWindow++; + } + + currentWindow->left = left; + currentWindow->top = top; + currentWindow->width = width; + currentWindow->height = height; + currentWindow->title = title ? strdup(title) : NULL; + + currentWindow->buffer = malloc(sizeof(SLsmg_Char_Type) * (width + 3) * (height + 3)); + + row = top - 1; + col = left - 1; + /* clip to the current screen bounds - msw */ + if (row < 0) + row = 0; + if (col < 0) + col = 0; + if (left + width > SLtt_Screen_Cols) + width = SLtt_Screen_Cols - left; + if (top + height > SLtt_Screen_Rows) + height = SLtt_Screen_Rows - top; + n = 0; + for (j = 0; j < height + 3; j++, row++) { + SLsmg_gotorc(row, col); + SLsmg_read_raw(currentWindow->buffer + n, + currentWindow->width + 3); + n += currentWindow->width + 3; + } + + newtTrashScreen(); + + SLsmg_set_color(NEWT_COLORSET_BORDER); + SLsmg_draw_box(top - 1, left - 1, height + 2, width + 2); + + if (currentWindow->title) { + i = strlen(currentWindow->title) + 4; + i = ((width - i) / 2) + left; + SLsmg_gotorc(top - 1, i); + SLsmg_set_char_set(1); + SLsmg_write_char(SLSMG_RTEE_CHAR); + SLsmg_set_char_set(0); + SLsmg_write_char(' '); + SLsmg_set_color(NEWT_COLORSET_TITLE); + SLsmg_write_string((char *)currentWindow->title); + SLsmg_set_color(NEWT_COLORSET_BORDER); + SLsmg_write_char(' '); + SLsmg_set_char_set(1); + SLsmg_write_char(SLSMG_LTEE_CHAR); + SLsmg_set_char_set(0); + } + + SLsmg_set_color(NEWT_COLORSET_WINDOW); + SLsmg_fill_region(top, left, height, width, ' '); + + SLsmg_set_color(NEWT_COLORSET_SHADOW); + SLsmg_fill_region(top + height + 1, left, 1, width + 2, ' '); + SLsmg_fill_region(top, left + width + 1, height + 1, 1, ' '); + + for (i = top; i < (top + height + 1); i++) { + SLsmg_gotorc(i, left + width + 1); + SLsmg_write_string(" "); + } + + return 0; +} + +int newtCenteredWindow(int width, int height, const char * title) { + int top, left; + + top = (SLtt_Screen_Rows - height) / 2; + + /* I don't know why, but this seems to look better */ + if ((SLtt_Screen_Rows % 2) && (top % 2)) top--; + + left = (SLtt_Screen_Cols - width) / 2; + + newtOpenWindow(left, top, width, height, title); + + return 0; +} + +void newtPopWindow(void) { + int j, row, col; + int n = 0; + + row = col = 0; + + row = currentWindow->top - 1; + col = currentWindow->left - 1; + if (row < 0) + row = 0; + if (col < 0) + col = 0; + for (j = 0; j < currentWindow->height + 3; j++, row++) { + SLsmg_gotorc(row, col); + SLsmg_write_raw(currentWindow->buffer + n, + currentWindow->width + 3); + n += currentWindow->width + 3; + } + + free(currentWindow->buffer); + free(currentWindow->title); + + if (currentWindow == windowStack) + currentWindow = NULL; + else + currentWindow--; + + SLsmg_set_char_set(0); + + newtTrashScreen(); + + newtRefresh(); +} + +void newtGetWindowPos(int * x, int * y) { + if (currentWindow) { + *x = currentWindow->left; + *y = currentWindow->top; + } else + *x = *y = 0; +} + +void newtGetrc(int * row, int * col) { + *row = cursorRow; + *col = cursorCol; +} + +void newtGotorc(int newRow, int newCol) { + if (currentWindow) { + newRow += currentWindow->top; + newCol += currentWindow->left; + } + + cursorRow = newRow; + cursorCol = newCol; + SLsmg_gotorc(cursorRow, cursorCol); +} + +void newtDrawBox(int left, int top, int width, int height, int shadow) { + if (currentWindow) { + top += currentWindow->top; + left += currentWindow->left; + } + + SLsmg_draw_box(top, left, height, width); + + if (shadow) { + SLsmg_set_color(NEWT_COLORSET_SHADOW); + SLsmg_fill_region(top + height, left + 1, 1, width - 1, ' '); + SLsmg_fill_region(top + 1, left + width, height, 1, ' '); + } +} + +void newtClearBox(int left, int top, int width, int height) { + if (currentWindow) { + top += currentWindow->top; + left += currentWindow->left; + } + + SLsmg_fill_region(top, left, height, width, ' '); +} + +#if 0 +/* This doesn't seem to work quite right. I don't know why not, but when + I rsh from an rxvt into a box and run this code, the machine returns + console key's (\033[B) rather then xterm ones (\033OB). */ +static void initKeymap(void) { + struct keymap * curr; + + for (curr = keymap; curr->code; curr++) { + if (!curr->str) + curr->str = SLtt_tgetstr(curr->tc); + } + + /* Newt's keymap handling is a bit broken. It assumes that any extended + keystrokes begin with ESC. If you're using a homebrek terminal you + will probably need to fix this, or just yell at me and I'll be so + ashamed of myself for doing it this way I'll fix it */ + + keyPrefix = 0x1b; /* ESC */ +} +#endif + +void newtDelay(int usecs) { + fd_set set; + struct timeval tv; + + FD_ZERO(&set); + + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + + select(0, &set, &set, &set, &tv); +} + +struct eventResult newtDefaultEventHandler(newtComponent c, + struct event ev) { + struct eventResult er; + + er.result = ER_IGNORED; + return er; +} + +void newtRedrawHelpLine(void) { + char * buf; + + SLsmg_set_color(NEWT_COLORSET_HELPLINE); + + buf = alloca(SLtt_Screen_Cols + 1); + memset(buf, ' ', SLtt_Screen_Cols); + buf[SLtt_Screen_Cols] = '\0'; + + if (currentHelpline) { + int len = strlen(*currentHelpline); + if (SLtt_Screen_Cols < len) + len = SLtt_Screen_Cols; + memcpy(buf, *currentHelpline, len); + } + SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); + SLsmg_write_string(buf); +} + +void newtPushHelpLine(const char * text) { + if (!text) + text = defaultHelpLine; + + if (currentHelpline) + (*(++currentHelpline)) = strdup(text); + else { + currentHelpline = helplineStack; + *currentHelpline = strdup(text); + } + + newtRedrawHelpLine(); +} + +void newtPopHelpLine(void) { + if (!currentHelpline) return; + + free(*currentHelpline); + if (currentHelpline == helplineStack) + currentHelpline = NULL; + else + currentHelpline--; + + newtRedrawHelpLine(); +} + +void newtDrawRootText(int col, int row, const char * text) { + SLsmg_set_color(NEWT_COLORSET_ROOTTEXT); + + if (col < 0) { + col = SLtt_Screen_Cols + col; + } + + if (row < 0) { + row = SLtt_Screen_Rows + row; + } + + SLsmg_gotorc(row, col); + SLsmg_write_string((char *)text); +} + +int newtSetFlags(int oldFlags, int newFlags, enum newtFlagsSense sense) { + switch (sense) { + case NEWT_FLAGS_SET: + return oldFlags | newFlags; + + case NEWT_FLAGS_RESET: + return oldFlags & (~newFlags); + + case NEWT_FLAGS_TOGGLE: + return oldFlags ^ newFlags; + + default: + return oldFlags; + } +} + +void newtBell(void) +{ + SLtt_beep(); +} + +void newtGetScreenSize(int * cols, int * rows) { + if (rows) *rows = SLtt_Screen_Rows; + if (cols) *cols = SLtt_Screen_Cols; +} + +void newtDefaultPlaceHandler(newtComponent c, int newLeft, int newTop) { + c->left = newLeft; + c->top = newTop; +} + +void newtDefaultMappedHandler(newtComponent c, int isMapped) { + c->isMapped = isMapped; +} + +void newtCursorOff(void) { + cursorOn = 0; + SLtt_set_cursor_visibility (cursorOn); +} + +void newtCursorOn(void) { + cursorOn = 1; + SLtt_set_cursor_visibility (cursorOn); +} + +void newtTrashScreen(void) { + if (trashScreen) + SLsmg_touch_lines (0, SLtt_Screen_Rows - 1); +} + diff --git a/mininewt/newt.h b/mininewt/newt.h new file mode 100644 index 000000000..6ffb25939 --- /dev/null +++ b/mininewt/newt.h @@ -0,0 +1,373 @@ +#ifndef H_NEWT +#define H_NEWT + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdarg.h> + +#define NEWT_COLORSET_ROOT 2 +#define NEWT_COLORSET_BORDER 3 +#define NEWT_COLORSET_WINDOW 4 +#define NEWT_COLORSET_SHADOW 5 +#define NEWT_COLORSET_TITLE 6 +#define NEWT_COLORSET_BUTTON 7 +#define NEWT_COLORSET_ACTBUTTON 8 +#define NEWT_COLORSET_CHECKBOX 9 +#define NEWT_COLORSET_ACTCHECKBOX 10 +#define NEWT_COLORSET_ENTRY 11 +#define NEWT_COLORSET_LABEL 12 +#define NEWT_COLORSET_LISTBOX 13 +#define NEWT_COLORSET_ACTLISTBOX 14 +#define NEWT_COLORSET_TEXTBOX 15 +#define NEWT_COLORSET_ACTTEXTBOX 16 +#define NEWT_COLORSET_HELPLINE 17 +#define NEWT_COLORSET_ROOTTEXT 18 +#define NEWT_COLORSET_EMPTYSCALE 19 +#define NEWT_COLORSET_FULLSCALE 20 +#define NEWT_COLORSET_DISENTRY 21 +#define NEWT_COLORSET_COMPACTBUTTON 22 +#define NEWT_COLORSET_ACTSELLISTBOX 23 +#define NEWT_COLORSET_SELLISTBOX 24 + +#define NEWT_ARG_LAST -100000 +#define NEWT_ARG_APPEND -1 + +struct newtColors { + char * rootFg, * rootBg; + char * borderFg, * borderBg; + char * windowFg, * windowBg; + char * shadowFg, * shadowBg; + char * titleFg, * titleBg; + char * buttonFg, * buttonBg; + char * actButtonFg, * actButtonBg; + char * checkboxFg, * checkboxBg; + char * actCheckboxFg, * actCheckboxBg; + char * entryFg, * entryBg; + char * labelFg, * labelBg; + char * listboxFg, * listboxBg; + char * actListboxFg, * actListboxBg; + char * textboxFg, * textboxBg; + char * actTextboxFg, * actTextboxBg; + char * helpLineFg, * helpLineBg; + char * rootTextFg, * rootTextBg; + char * emptyScale, * fullScale; + char * disabledEntryFg, * disabledEntryBg; + char * compactButtonFg, * compactButtonBg; + char * actSelListboxFg, * actSelListboxBg; + char * selListboxFg, * selListboxBg; +}; + +enum newtFlagsSense { NEWT_FLAGS_SET, NEWT_FLAGS_RESET, NEWT_FLAGS_TOGGLE }; + +#define NEWT_FLAG_RETURNEXIT (1 << 0) +#define NEWT_FLAG_HIDDEN (1 << 1) +#define NEWT_FLAG_SCROLL (1 << 2) +#define NEWT_FLAG_DISABLED (1 << 3) +/* OBSOLETE #define NEWT_FLAG_NOSCROLL (1 << 4) for listboxes */ +#define NEWT_FLAG_BORDER (1 << 5) +#define NEWT_FLAG_WRAP (1 << 6) +#define NEWT_FLAG_NOF12 (1 << 7) +#define NEWT_FLAG_MULTIPLE (1 << 8) +#define NEWT_FLAG_SELECTED (1 << 9) +#define NEWT_FLAG_CHECKBOX (1 << 10) +#define NEWT_FLAG_PASSWORD (1 << 11) /* draw '*' of chars in entrybox */ +#define NEWT_FLAG_SHOWCURSOR (1 << 12) /* Only applies to listbox for now */ +#define NEWT_FD_READ (1 << 0) +#define NEWT_FD_WRITE (1 << 1) +#define NEWT_FD_EXCEPT (1 << 2) + +#define NEWT_CHECKBOXTREE_UNSELECTABLE (1 << 12) +#define NEWT_CHECKBOXTREE_HIDE_BOX (1 << 13) + +#define NEWT_CHECKBOXTREE_COLLAPSED '\0' +#define NEWT_CHECKBOXTREE_EXPANDED '\1' +#define NEWT_CHECKBOXTREE_UNSELECTED ' ' +#define NEWT_CHECKBOXTREE_SELECTED '*' + +/* Backwards compatibility */ +#define NEWT_LISTBOX_RETURNEXIT NEWT_FLAG_RETURNEXIT +#define NEWT_ENTRY_SCROLL NEWT_FLAG_SCROLL +#define NEWT_ENTRY_HIDDEN NEWT_FLAG_HIDDEN +#define NEWT_ENTRY_RETURNEXIT NEWT_FLAG_RETURNEXIT +#define NEWT_ENTRY_DISABLED NEWT_FLAG_DISABLED + +#define NEWT_TEXTBOX_WRAP NEWT_FLAG_WRAP +#define NEWT_TEXTBOX_SCROLL NEWT_FLAG_SCROLL +#define NEWT_FORM_NOF12 NEWT_FLAG_NOF12 + +#define newtListboxAddEntry newtListboxAppendEntry + + +typedef struct newtComponent_struct * newtComponent; + +extern const struct newtColors newtDefaultColorPalette; + +typedef void (*newtCallback)(newtComponent, void *); +typedef void (*newtSuspendCallback)(void * data); + +int newtInit(void); +int newtFinished(void); +void newtCls(void); +void newtResizeScreen(int redraw); +void newtWaitForKey(void); +void newtClearKeyBuffer(void); +void newtDelay(int usecs); +/* top, left are *not* counting the border */ +int newtOpenWindow(int left, int top, int width, int height, + const char * title); +int newtCenteredWindow(int width, int height, const char * title); +void newtPopWindow(void); +void newtSetColors(struct newtColors colors); +void newtRefresh(void); +void newtSuspend(void); +void newtSetSuspendCallback(newtSuspendCallback cb, void * data); +void newtSetHelpCallback(newtCallback cb); +void newtResume(void); +void newtPushHelpLine(const char * text); +void newtRedrawHelpLine(void); +void newtPopHelpLine(void); +void newtDrawRootText(int col, int row, const char * text); +void newtBell(void); +void newtCursorOff(void); +void newtCursorOn(void); + +/* Components */ + +newtComponent newtCompactButton(int left, int top, const char * text); +newtComponent newtButton(int left, int top, const char * text); +newtComponent newtCheckbox(int left, int top, const char * text, char defValue, + const char * seq, char * result); +char newtCheckboxGetValue(newtComponent co); +void newtCheckboxSetValue(newtComponent co, char value); +void newtCheckboxSetFlags(newtComponent co, int flags, enum newtFlagsSense sense); + + +newtComponent newtRadiobutton(int left, int top, const char * text, int isDefault, + newtComponent prevButton); +newtComponent newtRadioGetCurrent(newtComponent setMember); +newtComponent newtListitem(int left, int top, const char * text, int isDefault, + newtComponent prevItem, const void * data, int flags); +void newtListitemSet(newtComponent co, const char * text); +void * newtListitemGetData(newtComponent co); +void newtGetScreenSize(int * cols, int * rows); + +newtComponent newtLabel(int left, int top, const char * text); +void newtLabelSetText(newtComponent co, const char * text); +newtComponent newtVerticalScrollbar(int left, int top, int height, + int normalColorset, int thumbColorset); +void newtScrollbarSet(newtComponent co, int where, int total); + +newtComponent newtListbox(int left, int top, int height, int flags); +void * newtListboxGetCurrent(newtComponent co); +void newtListboxSetCurrent(newtComponent co, int num); +void newtListboxSetCurrentByKey(newtComponent co, void * key); +void newtListboxSetEntry(newtComponent co, int num, const char * text); +void newtListboxSetWidth(newtComponent co, int width); +void newtListboxSetData(newtComponent co, int num, void * data); +int newtListboxAppendEntry(newtComponent co, const char * text, + const void * data); +/* Send the key to insert after, or NULL to insert at the top */ +int newtListboxInsertEntry(newtComponent co, const char * text, const void * data, void * key); +int newtListboxDeleteEntry(newtComponent co, void * data); +void newtListboxClear(newtComponent co); /* removes all entries from listbox */ +void newtListboxGetEntry(newtComponent co, int num, char **text, void **data); +/* Returns an array of data pointers from items, last element is NULL */ +void **newtListboxGetSelection(newtComponent co, int *numitems); +void newtListboxClearSelection(newtComponent co); +void newtListboxSelectItem(newtComponent co, const void * key, + enum newtFlagsSense sense); +/* Returns number of items currently in listbox. */ +int newtListboxItemCount(newtComponent co); + +newtComponent newtCheckboxTree(int left, int top, int height, int flags); +newtComponent newtCheckboxTreeMulti(int left, int top, int height, char *seq, int flags); +const void ** newtCheckboxTreeGetSelection(newtComponent co, int *numitems); +const void * newtCheckboxTreeGetCurrent(newtComponent co); +void newtCheckboxTreeSetCurrent(newtComponent co, void * item); +const void ** newtCheckboxTreeGetMultiSelection(newtComponent co, int *numitems, char seqnum); +/* last item is NEWT_ARG_LAST for all of these */ +int newtCheckboxTreeAddItem(newtComponent co, + const char * text, const void * data, + int flags, int index, ...); +int newtCheckboxTreeAddArray(newtComponent co, + const char * text, const void * data, + int flags, int * indexes); +int * newtCheckboxTreeFindItem(newtComponent co, void * data); +void newtCheckboxTreeSetEntry(newtComponent co, const void * data, + const char * text); +void newtCheckboxTreeSetWidth(newtComponent co, int width); +char newtCheckboxTreeGetEntryValue(newtComponent co, const void * data); +void newtCheckboxTreeSetEntryValue(newtComponent co, const void * data, + char value); + +newtComponent newtTextboxReflowed(int left, int top, char * text, int width, + int flexDown, int flexUp, int flags); +newtComponent newtTextbox(int left, int top, int width, int height, int flags); +void newtTextboxSetText(newtComponent co, const char * text); +void newtTextboxSetHeight(newtComponent co, int height); +int newtTextboxGetNumLines(newtComponent co); +char * newtReflowText(char * text, int width, int flexDown, int flexUp, + int * actualWidth, int * actualHeight); + +struct newtExitStruct { + enum { NEWT_EXIT_HOTKEY, NEWT_EXIT_COMPONENT, NEWT_EXIT_FDREADY, + NEWT_EXIT_TIMER } reason; + union { + int watch; + int key; + newtComponent co; + } u; +} ; + +newtComponent newtForm(newtComponent vertBar, void * helpTag, int flags); +void newtFormSetTimer(newtComponent form, int millisecs); +void newtFormWatchFd(newtComponent form, int fd, int fdFlags); +void newtFormSetSize(newtComponent co); +newtComponent newtFormGetCurrent(newtComponent co); +void newtFormSetBackground(newtComponent co, int color); +void newtFormSetCurrent(newtComponent co, newtComponent subco); +void newtFormAddComponent(newtComponent form, newtComponent co); +void newtFormAddComponents(newtComponent form, ...); +void newtFormSetHeight(newtComponent co, int height); +void newtFormSetWidth(newtComponent co, int width); +newtComponent newtRunForm(newtComponent form); /* obsolete */ +void newtFormRun(newtComponent co, struct newtExitStruct * es); +void newtDrawForm(newtComponent form); +void newtFormAddHotKey(newtComponent co, int key); + +typedef int (*newtEntryFilter)(newtComponent entry, void * data, int ch, + int cursor); +newtComponent newtEntry(int left, int top, const char * initialValue, int width, + char ** resultPtr, int flags); +void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd); +void newtEntrySetFilter(newtComponent co, newtEntryFilter filter, void * data); +char * newtEntryGetValue(newtComponent co); +void newtEntrySetFlags(newtComponent co, int flags, enum newtFlagsSense sense); + +newtComponent newtScale(int left, int top, int width, long long fullValue); +void newtScaleSet(newtComponent co, unsigned long long amount); + +void newtComponentAddCallback(newtComponent co, newtCallback f, void * data); +void newtComponentTakesFocus(newtComponent co, int val); + +/* this also destroys all of the components (including other forms) on the + form */ +void newtFormDestroy(newtComponent form); + +/* Key codes */ + +#define NEWT_KEY_TAB '\t' +#define NEWT_KEY_ENTER '\r' +#define NEWT_KEY_SUSPEND '\032' /* ctrl - z*/ +#define NEWT_KEY_ESCAPE '' +#define NEWT_KEY_RETURN NEWT_KEY_ENTER + +#define NEWT_KEY_EXTRA_BASE 0x8000 +#define NEWT_KEY_UP NEWT_KEY_EXTRA_BASE + 1 +#define NEWT_KEY_DOWN NEWT_KEY_EXTRA_BASE + 2 +#define NEWT_KEY_LEFT NEWT_KEY_EXTRA_BASE + 4 +#define NEWT_KEY_RIGHT NEWT_KEY_EXTRA_BASE + 5 +#define NEWT_KEY_BKSPC NEWT_KEY_EXTRA_BASE + 6 +#define NEWT_KEY_DELETE NEWT_KEY_EXTRA_BASE + 7 +#define NEWT_KEY_HOME NEWT_KEY_EXTRA_BASE + 8 +#define NEWT_KEY_END NEWT_KEY_EXTRA_BASE + 9 +#define NEWT_KEY_UNTAB NEWT_KEY_EXTRA_BASE + 10 +#define NEWT_KEY_PGUP NEWT_KEY_EXTRA_BASE + 11 +#define NEWT_KEY_PGDN NEWT_KEY_EXTRA_BASE + 12 +#define NEWT_KEY_INSERT NEWT_KEY_EXTRA_BASE + 13 + +#define NEWT_KEY_F1 NEWT_KEY_EXTRA_BASE + 101 +#define NEWT_KEY_F2 NEWT_KEY_EXTRA_BASE + 102 +#define NEWT_KEY_F3 NEWT_KEY_EXTRA_BASE + 103 +#define NEWT_KEY_F4 NEWT_KEY_EXTRA_BASE + 104 +#define NEWT_KEY_F5 NEWT_KEY_EXTRA_BASE + 105 +#define NEWT_KEY_F6 NEWT_KEY_EXTRA_BASE + 106 +#define NEWT_KEY_F7 NEWT_KEY_EXTRA_BASE + 107 +#define NEWT_KEY_F8 NEWT_KEY_EXTRA_BASE + 108 +#define NEWT_KEY_F9 NEWT_KEY_EXTRA_BASE + 109 +#define NEWT_KEY_F10 NEWT_KEY_EXTRA_BASE + 110 +#define NEWT_KEY_F11 NEWT_KEY_EXTRA_BASE + 111 +#define NEWT_KEY_F12 NEWT_KEY_EXTRA_BASE + 112 + +/* not really a key, but newtGetKey returns it */ +#define NEWT_KEY_RESIZE NEWT_KEY_EXTRA_BASE + 113 + +#define NEWT_ANCHOR_LEFT (1 << 0) +#define NEWT_ANCHOR_RIGHT (1 << 1) +#define NEWT_ANCHOR_TOP (1 << 2) +#define NEWT_ANCHOR_BOTTOM (1 << 3) + +#define NEWT_GRID_FLAG_GROWX (1 << 0) +#define NEWT_GRID_FLAG_GROWY (1 << 1) + +typedef struct grid_s * newtGrid; +enum newtGridElement { NEWT_GRID_EMPTY = 0, + NEWT_GRID_COMPONENT, NEWT_GRID_SUBGRID }; + +newtGrid newtCreateGrid(int cols, int rows); +/* TYPE, what, TYPE, what, ..., NULL */ +newtGrid newtGridVStacked(enum newtGridElement type, void * what, ...); +newtGrid newtGridVCloseStacked(enum newtGridElement type, void * what, ...); +newtGrid newtGridHStacked(enum newtGridElement type1, void * what1, ...); +newtGrid newtGridHCloseStacked(enum newtGridElement type1, void * what1, ...); +newtGrid newtGridBasicWindow(newtComponent text, newtGrid middle, + newtGrid buttons); +newtGrid newtGridSimpleWindow(newtComponent text, newtComponent middle, + newtGrid buttons); +void newtGridSetField(newtGrid grid, int col, int row, + enum newtGridElement type, void * val, int padLeft, + int padTop, int padRight, int padBottom, int anchor, + int flags); +void newtGridPlace(newtGrid grid, int left, int top); +#define newtGridDestroy newtGridFree +void newtGridFree(newtGrid grid, int recurse); +void newtGridGetSize(newtGrid grid, int * width, int * height); +void newtGridWrappedWindow(newtGrid grid, char * title); +void newtGridWrappedWindowAt(newtGrid grid, char * title, int left, int top); +void newtGridAddComponentsToForm(newtGrid grid, newtComponent form, + int recurse); + +/* convienve */ +newtGrid newtButtonBarv(char * button1, newtComponent * b1comp, va_list args); +newtGrid newtButtonBar(char * button1, newtComponent * b1comp, ...); + +/* automatically centered and shrink wrapped */ +void newtWinMessage(char * title, char * buttonText, char * text, ...); +void newtWinMessagev(char * title, char * buttonText, char * text, + va_list argv); + +/* having separate calls for these two seems silly, but having two separate + variable length-arg lists seems like a bad idea as well */ + +/* Returns 0 if F12 was pressed, 1 for button1, 2 for button2 */ +int newtWinChoice(char * title, char * button1, char * button2, + char * text, ...); +/* Returns 0 if F12 was pressed, 1 for button1, 2 for button2, + 3 for button3 */ +int newtWinTernary(char * title, char * button1, char * button2, + char * button3, char * message, ...); + +/* Returns the button number pressed, 0 on F12 */ +int newtWinMenu(char * title, char * text, int suggestedWidth, int flexDown, + int flexUp, int maxListHeight, char ** items, int * listItem, + char * button1, ...); + +struct newtWinEntry { + char * text; + char ** value; /* may be initialized to set default */ + int flags; +}; + +/* Returns the button number pressed, 0 on F12. The final values are + dynamically allocated, and need to be freed. */ +int newtWinEntries(char * title, char * text, int suggestedWidth, int flexDown, + int flexUp, int dataWidth, + struct newtWinEntry * items, char * button1, ...); + +#ifdef __cplusplus +} /* End of extern "C" { */ +#endif + +#endif /* H_NEWT */ diff --git a/mininewt/newt_pr.h b/mininewt/newt_pr.h new file mode 100644 index 000000000..258322d1e --- /dev/null +++ b/mininewt/newt_pr.h @@ -0,0 +1,83 @@ +#ifndef H_NEWT_PR +#define H_NEWT_PR + +#define COLORSET_ROOT NEWT_COLORSET_ROOT +#define COLORSET_BORDER NEWT_COLORSET_BORDER +#define COLORSET_WINDOW NEWT_COLORSET_WINDOW +#define COLORSET_SHADOW NEWT_COLORSET_SHADOW +#define COLORSET_TITLE NEWT_COLORSET_TITLE +#define COLORSET_BUTTON NEWT_COLORSET_BUTTON +#define COLORSET_ACTBUTTON NEWT_COLORSET_ACTBUTTON +#define COLORSET_CHECKBOX NEWT_COLORSET_CHECKBOX +#define COLORSET_ACTCHECKBOX NEWT_COLORSET_ACTCHECKBOX +#define COLORSET_ENTRY NEWT_COLORSET_ENTRY +#define COLORSET_LABEL NEWT_COLORSET_LABEL +#define COLORSET_LISTBOX NEWT_COLORSET_LISTBOX +#define COLORSET_ACTLISTBOX NEWT_COLORSET_ACTLISTBOX +#define COLORSET_TEXTBOX NEWT_COLORSET_TEXTBOX +#define COLORSET_ACTTEXTBOX NEWT_COLORSET_ACTTEXTBOX + +int newtSetFlags(int oldFlags, int newFlags, enum newtFlagsSense sense); + +void newtGotorc(int row, int col); +void newtGetrc(int * row, int * col); +void newtGetWindowPos(int * x, int * y); +void newtDrawBox(int left, int top, int width, int height, int shadow); +void newtClearBox(int left, int top, int width, int height); + +int newtGetKey(void); +void newtTrashScreen(void); + +struct newtComponent_struct { + /* common data */ + int height, width; + int top, left; + int takesFocus; + int isMapped; + + struct componentOps * ops; + + newtCallback callback; + void * callbackData; + + void * data; +} ; + +enum eventResultTypes { ER_IGNORED, ER_SWALLOWED, ER_EXITFORM, ER_SETFOCUS, + ER_NEXTCOMP }; +struct eventResult { + enum eventResultTypes result; + union { + newtComponent focus; + } u; +}; + +enum eventTypes { EV_FOCUS, EV_UNFOCUS, EV_KEYPRESS, EV_MOUSE }; +enum eventSequence { EV_EARLY, EV_NORMAL, EV_LATE }; + +struct event { + enum eventTypes event; + enum eventSequence when; + union { + int key; + struct { + enum { MOUSE_MOTION, MOUSE_BUTTON_DOWN, MOUSE_BUTTON_UP } type; + int x, y; + } mouse; + } u; +} ; + +struct componentOps { + void (* draw)(newtComponent c); + struct eventResult (* event)(newtComponent c, struct event ev); + void (* destroy)(newtComponent c); + void (* place)(newtComponent c, int newLeft, int newTop); + void (* mapped)(newtComponent c, int isMapped); +} ; + +void newtDefaultPlaceHandler(newtComponent c, int newLeft, int newTop); +void newtDefaultMappedHandler(newtComponent c, int isMapped); +struct eventResult newtDefaultEventHandler(newtComponent c, + struct event ev); + +#endif /* H_NEWT_PR */ diff --git a/mininewt/scale.c b/mininewt/scale.c new file mode 100644 index 000000000..f898916b3 --- /dev/null +++ b/mininewt/scale.c @@ -0,0 +1,85 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct scale { + long long fullValue; + int charsSet; + unsigned int percentage; +}; + +static void scaleDraw(newtComponent co); + +static struct componentOps scaleOps = { + scaleDraw, + newtDefaultEventHandler, + NULL, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +newtComponent newtScale(int left, int top, int width, long long fullValue) { + newtComponent co; + struct scale * sc; + + co = malloc(sizeof(*co)); + sc = malloc(sizeof(struct scale)); + co->data = sc; + + co->ops = &scaleOps; + + co->height = 1; + co->width = width; + co->top = top; + co->left = left; + co->takesFocus = 0; + + sc->fullValue = fullValue; + sc->charsSet = 0; + sc->percentage = 0; + + return co; +} + +void newtScaleSet(newtComponent co, unsigned long long amount) { + struct scale * sc = co->data; + int newPercentage; + + sc->charsSet = (amount * co->width) / sc->fullValue; + newPercentage = (amount * 100) / sc->fullValue; + + if (newPercentage > 100) + newPercentage = 100; + + if (newPercentage != sc->percentage) { + sc->percentage = newPercentage; + scaleDraw(co); + } +} + +static void scaleDraw(newtComponent co) { + struct scale * sc = co->data; + int i; + int xlabel = (co->width-4) /2; + char percent[10]; + + if (co->top == -1) return; + + newtGotorc(co->top, co->left); + + sprintf(percent, "%3d%%", sc->percentage); + + SLsmg_set_color(NEWT_COLORSET_FULLSCALE); + + for (i = 0; i < co->width; i++) { + if (i == sc->charsSet) + SLsmg_set_color(NEWT_COLORSET_EMPTYSCALE); + if (i >= xlabel && i < xlabel+4) + SLsmg_write_char(percent[i-xlabel]); + else + SLsmg_write_char(' '); + } +} diff --git a/mininewt/scrollbar.c b/mininewt/scrollbar.c new file mode 100644 index 000000000..1a737a409 --- /dev/null +++ b/mininewt/scrollbar.c @@ -0,0 +1,124 @@ +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct scrollbar { + int curr; + int cs, csThumb; + int arrows; +} ; + +static void sbDraw(newtComponent co); +static void sbDestroy(newtComponent co); +static void sbDrawThumb(newtComponent co, int isOn); + +static struct componentOps sbOps = { + sbDraw, + newtDefaultEventHandler, + sbDestroy, + newtDefaultPlaceHandler, + newtDefaultMappedHandler, +} ; + +void newtScrollbarSet(newtComponent co, int where, int total) { + struct scrollbar * sb = co->data; + int new; + + if (sb->arrows) + new = (where * (co->height - 3)) / (total ? total : 1) + 1; + else + new = (where * (co->height - 1)) / (total ? total : 1); + if (new != sb->curr) { + sbDrawThumb(co, 0); + sb->curr = new; + sbDrawThumb(co, 1); + } +} + +newtComponent newtVerticalScrollbar(int left, int top, int height, + int normalColorset, int thumbColorset) { + newtComponent co; + struct scrollbar * sb; + + co = malloc(sizeof(*co)); + sb = malloc(sizeof(*sb)); + co->data = sb; + + if (!strcmp(getenv("TERM"), "linux") && height >= 2) { + sb->arrows = 1; + sb->curr = 1; + } else { + sb->arrows = 0; + sb->curr = 0; + } + sb->cs = normalColorset; + sb->csThumb = thumbColorset; + + co->ops = &sbOps; + co->isMapped = 0; + co->left = left; + co->top = top; + co->height = height; + co->width = 1; + co->takesFocus = 0; + + return co; +} + +static void sbDraw(newtComponent co) { + struct scrollbar * sb = co->data; + int i; + + if (!co->isMapped) return; + + SLsmg_set_color(sb->cs); + + SLsmg_set_char_set(1); + if (sb->arrows) { + newtGotorc(co->top, co->left); + SLsmg_write_char(SLSMG_UARROW_CHAR); + for (i = 1; i < co->height - 1; i++) { + newtGotorc(i + co->top, co->left); + SLsmg_write_char(SLSMG_CKBRD_CHAR); + } + newtGotorc(co->top + co->height - 1, co->left); + SLsmg_write_char(SLSMG_DARROW_CHAR); + } else { + for (i = 0; i < co->height; i++) { + newtGotorc(i + co->top, co->left); + SLsmg_write_char(SLSMG_CKBRD_CHAR); + } + } + + SLsmg_set_char_set(0); + + sbDrawThumb(co, 1); +} + +static void sbDrawThumb(newtComponent co, int isOn) { + struct scrollbar * sb = co->data; + SLtt_Char_Type ch = isOn ? '#' : SLSMG_CKBRD_CHAR; + + if (!co->isMapped) return; + + newtGotorc(sb->curr + co->top, co->left); + SLsmg_set_char_set(1); + + /*if (isOn) + SLsmg_set_color(sb->csThumb); + else*/ + SLsmg_set_color(sb->cs); + + SLsmg_write_char(ch); + SLsmg_set_char_set(0); +} + +static void sbDestroy(newtComponent co) { + struct scrollbar * sb = co->data; + + free(sb); + free(co); +} diff --git a/mininewt/showchars.c b/mininewt/showchars.c new file mode 100644 index 000000000..71d0adbd1 --- /dev/null +++ b/mininewt/showchars.c @@ -0,0 +1,44 @@ +#include <slang.h> + +void printall(int offset) { + int n = 0; + int i, j; + + SLsmg_gotorc(0, offset); + SLsmg_write_string(" 0 1 2 3 4 5 6 7 8 9 A B C D E F"); + for (i = 0; i < 16; i++) { + SLsmg_gotorc(i + 1, offset); + SLsmg_printf("%x", i); + for (j = 0; j < 16; j++) { + SLsmg_gotorc(i + 1, (j + 1) * 2 + offset); + SLsmg_write_char(n++); + } + } +} + +int main(void) { + char n = 0; + + SLtt_get_terminfo(); + + SLtt_Use_Ansi_Colors = 1; + + SLsmg_init_smg(); + SLang_init_tty(4, 0, 0); + + SLsmg_cls(); + + printall(0); + SLsmg_set_char_set(1); + printall(40); + + SLsmg_refresh(); + SLang_getkey(); + + SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); + SLsmg_refresh(); + SLsmg_reset_smg(); + SLang_reset_tty(); + + return 0; +} diff --git a/mininewt/showkey.c b/mininewt/showkey.c new file mode 100644 index 000000000..5f465b5c2 --- /dev/null +++ b/mininewt/showkey.c @@ -0,0 +1,41 @@ +#include <stdio.h> +#include <slang.h> + +int main(void) { + char n = 0; + int i; + char * buf; + + SLtt_get_terminfo(); + SLang_init_tty(4, 0, 0); + + buf = SLtt_tgetstr("ku"); + if (!buf) { + printf("termcap entry not found for kl\n\r"); + } else { + printf("termcap entry found for kl: %s", buf); + while (*buf) { + printf("0x%02x ", *buf++); + } + printf("\n\r"); + } + + printf("\n\r"); + + printf("Press a key: "); + fflush(stdout); + + SLang_input_pending(50); + + printf("\n\r"); + printf("You pressed: "); + + while (SLang_input_pending(1)) { + i = SLang_getkey(); + printf("0x%02x ", i); + } + + printf("\n\r"); + + SLang_reset_tty(); +} diff --git a/mininewt/snackmodule.c b/mininewt/snackmodule.c new file mode 100644 index 000000000..14f07847d --- /dev/null +++ b/mininewt/snackmodule.c @@ -0,0 +1,1245 @@ + +#include "config.h" + +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <unistd.h> + +#include "Python.h" +#include "newt.h" + +typedef struct snackWidget_s snackWidget; +typedef struct snackGrid_s snackGrid; +typedef struct snackForm_s snackForm; + +struct callbackStruct { + PyObject * cb, * data; +}; + +/* Integer to pointer, 64-bit-sane */ +#define I2P(x) ((void *)(long)(x)) + +static struct callbackStruct suspend; +static struct callbackStruct helpCallback; + +static void emptyDestructor(PyObject * s); + +static snackWidget * buttonWidget(PyObject * s, PyObject * args); +static snackWidget * compactbuttonWidget(PyObject * s, PyObject * args); +static PyObject * centeredWindow(PyObject * s, PyObject * args); +static snackWidget * checkboxWidget(PyObject * s, PyObject * args); +static PyObject * choiceWindow(PyObject * s, PyObject * args); +static snackWidget * entryWidget(PyObject * s, PyObject * args); +static PyObject * drawRootText(PyObject * s, PyObject * args); +static PyObject * doResume(PyObject * s, PyObject * args); +static PyObject * doSuspend(PyObject * s, PyObject * args); +static PyObject * doSuspend(PyObject * s, PyObject * args); +static snackForm * formCreate(PyObject * s, PyObject * args); +static snackGrid * gridCreate(PyObject * s, PyObject * args); +static PyObject * gridWrappedWindow(PyObject * s, PyObject * args); +static PyObject * finishScreen(PyObject * s, PyObject * args); +static PyObject * initScreen(PyObject * s, PyObject * args); +static snackWidget * labelWidget(PyObject * s, PyObject * args); +static snackWidget * listboxWidget(PyObject * s, PyObject * args); +static PyObject * messageWindow(PyObject * s, PyObject * args); +static PyObject * openWindow(PyObject * s, PyObject * args); +static PyObject * popHelpLine(PyObject * s, PyObject * args); +static PyObject * popWindow(PyObject * s, PyObject * args); +static PyObject * pushHelpLine(PyObject * s, PyObject * args); +static snackWidget * radioButtonWidget(PyObject * s, PyObject * args); +static PyObject * refreshScreen(PyObject * s, PyObject * args); +static PyObject * scaleWidget(PyObject * s, PyObject * args); +static PyObject * scaleSet(snackWidget * s, PyObject * args); +static PyObject * screenSize(PyObject * s, PyObject * args); +static PyObject * setSuspendCallback(PyObject * s, PyObject * args); +static PyObject * setHelpCallback(PyObject * s, PyObject * args); +static PyObject * reflowText(PyObject * s, PyObject * args); +static snackWidget * textWidget(PyObject * s, PyObject * args); +static PyObject * ternaryWindow(PyObject * s, PyObject * args); +static snackWidget * checkboxTreeWidget(PyObject * s, PyObject * args, PyObject * kwargs); + +static PyMethodDef snackModuleMethods[] = { + { "button", (PyCFunction) buttonWidget, METH_VARARGS, NULL }, + { "compactbutton", (PyCFunction) compactbuttonWidget, METH_VARARGS, NULL }, + { "checkbox", (PyCFunction) checkboxWidget, METH_VARARGS, NULL }, + { "choice", choiceWindow, METH_VARARGS, NULL }, + { "centeredwindow", centeredWindow, METH_VARARGS, NULL }, + { "drawroottext", drawRootText, METH_VARARGS, NULL }, + { "entry", (PyCFunction) entryWidget, METH_VARARGS, NULL }, + { "finish", finishScreen, METH_VARARGS, NULL }, + { "form", (PyCFunction) formCreate, METH_VARARGS, NULL }, + { "grid", (PyCFunction) gridCreate, METH_VARARGS, NULL }, + { "gridwrappedwindow", gridWrappedWindow, METH_VARARGS, NULL }, + { "helpcallback", setHelpCallback, METH_VARARGS, NULL }, + { "init", initScreen, METH_VARARGS, NULL }, + { "label", (PyCFunction) labelWidget, METH_VARARGS, NULL }, + { "listbox", (PyCFunction) listboxWidget, METH_VARARGS, NULL }, + { "message", messageWindow, METH_VARARGS, NULL }, + { "openwindow", openWindow, METH_VARARGS, NULL }, + { "pophelpline", popHelpLine, METH_VARARGS, NULL }, + { "popwindow", popWindow, METH_VARARGS, NULL }, + { "pushhelpline", pushHelpLine, METH_VARARGS, NULL }, + { "radiobutton", (PyCFunction) radioButtonWidget, METH_VARARGS, NULL }, + { "reflow", (PyCFunction) reflowText, METH_VARARGS, NULL }, + { "refresh", refreshScreen, METH_VARARGS, NULL }, + { "resume", doResume, METH_VARARGS, NULL }, + { "scale", scaleWidget, METH_VARARGS, NULL }, + { "size", screenSize, METH_VARARGS, NULL }, + { "suspend", doSuspend, METH_VARARGS, NULL }, + { "suspendcallback", setSuspendCallback, METH_VARARGS, NULL }, + { "ternary", ternaryWindow, METH_VARARGS, NULL }, + { "textbox", (PyCFunction) textWidget, METH_VARARGS, NULL }, + { "checkboxtree", (PyCFunction) checkboxTreeWidget, METH_VARARGS | METH_KEYWORDS, NULL }, + { NULL } +} ; + +struct snackGrid_s { + PyObject_HEAD + newtGrid grid; +} ; + +static PyObject * gridGetAttr(PyObject * s, char * name); +static PyObject * gridPlace(snackGrid * s, PyObject * args); +static PyObject * gridSetField(snackGrid * s, PyObject * args); + +static PyMethodDef gridMethods[] = { + { "place", (PyCFunction) gridPlace, METH_VARARGS, NULL }, + { "setfield", (PyCFunction) gridSetField, METH_VARARGS, NULL }, + { NULL } +}; + +static PyTypeObject snackGridType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "snackgrid", /* tp_name */ + sizeof(snackGrid), /* tp_size */ + 0, /* tp_itemsize */ + emptyDestructor, /* tp_dealloc */ + 0, /* tp_print */ + gridGetAttr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ +}; + +struct snackForm_s { + PyObject_HEAD + newtComponent fo; +} ; + +static PyObject * formGetAttr(PyObject * s, char * name); +static PyObject * formAdd(snackForm * s, PyObject * args); +static PyObject * formDraw(snackForm * s, PyObject * args); +static PyObject * formRun(snackForm * s, PyObject * args); +static PyObject * formHotKey(snackForm * s, PyObject * args); +static PyObject * formSetCurrent(snackForm * form, PyObject * args); +static PyObject * formSetTimer(snackForm * form, PyObject * args); +static PyObject * formWatchFD(snackForm * form, PyObject * args); + +static PyMethodDef formMethods[] = { + { "add", (PyCFunction) formAdd, METH_VARARGS, NULL }, + { "draw", (PyCFunction) formDraw, METH_VARARGS, NULL }, + { "run", (PyCFunction) formRun, METH_VARARGS, NULL }, + { "addhotkey", (PyCFunction) formHotKey, METH_VARARGS, NULL }, + { "setcurrent", (PyCFunction) formSetCurrent, METH_VARARGS, NULL }, + { "settimer", (PyCFunction) formSetTimer, METH_VARARGS, NULL }, + { "watchfd", (PyCFunction) formWatchFD, METH_VARARGS, NULL }, + { NULL } +}; + +static PyTypeObject snackFormType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "snackform", /* tp_name */ + sizeof(snackForm), /* tp_size */ + 0, /* tp_itemsize */ + emptyDestructor, /* tp_dealloc */ + 0, /* tp_print */ + formGetAttr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ +}; + +struct snackWidget_s { + PyObject_HEAD + newtComponent co; + char achar; + void * apointer; + int anint; + struct callbackStruct scs; +} ; + +static PyObject * widgetAddCallback(snackWidget * s, PyObject * args); +static PyObject * widgetGetAttr(PyObject * s, char * name); +static void widgetDestructor(PyObject * s); +static PyObject * widgetEntrySetValue(snackWidget * s, PyObject * args); +static PyObject * widgetLabelText(snackWidget * s, PyObject * args); +static PyObject * widgetListboxSetW(snackWidget * s, PyObject * args); +static PyObject * widgetListboxAdd(snackWidget * s, PyObject * args); +static PyObject * widgetListboxIns(snackWidget * s, PyObject * args); +static PyObject * widgetListboxDel(snackWidget * s, PyObject * args); +static PyObject * widgetListboxGet(snackWidget * s, PyObject * args); +static PyObject * widgetListboxSet(snackWidget * s, PyObject * args); +static PyObject * widgetListboxClear(snackWidget * s, PyObject * args); +static PyObject * widgetTextboxText(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxTreeAddItem(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxTreeGetSel(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxTreeGetCur(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxTreeSetEntry(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxTreeSetWidth(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxTreeSetCurrent(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxTreeSetEntryValue(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxTreeGetEntryValue(snackWidget * s, PyObject * args); +static PyObject * widgetEntrySetFlags(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxSetFlags(snackWidget * s, PyObject * args); +static PyObject * widgetCheckboxSetValue(snackWidget * s, PyObject * args); + +static PyMethodDef widgetMethods[] = { + { "setCallback", (PyCFunction) widgetAddCallback, METH_VARARGS, NULL }, + { "labelText", (PyCFunction) widgetLabelText, METH_VARARGS, NULL }, + { "textboxText", (PyCFunction) widgetTextboxText, METH_VARARGS, NULL }, + { "entrySetValue", (PyCFunction) widgetEntrySetValue, METH_VARARGS, NULL }, + { "listboxAddItem", (PyCFunction) widgetListboxAdd, METH_VARARGS, NULL }, + { "listboxInsertItem", (PyCFunction) widgetListboxIns, METH_VARARGS, NULL }, + { "listboxGetCurrent", (PyCFunction) widgetListboxGet, METH_VARARGS, NULL }, + { "listboxSetCurrent", (PyCFunction) widgetListboxSet, METH_VARARGS, NULL }, + { "listboxSetWidth", (PyCFunction) widgetListboxSetW, METH_VARARGS, NULL }, + { "listboxDeleteItem", (PyCFunction) widgetListboxDel, METH_VARARGS, NULL }, + { "listboxClear", (PyCFunction) widgetListboxClear, METH_VARARGS, NULL }, + { "scaleSet", (PyCFunction) scaleSet, METH_VARARGS, NULL }, + { "checkboxtreeAddItem", (PyCFunction) widgetCheckboxTreeAddItem, + METH_VARARGS, NULL }, + { "checkboxtreeGetCurrent", (PyCFunction) widgetCheckboxTreeGetCur, + METH_VARARGS, NULL }, + { "checkboxtreeGetEntryValue", (PyCFunction) widgetCheckboxTreeGetEntryValue, + METH_VARARGS, NULL }, + { "checkboxtreeSetEntry", (PyCFunction) widgetCheckboxTreeSetEntry, + METH_VARARGS, NULL }, + { "checkboxtreeSetWidth", (PyCFunction) widgetCheckboxTreeSetWidth, METH_VARARGS, NULL }, + { "checkboxtreeSetCurrent", (PyCFunction) widgetCheckboxTreeSetCurrent, + METH_VARARGS, NULL }, + { "checkboxtreeSetEntryValue", (PyCFunction) widgetCheckboxTreeSetEntryValue, + METH_VARARGS, NULL }, + { "checkboxtreeGetSelection", (PyCFunction) widgetCheckboxTreeGetSel, + METH_VARARGS, NULL }, + { "entrySetFlags", (PyCFunction) widgetEntrySetFlags, METH_VARARGS, NULL }, + { "checkboxSetFlags", (PyCFunction) widgetCheckboxSetFlags, METH_VARARGS, NULL }, + { "checkboxSetValue", (PyCFunction) widgetCheckboxSetValue, METH_VARARGS, NULL }, + { NULL } +}; + +static PyTypeObject snackWidgetType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "snackwidget", /* tp_name */ + sizeof(snackWidget), /* tp_size */ + 0, /* tp_itemsize */ + widgetDestructor, /* tp_dealloc */ + 0, /* tp_print */ + widgetGetAttr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ +}; + +static snackWidget * snackWidgetNew (void) { + snackWidget * widget; + + widget = PyObject_NEW(snackWidget, &snackWidgetType); + + widget->scs.cb = NULL; + widget->scs.data = NULL; + + return widget; +} + +static PyObject * initScreen(PyObject * s, PyObject * args) { + suspend.cb = NULL; + suspend.data = NULL; + + newtInit(); + newtCls(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * finishScreen(PyObject * s, PyObject * args) { + Py_XDECREF (suspend.cb); + Py_XDECREF (suspend.data); + + newtFinished(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * refreshScreen(PyObject * s, PyObject * args) { + newtRefresh(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * scaleWidget(PyObject * s, PyObject * args) { + snackWidget * widget; + int width, fullAmount; + + if (!PyArg_ParseTuple(args, "ii", &width, &fullAmount)) return NULL; + + widget = snackWidgetNew (); + widget->co = newtScale(-1, -1, width, fullAmount); + + return (PyObject *) widget; +} + +static PyObject * scaleSet(snackWidget * s, PyObject * args) { + int amount; + + if (!PyArg_ParseTuple(args, "i", &amount)) return NULL; + + newtScaleSet(s->co, amount); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * screenSize(PyObject * s, PyObject * args) { + int width, height; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + + newtGetScreenSize(&width, &height); + + return Py_BuildValue("(ii)", width, height); +} + +static void helpCallbackMarshall(newtComponent co, void * data) { + PyObject * args, * result; + + args = Py_BuildValue("(O)", data); + result = PyEval_CallObject(helpCallback.cb, args); + Py_DECREF (args); + Py_XDECREF(result); + + return; +} + +static void suspendCallbackMarshall(void * data) { + struct callbackStruct * scs = data; + PyObject * args, * result; + + if (scs->data) { + args = Py_BuildValue("(O)", scs->data); + result = PyEval_CallObject(scs->cb, args); + Py_DECREF (args); + } else + result = PyEval_CallObject(scs->cb, NULL); + + if (!result) { + PyErr_Print(); + PyErr_Clear(); + } + + Py_XDECREF(result); + + return; +} + +static void callbackMarshall(newtComponent co, void * data) { + struct callbackStruct * scs = data; + PyObject * args, * result; + + if (scs->data) { + args = Py_BuildValue("(O)", scs->data); + result = PyEval_CallObject(scs->cb, args); + Py_DECREF (args); + } else + result = PyEval_CallObject(scs->cb, NULL); + + if (!result) { + PyErr_Print(); + PyErr_Clear(); + } + + Py_XDECREF(result); + + return; +} + +static PyObject * setSuspendCallback(PyObject * s, PyObject * args) { + if (!PyArg_ParseTuple(args, "O|O", &suspend.cb, &suspend.data)) + return NULL; + + Py_INCREF (suspend.cb); + Py_XINCREF (suspend.data); + + newtSetSuspendCallback(suspendCallbackMarshall, &suspend); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * setHelpCallback(PyObject * s, PyObject * args) { + if (!PyArg_ParseTuple(args, "O", &helpCallback.cb)) + return NULL; + + /*if (helpCallback.cb) { + Py_DECREF (helpCallback.cb); + }*/ + + Py_INCREF (helpCallback.cb); + + newtSetHelpCallback(helpCallbackMarshall); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * drawRootText(PyObject * s, PyObject * args) { + int left, top; + char * text; + + if (!PyArg_ParseTuple(args, "iis", &left, &top, &text)) + return NULL; + + newtDrawRootText(left, top, text); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * doSuspend(PyObject * s, PyObject * args) { + newtSuspend(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * doResume(PyObject * s, PyObject * args) { + newtResume(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * popHelpLine(PyObject * s, PyObject * args) { + newtPopHelpLine(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * pushHelpLine(PyObject * s, PyObject * args) { + char * text; + + if (!PyArg_ParseTuple(args, "s", &text)) + return NULL; + + if (!strcmp(text, "*default*")) + newtPushHelpLine(NULL); + else + newtPushHelpLine(text); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * reflowText(PyObject * s, PyObject * args) { + char * text, * new; + int width, minus = 5, plus = 5; + int realWidth, realHeight; + PyObject * tuple; + + if (!PyArg_ParseTuple(args, "si|ii", &text, &width, &minus, &plus)) + return NULL; + + new = newtReflowText(text, width, minus, plus, &realWidth, &realHeight); + + tuple = Py_BuildValue("(sii)", new, realWidth, realHeight); + free(new); + + return tuple; +} + +static PyObject * centeredWindow(PyObject * s, PyObject * args) { + int width, height; + char * title; + + if (!PyArg_ParseTuple(args, "iis", &width, &height, &title)) + return NULL; + + newtCenteredWindow(width, height, title); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * gridWrappedWindow(PyObject * s, PyObject * args) { + snackGrid * grid; + char * title; + int x = -1, y = -1; + + if (!PyArg_ParseTuple(args, "O!s|ii", &snackGridType, &grid, &title, + &x, &y)) + return NULL; + + if (y == -1) + newtGridWrappedWindow(grid->grid, title); + else + newtGridWrappedWindowAt(grid->grid, title, x, y); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * openWindow(PyObject * s, PyObject * args) { + int left, top, width, height; + char * title; + + if (!PyArg_ParseTuple(args, "iiiis", &left, &top, &width, &height, &title)) + return NULL; + + newtOpenWindow(left, top, width, height, title); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * popWindow(PyObject * s, PyObject * args) { + newtPopWindow(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * messageWindow(PyObject * s, PyObject * args) { + char * title, * text; + char * okbutton = "Ok"; + + if (!PyArg_ParseTuple(args, "ss|s", &title, &text, &okbutton)) + return NULL; + + newtWinMessage(title, okbutton, text); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * choiceWindow(PyObject * s, PyObject * args) { + char * title, * text; + char * okbutton = "Ok"; + char * cancelbutton = "Cancel"; + int rc; + + if (!PyArg_ParseTuple(args, "ss|ss", &title, &text, &okbutton, + &cancelbutton)) + return NULL; + + rc = newtWinChoice(title, okbutton, cancelbutton, text); + + return Py_BuildValue("i", rc); +} + +static PyObject * ternaryWindow(PyObject * s, PyObject * args) { + char * title, * text, * button1, * button2, * button3; + int rc; + + if (!PyArg_ParseTuple(args, "sssss", &title, &text, &button1, &button2, + &button3)) + return NULL; + + rc = newtWinTernary(title, button1, button2, button3, text); + + return Py_BuildValue("i", rc); +} + +static snackWidget * buttonWidget(PyObject * s, PyObject * args) { + snackWidget * widget; + char * label; + + if (!PyArg_ParseTuple(args, "s", &label)) return NULL; + + widget = snackWidgetNew (); + widget->co = newtButton(-1, -1, label); + + return widget; +} + +static snackWidget * compactbuttonWidget(PyObject * s, PyObject * args) { + snackWidget * widget; + char * label; + + if (!PyArg_ParseTuple(args, "s", &label)) return NULL; + + widget = snackWidgetNew (); + widget->co = newtCompactButton(-1, -1, label); + + return widget; +} + +static snackWidget * labelWidget(PyObject * s, PyObject * args) { + char * label; + snackWidget * widget; + + if (!PyArg_ParseTuple(args, "s", &label)) return NULL; + + widget = snackWidgetNew (); + widget->co = newtLabel(-1, -1, label); + + return widget; +} + +static PyObject * widgetLabelText(snackWidget * s, PyObject * args) { + char * label; + + if (!PyArg_ParseTuple(args, "s", &label)) return NULL; + + newtLabelSetText(s->co, label); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetTextboxText(snackWidget * s, PyObject * args) { + char * text; + + if (!PyArg_ParseTuple(args, "s", &text)) return NULL; + + newtTextboxSetText(s->co, text); + + Py_INCREF(Py_None); + return Py_None; +} + +static snackWidget * listboxWidget(PyObject * s, PyObject * args) { + snackWidget * widget; + int height; + int doScroll = 0, returnExit = 0, showCursor = 0 ; + + if (!PyArg_ParseTuple(args, "i|iii", &height, &doScroll, &returnExit, &showCursor)) + return NULL; + + widget = snackWidgetNew (); + widget->co = newtListbox(-1, -1, height, + (doScroll ? NEWT_FLAG_SCROLL : 0) | + (returnExit ? NEWT_FLAG_RETURNEXIT : 0) | + (showCursor ? NEWT_FLAG_SHOWCURSOR : 0) + ); + widget->anint = 1; + + return widget; +} + +static snackWidget * textWidget(PyObject * s, PyObject * args) { + char * text; + int width, height; + int scrollBar = 0; + int wrap = 0; + snackWidget * widget; + + if (!PyArg_ParseTuple(args, "iis|ii", &width, &height, &text, &scrollBar, &wrap)) + return NULL; + + widget = snackWidgetNew (); + widget->co = newtTextbox(-1, -1, width, height, + (scrollBar ? NEWT_FLAG_SCROLL : 0) | + (wrap ? NEWT_FLAG_WRAP : 0)); + newtTextboxSetText(widget->co, text); + + return widget; +} + +static snackWidget * radioButtonWidget(PyObject * s, PyObject * args) { + snackWidget * widget, * group; + char * text; + int isOn; + + if (!PyArg_ParseTuple(args, "sOi", &text, &group, &isOn)) + return NULL; + + widget = snackWidgetNew (); + + if ((PyObject *) group == Py_None) + widget->co = newtRadiobutton(-1, -1, text, isOn, NULL); + else + widget->co = newtRadiobutton(-1, -1, text, isOn, group->co); + + return widget; +} + +static snackWidget * checkboxWidget(PyObject * s, PyObject * args) { + snackWidget * widget; + char * text; + int isOn; + + if (!PyArg_ParseTuple(args, "si", &text, &isOn)) return NULL; + + widget = snackWidgetNew (); + widget->co = newtCheckbox(-1, -1, text, isOn ? '*' : ' ', NULL, + &widget->achar); + + return widget; +} + +static PyObject * widgetCheckboxSetFlags(snackWidget * s, PyObject * args) { + int flag, sense; + + if (!PyArg_ParseTuple(args, "ii", &flag, &sense)) return NULL; + + newtCheckboxSetFlags(s->co, flag, sense); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetCheckboxSetValue(snackWidget * s, PyObject * args) { + char *value; + + if (!PyArg_ParseTuple(args, "s", &value)) return NULL; + + newtCheckboxSetValue(s->co, *value); + + Py_INCREF(Py_None); + return Py_None; +} + +static snackWidget * entryWidget(PyObject * s, PyObject * args) { + snackWidget * widget; + int width; + char * initial; + int isHidden, isScrolled, returnExit, isPassword; + + if (!PyArg_ParseTuple(args, "isiiii", &width, &initial, + &isHidden, &isPassword, &isScrolled, &returnExit)) return NULL; + + widget = snackWidgetNew (); + widget->co = newtEntry(-1, -1, initial, width, (char **) &widget->apointer, + (isHidden ? NEWT_FLAG_HIDDEN : 0) | + (isPassword ? NEWT_FLAG_PASSWORD : 0) | + (returnExit ? NEWT_FLAG_RETURNEXIT : 0) | + (isScrolled ? NEWT_FLAG_SCROLL : 0)); + + return widget; +} + +static snackForm * formCreate(PyObject * s, PyObject * args) { + snackForm * form; + PyObject * help = Py_None; + + if (!PyArg_ParseTuple(args, "|O", &help)) return NULL; + + if (help == Py_None) + help = NULL; + + form = PyObject_NEW(snackForm, &snackFormType); + form->fo = newtForm(NULL, help, 0); + + return form; +} + +static snackGrid * gridCreate(PyObject * s, PyObject * args) { + int rows, cols; + snackGrid * grid; + + if (!PyArg_ParseTuple(args, "ii", &cols, &rows)) return NULL; + + grid = PyObject_NEW(snackGrid, &snackGridType); + grid->grid = newtCreateGrid(cols, rows); + + return grid; +} + +static PyObject * gridGetAttr(PyObject * s, char * name) { + return Py_FindMethod(gridMethods, s, name); +} + +static PyObject * gridPlace(snackGrid * grid, PyObject * args) { + int x, y; + + if (!PyArg_ParseTuple(args, "ii", &x, &y)) return NULL; + + newtGridPlace(grid->grid, x, y); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * gridSetField(snackGrid * grid, PyObject * args) { + snackWidget * w; + snackGrid * g; + int x, y; + int pLeft = 0, pTop = 0, pRight = 0, pBottom = 0; + int anchorFlags = 0, growFlags = 0; + + if (!PyArg_ParseTuple(args, "iiO|(iiii)ii", &x, &y, + &w, &pLeft, &pTop, &pRight, &pBottom, + &anchorFlags, &growFlags)) + return NULL; + + if (w->ob_type == &snackWidgetType) { + newtGridSetField(grid->grid, x, y, NEWT_GRID_COMPONENT, + w->co, pLeft, pTop, pRight, pBottom, anchorFlags, + growFlags); + } else { + g = (snackGrid *) w; + newtGridSetField(grid->grid, x, y, NEWT_GRID_SUBGRID, + g->grid, pLeft, pTop, pRight, pBottom, anchorFlags, + growFlags); + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * formGetAttr(PyObject * s, char * name) { + return Py_FindMethod(formMethods, s, name); +} + +static PyObject * formDraw(snackForm * s, PyObject * args) { + newtDrawForm(s->fo); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * formAdd(snackForm * s, PyObject * args) { + snackWidget * w; + int size = PyTuple_Size(args), i; + + if (!size) { + /* this is a hack, I should give an error directly */ + if (!PyArg_ParseTuple(args, "O!", &snackWidgetType, &w)) + return NULL; + } + + for (i = 0; i < size; i++) { + w = (snackWidget *) PyTuple_GET_ITEM(args, i); + newtFormAddComponent(s->fo, w->co); + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * formRun(snackForm * s, PyObject * args) { + struct newtExitStruct result; + + newtFormRun(s->fo, &result); + + if (result.reason == NEWT_EXIT_HOTKEY) + return Py_BuildValue("(si)", "hotkey", result.u.key); + else if (result.reason == NEWT_EXIT_TIMER) + return Py_BuildValue("(si)", "timer", 0); + else if (result.reason == NEWT_EXIT_FDREADY) + return Py_BuildValue("(si)", "fdready", result.u.watch); + else + return Py_BuildValue("(si)", "widget", result.u.co); +} + +static PyObject * formHotKey(snackForm * s, PyObject * args) { + int key; + + if (!PyArg_ParseTuple(args, "i", &key)) + return NULL; + + newtFormAddHotKey(s->fo, key); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * formSetTimer(snackForm * form, PyObject * args) { + int millisecs; + + if (!PyArg_ParseTuple(args, "i", &millisecs)) + return NULL; + + newtFormSetTimer(form->fo, millisecs); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * formWatchFD(snackForm * form, PyObject * args) { + int fd, fdflags; + + if (!PyArg_ParseTuple(args, "ii", &fd, &fdflags)) + return NULL; + + newtFormWatchFd(form->fo, fd, fdflags); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * formSetCurrent(snackForm * form, PyObject * args) { + snackWidget * w; + + if (!PyArg_ParseTuple(args, "O", &w)) + return NULL; + + newtFormSetCurrent(form->fo, w->co); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetGetAttr(PyObject * s, char * name) { + snackWidget * w = (snackWidget *) s; + + if (!strcmp(name, "key")) { + return Py_BuildValue("i", w->co); + } else if (!strcmp(name, "entryValue")) { + return Py_BuildValue("s", w->apointer); + } else if (!strcmp(name, "checkboxValue")) { + return Py_BuildValue("i", w->achar == ' ' ? 0 : 1); + } else if (!strcmp(name, "radioValue")) { + return Py_BuildValue("i", newtRadioGetCurrent(w->co)); + } + + return Py_FindMethod(widgetMethods, s, name); +} + +static void widgetDestructor(PyObject * o) { + snackWidget * s = (snackWidget *) o; + + Py_XDECREF (s->scs.cb); + Py_XDECREF (s->scs.data); + + PyMem_DEL(o); +} + +static PyObject * widgetAddCallback(snackWidget * s, PyObject * args) { + s->scs.cb = NULL; + s->scs.data = NULL; + + if (!PyArg_ParseTuple(args, "O|O", &s->scs.cb, &s->scs.data)) + return NULL; + + Py_INCREF (s->scs.cb); + Py_XINCREF (s->scs.data); + + newtComponentAddCallback(s->co, callbackMarshall, &s->scs); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetEntrySetValue(snackWidget * s, PyObject * args) { + char * val; + + if (!PyArg_ParseTuple(args, "s", &val)) + return NULL; + + newtEntrySet(s->co, val, 1); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetEntrySetFlags(snackWidget * s, PyObject * args) { + int flag, sense; + + if (!PyArg_ParseTuple(args, "ii", &flag, &sense)) return NULL; + + newtEntrySetFlags(s->co, flag, sense); + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * widgetListboxAdd(snackWidget * s, PyObject * args) { + char * text; + + if (!PyArg_ParseTuple(args, "s", &text)) + return NULL; + + newtListboxAddEntry(s->co, text, I2P(s->anint)); + + return PyInt_FromLong(s->anint++); +} + +static PyObject * widgetListboxIns(snackWidget * s, PyObject * args) { + char * text; + int key; + + if (!PyArg_ParseTuple(args, "si", &text, &key)) + return NULL; + + newtListboxInsertEntry(s->co, text, I2P(s->anint), I2P(key)); + + return PyInt_FromLong(s->anint++); +} + +static PyObject * widgetListboxDel(snackWidget * s, PyObject * args) { + int key; + + if (!PyArg_ParseTuple(args, "i", &key)) + return NULL; + + newtListboxDeleteEntry(s->co, I2P(key)); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetListboxGet(snackWidget * s, PyObject * args) { + if (!PyArg_ParseTuple(args, "")) + return NULL; + + return PyInt_FromLong((long) newtListboxGetCurrent(s->co)); +} + +static PyObject * widgetListboxSet(snackWidget * s, PyObject * args) { + int index; + + if (!PyArg_ParseTuple(args, "i", &index)) + return NULL; + + newtListboxSetCurrentByKey(s->co, I2P(index)); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetListboxSetW(snackWidget * s, PyObject * args) { + int width; + + if (!PyArg_ParseTuple(args, "i", &width)) + return NULL; + + newtListboxSetWidth(s->co, width); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetListboxClear(snackWidget * s, PyObject * args) { + if (!PyArg_ParseTuple(args, "")) + return NULL; + + newtListboxClear(s->co); + + Py_INCREF(Py_None); + return Py_None; +} + +static void emptyDestructor(PyObject * s) { +} + +static snackWidget * checkboxTreeWidget(PyObject * s, PyObject * args, PyObject * kwargs) { + int height; + int scrollBar = 0; + int hide_checkbox = 0; + int unselectable = 0; + int flags; + snackWidget * widget; + const char *kw[] = {"height", "scrollbar", "hide_checkbox", "unselectable", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|iii", (char **) kw, + &height, &scrollBar, &hide_checkbox, &unselectable)) + return NULL; + + flags = (scrollBar ? NEWT_FLAG_SCROLL : 0) | + (hide_checkbox ? NEWT_CHECKBOXTREE_HIDE_BOX : 0) | + (unselectable ? NEWT_CHECKBOXTREE_UNSELECTABLE : 0); + + widget = snackWidgetNew (); + widget->co = newtCheckboxTree(-1, -1, height, flags); + + widget->anint = 1; + + return widget; +} + +static PyObject * widgetCheckboxTreeAddItem(snackWidget * s, PyObject * args) { + char * text; + int selected = 0; + PyObject * pathList, * o; + int len; + int * path; + int i; + + if (!PyArg_ParseTuple(args, "sOi", &text, &pathList, &selected)) + return NULL; + + len = PyTuple_Size(pathList); + path = alloca(sizeof(*path) * (len + 1)); + for (i = 0; i < len; i++) { + o = PyTuple_GetItem(pathList, i); + path[i] = PyInt_AsLong(o); + } + path[len] = NEWT_ARG_LAST; + + newtCheckboxTreeAddArray(s->co, text, I2P(s->anint), + selected ? NEWT_FLAG_SELECTED : 0, path); + + return PyInt_FromLong(s->anint++); +} + +static PyObject * widgetCheckboxTreeGetCur(snackWidget * s, PyObject * args) { + if (!PyArg_ParseTuple(args, "")) + return NULL; + + return PyInt_FromLong((long)newtCheckboxTreeGetCurrent(s->co)); +} + +static PyObject * widgetCheckboxTreeSetEntry(snackWidget * s, PyObject * args) { + int data; + char *text; + + if (!PyArg_ParseTuple(args, "is", &data, &text)) return NULL; + + newtCheckboxTreeSetEntry(s->co, I2P(data), text); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetCheckboxTreeSetWidth(snackWidget * s, PyObject * args) { + int width; + + if (!PyArg_ParseTuple(args, "i", &width)) + return NULL; + + newtCheckboxTreeSetWidth(s->co, width); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetCheckboxTreeSetCurrent(snackWidget * s, PyObject * args) { + int data; + + if (!PyArg_ParseTuple(args, "i", &data)) return NULL; + + newtCheckboxTreeSetCurrent(s->co, I2P(data)); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetCheckboxTreeSetEntryValue(snackWidget * s, PyObject * args) { + int data; + int isOn = 1; + + if (!PyArg_ParseTuple(args, "i|i", &data, &isOn)) return NULL; + + newtCheckboxTreeSetEntryValue(s->co, I2P(data), + isOn ? NEWT_CHECKBOXTREE_SELECTED : + NEWT_CHECKBOXTREE_UNSELECTED); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * widgetCheckboxTreeGetEntryValue(snackWidget * s, PyObject * args) { + int data; + int isOn = 0; + int isBranch = 0; + char selection; + + if (!PyArg_ParseTuple(args, "i", &data)) return NULL; + + selection = newtCheckboxTreeGetEntryValue(s->co, I2P(data)); + + if (selection == -1) return NULL; + + switch (selection) { + case NEWT_CHECKBOXTREE_EXPANDED: + isOn = 1; + case NEWT_CHECKBOXTREE_COLLAPSED: + isBranch = 1; + break; + case NEWT_CHECKBOXTREE_UNSELECTED: + break; + default: + isOn = 1; + break; + } + return Py_BuildValue("(ii)", isBranch, isOn); +} + +static PyObject * widgetCheckboxTreeGetSel(snackWidget * s, + PyObject * args) { + void ** selection; + int numselected; + int i; + PyObject * sel; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + + selection = (void **) newtCheckboxTreeGetSelection(s->co, &numselected); + + sel = PyList_New(0); + + if (!selection) { + return sel; + } + + sel = PyList_New(0); + for (i = 0; i < numselected; i++) { + PyList_Append(sel, PyInt_FromLong((long) selection[i])); + } + free(selection); + + return sel; +} + +void init_snack(void) { + PyObject * d, * m; + + m = Py_InitModule("_snack", snackModuleMethods); + d = PyModule_GetDict(m); + + PyDict_SetItemString(d, "ANCHOR_LEFT", PyInt_FromLong(NEWT_ANCHOR_LEFT)); + PyDict_SetItemString(d, "ANCHOR_TOP", PyInt_FromLong(NEWT_ANCHOR_TOP)); + PyDict_SetItemString(d, "ANCHOR_RIGHT", PyInt_FromLong(NEWT_ANCHOR_RIGHT)); + PyDict_SetItemString(d, "ANCHOR_BOTTOM", + PyInt_FromLong(NEWT_ANCHOR_BOTTOM)); + PyDict_SetItemString(d, "GRID_GROWX", PyInt_FromLong(NEWT_GRID_FLAG_GROWX)); + PyDict_SetItemString(d, "GRID_GROWY", PyInt_FromLong(NEWT_GRID_FLAG_GROWY)); + + PyDict_SetItemString(d, "FD_READ", PyInt_FromLong(NEWT_FD_READ)); + PyDict_SetItemString(d, "FD_WRITE", PyInt_FromLong(NEWT_FD_WRITE)); + PyDict_SetItemString(d, "FD_EXCEPT", PyInt_FromLong(NEWT_FD_EXCEPT)); + + PyDict_SetItemString(d, "FORM_EXIT_HOTKEY", PyString_FromString("hotkey")); + PyDict_SetItemString(d, "FORM_EXIT_WIDGET", PyString_FromString("widget")); + PyDict_SetItemString(d, "FORM_EXIT_TIMER", PyString_FromString("timer")); + PyDict_SetItemString(d, "FORM_EXIT_FDREADY", PyString_FromString("fdready")); + + PyDict_SetItemString(d, "KEY_F1", PyInt_FromLong(NEWT_KEY_F1)); + PyDict_SetItemString(d, "KEY_F2", PyInt_FromLong(NEWT_KEY_F2)); + PyDict_SetItemString(d, "KEY_F3", PyInt_FromLong(NEWT_KEY_F3)); + PyDict_SetItemString(d, "KEY_F4", PyInt_FromLong(NEWT_KEY_F4)); + PyDict_SetItemString(d, "KEY_F5", PyInt_FromLong(NEWT_KEY_F5)); + PyDict_SetItemString(d, "KEY_F6", PyInt_FromLong(NEWT_KEY_F6)); + PyDict_SetItemString(d, "KEY_F7", PyInt_FromLong(NEWT_KEY_F7)); + PyDict_SetItemString(d, "KEY_F8", PyInt_FromLong(NEWT_KEY_F8)); + PyDict_SetItemString(d, "KEY_F9", PyInt_FromLong(NEWT_KEY_F9)); + PyDict_SetItemString(d, "KEY_F10", PyInt_FromLong(NEWT_KEY_F10)); + PyDict_SetItemString(d, "KEY_F11", PyInt_FromLong(NEWT_KEY_F11)); + PyDict_SetItemString(d, "KEY_F12", PyInt_FromLong(NEWT_KEY_F12)); + + PyDict_SetItemString(d, "FLAG_DISABLED", PyInt_FromLong(NEWT_FLAG_DISABLED)); + PyDict_SetItemString(d, "FLAGS_SET", PyInt_FromLong(NEWT_FLAGS_SET)); + PyDict_SetItemString(d, "FLAGS_RESET", PyInt_FromLong(NEWT_FLAGS_RESET)); + PyDict_SetItemString(d, "FLAGS_TOGGLE", PyInt_FromLong(NEWT_FLAGS_TOGGLE)); +} diff --git a/mininewt/textbox.c b/mininewt/textbox.c new file mode 100644 index 000000000..dc42dc558 --- /dev/null +++ b/mininewt/textbox.c @@ -0,0 +1,410 @@ +#include <ctype.h> +#include <slang.h> +#include <stdlib.h> +#include <string.h> + +#include "newt.h" +#include "newt_pr.h" + +struct textbox { + char ** lines; + int numLines; + int linesAlloced; + int doWrap; + newtComponent sb; + int topLine; + int textWidth; +}; + +static char * expandTabs(const char * text); +static void textboxDraw(newtComponent co); +static void addLine(newtComponent co, const char * s, int len); +static void doReflow(const char * text, char ** resultPtr, int width, + int * badness, int * heightPtr); +static struct eventResult textboxEvent(newtComponent c, + struct event ev); +static void textboxDestroy(newtComponent co); +static void textboxPlace(newtComponent co, int newLeft, int newTop); +static void textboxMapped(newtComponent co, int isMapped); + +static struct componentOps textboxOps = { + textboxDraw, + textboxEvent, + textboxDestroy, + textboxPlace, + textboxMapped, +} ; + +static void textboxMapped(newtComponent co, int isMapped) { + struct textbox * tb = co->data; + + co->isMapped = isMapped; + if (tb->sb) + tb->sb->ops->mapped(tb->sb, isMapped); +} + +static void textboxPlace(newtComponent co, int newLeft, int newTop) { + struct textbox * tb = co->data; + + co->top = newTop; + co->left = newLeft; + + if (tb->sb) + tb->sb->ops->place(tb->sb, co->left + co->width - 1, co->top); +} + +void newtTextboxSetHeight(newtComponent co, int height) { + co->height = height; +} + +int newtTextboxGetNumLines(newtComponent co) { + struct textbox * tb = co->data; + + return (tb->numLines); +} + +newtComponent newtTextboxReflowed(int left, int top, char * text, int width, + int flexDown, int flexUp, int flags) { + newtComponent co; + char * reflowedText; + int actWidth, actHeight; + + reflowedText = newtReflowText(text, width, flexDown, flexUp, + &actWidth, &actHeight); + + co = newtTextbox(left, top, actWidth, actHeight, NEWT_FLAG_WRAP); + newtTextboxSetText(co, reflowedText); + free(reflowedText); + + return co; +} + +newtComponent newtTextbox(int left, int top, int width, int height, int flags) { + newtComponent co; + struct textbox * tb; + + co = malloc(sizeof(*co)); + tb = malloc(sizeof(*tb)); + co->data = tb; + + co->ops = &textboxOps; + + co->height = height; + co->top = top; + co->left = left; + co->takesFocus = 0; + co->width = width; + + tb->doWrap = flags & NEWT_FLAG_WRAP; + tb->numLines = 0; + tb->linesAlloced = 0; + tb->lines = NULL; + tb->topLine = 0; + tb->textWidth = width; + + if (flags & NEWT_FLAG_SCROLL) { + co->width += 2; + tb->sb = newtVerticalScrollbar(co->left + co->width - 1, co->top, + co->height, COLORSET_TEXTBOX, COLORSET_TEXTBOX); + } else { + tb->sb = NULL; + } + + return co; +} + +static char * expandTabs(const char * text) { + int bufAlloced = strlen(text) + 40; + char * buf, * dest; + const char * src; + int bufUsed = 0; + int linePos = 0; + int i; + + buf = malloc(bufAlloced + 1); + for (src = text, dest = buf; *src; src++) { + if ((bufUsed + 10) > bufAlloced) { + bufAlloced += strlen(text) / 2; + buf = realloc(buf, bufAlloced + 1); + dest = buf + bufUsed; + } + if (*src == '\t') { + i = 8 - (linePos & 8); + memset(dest, ' ', i); + dest += i, bufUsed += i, linePos += i; + } else { + if (*src == '\n') + linePos = 0; + else + linePos++; + + *dest++ = *src; + bufUsed++; + } + } + + *dest = '\0'; + return buf; +} + +#define iseuckanji(c) (0xa1 <= (unsigned char)(c&0xff) && (unsigned char)(c&0xff) <= 0xfe) + +static void doReflow(const char * text, char ** resultPtr, int width, + int * badness, int * heightPtr) { + char * result = NULL; + const char * chptr, * end; + int i; + int howbad = 0; + int height = 0; + int kanji = 0; + + if (resultPtr) { + /* XXX I think this will work */ + result = malloc(strlen(text) + (strlen(text) / width) + 2); + *result = '\0'; + } + + while (*text) { + kanji = 0; + end = strchr(text, '\n'); + if (!end) + end = text + strlen(text); + + while (*text && text <= end) { + if (end - text < width) { + if (result) { + strncat(result, text, end - text); + strcat(result, "\n"); + height++; + } + + if (end - text < (width / 2)) + howbad += ((width / 2) - (end - text)) / 2; + text = end; + if (*text) text++; + } else { + chptr = text; + kanji = 0; + for ( i = 0; i < width - 1; i++ ) { + if ( !iseuckanji(*chptr)) { + kanji = 0; + } else if ( kanji == 1 ) { + kanji = 2; + } else { + kanji = 1; + } + chptr++; + } + if (kanji == 0) { + while (chptr > text && !isspace(*chptr)) chptr--; + while (chptr > text && isspace(*chptr)) chptr--; + chptr++; + } + + if (chptr-text == 1 && !isspace(*chptr)) + chptr = text + width - 1; + + if (chptr > text) + howbad += width - (chptr - text) + 1; + if (result) { + if (kanji == 1) { + strncat(result, text, chptr - text + 1 ); + chptr++; + kanji = 0; + } else { + strncat(result, text, chptr - text ); + } + strcat(result, "\n"); + height++; + } + + if (isspace(*chptr)) + text = chptr + 1; + else + text = chptr; + while (isspace(*text)) text++; + } + } + } + + if (badness) *badness = howbad; + if (resultPtr) *resultPtr = result; + if (heightPtr) *heightPtr = height; +} + +char * newtReflowText(char * text, int width, int flexDown, int flexUp, + int * actualWidth, int * actualHeight) { + int min, max; + int i; + char * result; + int minbad, minbadwidth, howbad; + char * expandedText; + + expandedText = expandTabs(text); + + if (flexDown || flexUp) { + min = width - flexDown; + max = width + flexUp; + + minbad = -1; + minbadwidth = width; + + for (i = min; i <= max; i++) { + doReflow(expandedText, NULL, i, &howbad, NULL); + + if (minbad == -1 || howbad < minbad) { + minbad = howbad; + minbadwidth = i; + } + } + + width = minbadwidth; + } + + doReflow(expandedText, &result, width, NULL, actualHeight); + free(expandedText); + if (actualWidth) *actualWidth = width; + return result; +} + +void newtTextboxSetText(newtComponent co, const char * text) { + const char * start, * end; + struct textbox * tb = co->data; + char * reflowed, * expanded; + int badness, height; + + if (tb->lines) { + free(tb->lines); + tb->linesAlloced = tb->numLines = 0; + } + + expanded = expandTabs(text); + + if (tb->doWrap) { + doReflow(expanded, &reflowed, tb->textWidth, &badness, &height); + free(expanded); + expanded = reflowed; + } + + for (start = expanded; *start; start++) + if (*start == '\n') tb->linesAlloced++; + + /* This ++ leaves room for an ending line w/o a \n */ + tb->linesAlloced++; + tb->lines = malloc(sizeof(char *) * tb->linesAlloced); + + start = expanded; + while ((end = strchr(start, '\n'))) { + addLine(co, start, end - start); + start = end + 1; + } + + if (*start) + addLine(co, start, strlen(start)); + + free(expanded); + + newtTrashScreen(); +} + +/* This assumes the buffer is allocated properly! */ +static void addLine(newtComponent co, const char * s, int len) { + struct textbox * tb = co->data; + + if (len > tb->textWidth) len = tb->textWidth; + + tb->lines[tb->numLines] = malloc(tb->textWidth + 1); + memset(tb->lines[tb->numLines], ' ', tb->textWidth); + memcpy(tb->lines[tb->numLines], s, len); + tb->lines[tb->numLines++][tb->textWidth] = '\0'; +} + +static void textboxDraw(newtComponent c) { + int i; + struct textbox * tb = c->data; + int size; + + if (tb->sb) { + size = tb->numLines - c->height; + newtScrollbarSet(tb->sb, tb->topLine, size ? size : 0); + tb->sb->ops->draw(tb->sb); + } + + SLsmg_set_color(NEWT_COLORSET_TEXTBOX); + + for (i = 0; (i + tb->topLine) < tb->numLines && i < c->height; i++) { + newtGotorc(c->top + i, c->left); + SLsmg_write_string(tb->lines[i + tb->topLine]); + } +} + +static struct eventResult textboxEvent(newtComponent co, + struct event ev) { + struct textbox * tb = co->data; + struct eventResult er; + + er.result = ER_IGNORED; + + if (ev.when == EV_EARLY && ev.event == EV_KEYPRESS && tb->sb) { + newtTrashScreen(); + switch (ev.u.key) { + case NEWT_KEY_UP: + if (tb->topLine) tb->topLine--; + textboxDraw(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_DOWN: + if (tb->topLine < (tb->numLines - co->height)) tb->topLine++; + textboxDraw(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_PGDN: + tb->topLine += co->height; + if (tb->topLine > (tb->numLines - co->height)) { + tb->topLine = tb->numLines - co->height; + if (tb->topLine < 0) tb->topLine = 0; + } + textboxDraw(co); + er.result = ER_SWALLOWED; + break; + + case NEWT_KEY_PGUP: + tb->topLine -= co->height; + if (tb->topLine < 0) tb->topLine = 0; + textboxDraw(co); + er.result = ER_SWALLOWED; + break; + } + } + if (ev.when == EV_EARLY && ev.event == EV_MOUSE && tb->sb) { + /* Top scroll arrow */ + if (ev.u.mouse.x == co->width && ev.u.mouse.y == co->top) { + if (tb->topLine) tb->topLine--; + textboxDraw(co); + + er.result = ER_SWALLOWED; + } + /* Bottom scroll arrow */ + if (ev.u.mouse.x == co->width && + ev.u.mouse.y == co->top + co->height - 1) { + if (tb->topLine < (tb->numLines - co->height)) tb->topLine++; + textboxDraw(co); + + er.result = ER_SWALLOWED; + } + } + return er; +} + +static void textboxDestroy(newtComponent co) { + int i; + struct textbox * tb = co->data; + + for (i = 0; i < tb->numLines; i++) + free(tb->lines[i]); + free(tb->lines); + free(tb); + free(co); +} diff --git a/mininewt/whiptail.c b/mininewt/whiptail.c new file mode 100644 index 000000000..7a7bda280 --- /dev/null +++ b/mininewt/whiptail.c @@ -0,0 +1,232 @@ +/* a reasonable dialog */ + +#include <fcntl.h> +#include <popt.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "dialogboxes.h" +#include "newt.h" + +enum mode { MODE_NONE, MODE_INFOBOX, MODE_MSGBOX, MODE_YESNO, MODE_CHECKLIST, + MODE_INPUTBOX, MODE_RADIOLIST, MODE_MENU, MODE_GAUGE }; + +#define OPT_MSGBOX 1000 +#define OPT_CHECKLIST 1001 +#define OPT_YESNO 1002 +#define OPT_INPUTBOX 1003 +#define OPT_FULLBUTTONS 1004 +#define OPT_MENU 1005 +#define OPT_RADIOLIST 1006 +#define OPT_GAUGE 1007 +#define OPT_INFOBOX 1008 + +static void usage(void) { + fprintf(stderr, "whiptail: bad parameters (see man dialog(1) for details)\n"); + exit(1); +} + +int main(int argc, const char ** argv) { + enum mode mode = MODE_NONE; + poptContext optCon; + int arg; + const char * optArg; + const char * text; + const char * nextArg; + char * end; + int height; + int width; + int fd = -1; + int needSpace = 0; + int noCancel = 0; + int noItem = 0; + int clear = 0; + int scrollText = 0; + int rc = 1; + int flags = 0; + int defaultNo = 0; + int separateOutput = 0; + char * result; + char ** selections, ** next; + char * title = NULL; + char * backtitle = NULL; + struct poptOption optionsTable[] = { + { "backtitle", '\0', POPT_ARG_STRING, &backtitle, 0 }, + { "checklist", '\0', 0, 0, OPT_CHECKLIST }, + { "clear", '\0', 0, &clear, 0 }, + { "defaultno", '\0', 0, &defaultNo, 0 }, + { "inputbox", '\0', 0, 0, OPT_INPUTBOX }, + { "fb", '\0', 0, 0, OPT_FULLBUTTONS }, + { "fullbuttons", '\0', 0, 0, OPT_FULLBUTTONS }, + { "gauge", '\0', 0, 0, OPT_GAUGE }, + { "infobox", '\0', 0, 0, OPT_INFOBOX }, + { "menu", '\0', 0, 0, OPT_MENU }, + { "msgbox", '\0', 0, 0, OPT_MSGBOX }, + { "nocancel", '\0', 0, &noCancel, 0 }, + { "noitem", '\0', 0, &noItem, 0 }, + { "radiolist", '\0', 0, 0, OPT_RADIOLIST }, + { "scrolltext", '\0', 0, &scrollText, 0 }, + { "separate-output", '\0', 0, &separateOutput, 0 }, + { "title", '\0', POPT_ARG_STRING, &title, 0 }, + { "yesno", '\0', 0, 0, OPT_YESNO }, + { 0, 0, 0, 0, 0 } + }; + + optCon = poptGetContext("whiptail", argc, argv, optionsTable, 0); + + while ((arg = poptGetNextOpt(optCon)) > 0) { + optArg = poptGetOptArg(optCon); + + switch (arg) { + case OPT_INFOBOX: + if (mode != MODE_NONE) usage(); + mode = MODE_INFOBOX; + break; + + case OPT_MENU: + if (mode != MODE_NONE) usage(); + mode = MODE_MENU; + break; + + case OPT_MSGBOX: + if (mode != MODE_NONE) usage(); + mode = MODE_MSGBOX; + break; + + case OPT_RADIOLIST: + if (mode != MODE_NONE) usage(); + mode = MODE_RADIOLIST; + break; + + case OPT_CHECKLIST: + if (mode != MODE_NONE) usage(); + mode = MODE_CHECKLIST; + break; + + case OPT_FULLBUTTONS: + useFullButtons(1); + break; + + case OPT_YESNO: + if (mode != MODE_NONE) usage(); + mode = MODE_YESNO; + break; + + case OPT_GAUGE: + if (mode != MODE_NONE) usage(); + mode = MODE_GAUGE; + break; + + case OPT_INPUTBOX: + if (mode != MODE_NONE) usage(); + mode = MODE_INPUTBOX; + break; + } + } + + if (arg < -1) { + fprintf(stderr, "%s: %s\n", + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(arg)); + exit(1); + } + + if (mode == MODE_NONE) usage(); + + if (!(text = poptGetArg(optCon))) usage(); + + if (!(nextArg = poptGetArg(optCon))) usage(); + height = strtoul(nextArg, &end, 10); + if (*end) usage(); + + if (!(nextArg = poptGetArg(optCon))) usage(); + width = strtoul(nextArg, &end, 10); + if (*end) usage(); + + if (mode == MODE_GAUGE) { + fd = dup(0); + close(0); + if (open("/dev/tty", O_RDWR) != 0) perror("open /dev/tty"); + } + + newtInit(); + newtCls(); + width -= 2; + height -= 2; + newtOpenWindow((80 - width) / 2, (24 - height) / 2, width, height, title); + + if (backtitle) + newtDrawRootText(0, 0, backtitle); + + if (noCancel) flags |= FLAG_NOCANCEL; + if (noItem) flags |= FLAG_NOITEM; + if (scrollText) flags |= FLAG_SCROLL_TEXT; + if (defaultNo) flags |= FLAG_DEFAULT_NO; + + switch (mode) { + case MODE_MSGBOX: + rc = messageBox(text, height, width, MSGBOX_MSG, flags); + break; + + case MODE_INFOBOX: + rc = messageBox(text, height, width, MSGBOX_INFO, flags); + break; + + case MODE_YESNO: + rc = messageBox(text, height, width, MSGBOX_YESNO, flags); + break; + + case MODE_INPUTBOX: + rc = inputBox(text, height, width, optCon, flags, &result); + if (!rc) fprintf(stderr, "%s", result); + break; + + case MODE_MENU: + rc = listBox(text, height, width, optCon, flags, &result); + if (!rc) fprintf(stderr, "%s", result); + break; + + case MODE_RADIOLIST: + rc = checkList(text, height, width, optCon, 1, flags, &selections); + if (!rc) { + fprintf(stderr, "%s", selections[0]); + free(selections); + } + break; + + case MODE_CHECKLIST: + rc = checkList(text, height, width, optCon, 0, flags, &selections); + + if (!rc) { + for (next = selections; *next; next++) { + if (!separateOutput) { + if (needSpace) putc(' ', stderr); + fprintf(stderr, "\"%s\"", *next); + needSpace = 1; + } else { + fprintf(stderr, "%s\n", *next); + } + } + + free(selections); + } + break; + + case MODE_GAUGE: + rc = gauge(text, height, width, optCon, fd, flags); + break; + + default: + usage(); + } + + if (rc == -1) usage(); + + if (clear) + newtPopWindow(); + newtFinished(); + + return rc; +} diff --git a/mininewt/whiptcl.c b/mininewt/whiptcl.c new file mode 100644 index 000000000..f137d791c --- /dev/null +++ b/mininewt/whiptcl.c @@ -0,0 +1,296 @@ +#include <string.h> +#include <stdlib.h> + +#include "dialogboxes.h" +#include "newt.h" +#include "popt.h" +#include "tcl.h" + +enum mode { MODE_NONE, MODE_MSGBOX, MODE_YESNO, MODE_CHECKLIST, MODE_INPUTBOX, + MODE_RADIOLIST, MODE_MENU }; + +#define OPT_MSGBOX 1000 +#define OPT_CHECKLIST 1001 +#define OPT_YESNO 1002 +#define OPT_INPUTBOX 1003 +#define OPT_MENU 1005 +#define OPT_RADIOLIST 1006 + +static char * setBacktext(ClientData data, Tcl_Interp * interp, + char * name1, char * name2, int flags); +static char * setHelptext(ClientData data, Tcl_Interp * interp, + char * name1, char * name2, int flags); +static char * setFullButtons(ClientData data, Tcl_Interp * interp, + char * name1, char * name2, int flags); + +static int wtFinish(ClientData clientData, Tcl_Interp * interp, int argc, + char ** argv) { + newtFinished(); + + return TCL_OK; +} + +static int wtInit(ClientData clientData, Tcl_Interp * interp, int argc, + char ** argv) { + newtInit(); + newtCls(); + + newtPushHelpLine(""); + + Tcl_TraceVar(interp, "whiptcl_backtext", + TCL_TRACE_WRITES | TCL_GLOBAL_ONLY, setBacktext, NULL); + Tcl_TraceVar(interp, "whiptcl_helpline", + TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY, + setHelptext, NULL); + Tcl_TraceVar(interp, "whiptcl_fullbuttons", + TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY, + setFullButtons, NULL); + + Tcl_SetVar(interp, "whiptcl_helpline", "", TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "whiptcl_fullbuttons", "1", TCL_GLOBAL_ONLY); + + return TCL_OK; +} + +static int wtCmd(ClientData clientData, Tcl_Interp * interp, int argc, + char ** argv) { + enum mode mode = MODE_NONE; + poptContext optCon; + int arg; + const char * optArg; + const char * text; + const char * nextArg; + char * end; + int height; + int width; + int noCancel = 0; + int noItem = 0; + int scrollText = 0; + int rc = 0; + int flags = 0; + int defaultNo = 0; + char * result; + char ** selections, ** next; + char * title = NULL; + struct poptOption optionsTable[] = { + { "checklist", '\0', 0, 0, OPT_CHECKLIST }, + { "defaultno", '\0', 0, &defaultNo, 0 }, + { "inputbox", '\0', 0, 0, OPT_INPUTBOX }, + { "menu", '\0', 0, 0, OPT_MENU }, + { "msgbox", '\0', 0, 0, OPT_MSGBOX }, + { "nocancel", '\0', 0, &noCancel, 0 }, + { "noitem", '\0', 0, &noItem, 0 }, + { "radiolist", '\0', 0, 0, OPT_RADIOLIST }, + { "scrolltext", '\0', 0, &scrollText, 0 }, + { "title", '\0', POPT_ARG_STRING, &title, 0 }, + { "yesno", '\0', 0, 0, OPT_YESNO }, + { 0, 0, 0, 0, 0 } + }; + + optCon = poptGetContext("whiptcl", argc, argv, optionsTable, 0); + + while ((arg = poptGetNextOpt(optCon)) > 0) { + optArg = poptGetOptArg(optCon); + + switch (arg) { + case OPT_MENU: + if (mode != MODE_NONE) rc = -1; + mode = MODE_MENU; + break; + + case OPT_MSGBOX: + if (mode != MODE_NONE) rc = -1; + mode = MODE_MSGBOX; + break; + + case OPT_RADIOLIST: + if (mode != MODE_NONE) rc = -1; + mode = MODE_RADIOLIST; + break; + + case OPT_CHECKLIST: + if (mode != MODE_NONE) rc = -1; + mode = MODE_CHECKLIST; + break; + + case OPT_YESNO: + if (mode != MODE_NONE) rc = -1; + mode = MODE_YESNO; + break; + + case OPT_INPUTBOX: + if (mode != MODE_NONE) rc = -1; + mode = MODE_INPUTBOX; + break; + } + } + + if (arg < -1) { + /* this could buffer oveflow, bug we're not setuid so I don't care */ + interp->result = malloc(200); + interp->freeProc = TCL_DYNAMIC; + sprintf(interp->result, "%s: %s\n", + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(arg)); + + return TCL_ERROR; + } + + if (mode == MODE_NONE) { + interp->result = "no dialog mode was specified"; + return TCL_ERROR; + } else if (rc) { + interp->result = "multiple modes were specified"; + return TCL_ERROR; + } + + if (!(text = poptGetArg(optCon))) { + interp->result = "missing text parameter"; + return TCL_ERROR; + } + + if (!(nextArg = poptGetArg(optCon))) { + interp->result = "height missing"; + return TCL_ERROR; + } + height = strtoul(nextArg, &end, 10); + if (*end) { + interp->result = "height is not a number"; + return TCL_ERROR; + } + + if (!(nextArg = poptGetArg(optCon))) { + interp->result = "width missing"; + return TCL_ERROR; + } + width = strtoul(nextArg, &end, 10); + if (*end) { + interp->result = "width is not a number"; + return TCL_ERROR; + } + + width -= 2; + height -= 2; + newtOpenWindow((80 - width) / 2, (24 - height) / 2, width, height, title); + + if (noCancel) flags |= FLAG_NOCANCEL; + if (noItem) flags |= FLAG_NOITEM; + if (scrollText) flags |= FLAG_SCROLL_TEXT; + if (defaultNo) flags |= FLAG_DEFAULT_NO; + + switch (mode) { + case MODE_MSGBOX: + rc = messageBox(text, height, width, MSGBOX_MSG, flags); + break; + + case MODE_YESNO: + rc = messageBox(text, height, width, MSGBOX_YESNO, flags); + if (rc == DLG_OKAY) + interp->result = "yes"; + else + interp->result = "no"; + if (rc == DLG_ERROR) rc = 0; + break; + + case MODE_INPUTBOX: + rc = inputBox(text, height, width, optCon, flags, &result); + if (!rc) { + interp->result = strdup(result); + interp->freeProc = TCL_DYNAMIC; + } + break; + + case MODE_MENU: + rc = listBox(text, height, width, optCon, flags, &result); + if (!rc) { + interp->result = strdup(result); + interp->freeProc = TCL_DYNAMIC; + } + break; + + case MODE_RADIOLIST: + rc = checkList(text, height, width, optCon, 1, flags, &selections); + if (!rc) { + interp->result = strdup(selections[0]); + interp->freeProc = TCL_DYNAMIC; + } + break; + + case MODE_CHECKLIST: + rc = checkList(text, height, width, optCon, 0, flags, &selections); + + if (!rc) { + for (next = selections; *next; next++) + Tcl_AppendElement(interp, *next); + + free(selections); + } + break; + + case MODE_NONE: + /* this can't happen */ + } + + newtPopWindow(); + + if (rc == DLG_ERROR) { + interp->result = "bad paramter for whiptcl dialog box"; + return TCL_ERROR; + } + + Tcl_SetVar(interp, "whiptcl_canceled", (rc == DLG_CANCEL) ? "1" : "0", + 0); + + return TCL_OK; +} + +static char * setBacktext(ClientData data, Tcl_Interp * interp, + char * name1, char * name2, int flags) { + static char blankLine[81] = " " + " "; + + newtDrawRootText(0, 0, blankLine); + newtDrawRootText(0, 0, Tcl_GetVar(interp, "whiptcl_backtext", + TCL_GLOBAL_ONLY)); + + return NULL; +} + +static char * setHelptext(ClientData data, Tcl_Interp * interp, + char * name1, char * name2, int flags) { + char * text = Tcl_GetVar(interp, "whiptcl_helpline", TCL_GLOBAL_ONLY); + + if (!text) + text = ""; + else if (!strlen(text)) + text = NULL; + + newtPopHelpLine(); + newtPushHelpLine(text); + + return NULL; +} + +static char * setFullButtons(ClientData data, Tcl_Interp * interp, + char * name1, char * name2, int flags) { + char * val = Tcl_GetVar(interp, "whiptcl_fullbuttons", TCL_GLOBAL_ONLY); + int rc; + int state; + + if ((rc = Tcl_ExprBoolean(interp, val, &state))) { + Tcl_FreeResult(interp); + return "whiptcl_fullbuttons may only contain a boolean value"; + } + + useFullButtons(state); + + return NULL; +} + +int Whiptcl_Init(Tcl_Interp * interp) { + Tcl_CreateCommand(interp, "whiptcl_finish", wtFinish, NULL, NULL); + Tcl_CreateCommand(interp, "whiptcl_init", wtInit, NULL, NULL); + Tcl_CreateCommand(interp, "whiptcl_cmd", wtCmd, NULL, NULL); + + return TCL_OK; +} diff --git a/mininewt/windows.c b/mininewt/windows.c new file mode 100644 index 000000000..792d3ed76 --- /dev/null +++ b/mininewt/windows.c @@ -0,0 +1,275 @@ +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "errno.h" +#include "newt.h" + +static void * newtvwindow(char * title, char * button1, char * button2, + char * button3, char * message, va_list args) { + newtComponent b1, b2 = NULL, b3 = NULL, t, f, answer; + char * buf = NULL; + int size = 0; + int i = 0; + int scroll = 0; + int width, height; + char * flowedText; + newtGrid grid, buttonGrid; + + do { + size += 1000; + if (buf) free(buf); + buf = malloc(size); + i = vsnprintf(buf, size, message, args); + } while (i >= size || i == -1); + + flowedText = newtReflowText(buf, 35, 5, 5, &width, &height); + if (height > 6) { + free(flowedText); + flowedText = newtReflowText(buf, 60, 5, 5, &width, &height); + } + free(buf); + + if (height > 12) { + height = 12; + scroll = NEWT_FLAG_SCROLL; + } + t = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP | scroll); + newtTextboxSetText(t, flowedText); + free(flowedText); + + if (button3) { + buttonGrid = newtButtonBar(button1, &b1, button2, &b2, + button3, &b3, NULL); + } else if (button2) { + buttonGrid = newtButtonBar(button1, &b1, button2, &b2, NULL); + } else { + buttonGrid = newtButtonBar(button1, &b1, NULL); + } + + newtGridSetField(buttonGrid, 0, 0, NEWT_GRID_COMPONENT, b1, + 0, 0, button2 ? 1 : 0, 0, 0, 0); + + grid = newtCreateGrid(1, 2); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, t, 0, 0, 0, 0, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, buttonGrid, + 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + newtGridWrappedWindow(grid, title); + + f = newtForm(NULL, NULL, 0); + newtFormAddComponents(f, t, b1, NULL); + + if (button2) + newtFormAddComponent(f, b2); + if (button3) + newtFormAddComponent(f, b3); + + answer = newtRunForm(f); + newtGridFree(grid, 1); + + newtFormDestroy(f); + newtPopWindow(); + + if (answer == f) + return NULL; + else if (answer == b1) + return button1; + else if (answer == b2) + return button2; + + return button3; +} + +int newtWinChoice(char * title, char * button1, char * button2, + char * message, ...) { + va_list args; + void * rc; + + va_start(args, message); + rc = newtvwindow(title, button1, button2, NULL, message, args); + va_end(args); + + if (rc == button1) + return 1; + else if (rc == button2) + return 2; + + return 0; +} + +void newtWinMessage(char * title, char * buttonText, char * text, ...) { + va_list args; + + va_start(args, text); + newtvwindow(title, buttonText, NULL, NULL, text, args); + va_end(args); +} + +void newtWinMessagev(char * title, char * buttonText, char * text, + va_list argv) { + newtvwindow(title, buttonText, NULL, NULL, text, argv); +} + +int newtWinTernary(char * title, char * button1, char * button2, + char * button3, char * message, ...) { + va_list args; + void * rc; + + va_start(args, message); + rc = newtvwindow(title, button1, button2, button3, message, args); + va_end(args); + + if (rc == button1) + return 1; + else if (rc == button2) + return 2; + else if (rc == button3) + return 3; + + return 0; +} + +/* only supports up to 50 buttons -- shucks! */ +int newtWinMenu(char * title, char * text, int suggestedWidth, int flexDown, + int flexUp, int maxListHeight, char ** items, int * listItem, + char * button1, ...) { + newtComponent textbox, listbox, result, form; + va_list args; + newtComponent buttons[50]; + newtGrid grid, buttonBar; + int numButtons; + int i, rc; + int needScroll; + char * buttonName; + + textbox = newtTextboxReflowed(-1, -1, text, suggestedWidth, flexDown, + flexUp, 0); + + for (i = 0; items[i]; i++) ; + if (i < maxListHeight) maxListHeight = i; + needScroll = i > maxListHeight; + + listbox = newtListbox(-1, -1, maxListHeight, + (needScroll ? NEWT_FLAG_SCROLL : 0) | NEWT_FLAG_RETURNEXIT); + for (i = 0; items[i]; i++) { + newtListboxAddEntry(listbox, items[i], (void *) i); + } + + newtListboxSetCurrent(listbox, *listItem); + + buttonName = button1, numButtons = 0; + va_start(args, button1); + while (buttonName) { + buttons[numButtons] = newtButton(-1, -1, buttonName); + numButtons++; + buttonName = va_arg(args, char *); + } + + va_end(args); + + buttonBar = newtCreateGrid(numButtons, 1); + for (i = 0; i < numButtons; i++) { + newtGridSetField(buttonBar, i, 0, NEWT_GRID_COMPONENT, + buttons[i], + i ? 1 : 0, 0, 0, 0, 0, 0); + } + + grid = newtGridSimpleWindow(textbox, listbox, buttonBar); + newtGridWrappedWindow(grid, title); + + form = newtForm(NULL, 0, 0); + newtGridAddComponentsToForm(grid, form, 1); + newtGridFree(grid, 1); + + result = newtRunForm(form); + + *listItem = ((long) newtListboxGetCurrent(listbox)); + + for (rc = 0; result != buttons[rc] && rc < numButtons; rc++); + if (rc == numButtons) + rc = 0; /* F12 or return-on-exit (which are the same for us) */ + else + rc++; + + newtFormDestroy(form); + newtPopWindow(); + + return rc; +} + +/* only supports up to 50 buttons and entries -- shucks! */ +int newtWinEntries(char * title, char * text, int suggestedWidth, int flexDown, + int flexUp, int dataWidth, + struct newtWinEntry * items, char * button1, ...) { + newtComponent buttons[50], result, form, textw; + newtGrid grid, buttonBar, subgrid; + int numItems; + int rc, i; + int numButtons; + char * buttonName; + va_list args; + + textw = newtTextboxReflowed(-1, -1, text, suggestedWidth, flexDown, + flexUp, 0); + + for (numItems = 0; items[numItems].text; numItems++); + + buttonName = button1, numButtons = 0; + va_start(args, button1); + while (buttonName) { + buttons[numButtons] = newtButton(-1, -1, buttonName); + numButtons++; + buttonName = va_arg(args, char *); + } + + va_end(args); + + buttonBar = newtCreateGrid(numButtons, 1); + for (i = 0; i < numButtons; i++) { + newtGridSetField(buttonBar, i, 0, NEWT_GRID_COMPONENT, + buttons[i], + i ? 1 : 0, 0, 0, 0, 0, 0); + } + + subgrid = newtCreateGrid(2, numItems); + for (i = 0; i < numItems; i++) { + newtGridSetField(subgrid, 0, i, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, items[i].text), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(subgrid, 1, i, NEWT_GRID_COMPONENT, + newtEntry(-1, -1, items[i].value ? + *items[i].value : NULL, dataWidth, + items[i].value, items[i].flags), + 1, 0, 0, 0, 0, 0); + } + + grid = newtCreateGrid(1, 3); + form = newtForm(NULL, 0, 0); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, textw, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, subgrid, + 0, 1, 0, 0, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttonBar, + 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + newtGridAddComponentsToForm(grid, form, 1); + newtGridWrappedWindow(grid, title); + newtGridFree(grid, 1); + + result = newtRunForm(form); + + for (rc = 0; rc < numItems; rc++) + *items[rc].value = strdup(*items[rc].value); + + for (rc = 0; result != buttons[rc] && rc < numButtons; rc++); + if (rc == numButtons) + rc = 0; /* F12 */ + else + rc++; + + newtFormDestroy(form); + newtPopWindow(); + + return rc; +} |