tkcvs-8.2.3.orig/0000755000175000017500000000000011664612512011765 5ustar timtimtkcvs-8.2.3.orig/LICENSE.txt0000644000175000017500000004325411664612512013620 0ustar timtim GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. tkcvs-8.2.3.orig/tkdiff/0000755000175000017500000000000011664612512013234 5ustar timtimtkcvs-8.2.3.orig/tkdiff/LICENSE.txt0000644000175000017500000004325411664612512015067 0ustar timtim GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. tkcvs-8.2.3.orig/tkdiff/Delta.ico0000644000175000017500000030253611664612512014772 0ustar timtim hV ЈО00 Ј%f@@ (B;€€ (6}(  @џџџџџџџџџџџџџџџџџџџџџџџџџџџm[7 oS џџџџџџџџџџџџџџџT›бЋžРяЙЃПюЙЃПюЙЃПюЙЃПюЙЃПюЙЃПюЙ”…˜Х›ŒљЅЊЪпЃПюЙЃПюЙЃПюЙ ПюЙaЄйБrГioЏфџТђџТђџТђџТђџТђџ›Едџšk`џТёџТђџТђџТђџТђџyЖщџ"yЙ‰ cЁ2„ТбwДфџ|Зцџ|Зцџ|Зцџ€ЉЭџ›P4џ@џЄФџ|Зцџ|Зцџ|ЗцџyЕхџ<ŒШх cЂџџџiЊIVžж§{Жхџ|Зцџ|Зцџ•k`џЅ@џ|A)џЄџ|Зцџ|Зцџ{Зхџ`ЅкџnЏgџџџџџџџџџ%{КГrБтџ|Зцџ€ЃУџžE$џЉE!џ„N1џzГрџ|Зцџ|ЗцџvДфџ/‚РЭ `›џџџџџџџџџ fІ%G”ЮїzЖхџœgYџЌF!џЏI%џІI џzЏйџ|Зцџ{ЖхџSœд§iЋ?џџџџџџџџџџџџџџџuЖ‰jЌоџЂ}xџВK!џЃK,џ›J*џ|Џкџ|ЗцџqАсџ${КЉџџџџџџџџџџџџџџџџџџ dЃ@ŒХуЈhPџСT#џБS0џŒЅџ|ЕуџzЖхџE’ЬѓdЄ!џџџџџџџџџџџџџџџџџџџџџDw eЖw?џЫe(џХX-џЈZAџ{ГсџhЋоџtДџџџџџџџџџџџџџџџџџџџџџџџџdcjБ~Mїаz5џЭp2џЖR*џsЁЪџ9ŠЦп cЂџџџџџџџџџџџџџџџџџџџџџџџџџџџЏ€bХЯ‰DџЫ‰>џЌiDџ\“Уџl­]џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊo[SЪ‹WџаˆCџ—џ-ПУ`šџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџВ•‡НsR™аŒMџŸ{mџ7oŸGџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџРŽ‚ ЪwGбЫ‹VџˆWTmџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМsWОvS‘_Zџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ( @ €џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭбмYiŒ wT ‚a@#бЬЫџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџR‘Уq|БлsˆАеs‡ЊЭs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬsˆЉЬu“‚–VWэ‰B.ї”gcї•ЎЮ…‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЊЬs‡ЋЮs‚Еоs^›Ьs xЖЧkАчџ ЩќџЏЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џЈБмџˆJ=џ­ЃАџЕХѓџВШњџАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џАЩ§џЏЩ§џЇЩќџ}М№џ0…Ус cЃ/${ЛѓuЕшџ›ЪњџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџЄШђџЇlSџ ˆ‘џЁЬћџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџŸЬќџžЫћџ‚Няџ8ŠЧ§ eЇe ^” gЉ<ЪџrБуџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~ЙшџЙцџЊ‡|џ•5 џŽ„‘џЙшџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~Йшџ~ЙшџwЕхџNšгџmЏЙ ^˜ џџџ cЁoБч\ЃйџxЕхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ}ЗфџƒŽџЂAџЁ>џ‹D'џІЭџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЖхџhЊоџ${Лљ cЂIџџџџџџџџџ fЇa0…Ф§lЎрџ{Жхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџИцџ‰€ƒџ›?џЂ>џЃ>џЄ@џ†~xџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџsВтџD’Юџ jЌ b›џџџџџџџџџeŸ k­ЭSеџvДфџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџŒž­џЄH'џЂ>џЈAџ—>џЂJџUFџ}Етџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџyЖхџaІлџuЖэ c -џџџџџџџџџџџџџџџbЁG&}ПљgЊоџzЖхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‹Œ˜џ›9џЄ?џЅBџS џc_dџ|†џИуџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗхџoЏсџ8ŠЩџ fЇџџџџџџџџџџџџџџџџџџZ gЈЏE“ЯџtВуџ{Зцџ|Зцџ|Зцџ|ЗцџƒГмџQDџŸ:џЈD џЉDџ™Eџl{wџ|Жхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџwЕфџVŸзџnАй `›џџџџџџџџџџџџџџџџџџџџџ eЂ)tЕёbЇлџyЖхџ|Зцџ|Зцџ~Жтџ…niџЋO"џ ;џЌF$џЊE"џІBџg6џtЈЮџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{Жхџk­пџ+Рџ eЄ[џџџџџџџџџџџџџџџџџџџџџџџџџџџ fЇy8‹ЩџoАсџ{Жхџ|Зцџ†”ЈџЂH%џГL'џЅ?џЏI%џ­G$џЋF#џœEџxЇЭџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџtГуџL˜вџlЎГ `˜џџџџџџџџџџџџџџџџџџџџџџџџџџџ dЂoАпZЂйџwЕфџ|Жхџ’ljџЖR*џБI$џЈCџАL&џЏJ%џЋF#џЅRџyЉЭџ|Зцџ|Зцџ|Зцџ|ЗцџzЖхџfЉнџ#zЛѕ dЃ?џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ dЅY.ƒУ§k­пџ}ЗфџЌ“‘џЙaAџВJ џЋFџГN)џВL(џЕN(џœAџ}ЇЬџ|Зцџ|Зцџ|Зцџ{ЗцџrБтџA‘Эџ iЌ—_Ёџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ bž kЌУM™гџuГфџ‹Гиџ–N8џЗK џДO%џАN*џxF5џ…YEџ—?џ|ЈЯџ|Зцџ|Зцџ|ЗцџxЕхџ]ЃкџsЕч aŸ'џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ dЄ?!yКїgЊнџ‡’џЖR,џРQ#џНR$џЙS,џ‘K;џƒДџ‚џВнџ|Зцџ|Зцџ{ЗцџoЏсџ4‡ХџdЅwџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ _™ hЉ—T–Ъџ’bSџШe/џЦW!џРU$џОW-џНV-џ›voџƒЗтџ|Зцџ|Зцџ|ЗцџvДфџSеџnАЭ _™ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ cЂ%D~­ыЁgAџЭ{5џЬg'џШY#џХV,џРU-џМW*џs|џ}Еуџ|ЗцџzЖхџjЌпџ*€ПћcЄSџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџTo‡ƒ mJџЬ=џЭx4џЬ]$џЩ\-џХX-џЧW+џŽI1џxЉдџ{ЗцџtГуџI–аџj­Џašџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџb`nGmM§ЫFџЭˆ=џбa&џЮk1џЫ[-џЪX,џžJ(џuœСџyЖхџdЉнџ!yЙѓ cЂ7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџjn_‘gWуЧ‘MџЫ‘Fџеo)џЮ†8џЮt3џЭ_.џЄH(џnƒЁџqБсџ>ŽЫџ gЊџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџg{|•onБУSџЩ’Qџжz,џЮ‹=џЬŠ;џЯx5џ­Y*џn}Ÿџ\ЃйџqГх cŸ#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ—}‹cЗ‚ZџЩWџеˆ=џв‡;џР‰EџŸm[џ•gUџsІбџ3†Ф§cЄoџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŸ‰ž ЊoWЭЪ[џЭ‰Pџе|7џЭ’LџЎ†mџЊЮџQœдџmЏЧ`šџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌn^qХ‚SџЪ‘_џиˆ;џХ‹NџЅ{fџh“Йџ(~ОљcЃOџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџВ•‡МmMПФzSћеŒGџЬŽOџГt]џW‘РџkЋЉcšџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅœЕЕjNŸЪŠWџгGџЧƒPџЊeHџst‹mЪЪШџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџР'ТwRёкˆDџЫ”`џЦ~Fџ‰YXЭввЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџИ`>gШnBёбŽSџЪ‹`џ†SLнЏŽЃ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџаЯвЛmNEПjCхУ~Wџ\TљZ|%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџА–”Еv\™cWУg|џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ(0` €%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭбм_wВ[K-Z %{Y 3 ˆ'рркџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџU‹Ж;k›У=yЁС={ŸМ=x‘І=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=—ЎIЋ›АpjпuB/яŠ4ѕ7!љ›Бƒ—ЋCwЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЄ=wЅ=z–­={ЅЧ=qžС=_’М;0~ИхaЈоё…Рёё˜Уђё›Рюё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›РяёœРюё“™Пѓ~KFћ[5џЄ{yџЂ’ЊџЎАХџІСъљœРяё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё›Ряё™РюёХѕёqДчёAЧыhЈ•6ŠЩѕpДщџšЩќџЏЪўџДЩўџЕЩџџЕЩџџЕЩўџЕЩџџЕЩџџЕЩўџДЩўџЕЩўџДЩўџДЩўџЕЩўџДЩўџДЩўџЕЩўџДЩўџДЩўџЕЩўџДЩўџИФ№џW^џ†N>џЅЂРџОЯќџЖЪўџДЩўџДЩџџДЩўџДЩўџДЩџџДЩўџДЩўџДЩџџДЩўџДЩўџДЩџџДЩўџДЩўџГЩџџЄЪўџ‚ПѓџM›ж§sДС biЌС8‹ЩџuЖщџ—ЩљџЅЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџЊЬњџЏ“•џ GџЉžБџЉЭќџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџІЭўџŸЬќџ„П№џSžзџqДя cЃGџџџ`ŸWoБыM™гџsГхџ†СяџŠУђџŠУђџŠУёџŠУђџŠУђџŠУёџŠУђџŠУђџŠУђџŠУђџŠУђџŠУђџŠУђџŠУђџŠУђџŠУђџ‘ЦёџЊЉДџœEџL6џŸНнџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџŠУёџ‰Уёџ|Йъџ^Ѕлџ'~Пћ bЂ› a џџџ _– fЉ“%}Пџ[ЂйџsВуџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ}Зцџ’ЋУџ­_=џ“7џ™A(џ‡šџ}Зхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџwЕхџgЊоџ;ЫџkЏй až#џџџџџџџџџ cЂ7 iЋя@ЭџiЋоџxЕхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{Зхџ|Зцџ{Зхџ{Зхџ|Зцџ{Зхџ{Зхџ|Зцџ|ЗхџˆЈЪџ–^KџІ?џЅAџ;џuK9џ~Њаџ|Жхџ|Жхџ{Жхџ|Зцџ|Жхџ{Жхџ|Зцџ|Жхџ{Жхџ|Зцџ|Жхџ{Жхџ{ЗцџzЗхџpАтџUžжџtЗћcЄƒdŸџџџџџџџџџ b› cЁ…!zЛљTžжџqБсџyЖхџ|Зцџ|Зцџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{Жцџ|Жфџ†ЌЮџŽYIџž>џЁ?џŸ<џЁ?џ Aџz‚џƒЗуџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{ЗхџuДуџbЇмџ5ˆЧџ hЉЧ b›џџџџџџџџџџџџџџџ cЂ! j­г5‰ЧџfЉнџvДфџ{Зцџ|Зцџ|Зхџ|Зцџ{Жхџ|Зхџ{Жхџ{Жхџ|Зхџ{Жхџ}ЗцџŠЉШџˆX>џœ>џЃ=џЃ>џЃ>џЃ>џЅ@џ~`Bџ‰Азџ|Жхџ|Зхџ{Жхџ|Жхџ|Зхџ{Жхџ|Жхџ|Зхџ{Жхџ|Жхџ|ЗхџyЖхџnЏсџK˜вџrЖѓ cЃ_џџџџџџџџџџџџџџџџџџiž cЂopГїQœеџoЏсџzЖхџ|Зцџ|Зцџ|Зцџ{Зхџ|Зцџ{Зхџ{Зхџ|Зцџ{Зхџ…Дзџ‘iVџЊH%џЁ= џЄ@џЊBџ ?џ­LџДOџvH'џ…žНџ|Жхџ{Жхџ|Зцџ|Жхџ{Жхџ|Зцџ|Жхџ{Жхџ|Зцџ|Жхџ{ЖхџuГуџ`Ілџ*€Сџ dІЛf џџџџџџџџџџџџџџџџџџџџџ a eЈБ0…Хџ_ЅлџtВуџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ|ЗцџАЯџ”G%џЈ>џ <џЇDџЅ>џy+џL,џu>&џv4џ‡•Ќџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{Жцџ{ЗцџwЕхџiЌпџE”ЯџnБх aŸCb›џџџџџџџџџџџџџџџџџџџџџa— ažOoДёG”аџkЌпџyЖхџ{Зхџ|Зцџ{Жхџ|Зхџ{Жхџ{Жхџ|Зхџ{Жхџ‰ЌЫџ‹Aџž9џЂ=џЈCџЅC џu)џ4.)џv„џ€†•џГдџ|Жхџ|Зхџ{Жхџ|Жхџ|Зхџ{Жхџ|Жхџ|Зхџ{ЖхџzЖхџrБтџYЁиџ"{Оџ eІ™ a› џџџџџџџџџџџџџџџџџџџџџџџџџџџZbЂБ$|Н§XЁиџtВуџzЗхџ|Зцџ{Зхџ|Зцџ{Зхџ{Зхџ|Зцџ†Зсџ„jrџ™>џž6 џІCџЈCџЉDџЅEџR?"џ†ІЗџ~Ицџ|Зцџ|Жхџ{Жхџ|Зцџ|Жхџ{Жхџ|Зцџ|Жхџ{Жхџ{ЗхџwЕхџfЉнџ9ŒЩџ gЉх `›1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ a1kЎл:ŒЪџhЋоџwЕфџ{Зцџ{Жцџ|Зцџ|Зцџ|Жцџ€Зуџ~ŽЁџ‘IџЈD!џ9џЉD#џЊE"џЈD!џЌFџEџMQ=џtЊвџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ{ЗцџzЖхџpАсџPœдџtЗљ dЃqcœџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџeœcЁ‡tЖџVŸзџqБсџzЗхџ{Жхџ|Зхџ{Жхџ|Зхџ‰ŸГџˆH.џВN"џЉD џЁ;џ­G$џЌG#џЊE"џЈD!џ AџY3џpЂТџ{Жхџ|Жхџ|Зхџ{Жхџ|Жхџ|Зхџ{Жхџ|Жхџ{ЗхџvДуџcЈмџ2†Хџ eЅЭ eŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ `š hЊН3‡ЧџbЇмџuГуџ{Зхџ|Зцџ{Зхџ„Џеџ„[RџІH#џДM'џЈAџЈCџЏI$џЎH%џЌG$џЋF#џЊE!џŒAџu Тџ|Зцџ|Жхџ{Жхџ|Зцџ|Жхџ{Жхџ|Зцџ{ЖхџxЖхџk­пџH–бџpГяaŸGgšџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ[‘ dЅYqЕљM™гџnЏрџyЖхџ{Зцџ|Зцџ–ŸЏџ•H'џЕN(џЕO*џЁ;џ­F"џАJ%џЎI%џ­H$џЌF#џЎI"џЅWџv Рџ{Жцџ|Зцџ|Зцџ{Жцџ|Зцџ|Зцџ{ЖцџzЗхџsВтџ^Єкџ&~Пџ gЊЇ `› џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ bž eЅП/„У§_ЅлџuГуџ{Зхџ|Жфџu€џЉL*џЙT+џЙO*џ›6џАL&џАM&џБL&џЏI%џ­I$џ­H#џ™LџzЂТџ{Жхџ|Жхџ|Зхџ{Жхџ|Жхџ|Зхџ{ЖхџxЖхџjЌпџD“Яџl­ы bŸGџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџaЂ9nБщB’ЮџjЌпџxЕхџЗтџЅ……џНs[џФ\2џИR)џ 8џЏK$џГO(џВL'џБK'џАK&џЎI$џ‘<џ~ Рџ|Жфџ|Жхџ{Жхџ|Зцџ|Жхџ{ЖхџzЖхџqБтџUŸжџ zМ§ dЅƒ_Ёџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ aŸ eЈ!zМ§XЁиџrБтџ{Зхџ™ЛдџŸšЊџ—F(џНP&џЇ?џДQ)џЕQ+џЇF&џЋN.џШe6џЕP*џ•<џ{ŸТџ|Жхџ|Зцџ|Зцџ{Жцџ|Зцџ{ЗцџwДфџeЉнџ7ŠШџ hЌл cЂ)џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ cŸ7 jЌй:ŒЪџgЊоџwДфџ…Йуџ’z}џЃG%џЛR*џАEџЗR+џЖS,џ=#џ[MEџcY[џˆH)џ‰4џ|ЂФџ{Жхџ|Жхџ|Зхџ{Жхџ|ЖхџyЖхџoЏсџO›дџsЖї dЂu bžџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ aš dЅssЖљQœдџpАсџ‹ЃНџ—TBџНR,џСU*џИIџОU*џКU-џЋK*џwKKџ‰“Йџˆ‡šџ’deџ‚­дџ|Зцџ|Зхџ|Зхџ|Зцџ{ЗхџuГуџaІлџ-‚ТџfЈН c џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ` eЅХ2‡ХџhЈкџŽ}ŠџЅ\4џШY,џФU*џРPџПT)џМV,џНT,џЊO2џxe{џ‡Днџ†Гнџ}Зфџ|Зцџ|Зцџ|Зцџ{ЗхџyЖхџk­рџG•аџnАэ`Q `џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ b QrДёl ЫџŒ_WџЗk5џЬa.џШ[*џХSџРV*џРW-џНW-џСX,џВcAџ”ЉМџ€Зфџ|Зхџ{Жцџ|Жхџ|Зхџ{ЗхџsВуџZЂйџ%}О§ eІЁ dšџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ _Ÿ hЈЋ_ŒДџYBџФ€8џЭo2џЬg-џЫZџШZ)џХV,џТV,џОS-џУ\+џ˜T=џ‘•Џџ~Жфџ|Зцџ|Зхџ{ЗхџxЕхџiЌпџ?Ьџ j­у cЃ3џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ^žežAQy›х—]IџФ‰<џЭ…8џЭx3џЫ]џЫZ)џЧW,џХW,џТX-џУW,џПQ&џxNSџzЋйџ|Зцџ|ЗцџyЖхџpАсџUŸжџwЙћ bЁ a џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ>m…YkЇ’cQџФ‹CџЬ<џЭ…9џЭ`$џЬc+џЩ_/џШZ-џФX-џУW,џМS+џƒJ2џvЁШџ{Жхџ{ЖхџvДфџeЈнџ5ˆЧџ j­Э `џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџbjlaaowaNџЦŒFџЫ‘EџЬŒ>џЯi*џв`(џЮk1џЬ`.џЩX,џЧX,џУT*џH'џt˜Лџ{ЖхџzЖхџnЏсџP›дџqДћ cЃk!pЂџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџg^g=‡^TэП†KџЫHџЬ’DџЯz4џбf&џЮ|5џЮo2џЭc/џЬX-џФV,џ•D(џkv‘џzДтџtГуџ_Ѕлџ/„Фџ fЇГ bžџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџhu_‚``ЯДPџЩ“NџЫ“IџЯƒ?џкo%џЮˆ9џЭƒ7џЮu3џЭc/џШZ-џ”A$џij‚џwБпџk­пџE”ЯџnВя bЂEџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџg{|‡m}ЇЌwR§Щ‘SџЩ’OџЬ‘Gџйo"џЯ‰<џЬ=џЭ„7џЯv4џЬf0џœO%џg`zџqЉйџZЂиџ#{Н§ eЅЉ cџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџДŸЃ[žjXљЧWџШ‘TџЩQџпv џвˆ;џЬ‘CџХ‹>џУ~>џЪƒDџV*џxБџkЉлџ<Ыџ iЌн aŸ-џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЕ­Н-“jjЧСŠYџЩYџЩ‘Tџз‡<џж}4џЭ“HџЗ‚EџWNџ™{‚џ†_\џvЄЭџUžжџvИћb ‡`œџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЦЦд—pxaГtPљЪ\џЩ’XџЮ„Mџйp.џЪLџЯ“JџВ„cџžКџyВоџaЇлџ3‡Цџ gЈЧ`šџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџˆp~ЋfOлЩŠXџЪ‘^џЮŠQџк1џЬ‘LџЪ‘NџПŠPџiaџqœУџL˜вџpГѕdЅ_џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‰‰І­o`‹СuJ§Ъ‘_џЪ’_џк‡<џг‹CџРƒMџЅo\џˆ›Бџ_žбџ,Сџ fІЕ d џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџБ‚-КfEЫТnDџЪ…YџдŽKџж‰<џШŒSџЗqVџ›ЖџC’ЮџmАщ b ;џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџШЯЬН‰v5­wrЅКrSяЭ”\џз‹>џЬ“UџФuFџАhLџkx‘џkЃ—`— џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџВpX ИfCЩФ„Zџж‰EџбMџЪ\џУwAџ­\<џˆp~ŸЏЈБџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџььїС‡yMПpLѓдWџй‡=џЫ”cџЪ‘SџТp:џƒRNѓІ‡AџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџИЏГ Лa@УШuPћм€BџЮ’ZџЫ“dџЩ~NџK<§›wŠAџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџВmT!АaC•Щl=§жŒFџЫcџЩbџ…MBџk|eџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЩИЕ#Йb@{Тg<§ФpGџФˆ]џ•`Qџ€TkЉџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЕU+ИsSWЗ|oЕПlIѓ gP§zN_ЏџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЁwd А†lЁi[Й‹eg_џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ(@€ BџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭбмf|ТO[bgF'ƒ_ 3zX AˆilKбЬЫџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџYŒВl“Зn”Бu•Ўs–Ўol}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆp‹!Ј Ж3КАФmkuЎЭcX]чw/эŠ/ё}'ѕ‡W\щИБИ[l}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆl}ˆm~‰r‰—tšИt“Ћj”ЗdД>‚ЖП`ŸгЧuБпЧ…ЙфЧŒЛфЧ‹БжЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧАжЩ‹Ћп_jї•R=џ“A$џ‡G?џšb]џЁzsџЉЂВћ•ВйгŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧŒБзЧ‹ГиЧ‹ОъЧ~ЕрЧkЊлЧNФУlЊу:ŽЫ§lГщџŠЦљџŸЩ§џІШћџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЊЪќџІЙъџ€UdџŒ<џ‡\6џЭТРџКТфџ­ПэџЏЦђџЎЪћџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЉШќџЈШћџЃШћџ—Ъ§џ|ПђџVЂлџ#yИё bžctЗлE”бџsЕъџ“ШћџЊЪўџЕЪџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџПЯќџ‘rџ:џ†_ZџЂЁЮџНЮўџЗЩўџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЖЩџџБЪџџŸЪ§џ…Сєџ^Інџ-ƒФї iЊ› b dЄƒrЕћK˜гџuЗъџ’ШјџІЭўџЊЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџГа§џМЖЩџЌY(џ—F,џЋЈСџЎЭћџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЋЭџџЉЭўџžЬќџ„РђџcЊрџ.ƒФџ hЌз bž1џџџ b 1bЃе%|Н§X йџuЕчџ‰Фѓџ“Ъјџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ˜ЬљџЗдџ•bUџЁ=џbbџЏбѓџ–Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”Ыљџ”ЫљџШїџ€НюџhЋрџ@Эџj­љbЂ‡^”џџџ ^” `š[lБы5ˆШџ\ЃйџqБтџ}Кщџ€МъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџ‚МъџЈЬщџЖjџЂ8џƒ+ џІ…Šџ”УшџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџМъџЛъџyЖцџhЋоџJ—вџyМџ dЅ­ ^˜џџџџџџџџџ X eЇЋtЙџF•аџfЉнџtВуџzЗцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ…ЕнџЄˆƒџЅGџ;џ 5џ‰E8џt…џ~Зхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџxЕхџnЏсџZЂйџ-ƒФџ jЏщaŸM Yџџџџџџџџџџџџ cЂUfЉѕ,‚Уџ[ЂйџoЏсџxЖхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‚Зтџ“ŒџЁIџЄ=џЊEџЅAџ‰3џjZSџЏдџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџtГуџfЊнџG•бџpГ§cЅЗdŸџџџџџџџџџџџџџџџ bažЋoВ§D”ЯџdЈнџsВуџzЗхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџƒИпџŒ…“џ‘C&џЂ@џ ?џ;џš6џЈCџ?џ‚|•џ~Жфџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџwЖфџnЎсџVŸзџ+‚Тџ dЅы bSџџџџџџџџџџџџџџџџџџb™ c- iЌЭ'~РџQœеџj­пџvДфџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ€Бзџ‹џŽG+џž< џЄ?џ >џ <џЃAџЁ>џЈDџnV7џŸЖдџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџxЖхџqБсџaЇлџ<ЫџpДћeЅw bš џџџџџџџџџџџџџџџџџџџџџfЊ bЁ‡nГљ9ŒЩџaІмџrБтџyЖхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџˆНшџ‡џ‰Aџ>џЅ= џЃ=џЃ?џЄ>џЅ>џ <џЇAџoKџŸЙџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџvДфџk­пџQœеџ zНџ fЉе a 1џџџџџџџџџџџџџџџџџџџџџџџџџџџf %cЅгwКџP›дџjЌпџvДфџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџЗтџ’žџ“Q6џЊH"џЂ> џЁ=џЇBџЇAџЂ<џЄDџВP!џИOџ]Aџ–…–џ|Жфџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџqБсџ`Ѕлџ9‹Ъџ hЌѕ cЂ{ižџџџџџџџџџџџџџџџџџџџџџџџџџџџbœ ažu i­я7ŠЩџ\ЃйџpАсџxЕхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџŠЙоџ—vXџŸFџДC&џ ; џЅCџЅ?џАAџž6џxDџƒAџIџ‹4џ~\]џ€Вкџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЖхџuГфџhЋоџL™гџxЛ§ cЃПbœ-џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ _šbЃЏuЙ§F•аџfЉнџsВуџzЖцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‰Куџ’bWџ™7џЄ<џž9џЈBџЊFџž=џz*џ# џD5!џg=?џ`8џ‡m~џŠЙнџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџwЕхџnЎрџYЁиџ/…Хџ hЌѕ `žMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџa— cŸS jЏх*€ТџW зџmЎрџwЕхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ„ГлџŒcKџ‘8џŸ8 џЂ=џЈCџЈE џІC џŠ2џ% џahmџ‚ЂХџˆ•­џ”Двџ‚Йфџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗхџsВуџdЈмџE“ЯџqЖ§eІЇ cџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџZ a БnБџ>ЭџcЇмџtВуџzЗцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‡Йфџ‚†ЁџŠ?"џž<џž5 џЅCџЈBџЇEџЊDџЏGџN( џ^c[џ‘МкџИцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџxЕхџmЎрџSжџ(€СџcЅэ `œKT†џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ a9 fЇЯ"{ОџPœеџk­пџvДуџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџŒЄРџŠ?.џЁAџЅ@џ›6џЉE"џЋD"џЈD џЊDџЎG џК\џPAџp‹џ{Есџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџyЖхџqБтџaІлџ9ŒЪџoГѕaŸ‡cœџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџdœ eЄu iЎї8‹Щџ`ІлџqБтџyЖхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџЙхџˆЎЮџv^Wџ‘Zџ­I&џ =џŸ:џЉD#џЌG$џЉD"џЈD"џЉCџ<џ€9џ21џkšИџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџvДфџjЌпџO›дџwКџ fЈб eœ%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ e'a ЯvЙџNšгџiЋоџvДуџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџБиџ‡pgџ‘<џЛM џБM(џ :џЃ=џЎG$џЌG$џЌG#џЊF#џЉE"џЈE"џ—=џR3џm›Зџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџyЖхџqАсџ]Єйџ7ŠЩџ gЉ§ bžyџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџhš_I jЎщ0†ЦџYЁиџmЎрџwЕхџ{Зцџ|Зцџ|Зцџ|Зцџ€Ифџ‚„›џ‰G+џЉH#џЖN&џБM'џ6џЋF#џЎH$џЏJ%џ­G$џЌG$џЋF#џЉD"џЇE џ{>џr™Зџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџsВуџeЉнџF•бџvЙ§cЄŸ `šџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџZ dЅЇrЗџD“ЯџfЊоџtГуџzЖхџ|Зцџ|Зцџ|Зцџ—Ппџ~VEџžBџИP(џВM(џБI'џ›3џАK(џАJ&џАI&џ­H$џ­G$џЌF#џЎH"џГO#џ˜Cџv•Дџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџxЕхџnЏрџYЁйџ*Уџ jЏч_›MџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџdЃCeЈч)€СџXЁиџmЎрџwЕфџ{Зцџ|Зцџ|ЗцџœЁМџŽP1џАM(џДO)џЙR,џЌF$џš5џЕL*џЏK%џЏJ%џЏJ&џЎH%џ­G$џЇA!џП[%џ™gџržДџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџyЗхџsВтџdЉнџD“ЯџoВ§dЅЃeЁ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџcž bŸoВїB‘ЯџbЇмџrБтџzЗхџ|Зцџ}Жфџ‰џ–B-џНW-џКU,џКP*џЄ=џ8џДR+џБN'џВM&џБL&џЏI&џЎK%џЋF#џЌJ$џ<џ}™Зџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџwЕфџmЎрџSжџ)€Сџ eІн cžMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ`  eЇб#|ОџP›дџjЌпџvДфџ{ЗцџЗ🛈ˆџЕhRџЩjDџЦZ.џКT,џЅ>џЁ9џВO)џГO)џГN(џБK'џАJ&џЎI%џ­H$џЈI$џ‹8џ€šЖџ|Есџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџyЖхџqАсџ`Ілџ:ŒЪџnБћaЂo` џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџUЊ cЅs mГљ7‰Шџ]ЄкџpАтџyЖхџ~ИфџЉГЙџЙААџЋukџ­M(џЛT(џ­BџІAџДP)џДP)џВM)џ­I'џЙQ,џФZ-џЖN)џЎJ&џ:џ|˜Зџ|Жфџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџuГфџiЋоџNšдџwК§h­Щ ` 'џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ až%aЃЯwЙџH—вџhЋоџvГфџ{Зцџ†Мчџ‹ДмџŠhiџ“? џМP(џВCџ­GџИU-џИT-џЊG'џŽ9"џ“O6џЏh?џЖ\4џДP'џŠ6џ{—Зџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџpАсџZЂйџ2ˆЧџ hЋѓ`Ÿ{ d—џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџdŸ c Y iЌч,ƒУџYЁиџnЎсџwЕхџ}Зхџ ЇИџˆG2џВK%џМU-џГEџГLџИT.џКU-џЄG(џj:"џUWYџilzџF2&џŸH%џ-џ}œКџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗхџtВуџfЉнџD“ЯџsЗџ eЄЋdŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ aš eІЇnГћB’ЮџdЉнџtВуџ†Геџ’opџœG*џРS,џТW,џЗJџИMџОW-џКV-џБP,џ’B)џjR\џ‰”Кџ‰–Вџ|YYџRKџƒІЩџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџwЕхџnЎрџW зџ(Рџ gЊя cЂCџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ aŸCdІч'ПџTžжџqЏпџ”ЂЛџ”WHџДT-џЪY,џХU,џСOџОNџТV-џМV-џОS,џОR+џŠG>џwk‹џ„­йџБбџ—ЋЪџ„Зсџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџsВуџcЇмџA‘ЮџmЏ§aЁ› aŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ _™ ažqmАћ;ŽЫџmЇжџqџ–T8џНk2џЪY,џЦY-џРRџПTџМS,џНV-џЛW-џКS-џМV.џ†NJџ‚Уџ‚Кцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗхџvДфџiЋпџP›дџ$}ПџdЄЫ `™+ \џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ a# fЈЫ${МџƒЉЪџb\џІ`8џЫn2џЭb/џЪ].џЩSџХVџСV-џТV-џПX-џОW-џСY,џХb.џŸŒџ‘Жжџ~Зхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџyЖхџpБтџ_Ѕкџ6‰ШџmБї b uџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ \›dЅgpАёy—ЗџOCџЕv8џЭ6џЭg0џЫc.џа_џЧWџШZ,џЦV,џУW,џУT-џМS-џЩ^,џžQ'џkoџ”ЌЯџЗфџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџuГфџiЋпџM™гџuЙџeЈУ _Ÿџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ a'hЃСa„Ј§”ZNџИ}:џЭŒ;џЮy5џЭw3џЪc(џШYџЫ[-џЦW,џУW-џУW-џПW.џПU,џЬY*џœG,џ~p…џ~Амџ|Зцџ|Зцџ|Зцџ{ЗцџxЕхџpАсџ[Ѓйџ4ˆЧџ fЉё ažo_œџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ)m—?YuŒѓ‹TOџЖ~>џЬ>џЭˆ9џЭ€6џЯh-џЫXџЪW.џШW,џЩY,џШZ,џУX-џТX-џзX)џЂE џn@Fџv Ьџ|Зцџ|Зцџ|ЗцџzЗхџsВтџdЈнџD”ЯџsЖ§` ЁašџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџMn}\h}С‰]WџИ„FџЫŽBџЬŽ=џЬˆ:џЭq3џЭYџЭn2џЪd0џШ].џШY,џУW-џТW-џСT,џЌS+џ{L4џt™Лџ|Зхџ|Зцџ{ЗцџwЕфџm­рџV зџ%}Пџ kАп `œ=џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџain bap›ƒYRџН„DџЬFџЬ’FџЬ>џЯu4џЯ[ џвb+џЮl1џЭa/џЫ[,џЧW,џФX-џЫX+џГP&џ‚I*џs“Гџ|Жхџ|ЗцџzЗхџsВуџcЈмџB‘ЮџmБџcЃ›!pЁ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџbjbc\km|UUљЗJџЪŽGџЬŽFџЬ“FџЮŒ>џаa$џвg*џЮu3џЮk1џЮa.џЫY-џЬW,џЬZ-џИP)џ‰A'џksŽџzВрџ{ЗцџvДфџk­пџQœеџ&~Пџ dЅб d 9џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ\gSlh]7|XXгЈvO§ЧŽLџЫ’KџЬ’BџЬBџгf*џбk&џЮ†7џЮ6џЯr3џЮk2џЬ\.џЭW,џЛR+џ@'џf\nџxЏкџxЖхџpАсџ^Єкџ6ŠШџlАѕ_Ÿc ^™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџgxc!yaiНŸjR§ТPџЪ”MџЬ”JџЪ’Lџеw2џнr%џЯ†9џЭŒ:џЮ~5џЮu3џЭg0џЭa/џОT+џŠ<"џeVhџvЊеџuГуџgЊнџK˜вџtИ§fЊС _™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџg{{ yk‡Ѕ˜eWћПŠRџЪ‘RџЪ‘NџЪ“LџбŒ8џкh џа‡;џЬ>џЭ;џЭ~6џЮv4џЮn2џФ_.џŽF"џcO`џpœЬџpАсџYЁиџ/„Хџ hЋё dЂs _џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯЬЫ?—b[чК…SџЪ“UџШ‘SџШ‘UџЮŽCџпgџв„;џЬ@џЬ@џЫŒ;џЯ7џв{9џЬn2џ–R%џmeџvЃбџeЉнџA‘ЮџpДџ cЂЁ a‘џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓєѓ3‚aqх­sTџШXџШ‘TџШTџЭJџц|џз…6џЬ“EџЬ’EџЗ€Aџ­j>џВ€[џМCџŽJ)џ|ОџtБрџVŸзџ%}ПџdЈп bЄ5џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџнно†j~[Ђvd§ХYџЪ‘[џЩWџЩ’Tџз9џкr,џЭ’GџЮ–IџБ}DџŒQCџ’{џŽq|џblџyЃЪџbІлџ?ЭџlЎљ až—bџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЦЦд –z‘!œoiЫМ~QџЪ‘]џЪ’WџЧ’\џЯ€Jџмh'џЫLџЫŽIџа•JџЙ†YџІџ„ГкџuВрџhЋнџNšдџ!zНџcЄХ`šџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџˆbjoВfE§Ъ[џЪ]џЪ“[џг€?џсf!џЮ‘JџЩOџа•Lџа›ZџЂl]џ“’ЄџxБмџ]Єйџ3‡Чџ kАћcЂ_џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŽ‚™9ДdGнЦPџЫ“aџЪ_џЫ^џзŽ;џд‹=џЩ‘RџФOџЖJџ kPџniqџc˜ЪџK˜вџsЗџdЄН`Ёџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџˆ“Џ ­qdЅНkB§ШŒ\џЫ‘_џЪ“_џйŒAџо|3џЭ”PџМ{HџІdPџšžЎџuЊдџZЁзџ1†Хџ eЇщ d WfЂџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‘™‘Џ‹{GИdDлТoEџХtIџЭbџв”Pџмz0џЯ˜MџЦ‡PџКlMџ”œ­џfЇйџA‘ЮџrЕћbЁŸcš џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЩЭЩ РwW?Зr\пЙfIяХ|VџЯ—bџй‹;џвŒEџЪŽXџУl?џК^:џ{‹Ѓџ;ЗџkЋпdœ/џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџдли –В5ЊqkcДlJџЦ`џеŽHџз=џЬ“YџЩŠXџТi;џГa=џ‚luџ8n“Y‘џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЖqY-ЛeBщТ{TџЭ^џмƒ5џЯ‘SџЫ‘_џШ‡QџСn9џВX5џhqЭЉŸЋMЪЪШџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџььїТ‚pqЛeAїЯ–eџл„?џжŽBџЫ”eџЫ“YџЩŠFџОf5џ|I>љЁ€™™ввЬ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџДŸЄ%ЙiNЫФsRџи“[џп}5џЭ•_џЫ”eџЫ“[џХtAџ{?)§yŒЃџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџПЧЫМZ4‰М_9зЦvVџнx;џвŒKџЬ’fџЭ‘fџЫQџy>!џ”p­џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ’”Ѕžqd5ЎY9ЩЪn=џк>џЬŒ\џЬgџЦbџ‹M<џƒ\jЭЏŽЃ!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџаЯвП™9Кb@ЛТk>џУkCџЧ~TџУbџœeTџ{LXя‹f‰CџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџДU+Н_5ЋКpTыПuXџУxPџЊuWџzGOљtOpMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊЏЁ А’“;ДpasНd@їЊqSљ„LOщ|\{Aџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ“ˆ~ЌŸ‹uЇpfЅŽ`Z…‰џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ(€ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭбмЭбм{Я#VoИ-NkŒ/QN@7^DEoH U~Y a‰e mŠg {mK‹}\Q›–x‹ЭЦЦCрркџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЖЖЖЖЖЖЭбмЭбм[{ЯVoИЩNkŒгTNKзa5лu% сƒ. хŒ2 щŒ1ыt' яzDCёŽnгбЪШiццп%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџV‹В9\Г;m’Ж?k”И?j”Ж?s”Ќ?u“Ћ?u—В?sžО?sŽŸ?q„?n‹?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?t‚ŽCЃžГY­ЁКmЏЂЛ‰ИЉОН€­пab’ѓm_kїxJ9љ€9ћ†,ћŽ1§.§„&џs! џD?џ—nyуАЁЏ‡–˜ЅWl}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?l}ˆ?n‹?q„?sŽŸ?sžО?u—В?u“Ћ?s”Ќ?j”Ж?k”И?m’Ж?\Г;F„В‹L‰Зb—Ф•hžЬ•nЄЮ•xЇЫ•}ЉЫ•­в•ƒГл•‚ЅУ•€ŸК•€И•œИ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•ƒœИ™œЙГšЊЭ”ƒ™хЁ€ћŠivџ}U[џŽQAџŽA(џŽ<џ@&џ–G)џ•G,џF1џ‡K:џ–jiџЃ‹šэœ ПЕ‹ ОŸœЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œЗ•œИ•€И•€ŸК•‚ЅУ•ƒГл•­в•}ЉЫ•xЇЫ•nЄЮ•hžЬ•b—Ф•L‰З5}Гё=„ЛѓVœећeЊсћrЕшћ}Мьћ†РяћŽХєћ”Щњћ’Пъћ‘Мчћ“Нъћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ•Кцћ•šТ§xn‹§^GTџu92џœBџДI џЌCџ†2џy=?џˆczџ™tŠџЇˆ˜џД ЇџНЙЛџЛШиџАЬюџ˜Ры§•Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ”Оыћ“Нъћ‘Мчћ’Пъћ”ЩњћŽХєћ†Ряћ}МьћrЕшћeЊсћVœећ=„ЛѓnЊё tБѕ8‹ЩџS кџjБчџyОёџ†Хїџ’ЪќџЪўџžЧјџ ЦїџЂЧјџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЇЩљџЈУяџ‰y џwJXџw3"џ›Aџ‰C џ]'џЩЇŠџФЌЈџИЎПџЊАдџЉБйџЊЖоџЎОуџБЦъџАЫєџЌЬњџЄШљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЃЧљџЂЧјџ ЦїџžЧјџЪўџ’Ъќџ†ХїџyОёџjБчџS кџ8‹Щџ tБѕ dЃЯjЊз!|Нћ?’Яџ\ІпџqЗэџТіџЩќџžЫџџЄЫџџЊЪџџ­ЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЖаџџЎРёџ››аџ‰[mџy7,џy.џЈQ-џ†W<џƒylџбзлџжрѕџЬмџџКбџџГЭџџАЫџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџЏЪџџ­ЪџџЊЪџџЄЫџџžЫџџЩќџТіџqЗэџ\Іпџ?’Яџ!|НћjЊз `š‰ eЂŸoГэ*‚Т§G•бџcЉпџvИэџ‡Уїџ—ЪўџЃЫџџЌЪџџВШџџЖЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЪзўџЋЅеџqL‹џ•A/џ}4 џd6џœglџ•|Ђџ˜šЯџХвќџНЭџџЗЩџџЗЩўџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЗЩџџЖЩџџВШџџЌЪџџЃЫџџ—Ъўџ‡УїџvИэџcЉпџG•бџ*‚Т§oГэ eЂŸ `š% dЂA gЉЃuЙп)‚ХџC“Яџ]ЅмџrЕщџ„Уѕџ‘ШњџŸЫ§џЋЬџџГЫџџЖЫџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЦвѕџЌ›Љџ~>6џ=џ†8џpGCџЉ˜ЄџЏБеџЏОђџЛЭўџЙЬџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЗЪџџЖЫџџГЫџџЋЬџџŸЫ§џ‘Шњџ„УѕџrЕщџ]ЅмџC“Яџ)‚ХџuЙп gЉЃ dЂAџџџ b  b i i­ХrЖ§)€ТџH•бџaЈпџuИыџƒСѓџ’Шљџ Ь§џЊЮџџЏЭџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџВЭџџЛвџџЭлџџБЋПџЅxiџЅHџ9 џ‘I8џqџГКбџЕЪђџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџАЬџџЏЭџџЊЮџџ Ь§џ’ШљџƒСѓџuИыџaЈпџH•бџ)€ТџrЖ§ i­Х b i b џџџbš c›9aЂЉ eЉѕqЖџ4ˆЧџPœжџg­сџvИъџ…Тђџ“ЪљџЮ§џЃЮўџЅЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџЈЯўџЗдњџжођџwvџЁP'џЧUџ•5џœabџЕЇУџИЯђџЎв§џІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџІЮџџЅЮџџЃЮўџЮ§џ“Ъљџ…ТђџvИъџg­сџPœжџ4ˆЧџqЖџ eЉѕaЂЉc›9bš џџџ Nzbš bЂwcЅгeЉџyЛџ;ЫџW йџiЌсџwЖшџ‚ПяџЧїџ”Ьћџ™Ю§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џ›ЭїџЖпџuƒЎџЎb>џЙKџž<џ|1џž}„џШдьџЊгљџŸаќџšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џšЮ§џ™Ю§џ”ЬћџЧїџ‚ПяџwЖшџiЌсџW йџ;ЫџyЛџeЉџcЅг bЂwbš Nzџџџџџџџџџ bŸAa “` яmАћ(СџG•бџYЁйџhЋрџuЕцџ~Мьџ‡ТёџЦєџШѕџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШѕџ’ЩіџšЬїџІПйџЃšЂџ‹ZOџЉJџЄ<џ‰4 џ†KDџž‘ЂџВдєџ˜Ыѕџ‘ЩіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШіџШѕџЦєџ‡Тёџ~МьџuЕцџhЋрџYЁйџG•бџ(СџmАћ` яa “ bŸAџџџџџџџџџџџџџџџ ^” ^™O _›П fЊыtИџ4‰ЩџI–вџ[ЂйџiЌпџqБтџ{Ичџ‚Ныџ„Пэџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ‡РюџФэџЉЯюџЕЊЎџЙz^џДFџš6 џˆ-џ0 џšfcџЂŸГџžЪэџ‰Сюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ…Рюџ„Пэџ‚Ныџ{ИчџqБтџiЌпџ[ЂйџI–вџ4‰ЩџtИџ fЊы _›П ^™O ^”џџџџџџџџџџџџџџџ ^”cœdœIiЎХoДџ yМџ8‹ЪџNšдџ`ІлџiЋпџqАтџwДхџzИчџ|Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ‚Кчџ˜ФчџамуџН‹yџЎM&џВ;џ‰,џ|$ џˆ+ џЊmeџГЅЎџЈЩпџ„Лцџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ}Ичџ|ИчџzИчџwДхџqАтџiЋпџ`ІлџNšдџ8‹Ъџ yМџoДџiЎХdœIcœ ^”џџџџџџџџџџџџџџџџџџ ^• ^•dІ™ i­хqЗџ)€УџA‘ЮџXЁиџdЈнџmЎрџsВуџwЕхџzЗцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ†Йрџ™БЦџИ„џБ_<џ @џŠ8 џ6џ•0џš.џšK<џŽccџџ}Ўиџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџwЕхџsВуџmЎрџdЈнџXЁиџA‘Юџ)€УџqЗџ i­хdІ™ ^• ^• џџџџџџџџџџџџџџџџџџџџџHH `oeЇУ kБџvКџ3ˆЧџO›еџ_ЅлџiЌпџqАтџvДфџyЗцџ{Ицџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ~Ицџ‡Лшџ‹ЋЩџ”‹џЌW4џЉCџœ= џ‰Aџš>џІ=џЌ;џ•7џj/%џPAHџ…ЇЧџ„Иуџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџyЗцџvДфџqАтџiЌпџ_ЅлџO›еџ3ˆЧџvКџ kБџeЇУ`oH HџџџџџџџџџџџџџџџџџџџџџџџџџџџdŸC dЄeЈџ kАџ$}ПџC“ЯџYЁкџfЉоџnЏсџtВфџxЖхџ{Ицџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‚ЙчџŸХьџŽŠџ‡PBџКIџЉ< џЇ@џТXџЅBџЇE џМW+џЂ=џQџ "$џЂЈДџ—Ммџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџxЖхџtВфџnЏсџfЉоџYЁкџC“Яџ$}Пџ kАџeЈџ dЄdŸCџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџdŸ cЃccЄнeЉїsЗџ3ˆЩџOšеџ^ЅкџiЋоџqАтџvДфџyЖцџ{Ицџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‚ЙцџŠДиџ„•џ’`Wџ˜H%џЌF џЄ?џž;џŸ=џЃ?џž9џ•1џЂ<џ’9џy;!џpcTџv•Њџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџyЖцџvДфџqАтџiЋоџ^ЅкџOšеџ3ˆЩџsЗџeЉїcЄн cЃcdŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ b 9 b ЃaЂчkЎџ$|ПџB’ЯџTŸжџbЇмџmЎрџsВуџwЕхџzИцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ„Йрџ‰ЋЪџ џŠUOџ–F'џЂDџЃAџЁ@џ<џ–6џЁ=џ›7џŽ+ џЃ>џЎFџЄGџj=#џtsџƒЅбџ|Жхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџzИцџwЕхџsВуџmЎрџbЇмџTŸжџB’Яџ$|ПџkЎџaЂч b Ѓ b 9џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџb™bšW `œЫ fІљqЕџ4‰ШџI—вџZЂйџiЋпџpАтџuДуџyЗхџzИцџ{Ицџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ}Зхџ’Йгџ’Ž˜џŠQKџŒ9џš; џЅA џŸ=џЁ@џЃ@џЁ=џŸ<џŸ<џЁ=џЄBџЋGџЈFџˆ3џˆV]џŠ…Їџ‚Есџ~Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџzИцџyЗхџuДуџpАтџiЋпџZЂйџI—вџ4‰ШџqЕџ fІљ `œЫbšWb™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџb™ bšce hЊЙ mГџ#|Оџ9‹ЪџNšдџ`ІлџiЌоџpБсџvДуџxЖхџzЗцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ}Ихџ}Ихџ~ЕуџЂЏџ˜iaџ3џ—< џ < џЃ=џЇDџЂ@џž=џœ;џЂ=џЄAџЃDџŸ>џЅBџЊGџЅAџvJ*џjncџЊЛдџŽКтџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџxЖхџvДуџpБсџiЌоџ`ІлџNšдџ9‹Ъџ#|Оџ mГџ hЊЙce bšb™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ fЁ fЁ/eЇ hЋёrЗ§*УџA‘ЮџXЁиџdЉнџlЎрџsВуџwЕхџzЗцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ…ГкџƒЁЙџx‚…џ•i]џ–L0џŒ4џ =џЂ= џЁ<џЇBџЃ?џŸ=џž=џЄ>џЅ@џЂBџŸ=џЁ>џІBџЎGџuCџd]?џГЏСџ‘Жмџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџwЕхџsВуџlЎрџdЉнџXЁиџA‘Юџ*УџrЗ§ hЋёeЇ fЁ/ fЁџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџfЊfЊ` kbЂг kБїxМџ3ˆЧџO›дџ_ЅлџiЌпџpБтџvДфџyЖхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ~Ицџ‚КчџЇУџŠ{џuEџ‡;џ”:џŸ?џІ>џЃ=џ =џЅ=џЃ>џЂ>џЂ@џІ?џЅ>џЂ=џЁ<џŸ;џЁ=џ­Gџy@ џiT/џЎœ­џЏеџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџvДфџpБтџiЌпџ_ЅлџO›дџ3ˆЧџxМџ kБїbЂг` kfЊfЊџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџfЊ bŸA dЃ›hЎэoДџ#{ОџB’ЮџW иџfЉоџnЏрџtГуџxЖхџzИцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ…Мшџ›Чьџ–‰˜џŽXHџˆ=џ H џЄDџŸ;џІ<џЅ=џЃ>џЄ<џЄ?џЅAџІ@џЂ<џЄ=џЋBџЃ>џ <џЂ=џЌDџvD џeW)џЌ‹˜џŽЉЭџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzИцџxЖхџtГуџnЏрџfЉоџW иџB’Юџ#{ОџoДџhЎэ dЃ› bŸAfЊџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџfŸ eЂUdЇЯ gЋѕqЕџ4ˆШџM™гџ_ЅлџiЋпџpАсџvДфџyЗцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ~Жтџ†­Яџš•Ѓџe]џ”K.џЅHџЂE!џЂAџЃ=џЁ; џЃ>џІBџЇBџЅBџЄ@џЃ=џ <џЃ>џЊCџВL!џЏLџЌIџМL џlAџLJ#џЈzџŽЃХџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЗцџvДфџpАсџiЋпџ_ЅлџM™гџ4ˆШџqЕџ gЋѕdЇЯ eЂUfŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџf™™fŸ! aŸЅbЄу i­џ$}РџA‘ЮџVŸзџcЇнџlЎрџsВтџxЖхџzЗцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ†ЙнџŽЂВџ‘eTџŒL,џžI#џСU2џЂ>џž:џІ@џŸ=џЃ@џЇCџЈCџЈDџЋAџГ:џ”@џ–EџЏJџЗT(џВTџАQџШV"џs@џL<џ›a`џŠ—Вџ~Дпџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџzЗцџxЖхџsВтџlЎрџcЇнџVŸзџA‘Юџ$}Рџ i­џbЄу aŸЅfŸ!f™™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџcœbœq bЁЧ fЇџqЕџ2‡ЧџK˜гџ[ЃйџhЋоџpБтџvДфџyЖцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ™РзџЄЈ›џ‡XџIџЌL(џжZGџІ7џœ4џЅA џЅCџЅC џЅ@џЄ8џ­EџФGџъ5 џtGџvWџХ`*џЅK,џGџЇM џХ\"џЂDџ2џ=.џ„€ŒџƒЋЬџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖцџvДфџpБтџhЋоџ[ЃйџK˜гџ2‡ЧџqЕџ fЇџ bЁЧbœqcœџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџbœbœ' aŸu dЅУ kВљ$}Рџ;ŽЬџO›еџ_ЅлџjЌпџpБтџuДфџyЖхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ™ЗжџЅ——џŒBџ™AџЉE$џЗF/џ 6џœ7 џЃAџІCџЇEџЇEџЇ@џЇ@џЈ;џЋ-џq/џT.џQ,џc6џj=џt@џ”>џŒ1џ}* џt3+џ„‚‘џ‡Ббџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџuДфџpБтџjЌпџ_ЅлџO›еџ;ŽЬџ$}Рџ kВљ dЅУ aŸubœ'bœџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ _œ7 bЃ‰fЋёsИџ,ƒФџB’ЯџV зџdЈнџlЎрџrБуџwДхџzЗцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ—ЗмџЃ’џŠ1 џ™7џЄ>џЈ?!џ6 џœ8 џЁ?џЇBџЈEџЈGџЊEџЄ?џ—7џ…,џp" џ@ џ џ:,џO:"џ\;-џr3/џo.џk/џs;AџŽŒџ‘Иеџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџwДхџrБуџlЎрџdЈнџV зџB’Яџ,ƒФџsИџfЋё bЃ‰ _œ7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџa— `œ]_ŸчjЎћwМџ3ˆШџNšдџ_ЅлџjЌпџqАсџuГфџyЖцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ–УчџžžЊџ~' џŒ- џž:џЎG"џŸ5 џœ4 џЁ<џЈ@џЉBџЊDџЌH$џІD!џš=џˆ5џn% џ<џџ50џS?4џdAKџjFXџTA,џRC$џzTnџЁœ­џЁОеџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖцџuГфџqАсџjЌпџ_ЅлџNšдџ3ˆШџwМџjЎћ_Ÿч `œ]a—џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџa— cž3 c Ѓ iЎсpЗџ"|ПџAЮџVŸзџcЈмџl­рџsВуџwЕхџzЗцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ’НоџšœŸџƒ9џŠ6џ—:џЅ>џœ4џž7 џЅAџЈD!џЈDџЈDџЉF#џЈE"џЁ@џ“6џr(џ= џџMI@џkwƒџx“Еџ‡Јџƒ‚џ…„ŽџšЂЗџ™ЖвџŽМсџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџzЗцџwЕхџsВуџl­рџcЈмџVŸзџAЮџ"|ПџpЗџ iЎс c Ѓ cž3a—џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ eŸ eŸ]eЈП iЎљpЕџ3‡ЧџJ—вџZЂйџgЊоџoАсџuДфџyЗхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџˆЄТџŽ}|џ‡; џŽ9џ—:џ 9 џ›5џŸ:џЇDџЈCџЈCџЈDџЈF!џЉE!џЇBџЂ?џƒ-џG џ џWSOџvЃџЖуџ‹БеџŒЈЫџŽЉЬџ•Нмџ‹Муџ‚Кцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЗхџuДфџoАсџgЊоџZЂйџJ—вџ3‡ЧџpЕџ iЎљeЈП eŸ] eŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ[\’_ž—bЃыfЌџ$}Пџ<ЭџPœеџaІмџl­рџsВтџwЕхџzЗцџ{Ицџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ~Ицџ€Зхџ~Брџ}–џ€PIџŒ5џ–9џœ9џž5 џœ8џ ?џЇE џЈ@џЈAџЇD џЇH џЈEџЌEџВL$џž8џ^џџUPHџvŒ›џ‚ИсџЙцџ~Ицџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџzЗцџwЕхџsВтџl­рџaІмџPœеџ<Эџ$}ПџfЌџbЃы_ž—\’[џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ Bi[‘ cs c ЭcЅџrЕџ,ƒУџC”аџYЁиџgЊоџqАтџvДхџyЖцџ{Ицџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџŠОчџ—КоџŽŒИџ\Xџ‚=џ›=џЃBџЃ=џ/џЁ:џЅBџЈG%џЈF#џЉDџЉDџЇD џЉDџЋDџ­F!џПT#џHџ;-џ=9 џszkџЊООџ—Ууџ†Ншџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџyЖцџvДхџqАтџgЊоџYЁиџC”аџ,ƒУџrЕџcЅџ c Э cs[‘ Biџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџcœ7 bŸaЁеlАѓxМџ4‰ШџMšдџ_Ѕлџk­рџrБтџvДфџyЖхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‹Йсџ˜ЇТџWdџˆ=+џ9џЇE"џЉEџЃ=џ™/џ ;џЇC џЋH%џЊF$џЊD!џЉDџЈD!џЊDџЋDџЊDџЛQ$џЖYџ—W џF8 џVX;џ…‚џŠИиџ‚Лчџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџvДфџrБтџk­рџ_ЅлџMšдџ4‰ШџxМџlАѓaЁе bŸcœ7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ _œ1 až— hЌсpЕџ$}Пџ@‘ЮџU зџeЉнџmЎрџrВтџwЕфџzИцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ}Зцџ~Ицџ|Зцџ„ЉЭџŠ‡“џ…9џŽ1џž< џАK(џЊE џ <џ–2џŸ>џЇD"џ­F$џ­E$џЋD"џЉD!џЈD#џЊE"џЊE џЈDџЌE џФWџиlџc= џB:џKUAџfЈџuЋдџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzИцџwЕфџrВтџmЎрџeЉнџU зџ@‘Юџ$}ПџpЕџ hЌс až— _œ1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџdœ eQ fЈЭ i­џpЕџ2‡ЧџK™гџ_ІлџiЌпџpБтџvДфџyЗцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџЙцџƒЛчџ~Ицџt†žџpXEџ€nџ•VџЇD"џБM*џЅBџ9џ›4џЂ@џІC#џЉC#џ­H&џЌG$џЉE#џЇB"џЈD#џЈE!џЈDџЊBџЃ=џ–7џ‘;џa6џ-1 џ.6џZџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЗцџvДфџpБтџiЌпџ_ІлџK™гџ2‡ЧџpЕџ i­џ fЈЭ eQdœџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџfœ f eЄ™eЈс iЎџ$|Пџ?ЭџV зџcЈнџl­рџsВтџwЕхџzЗцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ}ЗцџŒНтџПгџ„‡šџzZWџ}B"џWџЁO џЎH$џБL)џЄAџ8џœ4 џІ@џЊD#џЌF$џ­H%џЌH$џЋF#џЉD#џЈE#џЈE"џЉE џЉD џЃ@џ›<џŸ?џr7џџœ7џž;џЊE"џЎH%џЏH$џ­G$џ­G$џ­G$џЏH$џЌG#џЊF#џЊF#џЊE"џЉE"џЈE#џІC џ”7џt.џC8 џ_€†џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ИцџzЗцџwЕхџtВтџmЎрџaЇлџQеџ?Юџ"{Оџ hЋџ_œљ ašhš3џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџhš `šC`žfЊхsЖџ,ƒФџD”аџW иџeЉнџmЎрџsВуџwЕхџzЗцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зхџ}Жуџ…ЅЯџ„‚–џ€UHџˆDџ™BџЎH&џОP"џИP'џБN*џВM&џŸ9џ›6џІCџ­G#џЏH$џЎH$џЏJ%џЎI%џ­G$џЎG$џ­G$џЌG$џЌG$џЊF#џЈD#џЇB"џЇC џЂCџ@џU8џfˆџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџwЕхџsВуџmЎрџeЉнџW иџD”аџ,ƒФџsЖџfЊх`ž `šChšџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ^š `ŸCdІЩlАїxМџ5‰ЩџK˜гџ]ЄкџhЌоџoЏсџuГфџxЗцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ€ЙхџŠМуџŠІџadџˆD'џ•B џЈI#џЖO)џГN(џВM(џВM(џВM'џž6џœ4џЌH%џАI%џАH$џЎH$џВM'џАJ&џ­G$џ­G$џ­G$џ­G$џ­G$џЊF#џЊE#џЌE"џЈD!џЎL"џІNџe2џmzˆџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџxЗцџuГфџoЏсџhЌоџ]ЄкџK˜гџ5‰ЩџxМџlАїdІЩ `ŸC ^šџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџNuZ!`Ѕ fЉхoЕџ%~Сџ@ЮџV иџeЉоџmЎрџsВтџwЖхџzЗцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџˆНцџЃШфџtjpџs@/џ’>џЂC џГN&џОV*џГL'џВK'џГL(џДL(џž3џœ3џАL)џБK'џАI%џАI%џДM)џБK'џ­G$џ­G$џ­G$џ­G$џ­G$џЋF#џЏH#џДK#џЊG"џИR$џЙU џr( џqt…џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџzЗцџwЖхџsВтџmЎрџeЉоџV иџ@Юџ%~СџoЕџ fЉх`ЅZ!Nuџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ^– bŸ] hЋЙ lВџrЗџ0†ЦџK˜гџ_ЅлџhЌпџpАсџvДфџyЖхџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџŠКрџЉРвџlUџ‡C џ“= џДL%џКP(џЕO)џВQ*џГN*џВI(џЋD$џ˜3 џš6џАL)џВL(џВL'џБL'џЎF$џЎG%џЎI%џЎK%џЎI%џ­G$џ­G$џЌG$џЌG#џЋG"џЋG#џТ\&џЧf!џq)џpt‚џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџvДфџpАсџhЌпџ_ЅлџK˜гџ0†ЦџrЗџ lВџ hЋЙ bŸ]^–џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџeЁeЁ'fЈ‰gЋщ iЏ§"|Оџ>ЭџW иџcЈнџlЎрџsВтџwЕфџyЗцџ{Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџŒВиџЎЇЗџ‘dHџEџ D$џИP)џИP*џБM)џВP+џМT-џКO*џ = џ•2 џ8џЕL*џГL)џВL'џАL&џЎG$џЎH%џЏK&џЏK&џЎI%џЎH%џ­G$џ­G$џЊD#џІA!џЏJ$џЭi&џб{!џgBџj‚џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЗцџwЕфџsВтџlЎрџcЈнџW иџ>Эџ"|Оџ iЏ§gЋщfЈ‰eЁ'eЁџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџbŸYbЁСdЇїsЖџ0…ЦџMšдџ\ЃкџgЋоџpАсџuГуџxЖхџzИцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‹ЇЭџ­„•џ‰T@џE џВN'џЖR,џЕR+џВO)џГL*џХX/џТV,џ–6џ•1 џЂ:џКN,џЕM)џАK&џ­J$џВL'џБM'џЏL&џЏI&џЏI&џЏI&џЎH%џ­G$џЉC"џЄ=џГM%џаo&џЯ… џ_[ џf‹„џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzИцџxЖхџuГуџpАсџgЋоџ\ЃкџMšдџ0…ЦџsЖџdЇїbЁСbŸYџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџcž= cŸ“bЁяj­џ!zМџA‘ЯџRœжџaІмџmЏрџsВтџwЕфџzИцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ‚žРџŽhmџ‚<1џ’8џДO*џИU-џЙW-џИT+џЗM(џНQ-џЕK'џ“1 џ•2 џЂ>џЗS-џГS+џБP(џАL&џБM&џВM&џВM&џБJ&џАI&џЏJ&џАN'џЎI%џЋF#џЉD"џАI$џЏO$џЃN џ6џz}ˆџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzИцџwЕфџsВтџmЏрџaІмџRœжџA‘Яџ!zМџj­џbЁя cŸ“cž=џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџcž cžM bŸЅ gЉпqЕџ0†ЧџE”аџW иџfЊоџnЎрџsВуџxЖхџzЗцџ|Зцџ|Зцџ|ЗцџЕоџ‡™Дџ^_џ—J8џ­M,џЦ^1џУZ-џПW,џКT,џЛR*џЛQ,џБH$џ—1 џ™3џЄ?џДQ*џГR+џГQ)џВN'џВN'џГN'џВN'џАL&џЏJ&џЏJ&џЎK%џЏJ&џ­H%џЋG#џБK%џЃG$џ‘> џŒ0џƒ{‰џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџxЖхџsВуџnЎрџfЊоџW иџE”аџ0†ЧџqЕџ gЉп bŸЅ cžMcžџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ bŸ bŸOdІЗkЎ§ zНџ6ŠЩџL™гџ^ЅкџhЋоџoЏсџuГуџyЗхџ{Зцџ|Зцџ|Зцџ†Ейџ‘™­џ™^^џЏaJџСeAџЮj?џЯd3џЦZ.џНS-џНU-џКS,џАI#џ›2 џ4џІ?џГO(џДP*џДP*џГO(џГO(џГN(џВM(џАL&џАK&џЏI&џ­H$џЏJ&џЎI&џЌG#џВM%џŸG$џŠ<џ4џ…}‰џ|Зхџ|Жуџ|Зхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЗхџuГуџoЏсџhЋоџ^ЅкџL™гџ6ŠЩџ zНџkЎ§dІЗ bŸO bŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџZІZІ` ™ eЅѕnДџ'Сџ?ЭџW иџdЈнџmЎрџsВуџwЕхџzЗцџ|Зцџ|ЗцџˆКЂДџ­haџВn`џГm^џЖiYџдtFџвg5џУU+џКT-џЙV-џБM%џ™3џ5џЇ?џБO(џГP*џДP*џГO(џГO(џГL(џГJ(џБJ&џВK'џБL'џЏL&џЎI%џЎH$џЏH$џВM&џŸD#џ‰9џ‹7џƒˆџ|Жуџ~Глџ}Етџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџwЕхџsВуџmЎрџdЈнџW иџ?Эџ'СџnДџ eЅѕ` ™ZІZІџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџUЊUЊ ` a eЇЛ lГ§uЙџ/„ФџM™гџ\ЃкџhЋоџpАсџuГфџyЖхџ{Зцџ|ЗцџƒКсџ™ВХџРžџРЄšџОŸ˜џЙ‹…џБ^EџЕS.џЛT*џЖS)џРX*џМQ"џ™4 џ :џЊDџВO(џДP*џДP*џГO(џГP)џГM)џБJ(џБK(џЕN*џКQ+џПT)џЖP)џВL(џВI&џГL'џЈH&џ—@!џ…5џ~}Šџ|Жхџ}Есџ|Жфџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџuГфџpАсџhЋоџ\ЃкџM™гџ/„ФџuЙџ lГ§ eЇЛ ` aUЊUЊџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ b 1 fЈiЏэ kВћ yМџ@‘ЮџRеџaІлџk­рџrБуџwДфџzЗцџ|ЗцџИфџ’МзџКТНџДФЪџДКХџВŸІџM>џ˜@#џЌN(џГQ&џЦV)џФO"џœ5џЄ>џ­HџЕR*џЕR+џЕQ+џДP*џДO+џЏK)џЋH'џЉH(џГO-џТX1џдd3џЩ`2џПX.џЕM)џВK'џЎJ'џЁE$џ.џzyŠџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџwДфџrБуџk­рџaІлџRеџ@‘Юџ yМџ kВћiЏэ fЈ b 1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ f™aŸM_ ЭcЈёqЕџ0†ЧџE–бџXЁйџgЊоџoЏсџuГуџyЖхџ{Ицџ|ЗцџƒЛчџ”Ущџ„Лчџ‰Ўкџ——Зџ}I4џƒ7џ˜A$џБQ#џЧO*џХF%џЁ9џЇBџБM$џКX-џИU.џИT.џЗS.џГM,џЈF'џž@$џš>&џЊK/џХa:џы€Fџшz?џеl6џКW-џЏK%џ­J%џЃE#џy#џwsˆџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџyЖхџuГуџoЏсџgЊоџXЁйџE–бџ0†ЧџqЕџcЈё_ ЭaŸM f™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ` bŸ3 bŸŸcЄс k­џzНџ7‹ЪџLšдџ_Ілџl­пџsБуџwЕхџzИцџ{Зцџ~ИцџƒЛчџŽРщџ“ЇШџ’x‰џ„J2џ@џЄF(џКQ(џРR,џКM%џЈA џЌDџВK#џЙU.џИT.џЙU.џИU-џАL)џŸ@"џ‹6џv2#џoB4џnP>џqV?џzS8џ”U6џЗ[7џЧ]/џЏO#џ=џ‚5џ~}‰џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџzИцџwЕхџsБуџl­пџ_ІлџLšдџ7‹ЪџzНџ k­џcЄс bŸŸ bŸ3 `џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџdŸdŸWcЁЏ hЈчpДџ(€Тџ?‘ЮџV зџfЉнџoЎсџtВфџxЖхџzЗцџ|Зцџ}Зцџ›РсџœœЌџŠ\Xџ‰D)џD"џЖN*џОT,џМT-џЖN#џЎA џАGџДM#џИT.џИT.џКU.џКV-џ­M*џ›?"џ5џ[6$џND>џSXYџjqsџB?DџN5-џ‚I*џЙ[/џІFџ€-џƒ:џŒџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџzЗцџxЖхџtВфџoЎсџfЉнџV зџ?‘Юџ(€ТџpДџ hЈчcЁЏdŸWdŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџd a eІЙ h­џuЙџ1†ЦџJ˜гџ^ЄкџhЊоџoЏтџuГфџxЖхџ|Ицџ€ИфџЈИЭџЂ‹†џF)џŒ9џ­E!џЩV)џОY/џЛU.џИK!џВ<џГJџЕS#џИT.џИT.џЛU.џМU-џЌO.џœB'џ;"џNE'џHNKџks‚џЛМЭџ?Egџ,џ40џK-џ’5џ{џ6 џ~‘џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџxЖхџuГфџoЏтџhЊоџ^ЄкџJ˜гџ1†ЦџuЙџ h­џ eІЙd aџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ bœ) eЅƒ gЉџ jЏџ!zНџ=ЭџUŸзџcЈмџlЎрџtВуџwЕхџ~ЗсџŠЕеџ•œџ“f`џ‹E)џŸE+џЕM,џХU,џФX-џПV-џЙP%џВBџДJџИR"џНX.џМV-џЛV-џКW-џВS.џЇK+џ”B(џs;)џ`IJџkh|џ™›НџvЖџt†Ѕџ†zˆџrROџq4&џ{)џŒH>џ…‰ џ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџwЕхџtВуџlЎрџcЈмџUŸзџ=Эџ!zНџ jЏџ gЉџ eЅƒ bœ)џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ_— dЄS eІЫfЋёqЕџ-ƒФџJ˜вџ\ЃкџhЋпџpАсџuДфџ€Длџ’ЏЦџ‘v|џ’THџ–E)џВN.џРT.џЦX-џЦX-џТW,џНR%џЖEџИIџМP"џСY.џОW-џМV-џКV-џИR-џЕP+џЌL*џ—A*џuD@џkVkџ„{Њџ”ЊЮџ˜Езџ“ЃЦџˆ}‹џ†afџYZџІ~}џЁЛџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџuДфџpАсџhЋпџ\ЃкџJ˜вџ-ƒФџqЕџfЋё eІЫ dЄS_— џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ bЁ+ bЂdЇн kЏ§xКџ>ЬџSжџbЇмџk­пџuВтџ„АеџšЃДџ”ifџ–M:џЂJ+џСT-џЫY-џЪ[,џХW-џФW+џСT$џНHџНIџПO!џФW-џРV-џНV-џМU-џНP,џСS+џСT+џЖM+џŠB5џpFTџu^Žџ˜žПџ—ЙмџЖуџЈБЪџЈЖџ ’ЎџЕЖТџ—Киџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџzИцџwЕхџrБтџk­пџbЇмџSжџ>ЬџxКџ kЏ§dЇн bЂ bЁ+џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ aœ `œK_œЧ dЄљoВџ0†ЦџF•бџXЁиџhЊоџ~Ерџ’ЌЫџЂŠšџ’ZQџ–O1џЊ\0џУY.џЪY-џЪY-џЧU,џХT,џФS$џУPџПOџПRџФW-џРV-џМW-џЛW-џМU-џОU-џРU,џУS,џЈL1џ‡H>џaJTџpgŽџyŽРџИцџ•ПфџИрџ‚Амџ†ЗпџЗуџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Ицџ{ИцџyЗцџuГфџoЏсџgЊоџXЁиџF•бџ0†ЦџoВџ dЄљ_œЧ `œK aœџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ \ `™ _™mbЁН hЊ§!zНџ7‹ЪџMšгџaЅлџw­иџ…˜ЕџˆalџOBџ›Y2џБq4џФ_/џЪY,џЪY,џШY-џФV,џСS$џСRџПSџОS!џНS+џОU-џНV-џМW-џЛW-џМW-џНU-џНR-џНU.џЉQ2џ|E7џqVqџtЎџ€Йцџ†МшџƒЛчџ}Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЗхџvДфџpАтџhЋпџ_ЅлџMšгџ7‹Ъџ!zНџ hЊ§bЁН _™m `™ \ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ dš dša cЅёpД§(ТџC’ЮџaЄиџ{Јаџƒ‰ џuBEџŒJ8џЄa5џК|6џЧ`/џЫY,џЫZ,џЩ\.џФY-џРT$џРP џСUџОU%џКQ+џНU-џПV-џОW-џМX-џЛW-џКU-џЗR-џЦZ-џФZ,џЇN+џ…QVџƒ}˜џ”РсџƒКфџ~Ицџ~Ицџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџzЗхџwЕфџrВуџk­рџaЇмџUŸзџ@‘Юџ(ТџpД§cЅёa  dš dšџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ dš bž[ cЁЯ h­їuЙџ;‹ШџrЋиџœГЬџЃ˜џƒ@6џ—N7џЏb6џШt4џЬ\.џЭZ-џЭ^.џЪZ-џЩY-џШV"џХNџХUџФY$џРV.џСW.џСW.џСW.џПY.џМY-џЛX-џНW/џПX-џЧ_,џеk-џЅP4џЊ}tџЫежџ“РсџИхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџyЖхџuДфџpАсџiЋоџ]ЄкџL™гџ1†ЧџuЙџ h­ї cЁЯ bž[ dšџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ a/ cЂ} i­чnВџ/ОџВиџ•Ўџ€osџŽI/џža:џДr:џЬ{5џЮm2џЮi1џЮi1џЭe0џЫ_.џЪX!џЭRџШUџФW"џПV-џХW,џХW-џТV.џТX-џПW-џОW-џХX-џПU-џРX-џЮb,џЬk-џЂmFџgquџšЈКџ™Ккџ„Кхџ}Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџwЕхџrВуџlЎрџcЈнџTžжџ>ЬџyЛџnВџ i­ч cЂ} a/џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ \› bЃ= hЋСhЌё&{Иџ„Диџo™џmV[џ’M-џІm:џЛ~;џЭ‚6џЮ{5џЮq3џЮh1џЭf/џЫe/џЬ`"џвVџЪVџФW џТY-џЩX,џШV,џУU.џФW-џТW-џСV-џЧV,џОS-џМT-џФX,џкr+џЉa*џU;2џ”{|џžБџ‘Аеџ†ЗсџИхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖцџuГфџoЏсџhЋоџ\ЄкџI—вџ/…ХџoГџhЌё hЋС bЃ= \›џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ]ša …dЇг!xЖџ~Биџvy“џvPUџK0џБo6џХ8џЮ†7џЮ‚7џЮm2џЭX-џЫX+џЬi0џЯn&џдZџЩXџЦZ!џЪ`-џЬX,џЩV,џФW-џХW,џФW-џУU-џСQ.џНR.џМS.џПS.џЩ\+џЦZ%џЖNџ‚8)џŠ]gџЂ”Вџ—Жжџ‡Йтџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџzЗцџwДхџrБуџk­рџdЈнџUžзџ=ŽЬџyНџ jЏџdЇгa …]šџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ`› bžYbЁЙmЉџKРџ„žџškkџŽJ/џЏr7џФˆ;џЭ<џЭ†9џЮx5џЮn2џЬt3џЭs3џЬl-џЩ\џЧXџШZџЫ^-џЫY-џШW-џФW,џУW-џУW-џУW-џСU.џПW.џОV.џПR-џПX,џЦZ+џЫW(џІF$џ‹KBџ~evџžМџ†Бкџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџyЖхџvГфџoАсџfЊоџZЂйџI—вџ/„ФџnГџ eІџbЁЙ bžY`›џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ]Ёbœ+ b}kЂЭ=€Ќћ|˜џ›loџH1џЎr:џТ‰=џЭ‘=џЭŒ;џЮƒ8џЮ|6џЭ5џЮx4џЭl0џЩ['џШZџЪ[џЭ]/џЬ[.џЩY-џЦW,џФW-џФW-џФW-џФY-џТY.џРW.џСT,џОW-џЩZ,џиZ*џДN#џ‹@)џnCFџ|zœџ}ЁЭџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{Зцџ{ЗцџyЖхџvДфџqБтџjЌпџ_ЅкџO›дџ:Ыџ!zМџeЊљ bЁЭ a}bœ+]Ёџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџc-+p›{I~šёmp‡џ‡]eџ‘H6џЌp<џРˆ>џЬ‘=џЭ’>џЭŒ;џЭ„7џЭ5џЮz4џЯn0џЯZ$џЬ[џЫ\џЮ\0џЬ[/џЪY-џЩX,џШX,џЦX,џЦX,џЩ\,џФY-џТW.џФW-џСY.џЭY,џоW'џВP%џŒ:џo-#џbSxџpŽМџ|Зхџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџyЗхџvЕфџrБтџk­пџcЇмџUžжџA“Яџ*ТџoГџ_Ђя aŸybœ-џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ\‘ 9sIDzхhmƒћƒ^hџ’O;џЌw@џР‹AџЭ?џЬ?џЭ‹;џЮ†7џЮ‡8џЮ‚7џЮw3џаd-џЫXџЪSџЬT.џУT-џФV,џЪX,џЭ[-џЩY-џЦX-џЫ\-џХY.џТW-џХW,џСY-џЯY+џтW&џЗP)џ–<џ|2џkPbџrŠЏџ{Гсџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ|Зцџ{ИцџyЗхџuГфџpЏсџiЋпџ\ЃкџJ˜вџ2ˆШџvЙџiЌї a й ažE^– џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџHq‚/Is„Йggы^jџ•ZBџЎ|DџРŒEџЬDџЬ>џЭŽ<џЭ‹;џЭ†8џЭ‰;џЭ€9џЯb-џЬXџЬWџЭ_0џЩm3џШf1џЩX-џЪ].џШ[-џЧX-џЪ[-џХY.џТW.џХW,џСX.џХW,џЫT*џЖS.џŸL$џ‡F"џlLLџq„ џzБоџ|Зхџ|Зцџ|Зцџ|Зцџ|Зцџ{ЗцџzЗцџwЕфџrБтџl­рџcЈмџSžжџ=ŽЬџyНџoЕџ hЌй až `œ#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџVjx#Vjx‹g`zз{^jџ—aGџА~GџТŒHџЫHџЬ?џЬ’?џЬ@џЭ…8џЬŽ>џЬ†=џЭ]*џЮYџЯ]!џаh0џб„7џЯw5џЪ[/џШ_/џЩ].џЩY,џШY-џФX-џТW.џУW-џТW.џНU.џЗS.џДV0џЇZ+џY)џlH<џo•џyАлџ|Зхџ|Зцџ|Зцџ|Зцџ|ЗцџzЗцџxЖхџuГуџoЏсџgЊоџ\ЄкџH–бџ.„Фџ lВџ kБїi­Џ `›9 `›џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ_hq_hqsf^sЭw[dџ—[AџД|BџХ‹DџЬŒFџЬ“GџЬ•HџЬ“EџЮŠ;џЫŽ>џЬƒ<џв\+џЯZ џаZџв[(џаk0џЮn2џЭh0џЮ_/џЭ].џЬ]-џЩY,џЦW,џФW-џСW.џСW.џХW,џЩV)џЗR,џЉT#џ–UџnG;џp~“џxЎиџ|Жхџ|Зцџ|Зцџ|Зцџ{ИцџyЗцџvЕхџrБтџk­пџcЈмџUŸзџ<Ьџ zНџfЌџcІйcЃ…nЁnЁџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџdlkdlkg_\pЩnXbџ‘XAџД}DџЧFџЬ‘GџЬ’FџЬ‘FџЬEџЭŽ@џЬ?џЭ‡;џбp1џЯa"џаZџе\(џвh/џаm2џЮm2џЮg0џЭc/џЬ_.џЬX,џЪX-џШY-џЧZ-џШY,џЭZ-џб[-џРP,џЎJ џ—GџpD7џoyŽџwЇдџ|Жхџ|Зцџ|Зцџ|Зцџ{ИцџyЖхџuДфџoАсџfЊнџZЂиџJ˜вџ-ƒУџpДџdЇџcЃЕ c ]-xЅ-xЅџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџeneeneM^ZnЙkUbћ‹VDџБ|HџХIџЫ’HџЬEџЬŽEџЬEџЬ‘EџЭ‘BџЮ>џЯƒ8џЯg&џб[ џе^(џгl0џаr3џЮr3џЮm2џЭi0џЭc/џЯZ-џЬY.џЫZ-џЬZ,џЭY,џЯ[-џб]/џФR-џБG!џ˜?џuA2џnlџq—Тџ{Гсџ|Жхџ|Зцџ{ЗцџzЗцџwЕфџrВтџk­рџ_ЅлџO›дџ<ŽЬџxЛџ iЌїcЂс dЁe 3џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџY[YY[YjWo™tVdя†TLџЉvLџП†KџЩ‰HџЫŽGџЬGџЬGџЬ•HџЬ–IџЭ”EџЮ=џбj*џвY!џг^#џаu/џЯ|4џЮw5џЯm2џЮk1џЮi0џЮ`/џЬ\.џЬX.џЮS-џЫW-џЪY,џЩX,џПY/џЏO)џšC$џB.џjU_џdwšџx­иџ{Еуџ{ЗцџzЗцџwЕхџrВтџl­рџdЈнџUŸзџB“Яџ,ƒХџoГџcІп cž‘cž1e џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ]eU]eUocckx\]Н‚SRћ qP§З‚MџЧ‹KџЪKџЫ‘KџЫ‘IџЬ”EџЬ‘@џЭ?џЭBџвn/џг]#џа^!џаy/џЯ…6џЭ‡8џЮ~6џЯw4џЯq3џЯn2џЮj2џЭe0џЭ^.џЭX-џЭV,џЬY+џУY.џБM)џšA$џ‚C,џjOUџcnџwЋеџzЕтџzЗцџyЖхџuГуџoЏсџgЋпџ\ЃкџI—вџ3ˆЧџvЙџiЋ§aЂС ^™7 ^™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ[iR[iRqrZOxdZŸ~UYїšjR§Б}NџФNџЩ“OџЫ”NџЫ“KџЬ”EџЬ‘AџЬAџЬŒFџгo3џж`$џдcџгt-џа‚5џЭŒ9џЮŠ8џЮ€6џЯv4џЯu4џЯs4џЮm2џЬa/џЭZ.џЮX-џЭX,џЧW-џВL*џ—@&џƒ?'џjJNџbjˆџwЉвџzДсџyЗхџwЕфџrБтџk­рџbЇмџSжџ<Ыџ#|ПџnГџ fЈн`Ÿ3zџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџE\EE\E k{aCtia“zX`ѕ–bT§­wPџСQџШ–QџЪ—OџЫ—LџЬ–IџЫ”LџЫ‘MџЫŽJџгn7џкc$џсkџйi(џгu2џЮˆ8џЭŽ:џЮ…8џЮz5џЯv4џЯu4џЮn2џЭ\.џЮ_/џЮ].џЭX-џЩV-џБK-џ”>*џ7!џiEKџ`hˆџvІаџyГрџxЖхџuГуџnЏрџeЊнџ[ЂйџH—вџ,‚УџpЖџ kГџ fЊЁ _œEџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџLYLl~o1wms}`tё“aYћЈqOџМ‰QџЦOџЪ‘OџЫ‘MџЭ’HџЬ”LџЫ–OџЪ”Lџа>џй€/џтp!џи‡5џбŽ=џЭ=џЭ‘=џЭŽ;џЭˆ8џЮ‚6џЮ|5џЮw4џЮr3џЮp3џЮl2џЮe0џЬ].џДR'џ•E џ€6џjDIџbg…џtЁЪџwАнџvЕфџsБтџj­пџ_ЅлџP›еџ:ЫџyЛџ h­ћcІщbЁ} _–'џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџDDDk{vnm{f‚ѓ`]ћЄmPџЙ†QџЦŽQџЪQџЪ‘PџЫMџЫ”MџЫ–MџЪ”MџЯ–Aџж‡0џоjџи{0џвˆ;џЭ‘?џЭ‘?џЭ‘=џЭ;џЮ‹9џЮ€6џЮy5џЮx5џЯv4џЯq3џЮk1џЭa/џИZ*џšO#џ6џhBEџ`aџp–УџuЋкџuГуџpАсџeЉнџVŸзџC’Яџ+‚УџqДџdЈёbЂП bžW _џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџe|‡ xqU{nсŽ_aїЂjPџЖ„RџЧUџЫ“UџЪ’SџШSџЪ“NџЫ”KџЪ‘NџЮ“Bџд‚-џкaџй]"џдt3џЮ’@џЬ?џЬ‘?џЭ’>џЭ;џЭ„8џЮ{5џЯw4џЯv4џЯr3џЮj1џЮb/џОa0џЃ[-џ~9џf?Cџ^[zџl‹МџrІзџrВтџlЎпџ^ЄкџK˜гџ5‰ЩџvКџ j­ћdЄн hЄ…iЄ1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџe|‡ЖГИ%МЕКm˜bbгeQџВƒPџШ’WџЭ•VџЪ’SџШSџЩ’SџЩ“TџШTџЫ–Mџг‡5џнfџнd џжw3џЮCџЬ@џЬ‘@џЬ’@џЬ>џЭ;џЮˆ8џа€6џа{5џаr3џбg1џа`/џЧh.џБj*џ~@ џjDOџf`‰џs‘ПџrІзџmЎрџfЉнџSžзџ=ŽЭџ%}РџlБџ eІу bЃ c—1 e“ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяёчээфO“ejЫ•`VџЌuPџХWџЫ“WџЩ’UџШ’TџШ’TџШ’UџЧ‘WџЩ”Sџг‡8џуl џтhџйy1џЮ‘EџЬ‘AџЬ‘BџЬ‘CџЭ‘@џЪ‘>џЩŒ;џЬ6џа‰=џг‹@џжˆAџж:џЦr1џЊ`)џ@#џsdrџtŽКџЉвџtЌмџgЋоџ]ЄкџG•бџ-ƒФџpЕџ hЌџ dІБ `œQ]‡UUџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ№ђ№ю№юOˆiwЫ‰[_џЄfQџР†WџЩ‘YџЩ’XџШ’UџШ‘TџШ‘TџЧ’WџШ‘Tџг‰<џчzџцlџмz-џЯ“DџЭ“CџЬ’EџЭ‘FџЮ“BџФAџН…=џПu7џУ‡DџШ”PџЭ›Wџв˜IџОw5џžQ&џ€A)џ{‚”џИхџ†ЛуџsБрџaЇмџTžжџ:ŒЫџxЛџfЋћ fЉы eЈyTЈ TЈџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїїїљљњK|kŠЩ|Zkџ™_RџЙƒWџЦYџЪ‘XџЩ‘UџЩ‘TџЩTџЩŽSџЩRџг•Gџф™2џчs"џпx*џв’>џЭ•FџЬ•HџЬ•HџЬ’FџИFџІkAџ›T9џ—YEџ˜bYџžplџД‚XџЌn7џ—PџƒG8џ}…œџyЕхџrАсџhЊоџ[ЂйџJ—гџ*УџpЕџdЉѓ cЂЇažIKЭџ&~РџoВ§bЃѓ b {cš'џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЉГЂЏvGЖfKЉКeFуОgCџУk@џТf<џХnGџЫ…`џЯ–fџа™^џбMџлs1џкƒ2џд—AџЫ—WџЪ–[џЧ„NџР]0џЛt_џЉ‰ˆџŒ›­џoЇвџZЃйџG–бџ-„ХџtИџ h­ё cЁЧ bЁEџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭзд ЩЧСКv_ОmI›ОkHџНgCџО_6џС`6џФlDџЯ‘gџв›jџб•Yџк‹;џл3џж€:џЬ”UџЫ”\џЩ…RџТe6џСe=џОb=џВcGџu˜ЙџRžжџ9ŒЪџvКџmВџ i­еfŸ_eџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЧббЧвв иппХŒpMЙ…vŸЈ{сЏrjлЖkUуЛiJџЦ‹fџЫ—jџЯ—^џж˜Gџй‹:џи†9џЯ˜OџЬ•YџЪŒZџШ€OџТh;џП[0џЛ[4џЁvkџ„‚•џc„Џџ9ЋџpЈё dЅЕa™1`–џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџдлиБ­ПE™‹ЌЂ‚”‡Ўpf­ЕeJџЛ€\џФdџЭ—aџгšPџи?џк‡8џв›HџЮ–VџЫ‘^џЫ‘^џХwGџСe8џП]1џОc:џЅhYџ‚pџd€™џ7tŸг bŸ‡ W P†џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЏZ;[Б\=џВpGџН…XџЬ–eџб’Uџи‚?џм~4џжœBџЯ—TџЫ‘^џЫ‘^џЫ‘^џЧ€NџТe6џТf5џЇV.џN9џ—sƒџmu˜ЉjЄK"oЅџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ’’’ДeIaЗbCџКc:џРvMџЦiџШiџгŠMџм…4џлˆ7џбOџЫ“^џЫ‘^џЫ‘^џЩ‰VџХ|GџУp;џЙ`2џЎT1џЊ[Gџ”hpз„|š›ЇЋАGДИИЦЦЦ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅЅЅО|fEРv]ЙП`8эРi@џТ[џШ’nџв“WџлŠ;џоw0џе‹GџЭ—YџЫ’aџЫ’_џЪYџШ‹OџЧ€BџУk6џНX.џДZ1џ–]Sѓ…hyбЄ“ЄБЅБCЭЭЪџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџььїугжGСhCЩН_6§ПjAџЭ•gџдœ_џйŽHџрm.џйˆ=џбšQџЬ“eџЫ“bџЫ“ZџЫ“PџЫ“JџХx<џО]0џЙg7џ‡P:џpLQѓŸ~™бЎ–ЊaввЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџььїгЛОГyqŸЖjSхНe>џЬiџв›hџз–Sџн4џлˆ5џе‘GџЬ–gџЬ–fџЫ”bџЫ’[џЫ”OџЩŠEџХx<џО_2џ‰D,џpDCїžy’нЅƒœSввЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџБ•Б•cЖtbЗЛcBљУvUџЭ‰eџж•eџн‘Cџр‚3џл‚;џЭ—aџЬ–eџЬ•eџЫ”bџЫ•YџЫ‘QџЩƒGџРa4џ‰@$џo@7ћxыЁ|“SџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџПЧЫПЧЫ'Кxb‰ЙaCѓК^;§ЦrVџгŠlџмZџу}6џпu1џЯ–XџЭ”aџЬ”fџЬ–gџЫ•dџЭ“^џЭˆRџРd9џ‡@џm>.§œx‰љ |WџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџПЧЫН]7uНX1§ОZ0џР^5џЦmIџб•|џо}Gџпw.џб“KџЭ“]џЬ“gџЫ“iџЫfџЯ”eџа[џС`5џƒ=џh=&§›vˆџžzŽYџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџН[65М]8{ЗiJ‘ЙcAЭОd@џЦtTџиvCџрx6џк}4џвŽRџЭ”cџЬ‘hџЬgџЯ“iџб“eџЪ}OџŽI%џl6&џhwџl}[џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊЊЊЂЂДœ В ™ЁЌ+ЎlVƒД^?зЙZ3ћЭl>џлx=џрx+џжŽJџЯ“]џЫŒdџЬŽfџЮ“jџЯ–kџЪaџ˜V8џu7.џ€Yfџ‹gwuЏŽЃџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ68>qwmK>!P;gЋX<яЙ`=џЩt<џм˜:џж–IџаŽTџЩ…\џЫdџЮ“hџЫ’fџЕYџ›^Hџ…IEџwMYџ”oПЏŽЃeџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџУПС+НЇЁS­`G[И`AППh@џЧu;џШyIџЧuKџХpGџЧ}SџЫ‹`џЪ”gџЛˆ_џЄlUџ‹SMџuDJџ‡^sз•q‘џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџтфщ#тфщ3тфщИaCiЙ`>ХМ]7џРc@џСd@џСd;џХrHџШVџШŒ`џСŽbџ­z\џ”^Rџv@Bџ~Riч„^‚џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџИW,OПZ.љП[/љП^4§Пe=џХpGџЦuKџХ{PџУbџЗ…^џŸlRџzACџxKbёyRv™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџИW,ПZ.GЗ{ZsЕ‚pЕЖ‚yџЖ~wџЛqXџТf<џХuSџЖyZџŸlUџ€G@џvK[ѕqNlџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊЏЁ#Џš—YВŽ‘•А„‰•ЗkUЧН_9џУhFџЕtTџ nTџ…H>џ{O^щvUs‘џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЕaBqЗcBэПhCяДvQѓЂpSѕŠG@ѕ‡WhЩ…e†uџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ›p;Ђ†tМŸ‚‰АlЇ e[ЙŽUP­Œigs€7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‰”‘-”ž–aКСЈmЎƒy‘ `^Ї[W™tg[“˜}%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџtkcvs-8.2.3.orig/tkdiff/tkdiff0000755000175000017500000123257411664612512014447 0ustar timtim#!/bin/sh #-*-tcl-*- # the next line restarts using wish \ exec wish "$0" -- ${1+"$@"} ############################################################################### # # TkDiff -- A graphical front-end to diff for Unix and Windows. # Copyright (C) 1994-1998 by John M. Klassa. # Copyright (C) 1999-2001 by AccuRev Inc. # Copyright (C) 2002-2005 by John M. Klassa. # # TkDiff Home Page: http://tkdiff.sourceforge.net # # Usage: see "tkdiff -h" or "tkdiff --help" # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################### package require Tk 8.0 # Change to t for trace info on stderr set g(debug) f # get this out of the way -- we want to draw the whole user interface # behind the scenes, then pop up in all of its well-laid-out glory set screenWidth [winfo vrootwidth .] set screenHeight [winfo vrootheight .] wm withdraw . # set a couple o' globals that we might need sooner than later set g(name) "TkDiff" set g(version) "4.2" set g(started) 0 # FIXME - move to preferences option add "*TearOff" false 100 option add "*BorderWidth" 1 100 option add "*ToolTip.background" LightGoldenrod1 option add "*ToolTip.foreground" black # determine the windowing platform, since there are different ways to # do this for different versions of tcl if {[catch {tk windowingsystem} g(windowingSystem)]} { if {"$::tcl_platform(platform)" == "windows"} { set g(windowingSystem) "win32" } elseif {"$::tcl_platform(platform)" == "unix"} { set g(windowingSystem) "x11" } elseif {"$::tcl_platform(platform)" == "macintosh"} { set g(windowingSystem) "classic" } else { # this should never happen, but just to be sure... set g(windowingSystem) "x11" } } # determine the name of the temporary directory and the name of # the rc file, both of which are dependent on the platform. # This is overridden by the preference in .tkdiffrc except for the very first # time you run switch -- $::tcl_platform(platform) { windows { if {[info exists ::env(TEMP)]} { set opts(tmpdir) [file nativename $::env(TEMP)] } else { set opts(tmpdir) C:/temp } set basercfile "_tkdiff.rc" # Native look for toolbar set opts(fancyButtons) 1 set opts(relief) flat } default { if {[info exists ::env(TMPDIR)] && $g(windowingSystem) != "aqua"} { # MacOS X sets TMPDIR to something like /var/folders/uC/uCFr1z6qESSEYkTuOsevX++++yw/-Tmp-/ # don't let it do that set opts(tmpdir) $::env(TMPDIR) } else { set opts(tmpdir) /tmp } set basercfile ".tkdiffrc" # Native look for toolbar set opts(fancyButtons) 0 set opts(relief) raised } } # compute preferences file location. Note that TKDIFFRC can hold either # a directory or a file, though we document it as being a file name if {[info exists ::env(TKDIFFRC)]} { set rcfile $::env(TKDIFFRC) if {[file isdirectory $rcfile]} { set rcfile [file join $rcfile $basercfile] } } elseif {[info exists ::env(HOME)]} { set rcfile [file join $::env(HOME) $basercfile] } else { set rcfile [file join "/" $basercfile] } # Where should we start? MacOSX apps want to start in / which is obnoxious if {[pwd] == "/"} { if {[info exists ::env(HOME)]} { catch {cd $::env(HOME)} } } # Try to find a pleasing native look for each platform. # Fonts. set sysfont [font actual system] #debug-info "system font: $sysfont" # See what the native menu font is . configure -menu .native menu .native set menufont [lindex [.native configure -font] 3] destroy .native # Find out what the tk default is label .testlbl -text "LABEL" set w(background) [lindex [.testlbl cget -background] 0] set w(foreground) [lindex [.testlbl cget -foreground] 0] set labelfont [lindex [.testlbl configure -font] 3] destroy .testlbl text .testtext set textfont [lindex [.testtext configure -font] 3] destroy .testtext entry .testent set w(selcolor) [lindex [.testent configure -selectbackground] 4] set entryfont [lindex [.testent configure -font] 3] destroy .testent #debug-info "menufont $menufont" #debug-info "labelfont $labelfont" #debug-info "textfont $textfont" #debug-info "entryfont $entryfont" set fs [lindex $textfont 1] if {$fs == ""} { # This happens on Windows in tk8.5 # You get {TkDefaultFont} instead of {fixed 12} or whatever # Then when you add "bold" to it you have a bad spec set fa [font actual $textfont] #puts " actual font: $fa" set fm [lindex $fa 1] set fs [lindex $fa 3] set textfont [list $fm $fs] } set font [list $textfont] set bold [list [concat $textfont bold]] #debug-info "font: $font" #debug-info "bold: $bold\n" option add *Label.font $labelfont userDefault option add *Button.font $labelfont userDefault option add *Menu.font $menufont userDefault option add *Entry.font $entryfont userDefault # This makes tk_messageBox use our font. The default tends to be terrible # no matter what platform option add *Dialog.msg.font $labelfont userDefault # Initialize arrays array set g { ancfileset 0 conflictset 0 ancfile "" changefile "tkdiff-change-bars.out" destroy "" ignore_event,1 0 ignore_event,2 0 ignore_hevent,1 0 ignore_hevent,2 0 initOK 0 mapborder 0 mapheight 0 mergefile "" returnValue 0 showmerge 0 started 0 mergefileset 0 tempfiles "" thumbMinHeight 10 thumbHeight 10 thumbDeltaY 0 } array set finfo { f,1 "" f,2 "" pth,1 "" pth,2 "" revs,1 "" revs,2 "" lbl,1 "" lbl,2 "" userlbl,1 "" userlbl,2 "" title {} tmp,1 0 tmp,2 0 } set uniq 0 # These options may be changed at runtime array set opts { autocenter 1 autoselect 0 colorcbs 0 customCode {} diffcmd "diff" ignoreblanksopt "-b" ignoreblanks 0 editor "" filetypes {{{All Files} *} {{Text Files} .txt} {{TclFiles} .tcl}} geometry "80x30" showcbs 1 showln 1 showmap 1 showlineview 0 showinline1 0 showinline2 1 syncscroll 1 toolbarIcons 1 tagcbs 0 tagln 0 tagtext 1 tabstops 8 } # reporting options array set report { doSideLeft 0 doLineNumbersLeft 1 doChangeMarkersLeft 1 doTextLeft 1 doSideRight 1 doLineNumbersRight 1 doChangeMarkersRight 1 doTextRight 1 filename "tkdiff.out" } if {[string first "color" [winfo visual .]] >= 0} { # We have color # (but, let's not go crazy...) set colordel Tomato set colorins PaleGreen set colorchg DodgerBlue array set opts [subst { textopt "-background white -foreground black -font $font" currtag "-background Khaki" difftag "-background gray" deltag "-background $colordel -font $bold" instag "-background $colorins -font $bold" chgtag "-background LightSteelBlue" overlaptag "-background yellow" bytetag "-background blue -foreground white" inlinetag "-background $colorchg -font $bold" - "-background $colordel -foreground $colorins" + "-background $colorins -foreground $colordel" ! "-background $colorchg -foreground $colorchg" ? "-background yellow -foreground yellow" mapins "$colorins" mapdel "$colordel" mapchg "$colorchg" }] } else { # Assume only black and white set bg "black" array set opts [subst { textopt "-background white -foreground black -font $font" currtag "-background black -foreground white" difftag "-background white -foreground black -font $bold" deltag "-background black -foreground white" instag "-background black -foreground white" chgtag "-background black -foreground white" overlaptag "-background black -foreground white" bytetag "-underline 1" inlinetag "-underline 1" - "-background black -foreground white" + "-background black -foreground white" ! "-background black -foreground white" ? "-background black -foreground white" mapins "black" mapdel "black" mapchg "black" }] } # make sure wrapping is turned off. This might piss off a few people, # but it would screw up the display to have things wrap set opts(textopt) "$opts(textopt) -wrap none" # This proc is used in the rc file proc define {name value} { global opts set opts($name) $value } # Source the rc file, which may override some of the defaults # Any errors will be reported. Before doing so, we need to define the # "define" proc, which lets the rc file have a slightly more human-friendly # interface. Old-style .rc files should still load just fine for now, though # it ought to be noted new .rc files won't be able to be processed by older # versions of TkDiff. That shouldn't be a problem. if {[file exists $rcfile]} { if {[catch {source $rcfile} error]} { set startupError [join [list "There was an error in processing your \ startup file." "\n$g(name) will still run, but some of your \ preferences" "\nmay not be in effect." "\n\nFile: $rcfile" \ "\nError: $error"] " "] } } # a hack to handle older preferences files... # if the user has a diffopt defined in their rc file, we'll magically # convert that to diffcmd... if {[info exists opts(diffopt)]} { set opts(diffcmd) "diff $opts(diffopt)" } # Work-around for bad font approximations, # as suggested by Don Libes (libes@nist.gov). catch {tk scaling [expr {100.0 / 72}]} ############################################################################### # # HERE BEGIN THE PROCS ############################################################################### ############################################################################### # Exit with proper code ############################################################################### proc do-exit {{returncode {}}} { debug-info "do-exit ($returncode)" global g # we don't particularly care if del-tmp fails. catch {del-tmp} if {$returncode == ""} { set returncode $g(returnValue) } # exit with an appropriate return value exit $returncode } ############################################################################### # Modal error dialog. ############################################################################### proc do-error {msg} { debug-info "do-error ($msg)" global g tk_messageBox -message "$msg" -title "$g(name): Error" -icon error -type ok } ############################################################################### # Throw up a modal error dialog or print a message to stderr. For # Unix we print to stderr and exit if the main window hasn't been # created, otherwise put up a dialog and throw an exception. ############################################################################### proc fatal-error {msg} { debug-info "fatal-error ($msg)" global g tcl_platform if {$g(started)} { tk_messageBox -title "Error" -icon error -type ok -message $msg do-exit 2 } else { puts stderr $msg del-tmp do-exit 2 } } ############################################################################### # Return the name of a temporary file ############################################################################### proc tmpfile {n} { #debug-info "tmpfile ($n)" global g opts global uniq set uniq [expr ($uniq + 1) ] set tmpdir [file nativename $opts(tmpdir)] set tmpfile [file join $tmpdir "[pid]-$n-$uniq"] set access [list RDWR CREAT EXCL TRUNC] set perm 0600 if {[catch {open $tmpfile $access $perm} fid ]} { # something went wrong error "Failed creating temporary file: $fid" } close $fid lappend g(tempfiles) $tmpfile debug-info "temp file $tmpfile" return $tmpfile } ############################################################################### # Execute a command. # Returns "$stdout $stderr $exitcode" if exit code != 0 ############################################################################### proc run-command {cmd} { #debug-info "run-command ($cmd)" global opts errorCode set stderr "" set exitcode 0 set errfile [tmpfile "r"] debug-info "$cmd" set failed [catch "$cmd \"2>$errfile\"" stdout] # Read stderr output catch { set hndl [open "$errfile" r] set stderr [read $hndl] close $hndl } if {$failed} { switch -- [lindex $errorCode 0] { "CHILDSTATUS" { set exitcode [lindex $errorCode 2] } "POSIX" { if {$stderr == ""} { set stderr $stdout } set exitcode -1 } default { set exitcode -1 } } } catch {file delete $errfile} return [list "$stdout" "$stderr" "$exitcode"] } ############################################################################### # Execute a command. Die if unsuccessful. ############################################################################### proc die-unless {cmd file} { #debug-info "die-unless ($cmd $file)" global opts errorCode set file [string trim $file "\""] set result [run-command "$cmd \">$file\""] set stdout [lindex $result 0] set stderr [lindex $result 1] set exitcode [lindex $result 2] if {$exitcode != 0} { fatal-error "$stderr\n$stdout" } } ############################################################################### # Filter PVCS output files that have CR-CR-LF end-of-lines ############################################################################### proc filterCRCRLF {file} { debug-info "filterCRCLF ($file)" set outfile [tmpfile 9] set inp [open $file r] set out [open $outfile w] fconfigure $inp -translation binary fconfigure $out -translation binary set CR [format %c 13] while {![eof $inp]} { set line [gets $inp] if {[string length $line] && ![eof $inp]} { regsub -all "$CR$CR" $line $CR line puts $out $line } } close $inp close $out file rename -force $outfile $file } ############################################################################### # Return the smallest of two values ############################################################################### proc min {a b} { return [expr {$a < $b ? $a : $b}] } ############################################################################### # Return the largest of two values ############################################################################### proc max {a b} { return [expr {$a > $b ? $a : $b}] } ############################################################################### # Toggle change bars ############################################################################### proc do-show-changebars {{show {}}} { global opts global w if {$show != {}} { set opts(showcbs) $show } if {$opts(showcbs)} { grid $w(LeftCB) -row 0 -column 2 -sticky ns grid $w(RightCB) -row 0 -column 1 -sticky ns } else { grid forget $w(LeftCB) grid forget $w(RightCB) } } ############################################################################### # Toggle ignore white spaces ############################################################################### proc do-show-ignoreblanks {{showIgn {}}} { global opts global finfo if {$showIgn != {}} { set opts(ignoreblanks) $showIgn } if {$finfo(pth,1) != {} && $finfo(pth,2) != {}} { recompute-diff } } ############################################################################### # Toggle line numbers. ############################################################################### proc do-show-linenumbers {{showLn {}}} { global opts global w if {$showLn != {}} { set opts(showln) $showLn } if {$opts(showln)} { grid $w(LeftInfo) -row 0 -column 1 -sticky nsew grid $w(RightInfo) -row 0 -column 0 -sticky nsew } else { grid forget $w(LeftInfo) grid forget $w(RightInfo) } } ############################################################################### # Show line numbers in info windows ############################################################################### proc draw-line-numbers {} { debug-info "draw-line-numbers ()" global g global w $w(LeftInfo) configure -state normal $w(RightInfo) configure -state normal $w(LeftCB) configure -state normal $w(RightCB) configure -state normal set lines(Left) [lindex [split [$w(LeftText) index end-1lines] .] 0] set lines(Right) [lindex [split [$w(RightText) index end-1lines] .] 0] # Smallest line count set minlines [min $lines(Left) $lines(Right)] # cache all the blank lines for the info and cb windows, and do # one big insert after we're done. This seems to be much quicker # than inserting them in the widgets one line at a time. set linestuff {} set cbstuff {} for {set i 1} {$i < $minlines} {incr i} { append linestuff "$i\n" append cbstuff " \n" ;# for now, just put in place holders... } $w(LeftInfo) insert end $linestuff $w(RightInfo) insert end $linestuff $w(LeftCB) insert end $cbstuff $w(RightCB) insert end $cbstuff # Insert remaining line numbers. We'll cache the stuff to be # inserted so we can do just one call in to the widget. This # should be much faster, relatively speaking, then inserting # data one line at a time. foreach mod {Left Right} { set linestuff {} set cbstuff {} for {set i $minlines} {$i < $lines($mod)} {incr i} { append linestuff "$i\n" append cbstuff " \n" ;# for now, just put in place holders... } $w(${mod}Info) insert end $linestuff $w(${mod}CB) insert end $cbstuff } $w(LeftCB) configure -state disabled $w(RightCB) configure -state disabled $w(LeftInfo) configure -state disabled $w(RightInfo) configure -state disabled } ############################################################################### # Pop up a window for file merge. ############################################################################### proc popup-merge {{writeproc merge-write-file}} { debug-info "popup-merge ($writeproc)" global g global w global opts if {$g(mergefileset)} { $writeproc return } set path [tk_getSaveFile -defaultextension "" \ -filetypes $opts(filetypes) \ -initialfile [file nativename $g(mergefile)]] if {[string length $path] > 0} { set g(mergefile) $path $writeproc } } ############################################################################### # Split a file containing CVS conflict markers into two temporary files # name Name of file containing conflict markers # Returns the names of the two temporary files and the names of the # files that were merged ############################################################################### proc split-conflictfile {name} { debug-info "conflicts ($name)" global g opts set first ${name}.1 set second ${name}.2 set temp1 [tmpfile 1] set temp2 [tmpfile 2] if {[catch {set input [open $name r]}]} { fatal-error "Couldn't open file '$name'" } set first [open $temp1 w] set second [open $temp2 w] set firstname "" set secondname "" set output 3 set firstMatch "" set secondMatch "" set thirdMatch "" while {[gets $input line] >= 0} { if {$firstMatch == ""} { if {[regexp {^<<<<<<<* +(.*)} $line]} { set firstMatch {^<<<<<<<* +(.*)} set secondMatch {^=======*} set thirdMatch {^>>>>>>>* +(.*)} } elseif {[regexp {^>>>>>>>* +(.*)} $line]} { set firstMatch {^>>>>>>>* +(.*)} set secondMatch {^<<<<<<<* +(.*)} set thirdMatch {^=======*} } } if {$firstMatch != ""} { if {[regexp $firstMatch $line]} { set output 2 if {$secondname == ""} { regexp $firstMatch $line all secondname } } elseif {[regexp $secondMatch $line]} { set output 1 if {$firstname == ""} { regexp $secondMatch $line all firstname } } elseif {[regexp $thirdMatch $line]} { set output 3 if {$firstname == ""} { regexp $thirdMatch $line all firstname } } else { if {$output & 1} { puts $first $line } if {$output & 2} { puts $second $line } } } else { puts $first $line puts $second $line } } close $input close $first close $second if {$firstname == ""} { set firstname "old" } if {$secondname == ""} { set secondname "new" } return "{$temp1} {$temp2} {$firstname} {$secondname}" } ############################################################################### # Get a revision of a file # f file name # index index in finfo array # r revision, "" for head revision ############################################################################### proc get-file-rev {f index {r ""}} { debug-info "get-file-rev ($f $index \"$r\")" global finfo global opts global tcl_platform if {"$r" == ""} { set rev "HEAD" set acrev "HEAD" set acopt "" set cvsopt "" set svnopt "" set gitopt "" set rcsopt "" set sccsopt "" set bkopt "" set pvcsopt "" set p4file "$f" set hgopt "" } else { set rev "r$r" set acrev "\"$r\"" set acopt "-v \"$r\"" set cvsopt "-r $r" set svnopt "-r $r" set gitopt "$r:" set rcsopt "$r" set sccsopt "-r$r" set bkopt "-r$r" set pvcsopt "-r$r" set p4file "$f#$r" set hgopt "-r$r" } set finfo(pth,$index) [tmpfile $index] set finfo(tmp,$index) 1 # NB: it would probably be a Good Thing to move the definition # of the various command to exec, to the preferences dialog. regsub -all {\$} $f {\$} f set dirname [file dirname $f] set tailname [file tail $f] debug-info " $f" # For CVS, if it isn't checked out there is neither a CVS nor RCS # directory. It will however have a ,v suffix just like rcs. # There is not necessarily a RCS directory for RCS, either. The file # always has a ,v suffix. if {[file isdirectory [file join $dirname CVS]]} { set cmd "cvs" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (CVS $rev)" die-unless "exec $cmd update -p $cvsopt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {! [catch {eval "exec svn info"}]} { set cmd "svn" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } if {"$r" == "" || "$rev" == "rBASE"} { set finfo(lbl,$index) "$f (SVN BASE)" } else { set finfo(lbl,$index) "$f (SVN $rev)" } die-unless "exec $cmd cat $svnopt \"$f\"" $finfo(pth,$index) } elseif {[file isdirectory [file join $dirname .git]]} { set cmd "git" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } # Won't work if you aren't actually in the work tree debug-info "exec $cmd rev-parse --is-inside-work-tree" if {[catch {eval "exec $cmd rev-parse --is-inside-work-tree"} err] } { puts stderr "$err" puts stderr "Please start in a git work tree." exit 1 } debug-info "exec $cmd rev-parse --show-prefix" set prefix [exec $cmd rev-parse --show-prefix] if {"$r" == "" || "$rev" == "rBASE"} { set finfo(lbl,$index) "$f (GIT BASE)" die-unless "exec $cmd show \":$prefix$f\"" $finfo(pth,$index) } else { set finfo(lbl,$index) "$f (GIT $rev)" die-unless "exec $cmd show \"$gitopt$prefix$f\"" $finfo(pth,$index) } } elseif {[regexp {://} $f]} { # Subversion command can have the form # svn diff OLD-URL[@OLDREV] NEW-URL[@NEWREV] if {![regsub {^.*@} $f {} rev]} { set rev "HEAD" } regsub {@\d+$} $f {} path set finfo(lbl,$index) "$f" set cmd "svn" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } if {"$rev" == ""} { set command "$cmd cat $path" } else { set command "$cmd cat -r$rev $path" } die-unless "exec $command" $finfo(pth,$index) } elseif {[sccs-is-bk]} { set cmd "bk" set opt $bkopt set finfo(lbl,$index) "$f (bitkeeper $rev)" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } die-unless "exec $cmd get -p $opt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {[file isdirectory [file join $dirname SCCS]]} { set finfo(lbl,$index) "$f (SCCS $rev)" set opt $sccsopt set cmd "sccs" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } die-unless "exec $cmd get -p $opt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {[file isdirectory [file join $dirname RCS]]} { set cmd "co" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (RCS $rev)" die-unless "exec $cmd -p$rcsopt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {[file exists [file join $dirname $tailname,v]]} { set cmd "co" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (RCS $rev)" die-unless "exec $cmd -p$rcsopt \"$f\"" \""$finfo(pth,$index)\"" } elseif {[file exists [file join $dirname vcs.cfg]] || [info exists ::env(VCSCFG)]} { set cmd "get" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (PVCS $rev)" die-unless "exec $cmd -p $pvcsopt \"$f\"" "\"$finfo(pth,$index)\"" filterCRCRLF $finfo(pth,$index) } elseif {[info exists ::env(P4CLIENT)] || [info exists ::env(P4CONFIG)]} { set cmd "p4" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (Perforce $rev)" die-unless "exec $cmd print -q \"$p4file\"" "\"$finfo(pth,$index)\"" } elseif {[info exists ::env(ACCUREV_BIN)]} { set cmd "accurev" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f ($acrev)" die-unless "exec $cmd cat $acopt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {[info exists ::env(CLEARCASE_ROOT)]} { set cmd "cleartool" set finfo(lbl,$index) "$f (ClearCase $rev)" debug-info "exec $cmd ls -s $f" catch {exec $cmd ls -s $f} ctls # get the path name to file minus the revision info # (either CHECKEDOUT or a number) if {![regexp {(\S+)/([^/]+)$} $ctls dummy path checkedout]} { puts "Couldn't parse ct ls output '$ctls'" exit } debug-info "exec $cmd lshistory -last 50 $f" catch {exec $cmd lshistory -last 50 $f} ctlshistory set lines [split $ctlshistory "\n"] set predecessor "" # find the previous version if {$checkedout == "CHECKEDOUT" || $checkedout == 0} { if {$checkedout == 0} { set path [file dirname $path] } set pattern "create version \"($path/\[^/\]+)\"" } else { incr checkedout -1 set pattern "create version \"($path/$checkedout)\"" } # search the history of the file for the latest version on our branch foreach l $lines { if {[regexp $pattern $l dummy predecessor]} { break } } if {$predecessor != ""} { set finfo(pth,$index) $predecessor debug-info " Setting lbl from predecessor $finfo(lbl,$index)" } else { puts "Couldn't deal with $f, exiting..." exit } } elseif {[is-mercurial-repository $dirname]} { # mercurial support set cmd "hg" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } if {"$r" == "" || "$rev" == "PARENT"} { # in hg, the revision for cat defaults to the parent revision # of the working directory set finfo(lbl,$index) "$f (HG PARENT)" die-unless "exec $cmd cat $f" $finfo(pth,$index) } else { set finfo(lbl,$index) "$f (HG $rev)" die-unless "exec $cmd cat $hgopt $f" $finfo(pth,$index) } } else { fatal-error "File '$f' is not part of a revision control system" } # Header above each file - if user has specified -L, override #debug-info " $finfo(lbl,$index)" if {$finfo(userlbl,$index) != {}} { set finfo(lbl,$index) $finfo(userlbl,$index) debug-info " User label: $finfo(lbl,$index)" } } proc is-mercurial-repository {dirname} { debug-info "is-mercurial-repository ($dirname)" # check for a .hg directory in all parent directories set dirname [file normalize $dirname] set prevdir {} while {$dirname != $prevdir} { set hgfilename [file join $dirname .hg] if {[file isdirectory $hgfilename]} { return true } set prevdir $dirname set dirname [file dirname $dirname] } return false } proc sccs-is-bk {} { debug-info "sccs-is-bk ()" set cmd [auto_execok "bk"] set result 0 if {[string length $cmd] > 0} { debug-info "exec bk root" if {![catch {exec bk root} error]} { set result 1 } } return $result } ############################################################################### # Setup ordinary file # f file name # index index in finfo array ############################################################################### proc get-file {f index} { debug-info "get-file ($f $index)" global finfo #set finfo(f,$index) $f if {[file exists $f] != 1} { fatal-error "File '$f' does not exist" return 1 } if {[file isdirectory $f]} { fatal-error "'$f' is a directory" return 1 } # Header above each file - use filename unless # user has specified one with -L set finfo(lbl,$index) "$f" if {$finfo(userlbl,$index) != {}} { set finfo(lbl,$index) $finfo(userlbl,$index) debug-info " User label: $finfo(lbl,$index)" } set finfo(pth,$index) "$f" set finfo(tmp,$index) 0 return 0 } ############################################################################### # Read the commandline ############################################################################### proc commandline {} { debug-info "commandline" global argv global argc debug-info " argv: $argv" global finfo global opts global g set g(initOK) 0 set argindex 0 set revs 0 set pths 0 set lbls 0 # Loop through argv, storing revision args in rev and file args in # finfo. revs and pths are counters. while {$argindex < $argc} { set arg [lindex $argv $argindex] switch -regexp -- $arg { "^-h" - "^--help" { do-usage cline exit 0 } "^-a$" { incr argindex set g(ancfile) [lindex $argv $argindex] } "^-a.*" { set g(ancfile) [string range $arg 2 end] } "^-v$" - "^-r$" { incr argindex incr revs set finfo(revs,$revs) [lindex $argv $argindex] } "^-v.*" - "^-r.*" { incr revs set finfo(revs,$revs) [string range $arg 2 end] } "^-L$" { incr argindex incr lbls set finfo(userlbl,$lbls) [lindex $argv $argindex] } "^-L.*" { incr lbls set finfo(userlbl,$lbls) [string range $arg 2 end] } "^-conflict$" { set g(conflictset) 1 } "^-o$" { incr argindex set g(mergefile) [lindex $argv $argindex] } "^-o.*" { set g(mergefile) [string range $arg 2 end] } "^-u$" { # Ignore flag from "svn diff --diff-cmd=tkdiff" } "^-d$" { set g(debug) t } "^-psn" { # Ignore the Carbon Process Serial Number set argv [lreplace $argv $argindex $argindex] incr argc -1 incr argindex } "^-" { append opts(diffcmd) " $arg " } default { incr pths set finfo(pth,$pths) $arg set finfo(f,$pths) $arg } } incr argindex } # Add our counters to the global array # Now check how many revision and file args we have. #debug-info " $pths files, $revs revisions" # Maybe adjustment is needed if {$revs == 1 && $pths == 0} { # tkdiff -r FILE; same as tkdiff FILE set finfo(pths,1) $finfo(revs,1) set finfo(f,1) $finfo(revs,1) incr pths 1 incr revs -1 unset finfo(revs,1) } elseif {$revs == 2 && $pths == 0} { # tkdiff -rREV -r FILE; same as tkdiff -rREV FILE set finfo(pths,1) $finfo(revs,2) set finfo(f,1) $finfo(revs,2) incr pths 1 incr revs -1 unset finfo(revs,2) } # What have we got now? debug-info " $pths files, $revs revisions" if {$revs == 0 && $pths == 0} { # Return "empty" flag, and we'll do a pop-up return 1 } elseif {$revs > 1 && $pths != 1} { puts stderr "Error: you specified $pths file(s) and $revs revision(s)" do-usage cline exit 1 } if {$g(mergefile) != ""} { set g(mergefileset) 1 } return 0 } ############################################################################### # Process the arguments, whether from the command line or from the dialog ############################################################################### proc assemble-args {} { debug-info "assemble-args ()" global finfo global opts global g if {$g(ancfile) != ""} { set g(ancfileset) 1 } #debug-info " conflict: $g(conflictset)" #debug-info " ancestor: $g(ancfileset) $g(ancfile)" #debug-info " mergefile set: $g(mergefileset) $g(mergefile)" #debug-info " diff command: $opts(diffcmd) " # Count up how many files and revs we got from the GUI or commandline set pths 0 foreach p [array names finfo f,*] { if {$finfo($p) != ""} { incr pths } } set revs 0 foreach r [array names finfo revs,*] { if {$finfo($r) != ""} { incr revs } } debug-info " $pths files, $revs revisions" if {$revs == 0 && $pths == 0} { return } if {$g(conflictset)} { if {$revs == 0 && $pths == 1} { ############################################################ # tkdiff -conflict FILE ############################################################ set files [split-conflictfile "$finfo(f,1)"] if {[get-file [lindex "$files" 0] 1]} {return} if {[get-file [lindex "$files" 1] 2]} {return} # A conflict file may come from merge, cvs, or vmrg. The # names of the files/revisions depend on how it was made and # are taken from the <<<<<<< and >>>>>>> lines inside it. set finfo(lbl,1) [lindex "$files" 2] set finfo(lbl,2) [lindex "$files" 3] } else { fatal-error "Usage: tkdiff -conflict FILE" } } else { if {$revs == 2 && $pths == 1} { ############################################################ # tkdiff -rREV1 -rREV2 FILE ############################################################ set f $finfo(f,1) get-file-rev "$f" 1 "$finfo(revs,1)" get-file-rev "$f" 2 "$finfo(revs,2)" } elseif {$revs == 1 && $pths == 1} { ############################################################ # tkdiff -rREV FILE ############################################################ set f $finfo(f,1) get-file-rev "$f" 1 "$finfo(revs,1)" if {[get-file "$f" 2]} {return} } elseif {$revs == 0 && $pths == 2} { ############################################################ # tkdiff FILE1 FILE2 ############################################################ set f1 $finfo(f,1) set f2 $finfo(f,2) if {[file isdirectory $f1] && [file isdirectory $f2]} { fatal-error "Cannot diff two directories" } if {[file isdirectory $f1]} { set f1 [file join $f1 [file tail $f2]] } elseif {[file isdirectory $f2]} { set f2 [file join $f2 [file tail $f1]] } # Maybe they're Subversion URL paths, not local files if {[regexp {://} $f1]} { get-file-rev "$f1" 1 } else { if {[get-file "$f1" 1]} {return} } if {[regexp {://} $f2]} { get-file-rev "$f2" 2 } else { if {[get-file "$f2" 2]} {return} } } elseif {$revs == 0 && $pths == 1} { ############################################################ # tkdiff FILE ############################################################ set f $finfo(f,1) get-file-rev "$f" 1 if {[get-file "$f" 2]} {return} } else { if {[winfo exists .toolbar]} { do-error "Error: you specified $pths file(s) and $revs revision(s)" do-usage gui tkwait window .usage return 1 } else { puts stderr "Error: you specified $pths file(s) and $revs revision(s)" do-usage cline exit 1 } } } set finfo(title) "[file tail $finfo(lbl,1)] vs. [file tail $finfo(lbl,2)]" #debug-info " Setting title $finfo(title)" set rootname [file rootname $finfo(pth,1)] # set path [file dirname $finfo(pth,1)] set path [pwd] set suffix [file extension $finfo(pth,1)] if {! $g(mergefileset)} { set g(mergefile) [file join $path "${rootname}-merge$suffix"] } set g(initOK) 1 #debug-info " $revs revs $pths files" wm title . "$finfo(title) - $g(name) $g(version)" return 0 } ############################################################################### # Set up the display ############################################################################### proc create-display {} { debug-info "create-display ()" global g opts bg tk_version global w global tmpopts # these are the four major areas of the GUI: # menubar - the menubar (duh) # toolbar - the toolbar (duh, again) # client - the area with the text widgets and the graphical map # status us - a bottom status line # this block of destroys is only for stand-alone testing of # the GUI code, and can be blown away (or not, if we want to # be able to call this routine to recreate the display...) catch { destroy .menubar destroy .toolbar destroy .client destroy .map destroy .status } # create the top level frames and store them in a global # array.. set w(client) .client set w(menubar) .menubar set w(toolbar) .toolbar set w(status) .status # other random windows... set w(preferences) .pref set w(findDialog) .findDialog set w(popupMenu) .popupMenu # now, simply build all the pieces build-menubar build-toolbar build-client build-status build-popupMenu frame .separator1 -height 2 -borderwidth 2 -relief groove frame .separator2 -height 2 -borderwidth 2 -relief groove # ... and fit it all together... . configure -menu $w(menubar) pack $w(toolbar) -side top -fill x -expand n pack .separator1 -side top -fill x -expand n pack $w(client) -side top -fill both -expand y pack .separator2 -side top -fill x -expand n pack $w(status) -side bottom -fill x -expand n # apply user preferences by calling the proc that gets # called when the user presses "Apply" from the preferences # window. That proc uses a global variable named "tmpopts" # which should have the values from the dialog. Since we # aren't using the dialog, we need to populate this array # manually foreach key [array names opts] { set ::tmpopts($key) $opts($key) } apply 0 # Make sure temporary files get deleted #bind . {del-tmp} # other misc. bindings common-navigation $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB) # normally, keyboard traversal using tab and shift-tab isn't # enabled for text widgets, since the default binding for these # keys is to actually insert the tab character. Because all of # our text widgets are for display only, let's redefine the # default binding so the global and bindings # are used. bind Text {continue} bind Text {continue} # if the user toggles scrollbar syncing, we want to make sure # they sync up immediately trace variable opts(syncscroll) w toggleSyncScroll wm deiconify . focus -force $w(RightText) update idletasks # Need this to make the pane-resizing behave grid propagate $w(client) f } ############################################################################### # when the user changes the "sync scrollbars" option, we want to # sync up the left scrollbar with the right if they turn the option on ############################################################################### proc toggleSyncScroll {args} { global opts global w if {$opts(syncscroll) == 1} { eval vscroll-sync {{}} 2 [$w(RightText) yview] } } ############################################################################### # show the popup menu, optionally changing some of the entries based on # where the user clicked ############################################################################### proc show-popupMenu {x y} { global w global g set window [winfo containing $x $y] if {[winfo class $window] == "Text"} { $w(popupMenu) entryconfigure "Find..." -state normal $w(popupMenu) entryconfigure "Find Nearest*" -state normal $w(popupMenu) entryconfigure "Edit*" -state normal if {$window == $w(LeftText) || $window == $w(LeftInfo) || $window == \ $w(LeftCB)} { $w(popupMenu) configure -title "File 1" set g(activeWindow) $w(LeftText) } else { $w(popupMenu) configure -title "File 2" set g(activeWindow) $w(RightText) } } else { $w(popupMenu) entryconfigure "Find..." -state disabled $w(popupMenu) entryconfigure "Find Nearest*" -state disabled $w(popupMenu) entryconfigure "Edit*" -state disabled } tk_popup $w(popupMenu) $x $y } ############################################################################### # build the right-click popup menu ############################################################################### proc build-popupMenu {} { debug-info "build-popupMenu ()" global w g # this routine assumes the other windows already exist... menu $w(popupMenu) foreach win [list LeftText RightText LeftInfo RightInfo LeftCB RightCB \ mapCanvas] { bind $w($win) <3> {show-popupMenu %X %Y} } set m $w(popupMenu) $m add command -label "First Diff" -underline 0 -command [list popupMenu \ first] -accelerator "f" $m add command -label "Previous Diff" -underline 0 -command \ [list popupMenu previous] -accelerator "p" $m add command -label "Center Current Diff" -underline 0 -command \ [list popupMenu center] -accelerator "c" $m add command -label "Next Diff" -underline 0 -command [list popupMenu \ next] -accelerator "n" $m add command -label "Last Diff" -underline 0 -command [list popupMenu \ last] -accelerator "l" $m add separator $m add command -label "Find Nearest Diff" -underline 0 -command \ [list popupMenu nearest] -accelerator "Double-Click" $m add separator $m add command -label "Find..." -underline 0 -command [list popupMenu find] $m add command -label "Edit" -underline 0 -command [list popupMenu edit] } ############################################################################### # handle popup menu commands ############################################################################### proc popupMenu {command args} { debug-info "popupMenu ($command $args)" global g global w switch -- $command { center { center } edit { do-edit } find { do-find } first { move first } last { move last } next { move 1 } previous { move -1 } nearest { moveNearest $g(activeWindow) xy [winfo pointerx $g(activeWindow)] \ [winfo pointery $g(activeWindow)] } } } # Resize the text windows relative to each other. The 8.4 method works # much better. proc pane_drag {win x} { global w global finfo global tk_version set relX [expr $x - [winfo rootx $win]] set maxX [winfo width $win] set frac [expr int((double($relX) / $maxX) * 100)] if {$tk_version < 8.4} { if {$frac < 15} { set frac 15 } if {$frac > 85} { set frac 85 } #debug-info "frac $frac" set L $frac set R [expr 100 - $frac] .client.leftlabel configure -width [expr $L * 2] .client.rightlabel configure -width [expr $R * 2] } else { if {$frac < 5} { set frac 5 } if {$frac > 95} { set frac 95 } #debug-info "frac $frac" set L $frac set R [expr 100 - $frac] grid columnconfigure $win 0 -weight $L grid columnconfigure $win 2 -weight $R } #debug-info " new: $L $R" } ############################################################################### # build the main client display (the text widgets, scrollbars, that # sort of fluff) ############################################################################### proc build-client {} { debug-info "build-client ()" global g global w global opts global map global tk_version frame $w(client) -bd 2 -relief flat # set up global variables to reference the widgets, so # we don't have to use hardcoded widget paths elsewhere # in the code # # Text - holds the text of the file # Info - sort-of "invisible" text widget which is kept in sync # with the text widget and holds line numbers # CB - contains changebars or status or something like that... # VSB - vertical scrollbar # HSB - horizontal scrollbar # Label - label to hold the name of the file set w(LeftText) $w(client).left.text set w(LeftInfo) $w(client).left.info set w(LeftCB) $w(client).left.changeBars set w(LeftVSB) $w(client).left.vsb set w(LeftHSB) $w(client).left.hsb set w(LeftLabel) $w(client).leftlabel set w(RightText) $w(client).right.text set w(RightInfo) $w(client).right.info set w(RightCB) $w(client).right.changeBars set w(RightVSB) $w(client).right.vsb set w(RightHSB) $w(client).right.hsb set w(RightLabel) $w(client).rightlabel set w(BottomText) $w(client).bottomtext set w(map) $w(client).map set w(mapCanvas) $w(map).canvas # these don't need to be global... set leftFrame $w(client).left set rightFrame $w(client).right # we'll create each widget twice; once for the left side # and once for the right. debug-info " Assigning labels to headers" scan $opts(geometry) "%dx%d" width height label $w(LeftLabel) -bd 1 -relief flat -textvariable finfo(lbl,1) -width $width label $w(RightLabel) -bd 1 -relief flat -textvariable finfo(lbl,2) -width $width # this holds the text widgets and the scrollbars. The reason # for the frame is purely for aesthetics. It just looks # nicer, IMHO, to "embed" the scrollbars within the text # widget frame $leftFrame -bd 1 -relief sunken frame $rightFrame -bd 1 -relief sunken scrollbar $w(LeftHSB) -borderwidth 1 -orient horizontal -command \ [list $w(LeftText) xview] scrollbar $w(RightHSB) -borderwidth 1 -orient horizontal -command \ [list $w(RightText) xview] scrollbar $w(LeftVSB) -borderwidth 1 -orient vertical -command \ [list $w(LeftText) yview] scrollbar $w(RightVSB) -borderwidth 1 -orient vertical -command \ [list $w(RightText) yview] text $w(LeftText) -padx 0 -wrap none -width $width -height $height \ -borderwidth 0 -setgrid 1 -yscrollcommand [list vscroll-sync \ "$w(LeftInfo) $w(LeftCB)" 1] -xscrollcommand [list hscroll-sync 1] text $w(RightText) -padx 0 -wrap none -width $width -height $height \ -borderwidth 0 -setgrid 1 -yscrollcommand [list vscroll-sync \ "$w(RightInfo) $w(RightCB)" 2] -xscrollcommand [list hscroll-sync 2] text $w(LeftInfo) -height 0 -padx 0 -width 6 -borderwidth 0 -setgrid 1 \ -yscrollcommand [list vscroll-sync "$w(LeftCB) $w(LeftText)" 1] text $w(RightInfo) -height 0 -padx 0 -width 6 -borderwidth 0 -setgrid 1 \ -yscrollcommand [list vscroll-sync "$w(RightCB) $w(RightText)" 2] # each and every line in a text window will have a corresponding line # in this widget. And each line in this widget will be composed of # a single character (either "+", "-" or "!" for insertion, deletion # or change, respectively text $w(LeftCB) -height 0 -padx 0 -highlightthickness 0 -wrap none \ -foreground white -width 1 -borderwidth 0 -yscrollcommand \ [list vscroll-sync "$w(LeftInfo) $w(LeftText)" 1] text $w(RightCB) -height 0 -padx 0 -highlightthickness 0 -wrap none \ -background white -foreground white -width 1 -borderwidth 0 \ -yscrollcommand [list vscroll-sync "$w(RightInfo) $w(RightText)" 2] # this widget is the two line display showing the current line, so # one can compare character by character if necessary. text $w(BottomText) -wrap none -borderwidth 1 -height 2 -width 0 # this is how we highlight bytes that are different... # the bottom window (lineview) uses reverse video to highlight # diffs, so we need to figure out what reverse video is, and # define the tag appropriately eval $w(BottomText) tag configure diff $opts(bytetag) # Set up text tags for the 'current diff' (the one chosen by the 'next' # and 'prev' buttons) and any ol' diff region. All diff regions are # given the 'diff' tag initially... As 'next' and 'prev' are \ pressed, # to scroll through the differences, one particular diff region is # always chosen as the 'current diff', and is set off from the others # via the 'diff' tag -- in particular, so that it's obvious which diffs # in the left and right-hand text widgets match. foreach widget [list $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB)] { eval "$widget configure $opts(textopt)" foreach tag {difftag currtag inlinetag deltag instag chgtag \ overlaptag + - ! ?} { eval "$widget tag configure $tag $opts($tag)" } } # adjust the tag priorities a bit... foreach window [list LeftText RightText LeftCB RightCB LeftInfo RightInfo] { $w($window) tag raise deltag currtag $w($window) tag raise chgtag currtag $w($window) tag raise instag currtag $w($window) tag raise currtag difftag $w($window) tag raise inlinetag } # these tags are specific to change bars foreach widget [list $w(LeftCB) $w(RightCB)] { eval "$widget tag configure + $opts(+)" eval "$widget tag configure - $opts(-)" eval "$widget tag configure ! $opts(!)" eval "$widget tag configure ? $opts(?)" } # build the map... # we want the map to be the same width as a scrollbar, so we'll # steal some information from one of the scrollbars we just # created... set cwidth [winfo reqwidth $w(LeftVSB)] set ht [$w(LeftVSB) cget -highlightthickness] set cwidth [expr {$cwidth -($ht*2)}] set color [$w(LeftVSB) cget -troughcolor] set map [frame $w(client).map -bd 1 -relief sunken -takefocus 0 \ -highlightthickness 0] # now for the real map... image create photo map canvas $w(mapCanvas) -width [expr {$cwidth + 1}] \ -yscrollcommand map-resize -background $color -borderwidth 0 \ -relief sunken -highlightthickness 0 $w(mapCanvas) create image 1 1 -image map -anchor nw pack $w(mapCanvas) -side top -fill both -expand y # I'm not too pleased with these bindings -- it results in a rather # jerky, cpu-intensive maneuver since with each move of the mouse # we are finding and tagging the nearest diff. But, what *should* # it do? # # I think what I *want* it to do is update the combobox and status # bar so the user can see where in the scheme of things they are, # but not actually select anything until they release the mouse. bind $w(mapCanvas) [list handleMapEvent B1-Press %y] bind $w(mapCanvas) [list handleMapEvent B1-Motion %y] bind $w(mapCanvas) [list handleMapEvent B1-Release %y] # this is a grip for resizing the sides relative to each other. button $w(client).grip -borderwidth 3 -relief raised \ -cursor sb_h_double_arrow -image resize bind $w(client).grip {pane_drag $w(client) %X} # use grid to manage the widgets in the left side frame grid $w(LeftVSB) -row 0 -column 0 -sticky ns grid $w(LeftInfo) -row 0 -column 1 -sticky nsew grid $w(LeftCB) -row 0 -column 2 -sticky ns grid $w(LeftText) -row 0 -column 3 -sticky nsew grid $w(LeftHSB) -row 1 -column 1 -sticky ew -columnspan 3 grid rowconfigure $leftFrame 0 -weight 1 grid rowconfigure $leftFrame 1 -weight 0 grid columnconfigure $leftFrame 0 -weight 0 grid columnconfigure $leftFrame 1 -weight 0 grid columnconfigure $leftFrame 2 -weight 0 grid columnconfigure $leftFrame 3 -weight 1 # likewise for the right... grid $w(RightVSB) -row 0 -column 3 -sticky ns grid $w(RightInfo) -row 0 -column 0 -sticky nsew grid $w(RightCB) -row 0 -column 1 -sticky ns grid $w(RightText) -row 0 -column 2 -sticky nsew grid $w(RightHSB) -row 1 -column 0 -sticky ew -columnspan 3 grid rowconfigure $rightFrame 0 -weight 1 grid rowconfigure $rightFrame 1 -weight 0 grid columnconfigure $rightFrame 0 -weight 0 grid columnconfigure $rightFrame 1 -weight 0 grid columnconfigure $rightFrame 2 -weight 1 grid columnconfigure $rightFrame 3 -weight 0 # use grid to manage the labels, frames and map. We're going to # toss in an extra row just for the benefit of our dummy frame. # the intent is that the dummy frame will match the height of # the horizontal scrollbars so the map stops at the right place... grid $w(LeftLabel) -row 0 -column 0 -sticky ew grid $w(RightLabel) -row 0 -column 2 -sticky ew grid $leftFrame -row 1 -column 0 -sticky nsew -rowspan 2 grid $map -row 1 -column 1 -stick ns grid $w(client).grip -row 2 -column 1 grid $rightFrame -row 1 -column 2 -sticky nsew -rowspan 2 grid rowconfigure $w(client) 0 -weight 0 grid rowconfigure $w(client) 1 -weight 1 grid rowconfigure $w(client) 2 -weight 0 grid rowconfigure $w(client) 3 -weight 0 if {$tk_version < 8.4} { grid columnconfigure $w(client) 0 -weight 1 grid columnconfigure $w(client) 2 -weight 1 } else { grid columnconfigure $w(client) 0 -weight 100 -uniform a grid columnconfigure $w(client) 2 -weight 100 -uniform a } grid columnconfigure $w(client) 1 -weight 0 # this adjusts the variable g(activeWindow) to be whatever text # widget has the focus... bind $w(LeftText) <1> {set g(activeWindow) $w(LeftText)} bind $w(RightText) <1> {set g(activeWindow) $w(RightText)} set g(activeWindow) $w(LeftText) ;# establish a default rename $w(RightText) $w(RightText)_ rename $w(LeftText) $w(LeftText)_ proc $w(RightText) {command args} $::text_widget_proc proc $w(LeftText) {command args} $::text_widget_proc } ############################################################################### # Functionality: Inline diffs # Athr: Michael D. Beynon : mdb - beynon@yahoo.com # Date: 04/08/2003 : mdb - Added inline character diffs. # 04/16/2003 : mdb - Rewrote longest-common-substring to be faster. # - Added byte-by-byte algorithm. # # the recursive version is derived from the Ratcliff/Obershelp pattern # recognition algorithm (Dr Dobbs July 1988), where we search for a # longest common substring between two strings. This match is used as # an archor, around which we recursively do the same for the two left # and two right remaining pieces (omitting the anchor). This # precisely determines the location of the intraline tags. ################################################################################# proc longest-common-substring {s1 off1 len1 s2 off2 len2 lcsoff1_ref \ lcsoff2_ref} { upvar $lcsoff1_ref lcsoff1 upvar $lcsoff2_ref lcsoff2 set snippet "" set snippetlen 0 set longestlen 0 # extract just the search regions for efficiency in string searching set s1 [string range $s1 $off1 [expr $off1+$len1-1]] set s2 [string range $s2 $off2 [expr $off2+$len2-1]] set j 0 while {1} { # increase size of matching snippet while {$snippetlen < $len2-$j} { set tmp "$snippet[string index $s2 [expr $j+$snippetlen]]" if {[string first $tmp $s1] == -1} { break } set snippet $tmp incr snippetlen } if {$snippetlen == 0} { # nothing starting at this position incr j if {$snippetlen >= $len2-$j} { break } } else { set tmpoff [string first $snippet $s1] if {$tmpoff != -1 && $snippetlen > $longestlen} { # new longest? set longest $snippet set longestlen $snippetlen set lcsoff1 [expr $off1+$tmpoff] set lcsoff2 [expr $off2+$j] } # drop 1st char of prefix, but keep size the same as longest if {$snippetlen >= $len2-$j} { break } set snippet "[string range $snippet 1 end][string index $s2 \ [expr $j+$snippetlen]]" incr j } } return $longestlen } proc fid-ratcliff-aux {pos l1 l2 s1 off1 len1 s2 off2 len2} { global g if {$len1 <= 0 || $len2 <= 0} { if {$len1 == 0} { set g(scrinline,$pos,$g(scrinline,$pos)) [list r $l2 $off2 \ [expr $off2+$len2]] incr g(scrinline,$pos) } elseif {$len2 == 0} { set g(scrinline,$pos,$g(scrinline,$pos)) [list l $l1 $off1 \ [expr $off1+$len1]] incr g(scrinline,$pos) } return 0 } set cnt 0 set lcsoff1 -1 set lcsoff2 -1 set ret [longest-common-substring $s1 $off1 $len1 $s2 $off2 $len2 lcsoff1 \ lcsoff2] if {$ret > 0} { set rightoff1 [expr $lcsoff1+$ret] set rightoff2 [expr $lcsoff2+$ret] incr cnt [expr 2*$ret] if {$lcsoff1 > $off1 || $lcsoff2 > $off2} { # left incr cnt [fid-ratcliff-aux $pos $l1 $l2 $s1 $off1 \ [expr $lcsoff1-$off1] $s2 $off2 [expr $lcsoff2-$off2]] } if {$rightoff1<$off1+$len1 || $rightoff2<$off2+$len2} { # right incr cnt [fid-ratcliff-aux $pos $l1 $l2 $s1 $rightoff1 \ [expr $off1+$len1-$rightoff1] $s2 $rightoff2 \ [expr $off2+$len2-$rightoff2]] } } else { set g(scrinline,$pos,$g(scrinline,$pos)) [list r $l2 $off2 \ [expr $off2+$len2]] incr g(scrinline,$pos) set g(scrinline,$pos,$g(scrinline,$pos)) [list l $l1 $off1 \ [expr $off1+$len1]] incr g(scrinline,$pos) } return $cnt } proc find-inline-diff-ratcliff {pos l1 l2 s1 s2} { global g set len1 [string length $s1] set len2 [string length $s2] if {$len1 == 0 || $len2 == 0} { return 0 } return [fid-ratcliff-aux $pos $l1 $l2 $s1 0 $len1 $s2 0 $len2] } proc find-inline-diff-byte {pos l1 l2 s1 s2} { global g set len1 [string length $s1] set len2 [string length $s2] if {$len1 == 0 || $len2 == 0} { return 0 } set cnt 0 set lenmin [min $len1 $len2] set size 0 for {set i 0} {$i < $lenmin} {incr i} { if {$size > 0} { # in a diff section if {[string index $s1 $i] == [string index $s2 $i]} { # end of diff region set g(scrinline,$pos,$g(scrinline,$pos)) [list r $l2 \ [expr $i-$size] $i] incr g(scrinline,$pos) set g(scrinline,$pos,$g(scrinline,$pos)) [list l $l1 \ [expr $i-$size] $i] incr g(scrinline,$pos) set size 0 incr cnt } else { incr size } } else { if {[string index $s1 $i] != [string index $s2 $i]} { set size 1 } } } if {$size > 0} { # end of diff region set g(scrinline,$pos,$g(scrinline,$pos)) [list r $l2 [expr $i-$size] \ $len2] incr g(scrinline,$pos) set g(scrinline,$pos,$g(scrinline,$pos)) [list l $l1 [expr $i-$size] \ $len1] incr g(scrinline,$pos) incr cnt } return $cnt } ############################################################################### # the following code is used as the replacement body for the left and # right widget procs. The purpose is to catch when the insertion point # changes so we can update the line comparison window ############################################################################### set text_widget_proc { global w set real "[lindex [info level [info level]] 0]_" set result [eval $real $command $args] if {$command == "mark"} { if {[lindex $args 0] == "set" && [lindex $args 1] == "insert"} { set i [lindex $args 2] set i0 "$i linestart" set i1 "$i lineend" set left [$w(LeftText)_ get $i0 $i1] set right [$w(RightText)_ get $i0 $i1] $w(BottomText) delete 1.0 end $w(BottomText) insert end "< $left\n> $right" # find characters that are different, and underline them if {$left != $right} { set left [split $left {}] set right [split $right {}] # n.b. we set c to an offset equal to whatever we have # prepended to the data... set c 2 foreach l $left r $right { if {[string compare $l $r] != 0} { $w(BottomText) tag add diff 1.$c "1.$c+1c" $w(BottomText) tag add diff 2.$c "2.$c+1c" } incr c } $w(BottomText) tag remove diff "1.0 lineend" $w(BottomText) tag remove diff "2.0 lineend" } } } return $result } ############################################################################### # create (if necessary) and show the find dialog ############################################################################### proc show-find {} { debug-info "show-find ()" global w g global tcl_platform if {![winfo exists $w(findDialog)]} { toplevel $w(findDialog) wm group $w(findDialog) . wm transient $w(findDialog) . wm title $w(findDialog) "$g(name) Find" if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w(findDialog) } # we don't want the window to be deleted, just hidden from view wm protocol $w(findDialog) WM_DELETE_WINDOW [list wm withdraw \ $w(findDialog)] wm withdraw $w(findDialog) update idletasks frame $w(findDialog).content -bd 2 -relief groove pack $w(findDialog).content -side top -fill both -expand y -padx 0 \ -pady 5 frame $w(findDialog).buttons pack $w(findDialog).buttons -side bottom -fill x -expand n button $w(findDialog).buttons.doit -text "Find Next" -command do-find button $w(findDialog).buttons.dismiss -text "Dismiss" -command \ "wm withdraw $w(findDialog)" pack $w(findDialog).buttons.dismiss -side right -pady 5 -padx 0 pack $w(findDialog).buttons.doit -side right -pady 5 -padx 1 set ff $w(findDialog).content.findFrame frame $ff -height 100 -bd 2 -relief flat pack $ff -side top -fill x -expand n -padx 0 -pady 5 label $ff.label -text "Find what:" -underline 2 entry $ff.entry -textvariable g(findString) checkbutton $ff.searchCase -text "Ignore Case" -offvalue 0 -onvalue 1 \ -indicatoron true -variable g(findIgnoreCase) grid $ff.label -row 0 -column 0 -sticky e grid $ff.entry -row 0 -column 1 -sticky ew grid $ff.searchCase -row 0 -column 2 -sticky w grid columnconfigure $ff 0 -weight 0 grid columnconfigure $ff 1 -weight 1 grid columnconfigure $ff 2 -weight 0 # we need this in other places... set w(findEntry) $ff.entry bind $ff.entry do-find set of $w(findDialog).content.optionsFrame frame $of -bd 2 -relief flat pack $of -side top -fill y -expand y -padx 10 -pady 10 label $of.directionLabel -text "Search Direction:" -anchor e radiobutton $of.directionForward -indicatoron true -text "Down" \ -value "-forward" -variable g(findDirection) radiobutton $of.directionBackward -text "Up" -value "-backward" \ -indicatoron true -variable g(findDirection) label $of.windowLabel -text "Window:" -anchor e radiobutton $of.windowLeft -indicatoron true -text "Left" \ -value $w(LeftText) -variable g(activeWindow) radiobutton $of.windowRight -indicatoron true -text "Right" \ -value $w(RightText) -variable g(activeWindow) label $of.searchLabel -text "Search Type:" -anchor e radiobutton $of.searchExact -indicatoron true -text "Exact" \ -value "-exact" -variable g(findType) radiobutton $of.searchRegexp -text "Regexp" -value "-regexp" \ -indicatoron true -variable g(findType) grid $of.directionLabel -row 1 -column 0 -sticky w grid $of.directionForward -row 1 -column 1 -sticky w grid $of.directionBackward -row 1 -column 2 -sticky w grid $of.windowLabel -row 0 -column 0 -sticky w grid $of.windowLeft -row 0 -column 1 -sticky w grid $of.windowRight -row 0 -column 2 -sticky w grid $of.searchLabel -row 2 -column 0 -sticky w grid $of.searchExact -row 2 -column 1 -sticky w grid $of.searchRegexp -row 2 -column 2 -sticky w grid columnconfigure $of 0 -weight 0 grid columnconfigure $of 1 -weight 0 grid columnconfigure $of 2 -weight 1 set g(findDirection) "-forward" set g(findType) "-exact" set g(findIgnoreCase) 1 set g(lastSearch) "" if {$g(activeWindow) == ""} { set g(activeWindow) [focus] if {$g(activeWindow) != $w(LeftText) && $g(activeWindow) != \ $w(RightText)} { set g(activeWindow) $w(LeftText) } } } centerWindow $w(findDialog) wm deiconify $w(findDialog) raise $w(findDialog) after idle focus $w(findEntry) } ############################################################################### # do the "Edit->Copy" functionality, by copying the current selection # to the clipboard ############################################################################### proc do-copy {} { clipboard clear -displayof . # figure out which window has the selection... catch { clipboard append [selection get -displayof .] } } ############################################################################### # search for the text in the find dialog ############################################################################### proc do-find {} { debug-info "do-find ()" global g global w if {![winfo exists $w(findDialog)] || ![winfo ismapped $w(findDialog)]} { show-find return } set win $g(activeWindow) if {$win == ""} { set win $w(LeftText) } if {$g(lastSearch) != ""} { if {$g(findDirection) == "-forward"} { set start [$win index "insert +1c"] } else { set start insert } } else { set start 1.0 } if {$g(findIgnoreCase)} { set result [$win search $g(findDirection) $g(findType) -nocase \ -- $g(findString) $start] } else { set result [$win search $g(findDirection) $g(findType) \ -- $g(findString) $start] } if {[string length $result] > 0} { # if this is a regular expression search, get the whole line and try # to figure out exactly what matched; otherwise we know we must # have matched the whole string... if {$g(findType) == "-regexp"} { set line [$win get $result "$result lineend"] regexp $g(findString) $line matchVar set length [string length $matchVar] } else { set length [string length $g(findString)] } set g(lastSearch) $result $win mark set insert $result $win tag remove sel 1.0 end $win tag add sel $result "$result + ${length}c" $win see $result focus $win # should I somehow snap to the nearest diff? Probably not... } else { bell } } ############################################################################### # Build the menu bar ############################################################################### proc build-menubar {} { debug-info "build-menubar ()" global g global opts global w menu $w(menubar) # this is just temporary shorthand ... set menubar $w(menubar) # First, the menu buttons... set fileMenu $w(menubar).file set viewMenu $w(menubar).view set helpMenu $w(menubar).help set editMenu $w(menubar).edit set mergeMenu $w(menubar).window set markMenu $w(menubar).marks $w(menubar) add cascade -label "File" -menu $fileMenu -underline 0 $w(menubar) add cascade -label "Edit" -menu $editMenu -underline 0 $w(menubar) add cascade -label "View" -menu $viewMenu -underline 0 $w(menubar) add cascade -label "Mark" -menu $markMenu -underline 3 $w(menubar) add cascade -label "Merge" -menu $mergeMenu -underline 0 $w(menubar) add cascade -label "Help" -menu $helpMenu -underline 0 # these, however, are used in other places.. set w(fileMenu) $fileMenu set w(viewMenu) $viewMenu set w(helpMenu) $helpMenu set w(editMenu) $editMenu set w(mergeMenu) $mergeMenu set w(markMenu) $markMenu # Now, the menus... # Mark menu... menu $markMenu $markMenu add command -label "Mark Current Diff" -command [list diffmark \ mark] -underline 0 $markMenu add command -label "Clear Current Diff Mark" -command \ [list diffmark clear] -underline 0 set "g(tooltip,Mark Current Diff)" "Create a marker for the current \ difference record" set "g(tooltip,Clear Current Diff Mark)" "Clear the marker for the \ current difference record" # File menu... menu $fileMenu $fileMenu add command -label "New..." -underline 0 -command {do-new-diff} $fileMenu add separator $fileMenu add command -label "Recompute Diffs" -underline 0 \ -accelerator r -command recompute-diff $fileMenu add command -label "Write Report..." -command \ [list write-report popup] -underline 0 $fileMenu add separator $fileMenu add command -label "Exit" -underline 1 -accelerator q \ -command do-exit # Edit menu... If you change, add or remove labels, be sure and # update the tooltips. menu $editMenu $editMenu add command -label "Copy" -underline 0 -command do-copy $editMenu add separator $editMenu add command -label "Find..." -underline 0 -command show-find $editMenu add separator $editMenu add command -label "Edit File 1" -command { set g(activeWindow) $w(LeftText) do-edit } -underline 10 $editMenu add command -label "Edit File 2" -command { set g(activeWindow) $w(RightText) do-edit } -underline 10 $editMenu add separator $editMenu add command -label "Preferences..." -underline 0 \ -command customize set "g(tooltip,Copy)" "Copy the currently selected text to the clipboard" set "g(tooltip,Find...)" "Pop up a dialog to search for a string within \ either file" set "g(tooltip,Edit File 1)" "Launch an editor on the file on the left \ side of the window" set "g(tooltip,Edit File 2)" "Launch an editor on the file on the right \ side of the window" set "g(tooltip,Preferences...)" "Pop up a window to customize $g(name)" # View menu... If you change, add or remove labels, be sure and # update the tooltips. menu $viewMenu $viewMenu add checkbutton -label "Ignore White Spaces" -underline 8 \ -variable opts(ignoreblanks) \ -command do-show-ignoreblanks $viewMenu add checkbutton -label "Show Line Numbers" -underline 12 \ -variable opts(showln) \ -command do-show-linenumbers $viewMenu add checkbutton -label "Show Change Bars" -underline 12 \ -variable opts(showcbs) \ -command do-show-changebars $viewMenu add checkbutton -label "Show Diff Map" -underline 5 \ -variable opts(showmap) -command do-show-map $viewMenu add checkbutton -label "Show Line Comparison Window" \ -underline 11 -variable opts(showlineview) \ -command do-show-lineview $viewMenu add checkbutton -label "Show Inline Comparison (byte)" \ -variable opts(showinline1) \ -command do-show-inline1 $viewMenu add checkbutton -label "Show Inline Comparison (recursive)" \ -variable opts(showinline2) \ -command do-show-inline2 $viewMenu add separator $viewMenu add checkbutton -label "Synchronize Scrollbars" -underline 0 \ -variable opts(syncscroll) $viewMenu add checkbutton -label "Auto Center" -underline 0 \ -variable opts(autocenter) -command {if \ {$opts(autocenter)} {center}} $viewMenu add checkbutton -label "Auto Select" -underline 1 \ -variable opts(autoselect) $viewMenu add separator $viewMenu add command -label "First Diff" -underline 0 -command \ {move first} -accelerator "F" $viewMenu add command -label "Previous Diff" -underline 0 -command {move \ -1} -accelerator "P" $viewMenu add command -label "Center Current Diff" -underline 0 \ -command {center} -accelerator "C" $viewMenu add command -label "Next Diff" -underline 0 -command {move 1} \ -accelerator "N" $viewMenu add command -label "Last Diff" -underline 0 -command \ {move last} -accelerator "L" set "g(tooltip,Show Change Bars)" "If set, show the changebar column for \ each line of each file" set "g(tooltip,Show Line Numbers)" "If set, show line numbers beside each \ line of each file" set "g(tooltip,Synchronize Scrollbars)" "If set, scrolling either window \ will scroll both windows" set "g(tooltip,Diff Map)" "If set, display the graphical \"Difference \ Map\" in the center of the display" set "g(tooltip,Show Line Comparison Window)" "If set, display the window \ with byte-by-byte differences" set "g(tooltip,Show Inline Comparison (byte))" "If set, display inline \ byte-by-byte differences" set "g(tooltip,Show Inline Comparison (recursive))" "If set, display \ inline differences based on recursive matching regions" set "g(tooltip,Auto Select)" "If set, automatically selects the nearest \ diff record while scrolling" set "g(tooltip,Auto Center)" "If set, moving to another diff record will \ center the diff on the screen" set "g(tooltip,Center Current Diff)" "Center the display around the \ current diff record" set "g(tooltip,First Diff)" "Go to the first difference" set "g(tooltip,Last Diff)" "Go to the last difference" set "g(tooltip,Previous Diff)" "Go to the diff record just prior to the \ current diff record" set "g(tooltip,Next Diff)" "Go to the diff record just after the current \ diff record" set "g(tooltip,Ignore White Spaces)" "If set, changes in whitespaces are \ ignored" # Merge menu. If you change, add or remove labels, be sure and # update the tooltips. menu $mergeMenu $mergeMenu add checkbutton -label "Show Merge Window" -underline 9 \ -variable g(showmerge) -command "do-show-merge 1" $mergeMenu add command -label "Write Merge File..." -underline 6 \ -command popup-merge set "g(tooltip,Show Merge Window)" "Pops up a window showing the current \ merge results" set "g(tooltip,Write Merge File)" "Write the merge file to disk. You will \ be prompted for a filename" # Help menu. If you change, add or remove labels, be sure and # update the tooltips. menu $helpMenu $helpMenu add command -label "On GUI" -underline 3 -command do-help $helpMenu add command -label "On Command Line" -underline 3 \ -command "do-usage gui" $helpMenu add command -label "On Preferences" -underline 3 \ -command do-help-preferences $helpMenu add separator $helpMenu add command -label "About $g(name)" -underline 0 -command do-about $helpMenu add command -label "About Wish" -underline 0 -command about_wish $helpMenu add command -label "About Diff" -underline 0 -command about_diff bind $fileMenu <> {showTooltip menu %W} bind $editMenu <> {showTooltip menu %W} bind $viewMenu <> {showTooltip menu %W} bind $markMenu <> {showTooltip menu %W} bind $mergeMenu <> {showTooltip menu %W} bind $helpMenu <> {showTooltip menu %W} set "g(tooltip,On Preferences)" "Show help on the user-settable preferences" set "g(tooltip,On GUI)" "Show help on how to use the Graphical User \ Interface" set "g(tooltip,On Command Line)" "Show help on the command line arguments" set "g(tooltip,About $g(name))" "Show information about this application" set "g(tooltip,About Wish)" "Show information about Wish" set "g(tooltip,About Diff)" "Show information about diff" } ############################################################################### # Show explanation of item in the status bar at the bottom. # Now used only for menu items ############################################################################### proc showTooltip {which w} { global tooltip global g switch -- $which { menu { if {[catch {$w entrycget active -label} label]} { set label "" } if {[info exists g(tooltip,$label)]} { set g(statusInfo) $g(tooltip,$label) } else { set g(statusInfo) $label } update idletasks } button { if {[info exists g(tooltip,$w)]} { set g(statusInfo) $g(tooltip,$w) } else { set g(statusInfo) "" } update idletasks } } } ############################################################################### # Build the toolbar, in text or image mode ############################################################################### proc build-toolbar {} { debug-info "build-toolbar ()" global w g global opts frame $w(toolbar) -bd 0 set toolbar $w(toolbar) # these are used in other places.. set w(combo) $toolbar.combo set w(rediff_im) $toolbar.rediff_im set w(rediff_tx) $toolbar.rediff_tx set w(find_im) $toolbar.find_im set w(find_tx) $toolbar.find_tx set w(mergeChoiceLabel) $toolbar.mergechoicelbl set w(mergeChoice1_im) $toolbar.m1_im set w(mergeChoice1_tx) $toolbar.m1_tx set w(mergeChoice2_im) $toolbar.m2_im set w(mergeChoice2_tx) $toolbar.m2_tx set w(mergeChoice12_im) $toolbar.m12_im set w(mergeChoice12_tx) $toolbar.m12_tx set w(mergeChoice21_im) $toolbar.m21_im set w(mergeChoice21_tx) $toolbar.m21_tx set w(diffNavLabel) $toolbar.diffnavlbl set w(prevDiff_im) $toolbar.prev_im set w(prevDiff_tx) $toolbar.prev_tx set w(firstDiff_im) $toolbar.first_im set w(firstDiff_tx) $toolbar.first_tx set w(lastDiff_im) $toolbar.last_im set w(lastDiff_tx) $toolbar.last_tx set w(nextDiff_im) $toolbar.next_im set w(nextDiff_tx) $toolbar.next_tx set w(centerDiffs_im) $toolbar.center_im set w(centerDiffs_tx) $toolbar.center_tx set w(markLabel) $toolbar.bkmklbl set w(markSet_im) $toolbar.bkmkset_im set w(markSet_tx) $toolbar.bkmkset_tx set w(markClear_im) $toolbar.bkmkclear_im set w(markClear_tx) $toolbar.bkmkclear_tx # separators toolsep $toolbar.sep1 toolsep $toolbar.sep2 toolsep $toolbar.sep3 toolsep $toolbar.sep4 toolsep $toolbar.sep5 toolsep $toolbar.sep6 # The combo box ::combobox::combobox $toolbar.combo -borderwidth 1 -editable false \ -command moveTo -width 20 # rediff... toolbutton $toolbar.rediff_im -image rediffImage -command recompute-diff \ -bd 1 toolbutton $toolbar.rediff_tx -text "Rediff" -command recompute-diff \ -bd 1 -pady 1 # find... toolbutton $toolbar.find_im -image findImage -command do-find -bd 1 toolbutton $toolbar.find_tx -text "Find" -command do-find -bd 1 -pady 1 # navigation widgets label $toolbar.diffnavlbl -text "Diff:" -pady 0 -bd 2 -relief groove toolbutton $toolbar.prev_im -image prevDiffImage -command [list move -1] \ -bd 1 toolbutton $toolbar.prev_tx -text "Prev" -command [list move -1] -bd 1 \ -pady 1 toolbutton $toolbar.next_im -image nextDiffImage -command [list move 1] \ -bd 1 toolbutton $toolbar.next_tx -text "Next" -command [list move 1] -bd 1 \ -pady 1 toolbutton $toolbar.first_im -image firstDiffImage -command [list move \ first] -bd 1 toolbutton $toolbar.first_tx -text "First" -command [list move first] \ -bd 1 -pady 1 toolbutton $toolbar.last_im -image lastDiffImage -command [list move \ last] -bd 1 toolbutton $toolbar.last_tx -text "Last" -command [list move last] -bd 1 \ -pady 1 toolbutton $toolbar.center_im -image centerDiffsImage -command center -bd 1 toolbutton $toolbar.center_tx -text "Center" -command center -bd 1 -pady 1 # the merge widgets label $toolbar.mergechoicelbl -text "Merge:" -pady 0 -bd 2 -relief groove radiobutton $toolbar.m2_im -borderwidth 1 -indicatoron false \ -selectcolor $w(selcolor) \ -image mergeChoice2Image -value 2 -variable g(toggle) -command \ [list do-merge-choice 2] -takefocus 0 radiobutton $toolbar.m2_tx -borderwidth 1 -indicatoron true -text "R" \ -value 2 -variable g(toggle) -command [list do-merge-choice 2] \ -takefocus 0 radiobutton $toolbar.m1_im -borderwidth 1 -indicatoron false \ -selectcolor $w(selcolor) \ -image mergeChoice1Image -value 1 -variable g(toggle) -command \ [list do-merge-choice 1] -takefocus 0 radiobutton $toolbar.m1_tx -borderwidth 1 -indicatoron true -text "L" \ -value 1 -variable g(toggle) -command [list do-merge-choice 1] \ -takefocus 0 radiobutton $toolbar.m12_im -borderwidth 1 -indicatoron false \ -selectcolor $w(selcolor) \ -image mergeChoice12Image -value 12 -variable g(toggle) -command \ [list do-merge-choice 12] -takefocus 0 radiobutton $toolbar.m12_tx -borderwidth 1 -indicatoron true -text "LR" \ -value 12 -variable g(toggle) -command [list do-merge-choice 12] \ -takefocus 0 radiobutton $toolbar.m21_im -borderwidth 1 -indicatoron false \ -selectcolor $w(selcolor) \ -image mergeChoice21Image -value 21 -variable g(toggle) -command \ [list do-merge-choice 21] -takefocus 0 radiobutton $toolbar.m21_tx -borderwidth 1 -indicatoron true -text "RL" \ -value 21 -variable g(toggle) -command [list do-merge-choice 21] \ -takefocus 0 # The bookmarks label $toolbar.bkmklbl -text "Mark:" -pady 0 -bd 2 -relief groove toolbutton $toolbar.bkmkset_im -image markSetImage -command \ [list diffmark mark] -bd 1 toolbutton $toolbar.bkmkset_tx -text "Set" -command [list diffmark mark] \ -bd 1 -pady 1 toolbutton $toolbar.bkmkclear_im -image markClearImage -command \ [list diffmark clear] -bd 1 toolbutton $toolbar.bkmkclear_tx -text "Clear" -command [list diffmark \ clear] -bd 1 -pady 1 set_tooltips $w(find_im) {"Pop up a dialog to search for a string within \ either file"} set_tooltips $w(find_tx) {"Pop up a dialog to search for a string within \ either file"} set_tooltips $w(rediff_im) {"Recompute and redisplay the difference \ records"} set_tooltips $w(rediff_tx) {"Recompute and redisplay the difference \ records"} set_tooltips $w(mergeChoice12_im) {"select the diff on the left then \ right for merging"} set_tooltips $w(mergeChoice12_tx) {"select the diff on the left then \ right for merging"} set_tooltips $w(mergeChoice1_im) {"select the diff on the left for merging"} set_tooltips $w(mergeChoice1_tx) {"select the diff on the left for merging"} set_tooltips $w(mergeChoice2_im) {"select the diff on the right for \ merging"} set_tooltips $w(mergeChoice2_tx) {"select the diff on the right for \ merging"} set_tooltips $w(mergeChoice21_im) {"select the diff on the right then \ left for merging"} set_tooltips $w(mergeChoice21_tx) {"select the diff on the right then \ left for merging"} set_tooltips $w(prevDiff_im) {"Previous Diff"} set_tooltips $w(prevDiff_tx) {"Previous Diff"} set_tooltips $w(nextDiff_im) {"Next Diff"} set_tooltips $w(nextDiff_tx) {"Next Diff"} set_tooltips $w(firstDiff_im) {"First Diff"} set_tooltips $w(firstDiff_tx) {"First Diff"} set_tooltips $w(lastDiff_im) {"Last Diff"} set_tooltips $w(lastDiff_tx) {"Last Diff"} set_tooltips $w(markSet_im) {"Mark current diff"} set_tooltips $w(markSet_tx) {"Mark current diff"} set_tooltips $w(markClear_im) {"Clear current diff mark"} set_tooltips $w(markClear_tx) {"Clear current diff mark"} set_tooltips $w(centerDiffs_im) {"Center Current Diff"} set_tooltips $w(centerDiffs_tx) {"Center Current Diff"} pack-toolbuttons $toolbar } proc pack-toolbuttons {toolbar} { global opts if {$opts(toolbarIcons)} { set bp "im" } else { set bp "tx" } pack $toolbar.combo -side left -padx 2 pack $toolbar.sep1 -side left -fill y -pady 2 -padx 2 pack $toolbar.rediff_$bp -side left -padx 2 pack $toolbar.find_$bp -side left -padx 2 pack $toolbar.sep2 -side left -fill y -pady 2 -padx 2 pack $toolbar.mergechoicelbl -side left -padx 2 pack $toolbar.m12_$bp $toolbar.m1_$bp $toolbar.m2_$bp $toolbar.m21_$bp \ -side left -padx 2 pack $toolbar.sep3 -side left -fill y -pady 2 -padx 2 pack $toolbar.diffnavlbl -side left -pady 2 -padx 2 pack $toolbar.first_$bp $toolbar.last_$bp $toolbar.prev_$bp \ $toolbar.next_$bp -side left -pady 2 -padx 2 pack $toolbar.sep4 -side left -fill y -pady 2 -padx 2 pack $toolbar.center_$bp -side left -pady 2 -padx 1 pack $toolbar.sep5 -side left -fill y -pady 2 -padx 2 pack $toolbar.bkmklbl -side left -padx 2 pack $toolbar.bkmkset_$bp $toolbar.bkmkclear_$bp -side left -pady 2 -padx 2 pack $toolbar.sep6 -side left -fill y -pady 2 -padx 2 foreach b [info commands $toolbar.mark*] { pack $b -side left -fill y -pady 2 -padx 2 } foreach b [info commands $toolbar.mark*] { $b configure -relief $opts(relief) } foreach b [info commands $toolbar.*_$bp] { $b configure -relief $opts(relief) } # Radiobuttons ignore relief configuration if they have an image, so we # set their borderwidth to 0 if we want them flat. if {$opts(relief) == "flat" && $opts(toolbarIcons)} { set bord 0 } else { set bord 1 } foreach b [info commands $toolbar.m1*] { $b configure -bd $bord } foreach b [info commands $toolbar.m2*] { $b configure -bd $bord } } proc reconfigure-toolbar {} { debug-info "reconfigure-toolbar ()" global w foreach button [winfo children $w(toolbar)] { pack forget $button } pack-toolbuttons $w(toolbar) } proc build-status {} { debug-info "build-status ()" global w global g frame $w(status) -bd 0 set w(statusLabel) $w(status).label set w(statusCurrent) $w(status).current # MacOS has a resize handle in the bottom right which will sit # on top of whatever is placed there. So, we'll add a little bit # of whitespace there. It's harmless, so we'll do it on all of the # platforms. label $w(status).blank -image nullImage -width 16 -bd 1 -relief sunken label $w(statusCurrent) -textvariable g(statusCurrent) -anchor e \ -width 14 -borderwidth 1 -relief sunken -padx 4 -pady 2 label $w(statusLabel) -textvariable g(statusInfo) -anchor w -width 1 \ -borderwidth 1 -relief sunken -pady 2 pack $w(status).blank -side right -fill y pack $w(statusCurrent) -side right -fill y -expand n pack $w(statusLabel) -side left -fill both -expand y } ############################################################################### # handles events over the map ############################################################################### proc handleMapEvent {event y} { global opts global w global g #debug-info "handleMapEvent $event $y" switch -- $event { B1-Press { set ty1 [lindex $g(thumbBbox) 1] set ty2 [lindex $g(thumbBbox) 3] if {$y >= $ty1 && $y <= $ty2} { set g(mapScrolling) 1 # this captures the negative delta between the mouse press \ and the top # of the thumbbox. It's used so when we scroll by moving the # mouse, we can keep this distance constant. This is how all # scrollbars work, and it's what the user expects. set g(thumbDeltaY) [expr -1 * ($y - $ty1 - 2)] } } B1-Motion { if {[info exists g(mapScrolling)]} { incr y $g(thumbDeltaY) map-seek $y } } B1-Release { show-info "" set ty1 [lindex $g(thumbBbox) 1] set ty2 [lindex $g(thumbBbox) 3] # if we release over the trough (*not* over the thumb) # just scroll by the size of the thumb if {$y < $ty1 || $y > $ty2} { if {$y < $ty1} { # if vertical scrollbar syncing is turned on, # all the other windows should toe the line # appropriately... $w(RightText) yview scroll -1 pages } else { $w(RightText) yview scroll 1 pages } } else { # do nothing } catch {unset g(mapScrolling)} } } } # makes a toolbar "separator" proc toolsep {w} { label $w -image [image create photo] -highlightthickness 0 -bd 1 -width 0 \ -relief groove return $w } proc toolbutton {w args} { global tcl_platform global opts global g # create the button eval button $w $args # add minimal tooltip-like support bind $w [list toolbutton:handleEvent %W] bind $w [list toolbutton:handleEvent %W] bind $w [list toolbutton:handleEvent %W] bind $w [list toolbutton:handleEvent %W] $w configure -relief $opts(relief) return $w } # handle events in our fancy toolbuttons... proc toolbutton:handleEvent {event w {isToolbutton 1}} { global g global opts switch -- $event { "" { showTooltip button $w if {$opts(fancyButtons) && $isToolbutton && [$w cget -state] == \ "normal"} { $w configure -relief raised } } "" { set g(statusInfo) "" if {$opts(fancyButtons) && $isToolbutton} { $w configure -relief flat } } "" { showTooltip button $w if {$opts(fancyButtons) && $isToolbutton && [$w cget -state] == \ "normal"} { $w configure -relief raised } } "" { set g(statusInfo) "" if {$opts(fancyButtons) && $isToolbutton} { $w configure -relief flat } } } } ############################################################################### # move the map thumb to correspond to current shown merge... ############################################################################### proc map-move-thumb {y1 y2} { global g global w set thumbheight [expr {($y2 - $y1) * $g(mapheight)}] if {$thumbheight < $g(thumbMinHeight)} { set thumbheight $g(thumbMinHeight) } if {![info exists g(mapwidth)]} { set g(mapwidth) 0 } set x1 1 set x2 [expr {$g(mapwidth) - 3}] # why -2? it's the thickness of our border... set y1 [expr {int(($y1 * $g(mapheight)) - 2)}] if {$y1 < 0} { set y1 0 } set y2 [expr {$y1 + $thumbheight}] if {$y2 > $g(mapheight)} { set y2 $g(mapheight) set y1 [expr {$y2 - $thumbheight}] } set dx1 [expr {$x1 + 1}] set dx2 [expr {$x2 - 1}] set dy1 [expr {$y1 + 1}] set dy2 [expr {$y2 - 1}] $w(mapCanvas) coords thumbUL $x1 $y2 $x1 $y1 $x2 $y1 $dx2 $dy1 $dx1 $dy1 \ $dx1 $dy2 $w(mapCanvas) coords thumbLR $dx1 $y2 $x2 $y2 $x2 $dy1 $dx2 $dy1 $dx2 \ $dy2 $dx1 $dy2 set g(thumbBbox) [list $x1 $y1 $x2 $y2] set g(thumbHeight) $thumbheight } ############################################################################### # Bind keys for Next, Prev, Center, Merge choices 1 and 2 # # N.B. This is GROSS! It might have been necessary in earlier versions, # but now I think it needs a serious rewriite. We are now overriding # the text widget, so we can probably just disable the insert and delete # commands, and use something like insert_ and delete_ internally. ############################################################################### proc common-navigation {args} { global w bind . do-find foreach widget $args { # this effectively disables the widget, without having to # resort to actually disabling the widget (the latter which # has some annoying side effects). What we really want is to # only disable keys that get inserted, but that's difficult # to do, and this works almost as well... bind $widget {break} bind $widget {continue} bind $widget <> {break} # ... but now we need to restore some navigation key bindings # which got lost because we disable all keys. Since we are # attaching bindings that duplicate class bindings, we need # to be sure and include the break, so the events don't fire # twice (once for the widget, once for the class). There is # probably a much better way to do all this, but I'm too # lazy to figure it out... foreach event [list Next Prior Up Down Left Right Home End] { foreach modifier [list {} Shift Control Shift-Control] { set binding [bind Text <${modifier}${event}>] if {[string length $binding] > 0} { bind $widget "<${modifier}${event}>" " ${binding} break " } } } # these bindings allow control-f, tab and shift-tab to work # in spite of the fact we bound Any-KeyPress to a null action bind $widget continue bind $widget continue bind $widget continue bind $widget " center break " bind $widget " move 1 break " bind $widget

" move -1 break " bind $widget " move first break " bind $widget " move last break " bind $widget " do-exit break " bind $widget " recompute-diff break " bind $widget " moveNearest $widget mark insert break " # these bindings keep Alt- modified keys from triggering # the above actions. This way, any Alt combinations that # should open a menu will... foreach key [list c n p f l] { bind $widget {continue} } bind $widget " moveNearest $widget xy %x %y break " bind $widget " do-merge-choice 1 break " bind $widget " do-merge-choice 2 break " bind $widget " do-merge-choice 12 break " bind $widget " do-merge-choice 21 break " } } ############################################################################### # set or clear a "diff mark" -- a hot button to move to a particular diff ############################################################################### proc diffmark {option {diff -1}} { debug-info "diffmark ($option $diff)" global g global w if {$diff == -1} { set diff $g(pos) } set widget $w(toolbar).mark$diff switch -- $option { activate { move $diff 0 1 } mark { if {![winfo exists $widget]} { toolbutton $widget -text "\[$diff\]" -command [list diffmark \ activate $diff] -bd 1 -pady 1 pack $widget -side left -padx 2 set g(tooltip,$widget) "Diff Marker: Jump to diff record \ number $diff" } update-display } clear { if {[winfo exists $widget]} { destroy $widget catch {unset g(tooltip,$widget)} } update-display } clearall { set bookmarks [info commands $w(toolbar).mark*] if {[llength $bookmarks] > 0} { foreach widget $bookmarks { destroy $widget catch {unset g(tooltip,$widget)} } } update-display } } } ############################################################################### # Customize the display (among other things). ############################################################################### proc customize {} { debug-info "customize ()" global pref global g global w global opts global tmpopts global tcl_platform catch {destroy $w(preferences)} toplevel $w(preferences) wm title $w(preferences) "$g(name) Preferences" wm transient $w(preferences) . wm group $w(preferences) . if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w(preferences) } wm withdraw $w(preferences) # the button frame... frame $w(preferences).buttons -bd 0 button $w(preferences).buttons.dismiss -width 8 -text "Dismiss" \ -command {destroy $w(preferences)} button $w(preferences).buttons.apply -width 8 -text "Apply" \ -command {apply 1} button $w(preferences).buttons.save -width 8 -text "Save" -command save button $w(preferences).buttons.help -width 8 -text "Help" \ -command do-help-preferences pack $w(preferences).buttons -side bottom -fill x pack $w(preferences).buttons.dismiss -side right -padx 10 -pady 5 pack $w(preferences).buttons.help -side right -padx 10 -pady 5 pack $w(preferences).buttons.save -side right -padx 1 -pady 5 pack $w(preferences).buttons.apply -side right -padx 1 -pady 5 # a series of checkbuttons to act as a poor mans notebook tab frame $w(preferences).notebook -bd 0 pack $w(preferences).notebook -side top -fill x -pady 4 set pagelist {} # The relief makes these work, so we don't need to use the selcolor # Radiobuttons without indicators look rather sucky on MacOSX, so # we'll tweak the style for that platform if {$::tcl_platform(os) == "Darwin"} { set indicatoron true } else { set indicatoron false } foreach page [list General Display Appearance] { set frame $w(preferences).f$page lappend pagelist $frame set rb $w(preferences).notebook.f$page radiobutton $rb -command "customize-selectPage $frame" \ -selectcolor $w(background) \ -variable g(prefPage) -value $frame -height 2 -text $page \ -indicatoron $indicatoron -borderwidth 1 pack $rb -side left frame $frame -bd 2 -relief groove -width 400 -height 300 } set g(prefPage) $w(preferences).fGeneral # make sure our labels are defined customize-initLabels # this is an option that we support internally, but don't give # the user a way to directly edit (right now, anyway). But we # need to make sure tmpopts knows about it set tmpopts(customCode) $opts(customCode) # General set count 0 set frame $w(preferences).fGeneral foreach key {diffcmd ignoreblanksopt tmpdir editor filetypes geometry } { label $frame.l$count -text "$pref($key): " -anchor w set tmpopts($key) $opts($key) entry $frame.e$count -textvariable tmpopts($key) -width 50 -bd 2 \ -relief sunken grid $frame.l$count -row $count -column 0 -sticky w -padx 5 -pady 2 grid $frame.e$count -row $count -column 1 -sticky ew -padx 5 -pady 2 incr count } # this is just for filler... label $frame.filler -text {} grid $frame.filler -row $count incr count foreach key {fancyButtons toolbarIcons autocenter syncscroll autoselect} { label $frame.l$count -text "$pref($key): " -anchor w set tmpopts($key) $opts($key) checkbutton $frame.c$count -indicatoron true -text "$pref($key)" \ -justify left -onvalue 1 -offvalue 0 -variable tmpopts($key) set tmpopts($key) $opts($key) if {$key == "fancyButtons" && $g(windowingSystem) == "aqua"} { # Skipit - nothing to do incr count continue } grid $frame.c$count -row $count -column 0 -columnspan 2 -sticky w \ -padx 5 incr count } grid columnconfigure $frame 0 -weight 0 grid columnconfigure $frame 1 -weight 1 # this, in effect, adds a hidden row at the bottom which takes # up any extra room grid rowconfigure $frame $count -weight 1 # pack this window for a brief moment, and compute the window # size. We'll do this for each "page" and find the largest # size to be the size of the dialog pack $frame -side right -fill both -expand y update idletasks set maxwidth [winfo reqwidth $w(preferences)] set maxheight [winfo reqheight $w(preferences)] pack forget $frame # Appearance set frame $w(preferences).fAppearance set count 0 foreach key {textopt difftag deltag instag chgtag currtag bytetag \ inlinetag overlaptag} { label $frame.l$count -text "$pref($key): " -anchor w set tmpopts($key) $opts($key) entry $frame.e$count -textvariable tmpopts($key) -bd 2 -relief sunken grid $frame.l$count -row $count -column 0 -sticky w -padx 5 -pady 2 grid $frame.e$count -row $count -column 1 -sticky ew -padx 5 -pady 2 incr count } grid columnconfigure $frame 0 -weight 0 grid columnconfigure $frame 1 -weight 1 # tabstops are placed after a little extra whitespace, since it is # slightly different than all of the other options (ie: it's not # a list of widget options) frame $frame.sep$count -bd 0 -height 4 grid $frame.sep$count -row $count -column 0 -stick ew -columnspan 2 \ -padx 5 -pady 2 incr count set key "tabstops" set tmpopts($key) $opts($key) label $frame.l$count -text "$pref($key):" -anchor w set tmpopts($key) $opts($key) entry $frame.e$count -textvariable tmpopts($key) -bd 2 -relief sunken \ -width 3 grid $frame.l$count -row $count -column 0 -sticky w -padx 5 -pady 2 grid $frame.e$count -row $count -column 1 -sticky w -padx 5 -pady 2 incr count # add a tiny bit of validation, so the user can only enter numbers trace variable tmpopts($key) w [list validate integer] # this, in effect, adds a hidden row at the bottom which takes # up any extra room grid rowconfigure $frame $count -weight 1 pack $frame -side right -fill both -expand y update idletasks set maxwidth [max $maxwidth [winfo reqwidth $w(preferences)]] set maxheight [max $maxheight [winfo reqheight $w(preferences)]] pack forget $frame # Display set frame $w(preferences).fDisplay set row 0 # Option fields # Note that the order of the list is used to determine # the layout. So, if you add something to the list pay # attention to how it affects things. # # an x means an empty column; a - means an empty row set col 0 foreach key [list showln tagln showcbs tagcbs showmap colorcbs \ showlineview tagtext ignoreblanks showinline1 x showinline2 x] { if {$key == "x"} { set col [expr {$col ? 0 : 1}] if {$col == 0} { incr row } continue } if {$key == "-"} { frame $frame.f${row} -bd 0 -height 4 grid $frame.f${row} -row $row -column 0 -columnspan 2 -padx 20 \ -pady 4 -sticky nsew set col 1 ;# will force next column to zero and incr row } else { checkbutton $frame.c${row}${col} -indicatoron true \ -text "$pref($key)" -onvalue 1 -offvalue 0 -variable tmpopts($key) set tmpopts($key) $opts($key) grid $frame.c${row}$col -row $row -column $col -sticky w -padx 5 } set col [expr {$col ? 0 : 1}] if {$col == 0} { incr row } } grid columnconfigure $frame 0 -weight 0 grid columnconfigure $frame 1 -weight 0 grid columnconfigure $frame 2 -weight 0 grid columnconfigure $frame 3 -weight 0 grid columnconfigure $frame 4 -weight 1 # add validation to make only one of the showinline# options are set trace variable tmpopts(showinline1) w [list validate-inline showinline1] trace variable tmpopts(showinline2) w [list validate-inline showinline2] # this, in effect, adds a hidden row at the bottom which takes # up any extra room grid rowconfigure $frame $row -weight 1 pack $frame -side right -fill both -expand y update idletasks set maxwidth [max $maxwidth [winfo reqwidth $w(preferences)]] set maxheight [max $maxheight [winfo reqheight $w(preferences)]] pack forget $frame customize-selectPage # compute a reasonable location for the window... centerWindow $w(preferences) [list $maxwidth $maxheight] wm deiconify $w(preferences) } proc validate {type name index op} { global tmpopts # if we fail the check, attempt to do something clever if {![string is $type $tmpopts($index)]} { bell switch -- $type { integer { regsub -all {[^0-9]} $tmpopts($index) {} tmpopts($index) } default { # this should never happen. If you use this routine, # make sure you add cases to handle all possible # values of $type used by this program. set tmpopts($index) "" } } } } proc validate-inline {option name index op} { global tmpopts if {$tmpopts($index)} { if {$index == "showinline1"} { set tmpopts(showinline2) 0 } elseif {$index == "showinline2"} { set tmpopts(showinline1) 0 } } } proc customize-selectPage {{frame {}}} { global g w if {$frame == ""} { set frame $g(prefPage) } pack forget $w(preferences).fGeneral pack forget $w(preferences).fAppearance pack forget $w(preferences).fDisplay pack forget $w(preferences).fBehavior pack $frame -side right -fill both -expand y } ############################################################################### # define the labels for the preferences. This is done outside of # the customize proc since the labels are used in the help text. ############################################################################### proc customize-initLabels {} { global pref set pref(diffcmd) {diff command} set pref(ignoreblanksopt) {Ignore blanks option} set pref(ignoreblanks) {Ignore blanks when diffing} set pref(textopt) {Text widget options} set pref(bytetag) {Tag options for characters in line view} set pref(difftag) {Tag options for diff regions} set pref(currtag) {Tag options for the current diff region} set pref(inlinetag) {Tag options for diff region inline differences} set pref(deltag) {Tag options for deleted diff region} set pref(instag) {Tag options for inserted diff region} set pref(chgtag) {Tag options for changed diff region} set pref(overlaptag) {Tag options for overlap diff region} set pref(geometry) {Text window size} set pref(tmpdir) {Directory for scratch files} set pref(editor) {Program for editing files} set pref(filetypes) {Choice of file suffixes for file dialogs} set pref(fancyButtons) {Windows-style toolbar buttons} set pref(showlineview) {Show current line comparison window} set pref(showinline1) {Show inline diffs (byte comparisons)} set pref(showinline2) {Show inline diffs (recursive matching algorithm)} set pref(showmap) {Show graphical map of diffs} set pref(showln) {Show line numbers} set pref(showcbs) {Show change bars} set pref(autocenter) {Automatically center current diff region} set pref(syncscroll) {Synchronize scrollbars} set pref(toolbarIcons) {Use icons instead of labels in the toolbar} set pref(colorcbs) {Color change bars to match the diff map} set pref(tagtext) {Highlight file contents} set pref(tagcbs) {Highlight change bars} set pref(tagln) {Highlight line numbers} set pref(tabstops) {Tab stops} set pref(autoselect) "Automatically select the nearest diff region while \ scrolling" } ############################################################################### # Apply customization changes. ############################################################################### proc apply {{remark 0}} { debug-info "apply ($remark)" global opts global tmpopts global w global pref global screenWidth global screenHeight global tk_version grid propagate $w(client) t if {! [file isdirectory $tmpopts(tmpdir)]} { do-error "Invalid temporary directory $tmpopts(tmpdir)" } if {[catch " $w(LeftText) configure $tmpopts(textopt) $w(RightText) configure $tmpopts(textopt) $w(BottomText) configure $tmpopts(textopt) "]} { do-error "Invalid text widget setting: \n\n'$tmpopts(textopt)'" eval "$w(LeftText) configure $opts(textopt)" eval "$w(RightText) configure $opts(textopt)" eval "$w(BottomText) configure $opts(textopt)" return } # the text options must be ok. Configure the other text widgets # similarly eval "$w(LeftCB) configure $tmpopts(textopt)" eval "$w(LeftInfo) configure $tmpopts(textopt)" eval "$w(RightCB) configure $tmpopts(textopt)" eval "$w(RightInfo) configure $tmpopts(textopt)" set gridsize [wm grid .] set gridx [lindex $gridsize 2] set gridy [lindex $gridsize 3] #debug-info " wm grid is $gridx x $gridy" set maxunitsx [expr {$screenWidth / $gridx}] set maxunitsy [expr {$screenHeight / $gridy}] #debug-info " max X is $maxunitsx units" #debug-info " max Y is $maxunitsy units" set halfmax [expr {$maxunitsx / 2}] if {$tmpopts(geometry) == "" || [catch {scan $tmpopts(geometry) \ "%dx%d" width height} result]} { do-error "invalid geometry setting: $tmpopts(geometry)" return } #debug-info " width $width halfmax $halfmax" set maxw [expr {$halfmax - 18}] #debug-info " maxw $maxw" if {$width > $maxw} { set width $maxw } # re-center map if {$tk_version < 8.4} { grid columnconfigure $w(client) 0 -weight 1 grid columnconfigure $w(client) 2 -weight 1 } else { grid columnconfigure $w(client) 0 -weight 100 -uniform a grid columnconfigure $w(client) 2 -weight 100 -uniform a } if {[catch {$w(LeftText) configure -width $width -height $height} result]} { do-error "invalid geometry setting: $tmpopts(geometry)" return } $w(RightText) configure -width $width -height $height $w(LeftLabel) configure -width $width $w(RightLabel) configure -width $width grid forget $w(LeftLabel) grid forget $w(RightLabel) grid $w(LeftLabel) -row 0 -column 0 -sticky ew grid $w(RightLabel) -row 0 -column 2 -sticky ew foreach tag {difftag currtag inlinetag deltag instag chgtag overlaptag} { foreach win [list $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB)] { if {[catch "$win tag configure $tag $tmpopts($tag)"]} { do-error "Invalid settings for \"$pref($tag)\": \ \n\n'$tmpopts($tag)' is not a valid option string" eval "$win tag configure $tag $opts($tag)" return } } } if {[catch "$w(BottomText) tag configure diff $tmpopts(bytetag)"]} { do-error "Invalid settings for \"$pref(bytetag)\": \ \n\n'$tmpopts(bytetag)' is not a valid option string" eval "$w(BottomText) tag configure diff $opts(bytetag)" return } # tabstops require a little extra work. We need to figure out # the width of an "m" in the widget's font, then multiply that # by the tab stop width. For the bottom text widget the first tabstop # is adjusted by two to take into consideration the fact that we # add two bytes to each line (ie: "< " or "> "). set cwidth [font measure [$w(LeftText) cget -font] "m"] set tabstops [expr {$cwidth * $tmpopts(tabstops)}] $w(LeftText) configure -tabs $tabstops $w(RightText) configure -tabs $tabstops $w(BottomText) configure -tabs [list [expr {$tabstops +($cwidth * 2)}] \ [expr {2*$tabstops +($cwidth * 2)}]] if {[info exists w(mergeText)] && [winfo exists $w(mergeText)]} { $w(mergeText) configure -tabs $tabstops } # set opts to the values from tmpopts foreach key {autocenter autoselect chgtag colorcbs currtag deltag diffcmd \ difftag inlinetag editor fancyButtons filetypes geometry ignoreblanks \ ignoreblanksopt instag overlaptag showcbs showlineview showln showmap \ syncscroll tabstops tagcbs tagln tagtext textopt tmpdir toolbarIcons} { set opts($key) $tmpopts($key) } if {$opts(fancyButtons)} { set opts(relief) flat } else { set opts(relief) raised } # determine if we need to redo the inline diffs to avoid needless rediff if {$opts(showinline1) != $tmpopts(showinline1) || $opts(showinline2) != \ $tmpopts(showinline2)} { set opts(showinline1) $tmpopts(showinline1) set opts(showinline2) $tmpopts(showinline2) recompute-diff } # reconfigure the toolbar buttons reconfigure-toolbar # remark all the diff regions, show (or hide) the line numbers, # change bars and diff map, and we are done if {$remark} { remark-diffs } do-show-linenumbers do-show-changebars do-show-map do-show-lineview do-show-ignoreblanks grid propagate $w(client) f } ############################################################################### # Save customization changes. ############################################################################### proc save {} { debug-info "save ()" global g global tmpopts rcfile tcl_platform global pref if {[file exists $rcfile]} { file rename -force $rcfile "$rcfile~" } set fid [open $rcfile w] # put the tkdiff version in the file. It might be handy later puts $fid "# This file was generated by $g(name) $g(version)" puts $fid "# [clock format [clock seconds]]\n" puts $fid "set prefsFileVersion {$g(version)}\n" # now, put all of the preferences in the file foreach key [lsort [array names pref]] { regsub "\n" $pref($key) "\n# " comment puts $fid "# $comment" puts $fid "define $key {$tmpopts($key)}\n" } # ... and any custom code puts $fid "# custom code" puts $fid "# put any custom code you want to be executed in the" puts $fid "# following block. This code will be automatically executed" puts $fid "# after the GUI has been set up but before the diff is " puts $fid "# performed. Use this code to customize the interface if" puts $fid "# you so desire." puts $fid "# " puts $fid "# Even though you can't (as of version 3.09) edit this " puts $fid "# code via the preferences dialog, it will be automatically" puts $fid "# saved and restored if you do a SAVE from that dialog." puts $fid "" puts $fid "# Unless you really know what you are doing, it is probably" puts $fid "# wise to leave this unmodified." puts $fid "" puts $fid "define customCode {\n[string trim $tmpopts(customCode) \n]\n}\n" close $fid if {$::tcl_platform(platform) == "windows"} { file attribute $rcfile -hidden 1 } } ############################################################################### # Text has scrolled, update scrollbars and synchronize windows ############################################################################### proc hscroll-sync {id args} { global g opts global w # If ignore_event is true, we've already taken care of scrolling. # We're only interested in the first event. if {$g(ignore_hevent,$id)} { return } # Scrollbar sizes set size1 [expr {[lindex [$w(LeftText) xview] 1] - [lindex \ [$w(LeftText) xview] 0]}] set size2 [expr {[lindex [$w(RightText) xview] 1] - [lindex \ [$w(RightText) xview] 0]}] if {$opts(syncscroll) || $id == 1} { set start [lindex $args 0] if {$id != 1} { set start [expr {$start * $size2 / $size1}] } $w(LeftHSB) set $start [expr {$start + $size1}] $w(LeftText) xview moveto $start set g(ignore_hevent,1) 1 } if {$opts(syncscroll) || $id == 2} { set start [lindex $args 0] if {$id != 2} { set start [expr {$start * $size1 / $size2}] } $w(RightHSB) set $start [expr {$start + $size2}] $w(RightText) xview moveto $start set g(ignore_hevent,2) 1 } # This forces all the event handlers for the view alterations # above to trigger, and we lock out the recursive (redundant) # events using ignore_event. update idletasks # Restore to normal set g(ignore_hevent,1) 0 set g(ignore_hevent,2) 0 } ############################################################################### # Text has scrolled, update scrollbars and synchronize windows ############################################################################### proc vscroll-sync {windowlist id y0 y1} { global g opts global w if {$id == 1} { $w(LeftVSB) set $y0 $y1 } else { $w(RightVSB) set $y0 $y1 } # if syncing is disabled, we're done. This prevents a nasty # set of recursive calls if {[info exists g(disableSyncing)]} { return } # set the flag; this makes sure we only get called once set g(disableSyncing) 1 # scroll the other windows on the same side as this window foreach window $windowlist { $window yview moveto $y0 } eval map-move-thumb $y0 $y1 # Select nearest visible diff region, if the appropriate # options are set if {$opts(syncscroll) && $opts(autoselect) && $g(count) > 0} { set winhalf [expr {[winfo height $w(RightText)] / 2}] set result [find-diff [expr {int([$w(RightText) index @1,$winhalf])}]] set i [lindex $result 0] # have we found a diff other than the current diff? if {$i != $g(pos)} { # Also, make sure the diff is visible. If not, we won't # change the current diff region... set topline [$w(RightText) index @0,0] set bottomline [$w(RightText) index @0,10000] foreach {line s1 e1 s2 e2 type} $g(scrdiff,$i) { } if {$s1 >= $topline && $s1 <= $bottomline} { move $i 0 1 } } } # if syncing is turned on, scroll other windows. # Annoyingly, sometimes the *Text windows won't scroll properly, # at least under windows. And I can't for the life of me figure # out why. Maybe a bug in tk? if {$opts(syncscroll)} { if {$id == 1} { $w(RightText) yview moveto $y0 $w(RightInfo) yview moveto $y0 $w(RightCB) yview moveto $y0 $w(RightVSB) set $y0 $y1 } else { $w(LeftText) yview moveto $y0 $w(LeftInfo) yview moveto $y0 $w(LeftCB) yview moveto $y0 $w(LeftVSB) set $y0 $y1 } } # we apparently automatically process idle events after this # proc is called. Once that is done we'll unset our flag after idle {catch {unset g(disableSyncing)}} } ############################################################################### # Make a miniature map of the diff regions ############################################################################### proc create-map {name mapwidth mapheight} { global g global w global map global opts set map $name # Text widget always contains blank line at the end set lines [expr {double([$w(LeftText) index end]) - 2}] set factor [expr {$mapheight / $lines}] # We add some transparent stuff to make the map fill the canvas # in order to receive mouse events at the very bottom. $map blank $map put \#000 -to 0 $mapheight $mapwidth $mapheight # Line numbers start at 1, not at 0. for {set i 1} {$i <= $g(count)} {incr i} { # scan $g(scrdiff,$i) "%s %d %d %d %d %s" line s1 e1 s2 e2 type foreach {line s1 e1 s2 e2 type} $g(scrdiff,$i) { } set y [expr {int(($s2 - 1) * $factor) + $g(mapborder)}] set size [expr {round(($e2 - $s2 + 1) * $factor)}] if {$size < 1} { set size 1 } switch -- $type { "d" { set color $opts(mapdel) } "a" { set color $opts(mapins) } "c" { set color $opts(mapchg) } } if {[info exists g(overlap$i)]} { set color yellow } $map put $color -to 0 $y $mapwidth [expr {$y + $size}] } # let's draw a rectangle to simulate a scrollbar thumb. The size # isn't important since it will get resized when map-move-thumb # is called... $w(mapCanvas) create line 0 0 0 0 -width 1 -tags thumbUL -fill white $w(mapCanvas) create line 1 1 1 1 -width 1 -tags thumbLR -fill black $w(mapCanvas) raise thumb # now, move the thumb eval map-move-thumb [$w(LeftText) yview] } ############################################################################### # Resize map to fit window size ############################################################################### proc map-resize {args} { global g opts global w set mapwidth [winfo width $w(map)] set g(mapborder) [expr {[$w(map) cget -borderwidth] + [$w(map) cget \ -highlightthickness]}] set mapheight [expr {[winfo height $w(map)] - $g(mapborder) * 2}] # We'll get a couple of "resize" events, so don't draw a map # unless we've got the diffs and the map size has changed if {$g(count) == 0 || $mapheight == $g(mapheight)} { return } # If we don't have a map and don't want one, don't make one if {$g(mapheight) == 0 && $opts(showmap) == 0} { return } # This seems to happen on Windows!? _After_ the map is drawn the first time # another event triggers and [winfo height $w(map)] is then 0... if {$mapheight < 1} { return } set g(mapheight) $mapheight set g(mapwidth) $mapwidth create-map map $mapwidth $mapheight } ############################################################################### # scroll to diff region nearest to y ############################################################################### proc map-scroll {y} { global g global w global opts set yview [expr {double($y) / double($g(mapheight))}] # Show text corresponding to map catch {$w(RightText) yview moveto $yview} result update idletasks # Select the diff region closest to the middle of the screen set winhalf [expr {[winfo height $w(RightText)] / 2}] set result [find-diff [expr {int([$w(RightText) index @1,$winhalf])}]] move [lindex $result 0] 0 0 if {$opts(autocenter)} { center } if {$g(showmerge)} { merge-center } } ############################################################################### # Toggle showing the line comparison window ############################################################################### proc do-show-lineview {{showLineview {}}} { global opts global w if {$showLineview != {}} { set opts(showlineview) $showLineview } if {$opts(showlineview)} { grid $w(BottomText) -row 3 -column 0 -sticky ew -columnspan 4 } else { grid forget $w(BottomText) } } ############################################################################### # Toggle showing inline comparison ############################################################################### proc do-show-inline1 {{showInline1 {}}} { global opts if {$showInline1 != {}} { puts "passed in value=$showInline1" set opts(showinline1) $showInline1 } # mutually disjoint options if {$opts(showinline1)} { set opts(showinline2) 0 } recompute-diff } proc do-show-inline2 {{showInline2 {}}} { global opts if {$showInline2 != {}} { set opts(showinline2) $showInline2 } # mutually disjoint options if {$opts(showinline2)} { set opts(showinline1) 0 } recompute-diff } ############################################################################### # Toggle showing map or not ############################################################################### proc do-show-map {{showMap {}}} { global opts global w if {$showMap != {}} { set opts(showmap) $showMap } if {$opts(showmap)} { grid $w(map) -row 1 -column 1 -stick ns } else { grid forget $w(map) } } ############################################################################### # Find the diff nearest to $line. # Returns "$i $newtop" where $i is the index of the diff region # and $newtop is the new top line in the window to the right. ############################################################################### proc find-diff {line} { global g global w set top $line set newtop [expr {$top - int([$w(LeftText) index end]) + \ int([$w(RightText) index end])}] for {set low 1; set high $g(count); set i [expr {($low + $high) / 2}]} \ {$i >= $low} {set i [expr {($low + $high) / 2}]} { foreach {line s1 e1 s2 e2 type} $g(scrdiff,$i) { } if {$s1 > $top} { set newtop [expr {$top - $s1 + $s2}] set high [expr {$i-1}] } else { set low [expr {$i+1}] } } # do some range checking... set i [max 1 [min $i $g(count)]] # If next diff is closer than the one found, use it instead if {$i > 0 && $i < $g(count)} { set nexts1 [lindex $g(scrdiff,[expr {$i + 1}]) 1] set e1 [lindex $g(scrdiff,$i) 2] if {$nexts1 - $top < $top - $e1} { incr i } } return [list $i $newtop] } ############################################################################### # Calculate number of lines in diff region # pos Diff number # version 1 or 2, left or right window version # screen 1 for screen size, 0 for original diff size ############################################################################### proc diff-size {pos version {screen 0}} { global g if {$screen} { set diff scrdiff } else { set diff pdiff } foreach {thisdiff s(1) e(1) s(2) e(2) type} $g($diff,$pos) { } switch -- $version { 1 { set lines [expr {$e(1) - $s(1) + 1}] if {$type == "a"} { incr lines -1 } } 2 { set lines [expr {$e(2) - $s(2) + 1}] if {$type == "d"} { incr lines -1 } } 12 - 21 { set lines [expr {$e(1) - $s(1) + $e(2) - $s(2) + 1}] } } return $lines } ############################################################################### # Toggle showing merge preview or not ############################################################################### proc do-show-merge {{showMerge ""}} { #debug-info "do-show-merge ($showMerge)" global g global w if {$showMerge != ""} { set g(showmerge) $showMerge } if {$g(showmerge)} { watch-cursor if {! [info exists w(mergeText]} { merge-read-file merge-add-marks } wm deiconify .merge $w(mergeText) configure -state disabled focus -force $w(mergeText) merge-center } else { wm withdraw $w(merge) } #debug-info " ...restore-cursor from do-show-merge" restore-cursor } ############################################################################### # Create Merge preview window ############################################################################### proc merge-create-window {} { debug-info "merge-create-window ()" global opts global w global g set top .merge set w(merge) $top catch {destroy $top} toplevel $top set rx [winfo rootx .] set ry [winfo rooty .] set px [winfo width .] set py [winfo height .] #debug-info " rx $rx ry $ry px $px py $py" set x [expr {$rx + $px / 4}] set y [expr {$ry + $py / 2}] wm geometry $top "+${x}+$y" wm group $top . wm title $top "$g(name) Merge Preview" frame $top.frame -bd 1 -relief sunken pack $top.frame -side top -fill both -expand y -padx 10 -pady 10 set w(mergeText) $top.frame.text set w(mergeVSB) $top.frame.vsb set w(mergeHSB) $top.frame.hsb set w(mergeDismiss) $top.dismiss set w(mergeWrite) $top.mergeWrite set w(mergeWriteAndExit) $top.mergeWriteAndExit set w(mergeExit) $top.mergeExit set w(mergeRecenter) $top.mergeRecenter # Window and scrollbars scrollbar $w(mergeHSB) -orient horizontal -command [list $w(mergeText) \ xview] scrollbar $w(mergeVSB) -orient vertical -command [list $w(mergeText) yview] text $w(mergeText) -bd 0 -takefocus 1 -yscrollcommand [list $w(mergeVSB) \ set] -xscrollcommand [list $w(mergeHSB) set] grid $w(mergeText) -row 0 -column 0 -sticky nsew grid $w(mergeVSB) -row 0 -column 1 -sticky ns grid $w(mergeHSB) -row 1 -column 0 -sticky ew grid rowconfigure $top.frame 0 -weight 1 grid rowconfigure $top.frame 1 -weight 0 grid columnconfigure $top.frame 0 -weight 1 grid columnconfigure $top.frame 1 -weight 0 # buttons button $w(mergeRecenter) -width 8 -text "ReCenter" -underline 0 \ -command merge-center button $w(mergeDismiss) -width 8 -text "Dismiss" -underline 0 \ -command "do-show-merge 0" if {$g(mergefileset)} { button $w(mergeWrite) -width 8 -text "Save" -underline 0 \ -command [list popup-merge merge-write-file] button $w(mergeWriteAndExit) -width 8 -text "Save & Exit" \ -underline 8 -command { popup-merge merge-write-file exit } } else { button $w(mergeWrite) -width 8 -text "Save..." -underline 0 \ -command [list popup-merge merge-write-file] button $w(mergeWriteAndExit) -width 10 -text "Save & Exit..." \ -underline 8 -command { popup-merge merge-write-file exit } } button $w(mergeExit) -width 8 -text "Exit $g(name)" -underline 0 \ -command {exit} pack $w(mergeDismiss) -side right -pady 5 -padx 10 pack $w(mergeRecenter) -side right -pady 5 -padx 1 pack $w(mergeWrite) -side right -pady 5 -padx 1 pack $w(mergeWriteAndExit) -side right -pady 5 -padx 1 pack $w(mergeExit) -side right -pady 5 -padx 1 eval $w(mergeText) configure $opts(textopt) foreach tag {difftag currtag} { eval $w(mergeText) tag configure $tag $opts($tag) } # adjust the tabstops set cwidth [font measure [$w(mergeText) cget -font] "m"] set tabstops [expr {$cwidth * $opts(tabstops)}] $w(mergeText) configure -tabs $tabstops wm protocol $w(merge) WM_DELETE_WINDOW {do-show-merge 0} # adjust the tag priorities a bit... $w(mergeText) tag raise sel $w(mergeText) tag raise currtag difftag common-navigation $w(mergeText) if {! $g(showmerge)} { wm withdraw $w(merge) } } ############################################################################### # Read original file (Left window file) into merge preview window. # Not so good if it has changed. ############################################################################### proc merge-read-file {} { debug-info "merge-read-file ()" global finfo global w # hack; need to find a cleaner way... catch {destroy .merge} merge-create-window set hndl [open "$finfo(pth,1)" r] $w(mergeText) configure -state normal $w(mergeText) delete 1.0 end $w(mergeText) insert 1.0 [read $hndl] close $hndl # If last line doesn't end with a newline, add one. Important when # writing out the merge preview. if {![regexp {\.0$} [$w(mergeText) index "end-1lines lineend"]]} { $w(mergeText) insert end "\n" } $w(mergeText) configure -state disabled } ############################################################################### # Write merge preview to file ############################################################################### proc merge-write-file {} { global g global w set hndl [open "$g(mergefile)" w] set text [$w(mergeText) get 1.0 end-1lines] puts -nonewline $hndl $text close $hndl } ############################################################################### # Add a mark where each diff begins and tag diff regions so they are visible. # Assumes text is initially the bare original (Left) version. ############################################################################### proc merge-add-marks {} { global g global w # mark all lines first, so selection won't mess up line numbers for {set i 1} {$i <= $g(count)} {incr i} { foreach [list thisdiff s1 e1 s2 e2 type] $g(pdiff,$i) { } # set delta [expr {$type == "a" ? 1 : 0}] # $w(mergeText) mark set mark$i $s1.0+${delta}lines if {$type == "a"} { incr s1 } $w(mergeText) mark set mark$i $s1.0 $w(mergeText) mark gravity mark$i left } # if a 3-way merge, select right window as needed if {$g(ancfileset) && $g(count) > 0} { # # If there was something different between file1 # and the ancestor, pick the left window, but... # for {set i 1} {$i <= $g(count)} {incr i} { set s1 [lindex $g(pdiff,$i) 1] set s2 [lindex $g(pdiff,$i) 2] for {set p $s1} {$p <= $s2} {incr p} { if {[info exists g(diff3l$p)]} { set g(merge$i) 1 break } } } # # ... if there was a diff between file2 and the ancestor, # then file2 takes precedence # for {set i 1} {$i <= $g(count)} {incr i} { set s1 [lindex $g(pdiff,$i) 3] set s2 [lindex $g(pdiff,$i) 4] for {set p $s1} {$p <= $s2} {incr p} { if {[info exists g(diff3r$p)]} { set g(merge$i) 2 break } } } } # select merged lines for {set i 1} {$i <= $g(count)} {incr i} { foreach [list thisdiff s1 e1 s2 e2 type] $g(pdiff,$i) { } if {$g(merge$i) == 1} { # (If it's an insert it's not visible) if {$type != "a"} { set lines [expr {$e1 - $s1 + 1}] $w(mergeText) tag add difftag mark$i mark$i+${lines}lines } } else { # Insert right window version merge-select-version $i 1 2 } } # Tag current if {$g(count) > 0} { set pos $g(pos) set lines [diff-size $pos $g(merge$pos)] $w(mergeText) tag add currtag mark$pos "mark$pos+${lines}lines" } } ############################################################################### # Add a mark where each diff begins # pos diff index # oldversion 1 or 2, previous merge choice # newversion 1 or 2, new merge choice ############################################################################### proc merge-select-version {pos oldversion newversion} { global g global w catch { switch -- $oldversion { 1 - 2 {set oldlines [diff-size $pos $oldversion]} 12 - 21 {set oldlines [expr {[diff-size $pos 1] + [diff-size $pos 2]}]} } $w(mergeText) configure -state normal $w(mergeText) delete mark$pos "mark${pos}+${oldlines}lines" $w(mergeText) configure -state disabled } # Screen coordinates foreach {thisdiff s(1) e(1) s(2) e(2) type} $g(scrdiff,$pos) { } # Get the text directly from window switch -- $newversion { 1 { set newlines [diff-size $pos 1] set newtext [$w(LeftText) get $s(1).0 $s(1).0+${newlines}lines] } 2 { set newlines [diff-size $pos 2] set newtext [$w(RightText) get $s(2).0 $s(2).0+${newlines}lines] } 12 { set newlines [diff-size $pos 1] set newtext [$w(LeftText) get $s(1).0 $s(1).0+${newlines}lines] set newlines [diff-size $pos 2] append newtext [$w(RightText) get $s(2).0 $s(2).0+${newlines}lines] incr newlines [diff-size $pos 1] } 21 { set newlines [diff-size $pos 2] set newtext [$w(RightText) get $s(2).0 $s(2).0+${newlines}lines] set newlines [diff-size $pos 1] append newtext [$w(LeftText) get $s(1).0 $s(1).0+${newlines}lines] incr newlines [diff-size $pos 2] } } # Insert it $w(mergeText) configure -state normal $w(mergeText) insert mark$pos $newtext diff $w(mergeText) configure -state disabled if {$pos == $g(pos)} { $w(mergeText) tag add currtag mark$pos "mark${pos}+${newlines}lines" } } ############################################################################### # Center the merge region in the merge window ############################################################################### proc merge-center {} { global g global w # bail if there are no diffs if {$g(count) == 0} { return } # Size of diff in lines of text set difflines [diff-size $g(pos) $g(merge$g(pos))] set yview [$w(mergeText) yview] # Window height in percent set ywindow [expr {[lindex $yview 1] - [lindex $yview 0]}] # First line of diff set firstline [$w(mergeText) index mark$g(pos)] # Total number of lines in window set totallines [$w(mergeText) index end] if {$difflines / $totallines < $ywindow} { # Diff fits in window, center it $w(mergeText) yview moveto [expr {($firstline + $difflines / 2) / \ $totallines - $ywindow / 2}] } else { # Diff too big, show top part $w(mergeText) yview moveto [expr {($firstline - 1) / $totallines}] } } ############################################################################### # Update the merge preview window with the current merge choice # newversion 1 or 2, new merge choice ############################################################################### proc do-merge-choice {newversion} { debug-info "do-merge-choice ($newversion)" global g opts global w if {! [info exists w(mergeText)] || ! [winfo exists $w(mergeText)]} { return } $w(mergeText) configure -state normal merge-select-version $g(pos) $g(merge$g(pos)) $newversion $w(mergeText) configure -state disabled set g(merge$g(pos)) $newversion if {$g(showmerge) && $opts(autocenter)} { merge-center } set g(toggle) $newversion } ############################################################################### # Extract the start and end lines for file1 and file2 from the diff # stored in "line". ############################################################################### proc extract {line} { # the line darn well better be of the form , # where op is one of "a","c" or "d". range will either be a # single number or two numbers separated by a comma. # is this a cool regular expression, or what? :-) regexp {([0-9]*)(,([0-9]*))?([a-z])([0-9]*)(,([0-9]*))?} $line matchvar \ s1 x e1 op s2 x e2 if {[string length $e1] == 0} { set e1 $s1 } if {[string length $e2] == 0} { set e2 $s2 } if {[info exists s1] && [info exists s2]} { # return "$line $s1 $e1 $s2 $e2 $op" return [list $line $s1 $e1 $s2 $e2 $op] } else { fatal-error "Cannot parse output from diff:\n$line" } } ############################################################################### # Insert blank lines to match added/deleted lines in other file ############################################################################### proc add-lines {pos} { global g global w global opts # Figure out which lines we need to address... foreach [list thisdiff s1 e1 s2 e2 type] $g(pdiff,$pos) { } set size(1) [expr {$e1 - $s1}] set size(2) [expr {$e2 - $s2}] incr s1 $g(delta,1) incr s2 $g(delta,2) # Figure out what kind of diff we're dealing with switch -- $type { "a" { set lefttext " " ;# insert set righttext "+" set idx 1 set count [expr {$size(2) + 1}] incr s1 incr size(2) } "d" { set lefttext "-" ;# delete set righttext " " set idx 2 set count [expr {$size(1) + 1}] incr s2 incr size(1) } "c" { set lefttext "!" ;# change set righttext "!" ;# change if {$g(ancfileset)} { set change $g(pdiff,$g(count)) set leftBegin [lindex $change 1] set leftEnd [lindex $change 2] set rightBegin [lindex $change 3] set rightEnd [lindex $change 4] set changeLeft 0 set changeRight 0 for {set i $leftBegin} {$i <= $leftEnd} {incr i} { if {[info exists g(diff3l$i)]} { set changeLeft 1 break } } if {$changeLeft} { for {set i $rightBegin} {$i <= $rightEnd} {incr i} { if {[info exists g(diff3r$i)]} { set changeRight 1 break } } } if {$changeLeft && $changeRight} { set lefttext "?" ;# overlap set righttext "?" ;# overlap set g(overlap$pos) 1 } } set idx [expr {$size(1) < $size(2) ? 1 : 2}] set count [expr {abs($size(1) - $size(2))}] incr size(1) incr size(2) } } # Put plus signs in left info column if {$idx == 1} { set textWidget $w(LeftText) set infoWidget $w(LeftInfo) set cbWidget $w(LeftCB) # set blank "++++++\n" set blank " \n" } else { set textWidget $w(RightText) set infoWidget $w(RightInfo) set cbWidget $w(RightCB) set blank " \n" } # Insert blank lines to match other window set line [expr {$s1 + $size($idx)}] for {set i 0} {$i < $count} {incr i} { $textWidget insert $line.0 "\n" $infoWidget insert $line.0 $blank $cbWidget insert $line.0 "\n" } incr size($idx) $count set e1 [expr {$s1 + $size(1) - 1}] set e2 [expr {$s2 + $size(2) - 1}] incr g(delta,$idx) $count # Insert change bars or text to show what has changed. $w(RightCB) configure -state normal $w(LeftCB) configure -state normal for {set i $s1} {$i <= $e1} {incr i} { $w(LeftCB) insert $i.0 $lefttext $w(RightCB) insert $i.0 $righttext } # Save the diff block in window coordinates set g(scrdiff,$g(count)) [list $thisdiff $s1 $e1 $s2 $e2 $type] set g(scrinline,$pos) 0 if {$opts(showinline1) || $opts(showinline2)} { if {$type == "c"} { set numlines [max [expr {$e1-$s1+1}] [expr {$e2-$s2+1}]] for {set i 0} {$i < $numlines} {incr i} { set l1 [expr $s1+$i] set l2 [expr $s2+$i] if {$opts(showinline1)} { find-inline-diff-byte $pos $l1 $l2 [$w(LeftText) get \ $l1.0 $l1.end] [$w(RightText) get $l2.0 $l2.end] } else { find-inline-diff-ratcliff $pos $l1 $l2 [$w(LeftText) get \ $l1.0 $l1.end] [$w(RightText) get $l2.0 $l2.end] } } } } } ############################################################################### # Add a tag to a region. ############################################################################### proc add-tag {wgt tag start end type new {exact 0}} { global g $wgt tag add $tag $start.0 [expr {$end + 1}].0 } proc add-inline-tag {wgt tag line startcol endcol} { $wgt tag add $tag $line.$startcol $line.$endcol } ############################################################################### # Change the tag for a diff region. # 'pos' is the index in the diff array # If 'oldtag' is present, first remove it from the region # If 'setpos' is non-zero, make sure the region is visible. # Returns the diff expression. ############################################################################### proc set-tag {pos newtag {oldtag ""} {setpos 0}} { global g opts global w # Figure out which lines we need to address... if {![info exists g(scrdiff,$pos)]} { return } foreach {thisdiff s1 e1 s2 e2 dt} $g(scrdiff,$pos) { } # Remove old tag if {"$oldtag" != ""} { set e1next "[expr {$e1 + 1}].0" set e2next "[expr {$e2 + 1}].0" $w(LeftText) tag remove $oldtag $s1.0 $e1next $w(LeftInfo) tag remove $oldtag $s1.0 $e1next $w(RightText) tag remove $oldtag $s2.0 $e2next $w(RightInfo) tag remove $oldtag $s2.0 $e2next $w(LeftCB) tag remove $oldtag $s1.0 $e1next $w(RightCB) tag remove $oldtag $s2.0 $e2next catch { set lines [diff-size $pos $g(merge$pos)] $w(mergeText) tag remove $oldtag mark$pos "mark${pos}+${lines}lines" } } switch -- $dt { "d" { set coltag deltag set rcbtag " " set lcbtag "-" } "a" { set coltag instag set rcbtag "+" set lcbtag " " } "c" { set coltag chgtag set rcbtag "!" set lcbtag "!" } } if {[info exists g(overlap$pos)]} { set coltag overlaptag set rcbtag "?" set lcbtag "?" } # Add new tag if {$opts(tagtext)} { add-tag $w(LeftText) $newtag $s1 $e1 $dt 1 add-tag $w(RightText) $newtag $s2 $e2 $dt 1 add-tag $w(RightText) $coltag $s2 $e2 $dt 1 } if {$opts(tagcbs)} { if {$opts(colorcbs)} { add-tag $w(LeftCB) $lcbtag $s1 $e1 $dt 1 add-tag $w(RightCB) $rcbtag $s2 $e2 $dt 1 } else { add-tag $w(LeftCB) $newtag $s1 $e1 $dt 1 add-tag $w(RightCB) $newtag $s2 $e2 $dt 1 add-tag $w(RightCB) $coltag $s2 $e2 $dt 1 } } if {$opts(tagln)} { add-tag $w(LeftInfo) $newtag $s1 $e1 $dt 1 add-tag $w(RightInfo) $newtag $s2 $e2 $dt 1 add-tag $w(RightInfo) $coltag $s2 $e2 $dt 1 } catch { set lines [diff-size $pos $g(merge$pos)] $w(mergeText) tag add $newtag mark$pos "mark${pos}+${lines}lines" } # Move the view on both text widgets so that the new region is # visible. if {$setpos} { if {$opts(autocenter)} { center } else { $w(LeftText) see $s1.0 $w(RightText) see $s2.0 $w(LeftText) mark set insert $s1.0 $w(RightText) mark set insert $s2.0 if {$g(showmerge)} { $w(mergeText) see mark$pos } } } # make sure the sel tag has the highest priority foreach window [list LeftText RightText LeftCB RightCB LeftInfo RightInfo] { $w($window) tag raise sel $w($window) tag raise inlinetag } return $thisdiff } ############################################################################### # moves to the diff nearest the insertion cursor or the mouse click, # depending on $mode (which should be either "xy" or "mark") ############################################################################### proc moveNearest {window mode args} { switch -- $mode { "xy" { set x [lindex $args 0] set y [lindex $args 1] set index [$window index @$x,$y] set line [expr {int($index)}] set diff [find-diff $line] } "mark" { set index [$window index [lindex $args 0]] set line [expr {int($index)}] set diff [find-diff $line] } } # ok, we have an index move [lindex $diff 0] 0 1 } ############################################################################### ############################################################################### proc moveTo {window value} { global w global g # we know that the value is prefixed by the nunber/index of # the diff the user wants. So, just grab that out of the string regexp {([0-9]+) *:} $value matchVar index move $index 0 1 } ############################################################################### # this is called when the user scrolls the map thumb interactively. ############################################################################### proc map-seek {y} { global g global w set yview [expr {(double($y) / double($g(mapheight)))}] # Show text corresponding to map; $w(RightText) yview moveto $yview } ############################################################################### # Move the "current" diff indicator (i.e. go to the next or previous diff # region if "relative" is 1; go to an absolute diff number if "relative" # is 0). ############################################################################### proc move {value {relative 1} {setpos 1}} { #debug-info "move ($value $relative $setpos)" global g global w if {$value == "first"} { set value 1 set relative 0 } if {$value == "last"} { set value $g(count) set relative 0 } # Remove old 'curr' tag set-tag $g(pos) difftag currtag # Bump 'pos' (one way or the other). if {$relative} { set g(pos) [expr {$g(pos) + $value}] } else { set g(pos) $value } # Range check 'pos'. set g(pos) [max $g(pos) 1] set g(pos) [min $g(pos) $g(count)] # Set new 'curr' tag set g(currdiff) [set-tag $g(pos) currtag "" $setpos] # update the buttons.. #debug-info " ...update-display from move" update-display } proc update-display {} { debug-info "update-display ()" global g global w #debug-info " init_OK $g(initOK)" #debug-info " started $g(started)" #if {!$g(started)} return if {!$g(initOK)} { # disable darn near everything foreach b [list rediff find prevDiff firstDiff nextDiff lastDiff \ centerDiffs mergeChoice1 mergeChoice2 mergeChoice12 mergeChoice21] { $w(${b}_im) configure -state disabled $w(${b}_tx) configure -state disabled } foreach menu [list $w(popupMenu) $w(viewMenu)] { $menu entryconfigure "Previous*" -state disabled $menu entryconfigure "First*" -state disabled $menu entryconfigure "Next*" -state disabled $menu entryconfigure "Last*" -state disabled $menu entryconfigure "Center*" -state disabled } $w(popupMenu) entryconfigure "Find..." -state disabled $w(popupMenu) entryconfigure "Find Nearest*" -state disabled $w(popupMenu) entryconfigure "Edit*" -state disabled $w(editMenu) entryconfigure "Find*" -state disabled $w(editMenu) entryconfigure "Edit File 1" -state disabled $w(editMenu) entryconfigure "Edit File 2" -state disabled $w(fileMenu) entryconfigure "Write*" -state disabled $w(fileMenu) entryconfigure "Recompute*" -state disabled $w(mergeMenu) entryconfigure "Show*" -state disabled $w(mergeMenu) entryconfigure "Write*" -state disabled $w(markMenu) entryconfigure "Mark*" -state disabled $w(markMenu) entryconfigure "Clear*" -state disabled } else { # these are always enabled, assuming we have properly # diffed a couple of files $w(popupMenu) entryconfigure "Find..." -state normal $w(popupMenu) entryconfigure "Find Nearest*" -state normal $w(popupMenu) entryconfigure "Edit*" -state normal foreach b [list rediff find prevDiff firstDiff nextDiff lastDiff \ centerDiffs] { $w(${b}_im) configure -state normal $w(${b}_tx) configure -state normal } foreach b [list mergeChoice1 mergeChoice2 mergeChoice12 mergeChoice21] { $w(${b}_im) configure -state normal $w(${b}_tx) configure -state normal } $w(editMenu) entryconfigure "Find*" -state normal $w(editMenu) entryconfigure "Edit File 1" -state normal $w(editMenu) entryconfigure "Edit File 2" -state normal $w(fileMenu) entryconfigure "Write*" -state normal $w(fileMenu) entryconfigure "Recompute*" -state normal $w(mergeMenu) entryconfigure "Show*" -state normal $w(mergeMenu) entryconfigure "Write*" -state normal $w(find_im) configure -state normal $w(find_tx) configure -state normal # Hmmm.... on my Mac the combobox flashes if we don't add this # check. Is this a bug in AquaTk, or in my combobox... :-| if {[$w(combo) cget -state] != "normal"} { $w(combo) configure -state normal } } # Update the toggles. if {$g(count)} { set g(toggle) $g(merge$g(pos)) } # update the status line set g(statusCurrent) "$g(pos) of $g(count)" show-info $g(statusCurrent) # update the combobox. We don't want its command to fire, so # we'll disable it temporarily $w(combo) configure -commandstate "disabled" set i [expr {$g(pos) - 1}] $w(combo) configure -value [lindex [$w(combo) list get 0 end] $i] $w(combo) selection clear $w(combo) configure -commandstate "normal" # update the widgets if {$g(pos) <= 1} { foreach buttonpref {im tx} { $w(prevDiff_$buttonpref) configure -state disabled $w(firstDiff_$buttonpref) configure -state disabled } $w(popupMenu) entryconfigure "Previous*" -state disabled $w(popupMenu) entryconfigure "First*" -state disabled $w(viewMenu) entryconfigure "Previous*" -state disabled $w(viewMenu) entryconfigure "First*" -state disabled } else { foreach buttonpref {im tx} { $w(prevDiff_$buttonpref) configure -state normal $w(firstDiff_$buttonpref) configure -state normal } $w(popupMenu) entryconfigure "Previous*" -state normal $w(popupMenu) entryconfigure "First*" -state normal $w(viewMenu) entryconfigure "Previous*" -state normal $w(viewMenu) entryconfigure "First*" -state normal } if {$g(pos) >= $g(count)} { foreach buttonpref {im tx} { $w(nextDiff_$buttonpref) configure -state disabled $w(lastDiff_$buttonpref) configure -state disabled } $w(popupMenu) entryconfigure "Next*" -state disabled $w(popupMenu) entryconfigure "Last*" -state disabled $w(viewMenu) entryconfigure "Next*" -state disabled $w(viewMenu) entryconfigure "Last*" -state disabled } else { foreach buttonpref {im tx} { $w(nextDiff_$buttonpref) configure -state normal $w(lastDiff_$buttonpref) configure -state normal } $w(popupMenu) entryconfigure "Next*" -state normal $w(popupMenu) entryconfigure "Last*" -state normal $w(viewMenu) entryconfigure "Next*" -state normal $w(viewMenu) entryconfigure "Last*" -state normal } if {$g(count) > 0} { $w(popupMenu) entryconfigure "Center*" -state normal $w(viewMenu) entryconfigure "Center*" -state normal $w(markMenu) entryconfigure "Mark*" -state normal foreach buttonpref {im tx} { $w(centerDiffs_$buttonpref) configure -state normal $w(mergeChoice1_$buttonpref) configure -state normal $w(mergeChoice2_$buttonpref) configure -state normal $w(mergeChoice12_$buttonpref) configure -state normal $w(mergeChoice21_$buttonpref) configure -state normal } catch { $w(mergeChoiceLabel) configure -state normal } } else { foreach buttonpref {im tx} { $w(centerDiffs_$buttonpref) configure -state disabled $w(mergeChoice1_$buttonpref) configure -state disabled $w(mergeChoice2_$buttonpref) configure -state disabled $w(mergeChoice12_$buttonpref) configure -state disabled $w(mergeChoice21_$buttonpref) configure -state disabled } catch { $w(mergeChoiceLabel) configure -state disabled } $w(popupMenu) entryconfigure "Center*" -state disabled $w(viewMenu) entryconfigure "Center*" -state disabled $w(markMenu) entryconfigure "Mark*" -state disabled } # the mark clear button should only be enabled if there is # presently a mark at the current diff record set widget $w(toolbar).mark$g(pos) if {[winfo exists $widget]} { $w(markMenu) entryconfigure "Clear*" -state normal $w(markMenu) entryconfigure "Mark*" -state disabled foreach buttonpref {im tx} { $w(markClear_$buttonpref) configure -state normal $w(markSet_$buttonpref) configure -state disabled } } else { $w(markMenu) entryconfigure "Clear*" -state disabled $w(markMenu) entryconfigure "Mark*" -state normal foreach buttonpref {im tx} { $w(markClear_$buttonpref) configure -state disabled $w(markSet_$buttonpref) configure -state normal } } } ############################################################################### # Center the top line of the CDR in each window. ############################################################################### proc center {} { global g global w if {! [info exists g(scrdiff,$g(pos))]} {return} #scan $g(scrdiff,$g(pos)) "%s %d %d %d %d %s" dummy s1 e1 s2 e2 dt foreach {dummy s1 e1 s2 e2 dt} $g(scrdiff,$g(pos)) { } # Window requested height in pixels set opix [winfo reqheight $w(LeftText)] # Window requested lines set olin [$w(LeftText) cget -height] # Current window height in pixels set npix [winfo height $w(LeftText)] # Visible lines set winlines [expr {$npix * $olin / $opix}] # Lines in diff set diffsize [max [expr {$e1 - $s1 + 1}] [expr {$e2 - $s2 + 1}]] if {$diffsize < $winlines} { set h [expr {($winlines - $diffsize) / 2}] } else { set h 2 } set o [expr {$s1 - $h}] if {$o < 0} { set o 0 } set n [expr {$s2 - $h}] if {$n < 0} { set n 0 } $w(LeftText) mark set insert $s1.0 $w(RightText) mark set insert $s2.0 $w(LeftText) yview $o $w(RightText) yview $n if {$g(showmerge)} { merge-center } } ############################################################################### # Change the state on all of the diff-sensitive buttons. ############################################################################### proc buttons {{newstate "normal"}} { global w $w(combo) configure -state $newstate foreach buttonpref {im tx} { $w(prevDiff_$buttonpref) configure -state $newstate $w(nextDiff_$buttonpref) configure -state $newstate $w(firstDiff_$buttonpref) configure -state $newstate $w(lastDiff_$buttonpref) configure -state $newstate $w(centerDiffs_$buttonpref) configure -state $newstate } } ############################################################################### # Wipe the slate clean... ############################################################################### proc wipe {} { debug-info "wipe ()" global g set g(pos) 0 set g(count) 0 set g(diff) "" set g(currdiff) "" set g(delta,1) 0 set g(delta,2) 0 } ############################################################################### # Wipe all data and all windows ############################################################################### proc wipe-window {} { debug-info "wipe-window ()" global g global w wipe foreach mod {Left Right} { $w(${mod}Text) configure -state normal $w(${mod}Text) tag remove difftag 1.0 end $w(${mod}Text) tag remove currtag 1.0 end $w(${mod}Text) tag remove inlinetag 1.0 end $w(${mod}Text) delete 1.0 end $w(${mod}Info) configure -state normal $w(${mod}Info) delete 1.0 end $w(${mod}CB) configure -state normal $w(${mod}CB) delete 1.0 end } catch { $w(mergeText) configure -state normal $w(mergeText) delete 1.0 end eval $w(mergeText) tag delete [$w(mergeText) tag names] $w(mergeText) configure -state disabled } if {[string length $g(destroy)] > 0} { eval $g(destroy) set g(destroy) "" } $w(combo) list delete 0 end buttons disabled diffmark clearall } ############################################################################### # Mark difference regions and build up the combobox ############################################################################### proc mark-diffs {} { debug-info "mark-diffs ()" global g global w set numdiff [llength "$g(diff)"] set g(count) 0 # ain't this clever? We want to update the display as soon as # we've marked enough diffs to fill the display so the user will # have the impression we're fast. But, we don't want this # code to slow us down too much, so we'll put the # code in a variable and delete it when its no longer needed. set hack { # for now, just pick a number out of thin air. Ideally # we'd compute the number of lines that are visible and # use that, but I'm too lazy today... if {$g(count) > 25} { update idletasks set hack {} } } foreach d $g(diff) { set result [extract $d] if {$result != ""} { incr g(count) set g(merge$g(count)) 1 set g(pdiff,$g(count)) "$result" add-lines $g(count) $w(combo) list insert end [format "%-6d: %s" $g(count) $d] eval $hack } } remark-diffs return $g(count) } ############################################################################### # start a new diff from the popup dialog ############################################################################### proc do-new-diff {} { debug-info "do-new-diff ()" global g global finfo set g(mergefileset) 0 set g(mergefile) "" set finfo(pth,1) "" set finfo(pth,2) "" set finfo(tmp,1) 0 set finfo(tmp,2) 0 #foreach inf [lsort [array names finfo]] { debug-info " $inf: \ $finfo($inf)" } # Pop up the dialog to collect the args newDiffDialog # Put them together into a command if {[assemble-args] != 0} return #foreach inf [lsort [array names finfo]] { #debug-info " $inf: $finfo($inf)" #} set g(disableSyncing) 1 ;# turn off syncing until things settle down # remove all evidence of previous diff #wipe-window #update idletasks watch-cursor # do the diff do-diff #debug-info " move first 1 1 from do-new-diff" move first 1 1 #debug-info " ...restore-cursor from do-new-diff" restore-cursor #debug-info " ...update-display from do-new-diff" update-display catch {unset g(disableSyncing)} } ############################################################################### # Remark difference regions... ############################################################################### proc remark-diffs {} { debug-info "remark-diffs ()" global g global w global opts global pref # delete all known tags. foreach window [list $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB)] { eval $window tag delete [$window tag names] } if {[winfo exists .merge]} { eval $window tag delete [$w(mergeText) tag names] } # reconfigure all the tags based on the current options # first, the common tags: foreach tag {difftag currtag inlinetag deltag instag chgtag overlaptag} { foreach win [list $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB)] { if {[catch "$win tag configure $tag $opts($tag)"]} { do-error "Invalid settings for \"$pref($tag)\": \ \n\n'$opts($tag)' is not a valid option string." eval "$win tag configure $tag $opts($tag)" return } } } # next, changebar-specific tags foreach widget [list $w(LeftCB) $w(RightCB)] { eval $widget tag configure + $opts(+) eval $widget tag configure - $opts(-) eval $widget tag configure ! $opts(!) eval $widget tag configure ? $opts(?) } # ... and the merge text window if {[winfo exists .merge]} { foreach tag {difftag currtag} { eval $w(mergeText) tag configure $tag $opts($tag) } } # now, reapply the tags to all the diff regions for {set i 1} {$i <= $g(count)} {incr i} { set-tag $i difftag # add the inline annotation for {set j 0} {$j < $g(scrinline,$i)} {incr j} { foreach {side line startcol endcol} $g(scrinline,$i,$j) { } if {$side == "l"} { add-inline-tag $w(LeftText) inlinetag $line $startcol $endcol } else { add-inline-tag $w(RightText) inlinetag $line $startcol $endcol } } } # finally, reset the current diff set-tag $g(pos) currtag "" 0 } ############################################################################### # Put up some informational text. ############################################################################### proc show-info {message} { global g set g(statusInfo) $message update idletasks } ############################################################################### # Trace output, enabled by a global variable ############################################################################### proc debug-info {message} { global g if {$g(debug)} { puts "$message" } } ############################################################################### # Compute differences (start over, basically). ############################################################################### proc rediff {} { debug-info "rediff ()" global g global opts global finfo global w buttons disabled # Read the files into their respective widgets & add line numbers. foreach mod {1 2} { if {$mod == 1} { set text $w(LeftText) } else { set text $w(RightText) } show-info "reading $finfo(pth,$mod)..." if {[catch {set hndl [open "$finfo(pth,$mod)" r]}]} { fatal-error "Failed to open file: $finfo(pth,$mod)" } $text insert 1.0 [read $hndl] close $hndl } # Diff the two files and store the summary lines into 'g(diff)'. if {$opts(ignoreblanks) == 1} { set diffcmd "$opts(diffcmd) $opts(ignoreblanksopt) {$finfo(pth,1)} \ {$finfo(pth,2)}" } else { set diffcmd "$opts(diffcmd) {$finfo(pth,1)} {$finfo(pth,2)}" } show-info "Executing \"$diffcmd\"" set result [run-command "exec $diffcmd"] set stdout [lindex $result 0] set stderr [lindex $result 1] set exitcode [lindex $result 2] set g(returnValue) $exitcode # The exit code is 0 if there are no differences and 1 if there # are differences. Any other exit code means trouble if {$exitcode < 0 || $exitcode > 1 || $stderr != ""} { do-error "diff failed:\n$stderr" } set g(diff) {} set lines [split $stdout "\n"] # If there is no output and we got this far the files are equal, # otherwise check if the first line begins with a line number. If # not there was trouble and we abort. For instance, using a binary # file results in the message "Binary files ..." etc on stdout, # exit code 1. The message may wary depending on locale. if {$lines != "" && [string match {[0-9]*} $lines] != 1} { fatal-error "diff failed:\n$stdout" } # Collect all lines containing line numbers foreach line $lines { if {[string match {[0-9]*} $line]} { lappend g(diff) $line } } if {$g(ancfileset)} { # 3-way merge - compare 1st file (left: diff3l) with ancestor if {$opts(ignoreblanks) == 1} { set diffcmd "$opts(diffcmd) $opts(ignoreblanksopt) \ {$finfo(pth,1)} {$g(ancfile)}" } else { set diffcmd "$opts(diffcmd) {$finfo(pth,1)} {$g(ancfile)}" } show-info "Executing \"$diffcmd\"" set result [run-command "exec $diffcmd"] set stdout [lindex $result 0] set stderr [lindex $result 1] set exitcode [lindex $result 2] if {$exitcode < 0 || $exitcode > 1 || $stderr != ""} { fatal-error "diff3 failed:\n$stderr" } set lines [split $stdout "\n"] set g(diff3l) {} foreach line $lines { if {[string match {[0-9]*} $line]} { if {[regexp {^[0-9]*,} $line match]} { regexp {([0-9]*),([0-9]*).*} $line matchvar s1 s2 } else { regexp {([0-9]*).*} $line matchvar s1 set s2 $s1 } lappend g(diff3l) $s1 for {set i $s1} {$i <= $s2} {incr i} { set g(diff3l$i) 1 } } } # 3-way merge - compare 2nd file (right: diff3r) with ancestor if {$opts(ignoreblanks) == 1} { set diffcmd "$opts(diffcmd) $opts(ignoreblanksopt) \ {$finfo(pth,2)} {$g(ancfile)}" } else { set diffcmd "$opts(diffcmd) {$finfo(pth,2)} {$g(ancfile)}" } show-info "Executing \"$diffcmd\"" set result [run-command "exec $diffcmd"] set stdout [lindex $result 0] set stderr [lindex $result 1] set exitcode [lindex $result 2] if {$exitcode < 0 || $exitcode > 1 || $stderr != ""} { fatal-error "diff3 failed:\n$stderr" } set lines [split $stdout "\n"] set g(diff3r) {} foreach line $lines { if {[string match {[0-9]*} $line]} { if {[regexp {^[0-9]*,} $line match]} { regexp {([0-9]*),([0-9]*).*} $line matchvar s1 s2 } else { regexp {([0-9]*).*} $line matchvar s1 set s2 $s1 } lappend g(diff3r) $s1 for {set i $s1} {$i <= $s2} {incr i} { set g(diff3r$i) 1 } } } } # Mark up the two text widgets and go to the first diff (if there is one). draw-line-numbers show-info "Marking differences..." $w(LeftInfo) configure -state normal $w(RightInfo) configure -state normal $w(LeftCB) configure -state normal $w(RightCB) configure -state normal if {[mark-diffs]} { set g(pos) 1 move 1 0 1 buttons normal } else { after idle {show-info "Files are identical"} buttons disabled } # Prevent tampering in the line number widgets. The text # widgets are already taken care of $w(LeftInfo) configure -state disabled $w(RightInfo) configure -state disabled $w(LeftCB) configure -state disabled $w(RightCB) configure -state disabled } ############################################################################### # Set the X cursor to "watch" for a window and all of its descendants. ############################################################################### proc watch-cursor {args} { #debug-info "watch-cursor ($args)" global current global w . configure -cursor watch $w(LeftText) configure -cursor watch $w(RightText) configure -cursor watch $w(combo) configure -cursor watch update idletasks } ############################################################################### # Restore the X cursor for a window and all of its descendants. ############################################################################### proc restore-cursor {args} { #debug-info "restore-cursor ($args)" global current global w . configure -cursor {} $w(LeftText) configure -cursor {} $w(RightText) configure -cursor {} $w(combo) configure -cursor {} show-info "" update idletasks } ############################################################################### # Check if error was thrown by us or unexpected ############################################################################### proc check-error {result output} { global g errorInfo if {$result && $output != "Fatal"} { error $result $errorInfo } } ############################################################################### # redo the current diff. Attempt to return to the same diff region, # numerically speaking. ############################################################################### proc recompute-diff {} { debug-info "recompute-diff ()" global g set current $g(pos) debug-info "current position $g(pos)" do-diff move $current 0 1 center } ############################################################################### # Flash the "rediff" button and then kick off a rediff. ############################################################################### proc do-diff {} { debug-info "do-diff ()" global g finfo map errorInfo global opts wipe-window update idletasks set result [catch { if {$g(mapheight)} { ## FIXME this could better a catch catch {$map blank} } rediff merge-read-file merge-add-marks # If a map exists, recreate it if {$opts(showmap)} { set g(mapheight) -1 map-resize } } output] #debug-info " result: $result outptut: $output" check-error $result $output if {$g(mergefileset)} { do-show-merge 1 } } ############################################################################### # Get things going... ############################################################################### proc main {} { debug-info "main" global w global g errorInfo global startupError global opts global waitvar global tk_version wm withdraw . wm protocol . WM_DELETE_WINDOW do-exit wm title . "$g(name) $g(version)" if {![catch {set windowingsystem [tk windowingsystem]}]} { if {$windowingsystem == "x11"} { # All this nonsense is necessary to use an icon bitmap that's # not in a separate file. toplevel .icw if {[string first "color" [winfo visual .]] >= 0} { label .icw.l -image deltaGif } else { label .icw.l -image delta48 } pack .icw.l bind .icw "wm deiconify ." wm iconwindow . .icw } } if {$g(windowingSystem) == "x11"} { if {[get_gtk_params]} { debug-info "gtk" } elseif {[get_cde_params]} { debug-info "cde" } else { debug-info "x11 fallback" set hlbg "#4a6984" set hlfg "#ffffff" #set w(selcolor) $hlbg if {$tk_version >= 8.5} { option add *Menu.selectColor $w(foreground) option add *Checkbutton.selectColor "" option add *Radiobutton.selectColor "" } else { option add *selectColor $hlbg } } } if {$g(windowingSystem) == "aqua"} { get_aqua_params } set g(started) 1 wipe set cmd_empty [commandline] if {! $cmd_empty} { assemble-args } else { newDiffDialog # If they cancel the dialog before doing any diffs, exit if {[assemble-args] != 0} { return } set cmd_empty 0 } create-display update do-show-linenumbers do-show-map # evaluate any custom code the user has if {[info exists opts(customCode)]} { if {[catch [list uplevel \#0 $opts(customCode)] error]} { set startupError "Error in custom code: \n\n$error" } else { update } } if {$cmd_empty} { do-new-diff } move first 1 1 # this forces all of the various scrolling windows (line numbers, # change bars, etc) to get in sync. set yview [$w(RightText) yview] vscroll-sync [list $w(RightInfo) $w(LeftInfo)] 2 [lindex $yview 0] \ [lindex $yview 1] wm deiconify . update idletasks if {[info exists startupError]} { tk_messageBox -icon warning -type ok -title "$g(name) - Error in \ Startup File" -message $startupError } } ############################################################################### # Erase tmp files (if necessary) and destroy the application. ############################################################################### proc del-tmp {} { global g foreach f $g(tempfiles) { file delete $f } } ############################################################################### # Put up a window with formatted text ############################################################################### proc do-text-info {w title text} { global g catch "destroy $w" toplevel $w wm group $w . wm transient $w . wm title $w "$g(name) Help - $title" if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w } set width 64 set height 32 frame $w.f -bd 2 -relief sunken pack $w.f -side top -fill both -expand y text $w.f.title -highlightthickness 0 -bd 0 -height 2 -wrap word \ -width 50 -background white -foreground black text $w.f.text -wrap word -setgrid true -padx 20 -highlightthickness 0 \ -bd 0 -width $width -height $height -yscroll [list $w.f.vsb set] \ -background white -foreground black scrollbar $w.f.vsb -borderwidth 1 -command [list $w.f.text yview] \ -orient vertical pack $w.f.vsb -side right -fill y -expand n pack $w.f.title -side top -fill x -expand n pack $w.f.text -side left -fill both -expand y focus $w.f.text button $w.done -text Dismiss -command "destroy $w" pack $w.done -side right -fill none -pady 5 -padx 5 put-text $w.f.title "$title" put-text $w.f.text $text $w.f.text configure -state disabled wm geometry $w ${width}x${height} update idletasks raise $w } ############################################################################### # centers window w over parent ############################################################################### proc centerWindow {w {size {}}} { update set parent . if {[llength $size] > 0} { set wWidth [lindex $size 0] set wHeight [lindex $size 1] } else { set wWidth [winfo reqwidth $w] set wHeight [winfo reqheight $w] } set pWidth [winfo reqwidth $parent] set pHeight [winfo reqheight $parent] set pX [winfo rootx $parent] set pY [winfo rooty $parent] set centerX [expr {$pX +($pWidth / 2)}] set centerY [expr {$pY +($pHeight / 2)}] set x [expr {$centerX -($wWidth / 2)}] set y [expr {$centerY -($wHeight / 2)}] if {[llength $size] > 0} { wm geometry $w "=${wWidth}x${wHeight}+${x}+${y}" } else { wm geometry $w "=+${x}+${y}" } update } ############################################################################### # The "New Diff" dialog # In order to be able to enter only one filename if it's a revision-controlled # file, the dialog now collects the arguments and sends them through the # command line parser. ############################################################################### proc newDiffDialog {} { debug-info "newDiffDialog" global g w global finfo set g(mergefile) "" set g(mergefileset) 0 set waitvar {} set w(newDiffPopup) .newDiffPopup if {[winfo exists $w(newDiffPopup)]} { debug-info " $w(newDiffPopup) already exists, just centering" } else { debug-info " creating $w(newDiffPopup)" toplevel $w(newDiffPopup) wm group $w(newDiffPopup) . # Won't start as the first window on Windows if it's transient if {[winfo exists .client]} { wm transient $w(newDiffPopup) . } wm title $w(newDiffPopup) "New Diff" if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w(newDiffPopup) } wm protocol $w(newDiffPopup) WM_DELETE_WINDOW {wm withdraw \ $w(newDiffPopup)} wm withdraw $w(newDiffPopup) set simple [frame $w(newDiffPopup).simple -borderwidth 2 -relief groove] label $simple.l1 -text "File 1:" label $simple.l2 -text "File 2:" entry $simple.e1 -textvariable finfo(f,1) entry $simple.e2 -textvariable finfo(f,2) label $simple.lr1 -text "-r" label $simple.lr2 -text "-r" entry $simple.er1 -textvariable finfo(revs,1) entry $simple.er2 -textvariable finfo(revs,2) set w(newDiffPopup,entry1) $simple.e1 set w(newDiffPopup,entry2) $simple.e2 # we want these buttons to be the same height as # the entry, so we'll try to force the issue... button $simple.b1 -borderwidth 1 -highlightthickness 0 \ -text "Browse..." -command [list newDiffBrowse "File 1" $simple.e1] button $simple.b2 -borderwidth 1 -highlightthickness 0 \ -text "Browse..." -command [list newDiffBrowse "File 2" $simple.e2] # we'll use the grid geometry manager to get things lined up right... grid $simple.l1 -row 0 -column 0 -sticky e grid $simple.e1 -row 0 -column 1 -columnspan 4 -sticky nsew -pady 4 grid $simple.b1 -row 0 -column 5 -sticky nsew -padx 4 -pady 4 grid $simple.lr1 -row 1 -column 1 grid $simple.er1 -row 1 -column 2 grid $simple.lr2 -row 1 -column 3 grid $simple.er2 -row 1 -column 4 grid $simple.l2 -row 2 -column 0 -sticky e grid $simple.e2 -row 2 -column 1 -columnspan 4 -sticky nsew -pady 4 grid $simple.b2 -row 2 -column 5 -sticky nsew -padx 4 -pady 4 grid columnconfigure $simple 0 -weight 0 set options [frame $w(newDiffPopup).options -borderwidth 2 \ -relief groove] button $options.more -text "More" -command open-more-options label $options.ml -text "Merge Output" entry $options.me -textvariable g(mergefile) label $options.al -text "Ancestor" entry $options.ae -textvariable g(ancfile) label $options.l1l -text "Label for File 1" entry $options.l1e -textvariable finfo(userlbl,1) label $options.l2l -text "Label for File 2" entry $options.l2e -textvariable finfo(userlbl,2) grid $options.more -column 0 -row 0 -sticky nw grid columnconfigure $options -0 -weight 0 # here are the buttons for this dialog... set commands [frame $w(newDiffPopup).buttons] button $commands.ok -text "Ok" -width 5 -default active -command { if {$g(mergefile) == ""} { set g(mergefileset) 0 } else { set g(mergefileset) 1 } if {$g(ancfile) == ""} { set g(ancfileset) 0 } else { set g(ancfileset) 1 } set waitvar 1 } button $commands.cancel -text "Cancel" -width 5 -default normal \ -command { if {! [winfo exists .client]} {do-exit} wm withdraw $w(newDiffPopup); set waitvar 0 } pack $commands.ok $commands.cancel -side left -fill none -expand y \ -pady 4 catch {$commands.ok -default 1} # pack this crud in... pack $commands -side bottom -fill x -expand n pack $simple -side top -fill both -ipady 20 -ipadx 20 -padx 5 -pady 5 pack $options -side top -fill both -ipady 5 -ipadx 5 -padx 5 -pady 5 bind $w(newDiffPopup) [list $commands.ok invoke] bind $w(newDiffPopup) [list $commands.cancel invoke] } if {[winfo exists .client]} { centerWindow $w(newDiffPopup) } else { update } wm deiconify $w(newDiffPopup) raise $w(newDiffPopup) focus $w(newDiffPopup,entry1) tkwait variable waitvar wm withdraw $w(newDiffPopup) } proc open-more-options {} { global w grid $w(newDiffPopup).options.ml -row 0 -column 1 -sticky e grid $w(newDiffPopup).options.me -row 0 -column 2 -sticky nsew -pady 4 grid $w(newDiffPopup).options.al -row 1 -column 1 -sticky e grid $w(newDiffPopup).options.ae -row 1 -column 2 -sticky nsew -pady 4 grid $w(newDiffPopup).options.l1l -row 2 -column 1 -sticky e grid $w(newDiffPopup).options.l1e -row 2 -column 2 -sticky nsew -pady 4 grid $w(newDiffPopup).options.l2l -row 3 -column 1 -sticky e grid $w(newDiffPopup).options.l2e -row 3 -column 2 -sticky nsew -pady 4 $w(newDiffPopup).options.more configure -text "Less" \ -command close-more-options set x [winfo width $w(newDiffPopup)] set y [winfo height $w(newDiffPopup)] set yi [winfo reqheight $w(newDiffPopup).options] set newy [expr $y + $yi] if {[winfo exists .client]} { centerWindow $w(newDiffPopup) } else { update } } proc close-more-options {} { global w global finfo grid remove $w(newDiffPopup).options.ml grid remove $w(newDiffPopup).options.me grid remove $w(newDiffPopup).options.al grid remove $w(newDiffPopup).options.ae grid remove $w(newDiffPopup).options.l1l grid remove $w(newDiffPopup).options.l1e grid remove $w(newDiffPopup).options.l2l grid remove $w(newDiffPopup).options.l2e set g(mergefileset) "" set g(conflictset) "" set g(ancfileset) "" set g(ancfile) "" set finfo(userlbl,1) "" set finfo(userlbl,2) "" $w(newDiffPopup).options.more configure -text "More" \ -command open-more-options } ############################################################################### # File browser for the "New Diff" dialog ############################################################################### proc newDiffBrowse {title widget} { global w global opts debug-info "newDiffBrowse($title $widget)" set n [string index $widget end] set widgroot [string range $widget 0 end-1] if {$n == 1} { set other 2 } elseif { $n == 2} { set other 1 } set entrystuff [$widget get] debug-info "$n: entrystuff $entrystuff" if {$entrystuff != ""} { set initialdir [file dirname $entrystuff] } else { set otherentrystuff [${widgroot}$other get] if {$otherentrystuff != ""} { set initialdir [file dirname $otherentrystuff] } else { set initialdir [pwd] } } debug-info "initialdir $initialdir" set initialfile [file tail $entrystuff] set filename [tk_getOpenFile -title $title \ -filetypes $opts(filetypes) \ -initialfile $initialfile \ -initialdir $initialdir] if {[string length $filename] > 0} { $widget delete 0 end $widget insert 0 $filename $widget selection range 0 end $widget xview end focus $widget return $filename } else { after idle {raise $w(newDiffPopup)} return {} } } ############################################################################### # all the code to handle the report writing dialog. ############################################################################### proc write-report {command args} { global g global w global report global finfo set w(reportPopup) .reportPopup switch -- $command { popup { if {![winfo exists $w(reportPopup)]} { write-report build } set report(filename) [file join [pwd] $report(filename)] write-report update centerWindow $w(reportPopup) wm deiconify $w(reportPopup) raise $w(reportPopup) } cancel { wm withdraw $w(reportPopup) } update { set stateLeft "disabled" set stateRight "disabled" if {$report(doSideLeft)} { set stateLeft "normal" } if {$report(doSideRight)} { set stateRight "normal" } $w(reportLinenumLeft) configure -state $stateLeft $w(reportCMLeft) configure -state $stateLeft $w(reportTextLeft) configure -state $stateLeft $w(reportLinenumRight) configure -state $stateRight $w(reportCMRight) configure -state $stateRight $w(reportTextRight) configure -state $stateRight } save { set leftLines [lindex [split [$w(LeftText) index end-1lines] .] 0] set rightLines [lindex [split [$w(RightText) index end-1lines] .] 0] # number of lines of the largest window... set maxlines [max $leftLines $rightLines] # probably ought to catch this, in case it fails. Maybe later... set handle [open $report(filename) w] puts $handle "$g(name) $g(version) report" # write the file names if {$report(doSideLeft) == 1 && $report(doSideRight) == 1} { puts $handle "\nFile A: $finfo(lbl,1)\nFile B: $finfo(lbl,2)\n" } elseif {$report(doSideLeft) == 1} { puts $handle "\nFile: $finfo(lbl,1)" } else { puts $handle "\nFile: $finfo(lbl,2)" } puts $handle "number of diffs: $g(count)" set acount [set ccount [set dcount 0]] for {set i 1} {$i <= $g(count)} {incr i} { foreach {line s1 e1 s2 e2 type} $g(scrdiff,$i) { } switch -- $type { "d" { incr dcount } "a" { incr acount } "c" { incr ccount } } } puts $handle [format " %6d regions were deleted" $dcount] puts $handle [format " %6d regions were added" $acount] puts $handle [format " %6d regions were changed" $ccount] puts $handle "\n" for {set i 1} {$i <= $maxlines} {incr i} { set out(Left) [set out(Right) ""] foreach side {Left Right} { if {$side == "Left" && $i > $leftLines} break if {$side == "Right" && $i > $rightLines} break if {$report(doLineNumbers$side)} { set widget $w(${side}Info) set number [string trimright [$widget get "$i.0" \ "$i.0 lineend"]] append out($side) [format "%6s " $number] } if {$report(doChangeMarkers$side)} { set widget $w(${side}CB) set data [$widget get "$i.0" "$i.1"] append out($side) "$data " } if {$report(doText$side)} { set widget $w(${side}Text) append out($side) [string trimright [$widget get \ "$i.0" "$i.0 lineend"]] } } if {$report(doSideLeft) == 1 && $report(doSideRight) == 1} { set output [format "%-90s%-90s" $out(Left) $out(Right)] } elseif {$report(doSideRight) == 1} { set output $out(Right) } elseif {$report(doSideLeft) == 1} { set output $out(Left) } else { # what a wasted effort! set output "" } puts $handle [string trimright $output] } close $handle wm withdraw $w(reportPopup) } browse { set path [tk_getSaveFile \ -initialfile $report(filename)] if {[string length $path] > 0} { set report(filename) $path } } build { catch {destroy $w(reportPopup)} toplevel $w(reportPopup) wm group $w(reportPopup) . wm transient $w(reportPopup) . wm title $w(reportPopup) "$g(name) - Generate Report" wm protocol $w(reportPopup) WM_DELETE_WINDOW [list write-report \ cancel] wm withdraw $w(reportPopup) if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w(reportPopup) } set cf [frame $w(reportPopup).clientFrame -bd 2 -relief groove] set bf [frame $w(reportPopup).buttonFrame -bd 0] pack $cf -side top -fill both -expand y -padx 5 -pady 5 pack $bf -side bottom -fill x -expand n # buttons... set w(reportSave) $bf.save set w(reportCancel) $bf.cancel button $w(reportSave) -text "Save" -underline 0 -command \ [list write-report save] -width 6 button $w(reportCancel) -text "Cancel" -underline 0 \ -command [list write-report cancel] -width 6 pack $w(reportCancel) -side right -pady 5 -padx 5 pack $w(reportSave) -side right -pady 5 # client area. set col(Left) 0 set col(Right) 1 foreach side [list Left Right] { set choose [checkbutton $cf.choose$side] set linenum [checkbutton $cf.linenum$side] set cm [checkbutton $cf.changemarkers$side] set text [checkbutton $cf.text$side] $choose configure -text "$side Side" \ -variable report(doSide$side) -command [list write-report \ update] $linenum configure -text "Line Numbers" \ -variable report(doLineNumbers$side) $cm configure -text "Change Markers" \ -variable report(doChangeMarkers$side) $text configure -text "Text" -variable report(doText$side) grid $choose -row 0 -column $col($side) -sticky w grid $linenum -row 1 -column $col($side) -sticky w -padx 10 grid $cm -row 2 -column $col($side) -sticky w -padx 10 grid $text -row 3 -column $col($side) -sticky w -padx 10 # save the widget paths for later use... set w(reportChoose$side) $choose set w(reportLinenum$side) $linenum set w(reportCM$side) $cm set w(reportText$side) $text } # the entry, label and button for the filename will get # stuffed into a frame for convenience... frame $cf.fileFrame -bd 0 grid $cf.fileFrame -row 4 -columnspan 2 -sticky ew -padx 5 label $cf.fileFrame.l -text "File:" entry $cf.fileFrame.e -textvariable report(filename) -width 30 button $cf.fileFrame.b -text "Browse..." -pady 0 \ -highlightthickness 0 -borderwidth 1 -command \ [list write-report browse] pack $cf.fileFrame.l -side left -pady 4 pack $cf.fileFrame.b -side right -pady 4 -padx 2 pack $cf.fileFrame.e -side left -fill x -expand y -pady 4 grid rowconfigure $cf 0 -weight 0 grid rowconfigure $cf 1 -weight 0 grid rowconfigure $cf 2 -weight 0 grid rowconfigure $cf 3 -weight 0 grid columnconfigure $cf 0 -weight 1 grid columnconfigure $cf 1 -weight 1 # make sure the widgets are in the proper state write-report update } } } ############################################################################### # Report the version of wish ############################################################################### proc about_wish {} { global tk_patchLevel set version $tk_patchLevel set whichwish [info nameofexecutable] set about_string "$whichwish\n\n" append about_string "Tk version $version" tk_messageBox -title "About Wish" \ -message $about_string \ -parent . \ -type ok } ############################################################################### # Report the version of diff ############################################################################### proc about_diff {} { set whichdiff [auto_execok diff] if {[llength $whichdiff]} { set whichdiff [join $whichdiff] set commandline "diff -v" catch {eval "exec $commandline"} output set message "$whichdiff\n$output" } else { set message "diff was not found in your path!" } tk_messageBox -title "About Diff" \ -message $message \ -parent . \ -type ok } ############################################################################### # Throw up an "about" window. ############################################################################### proc do-about {} { global g set title "About $g(name)" set text { $g(name) $g(version) $g(name) is a Tcl/Tk front-end to diff for Unix and \ Windows, and is Copyright (C) 1994-2005 by John M. Klassa. Many of the toolbar icons were created by Dean S. Jones and used with his \ permission. The icons have the following copyright: Copyright(C) 1998 by Dean S. Jones dean@gallant.com http://www.gallant.com/icons.htm http://www.javalobby.org/jfa/projects/icons/ This program is free software; you can redistribute it and/or modify it \ under the terms of the GNU General Public License as published by the \ Free Software Foundation; either version 2 of the License, or (at your \ option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT \ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License \ for more details. You should have received a copy of the GNU General Public License along with \ this program; if not, write to the Free Software Foundation, Inc., 59 \ Temple Place, Suite 330, Boston, MA 02111-1307 USA } set text [subst -nobackslashes -nocommands $text] do-text-info .about $title $text } ############################################################################### # Throw up a "command line usage" window. ############################################################################### proc do-usage {mode} { debug-info "do-usage ($mode)" global g set usage { $g(name) may be started in any of the following ways: Interactive selection of files to compare: tkdiff Plain files: tkdiff FILE1 FILE2 Plain file with conflict markers: tkdiff -conflict FILE Source control (AccuRev, BitKeeper, ClearCase, CVS, Git, Mercurial, \ Perforce, PVCS, RCS, SCCS, Subversion) tkdiff FILE tkdiff -rREV FILE tkdiff -rREV1 -rREV2 FILE tkdiff OLD-URL[@OLDREV] NEW-URL[@NEWREV] (Subversion) Additional optional parameters: -a ANCESTORFILE -o MERGEOUTPUTFILE -L LEFT_FILE_LABEL [-L RIGHT_FILE_LABEL] -d debug } set usage [subst -nobackslashes -nocommands $usage] set text { $g(name) detects and supports RCS, CVS, Subversion and SCCS by looking for a \ directory with the same name. It detects and supports PVCS by looking \ for a vcs.cfg file. It detects and supports AccuRev, Perforce and \ ClearCase by looking for the environment variables named ACCUREV_BIN, \ P4CLIENT, and CLEARCASE_ROOT respectively. It detects Git by looking \ for a .git directory, but will only work when started in Git work-tree. \ Mercurial is supported by looking for a directory named ".hg" in the current \ directory or any of its ancestor directories. In the first form, tkdiff will present a dialog to allow you to choose the \ files to diff interactively. At present this dialog only supports a \ diff between two files that already exist. There is no way to do a diff \ against a file under souce code control (unless the previous versions \ can be found on disk via a standard file selection dialog). In the second form, at least one of the arguments must be the name of a plain \ text file. Symbolic links are acceptable, but at least one of the \ filename arguments must point to a real file rather than to a directory. In the remaining forms, REV (or REV1 and \ REV2) must be a valid revision number for FILE. \ When a source control system (RCS, CVS, etc.) is detected (see above) \ but no revision number is specified, FILE is compared with \ the revision most recently checked in. Git REV (see man git-rev-parse): -r FILE [compare with HEAD] -r HEAD FILE [compare with HEAD] -r HEAD^ FILE [compare with parent of HEAD] -r HEAD~5 FILE [compare with 5th parent of HEAD] -r HEAD~20 -r HEAD^ FILE [compare 20th parent and parent of HEAD] -r 29329e FILE [compare with commit 29329e (full/partial SHA1)] -r v1.2.3 FILE [compare with tag (UNTESTED)] To merge a file with conflict markers generated by "merge", \ "cvs", or "vmrg", use \ "tkdiff -conflict FILE". The file is split into two temporary \ files which you can merge as usual (see below). If the merge output filename is not specified, tkdiff will present a dialog \ to allow you to choose the name of the merge output file. Note further that anything with a leading dash that isn\'t recognized as a \ $g(name) option is passed through to diff. This permits you to \ temporarily alter the way diff is called, without resorting to a change \ in your preferences file. } if {$mode == "cline"} { puts $usage exit 0 } set text [subst -nobackslashes -nocommands $text] append usage $text do-text-info .usage "$g(name) Usage" $usage } ############################################################################### # Throw up a help window. ############################################################################### proc do-help {} { global g set title "How to use the $g(name) GUI" set text { Layout The top row contains the File, Edit, View, Mark, Merge and Help menus. The \ second row contains the labels which identify the contents of each text \ window. Below that is a toolbar which contains\ navigation and merge selection tools. The left-most text widget displays the contents of FILE1, the most \ recently checked-in revision, REV or REV1, \ respectively (as per the startup options described in\ the "On Command Line" help). The right-most widget displays the \ contents of FILE2, FILE or REV2, \ respectively. Clicking the right mouse button over either of\ these windows will give you a context sensitive menu with actions that \ will act on the window you clicked over. For example, if you click \ right over the right hand window and select\ "Edit", the file displayed on the right hand side will be loaded into a \ text editor. At the bottom of the display is a two line window called the \ "Line Comparison" window. This will show the "current line" from the \ left and right windows, one on top of the other. The "current line"\ is defined by the line that has the blinking insertion cursor, which \ can be set by merely clicking on any line in the display. This window \ may be hidden if the View menu item\ Show Line Comparison is deselected. All difference regions (DRs) are highlighted to set them apart from the \ surrounding text. The current difference region, or \ CDR, is further set apart so that it can be\ correlated to its partner in the other text widget (that is, the CDR on \ the left matches the CDR on the right). Changing the CDR The CDR can be changed in a sequential manner by means of the Next \ and Previous buttons. The First and \ Last buttons allow you to quickly navigate to the\ first or last CDR, respectively. For random access to the DRs, use the \ dropdown listbox in the toolbar or the diff map, described below. By clicking right over a window and using the popup menu you can select \ Find Nearest Diff to find the diff record nearest the point \ where you clicked. You may also select any highlighted diff region as the current diff region by \ double-clicking on it. Operations 1. From the File menu: The New... button displays a dialog where you may choose two files \ to compare. Selecting "Ok" from the dialog will diff the two files. The \ Recompute Diffs button recomputes the\ differences between the two files whose names appear at the top of the \ $g(name) window. The Write Report... lets you \ create a report file that contains the information\ visible in the windows. Lastly, the Exit button terminates \ $g(name). 2. From the Edit menu: Copy copies the currently selected text to the system clipboard. \ Find pops up a dialog to let you search either text window \ for a specified text string. Edit File 1 and Edit File \ 2 launch an editor on the files displayed in the left- and \ right-hand panes. Preferences pops up a dialog box from \ which display (and other) options can be changed and saved. 3. From the View menu: Show Line Numbers toggles the display of line numbers in the text \ widgets. If Synchronize Scrollbars is on, the left and right \ text widgets are synchronized i.e. scrolling one\ of the windows scrolls the other. If Auto Center is on, \ pressing the Next or Prev buttons centers the new CDR automatically. \ Show Diff Map toggles the display of the diff\ map (see below) on or off. Show Merge Preview shows or hides \ the merge preview (see below). Show Line Comparison toggles \ the display of the "line comparison" window at\ the bottom of the display. 4. From the Mark menu: The Mark Current Diff creates a new toolbar button that will jump \ to the current diff region. The Clear Current Diff Mark will \ remove the toolbar mark button associated with\ the current diff region, if one exists. 5. From the Merge menu: The Show Merge Window button pops up a window with the current \ merged version of the two files. The Write Merge File button \ will allow you to save the contents of that window\ to a file. 6. From the Help menu: The About $g(name) button displays copyright and author \ information. The On GUI button generates this window. The \ On Command Line button displays help on the\ $g(name) command line options. The On Preferences button \ displays help on the user-settable preferences. 7. From the toolbar: The first tool is a dropdown list of all of the differences in a standard \ diff-type format. You may use this list to go directly to any diff \ record. The Next and Previous\ buttons take you to the "next" and "previous" DR, respectively. The \ First and Last buttons take you to the \ "first" and "last" DR. The Center button centers the\ CDRs in their respective text windows. You can set Auto \ Center in Preferences to do this automatically for you \ as you navigate through the diff records. Keyboard Navigation When a text widget has the focus, you may use the following shortcut keys: f First diff c Center current diff l Last diff n Next diff p Previous diff 1 Merge Choice 1 2 Merge Choice 2 The cursor, Home, End, PageUp and PageDown keys work as expected, adjusting \ the view in whichever text window has the focus. Note that if \ Synchronize Scrollbars is set in\ Preferences, both windows will scroll at the same time. Scrolling To scroll the text widgets independently, make sure Synchronize \ Scrollbars in Preferences is off. If it is on, \ scrolling any text widget scrolls all others. Scrolling does not\ change the current diff record (CDR). Diff Marks You can set "markers" at specific diff regions for easier navigation. To do \ this, click on the Set Mark button. It will create a new \ toolbar button that will jump back to this diff\ region. To clear a diff mark, go to that diff record and click on the \ Clear Mark button. Diff Map The diff map is a map of all the diff regions. It is shown in the middle of \ the main window if "Diff Map" on the View menu is on. The map is a \ miniature of the file's diff regions from top to\ bottom. Each diff region is rendered as a patch of color, Delete as \ red, Insert as green and Change as blue. In the case of a 3-way merge, \ overlap regions are marked in yellow. The height of each patch \ corresponds to the relative size of the diff region. A\ thumb lets you interact with the map as if it were a scrollbar. All diff regions are drawn on the map even if too small to be visible. For \ large files with small diff regions, this may result in patches \ overwriting each other. Merging To merge the two files, go through the difference regions (via "Next", \ "Prev" or whatever other means you prefer) and select "Left" or \ "Right" (next to the "Merge Choice:" label) for each. Selecting\ "Left" means that the the left-most file's version of the difference \ will be used in creating the final result; choosing "Right" means that \ the right-most file's difference will be used. Each\ choice is recorded, and can be changed arbitrarily many times. To \ commit the final, merged result to disk, choose "Write Merge File..." \ from the Merge menu. Merge Preview To see a preview of the file that would be written by "Write Merge File...", \ select "Show Merge Window" in the View menu. A separate window is shown \ containing the preview. It is updated as you\ change merge choices. It is synchronized with the other text widgets if \ "Synchronize Scrollbars" is on. Author John M. Klassa Comments Questions and comments should be sent to the TkDiff mailing list at \ tkdiff-discuss@lists.sourceforge.net. } set text [subst -nobackslashes -nocommands $text] do-text-info .help $title $text } ###################################################################### # display help on the preferences ###################################################################### proc do-help-preferences {} { global g global pref customize-initLabels set title "$g(name) Preferences" set text { Overview Preferences are stored in a file in your home directory (identified by the \ environment variable HOME.) If the environment variable \ HOME is not set the platform-specific variant\ of "/" will be used. If you are on a Windows platform the file will be \ named _tkdiff.rc and will have the attribute "hidden". For \ all other platforms the file will be named\ ".tkdiffrc". You may override the name and location of this file by \ setting the environment variable TKDIFFRC to whatever \ filename you wish. Preferences are organized into three categories: General, Display and \ Appearance. General $pref(diffcmd) This is the command to run to generate a diff of the two files. Typically \ this will be "diff". When this command is run, the ignore-blanks \ options and the names of two files to be diffed will be added as the \ last to arguments on the command line. $pref(ignoreblanksopt) Arguments to send with the diff command to tell it how to ignore whitespace. \ If you are using gnu diff, "-b" or "--ignore-space-change" ignores \ changes in the amount of whitespace, while "-w" or \ "--ignore-all-space" ignores all white space. $pref(tmpdir) The name of a directory for files that are temporarily created while $g(name) \ is running. $pref(editor) The name of an external editor program to use when editing a file (ie: when \ you select "Edit" from the popup menu). If this value is blank, a \ simple editor built in to $g(name) will be used. For\ windows users you might want to set this to "notepad". Unix users may \ want to set this to "xterm -e vi" or perhaps "gnuclient". When run, the \ name of the file to edit will be appened as the\ last argument on the command line. If the supplied string contains the string "\$file", it\'s treated as a whole \ command line, where the following parameters can be used: \$file: the file of your choice \$line: the starting line of the current diff For example, in the case of NEdit or Emacs you can use "nc -line \$line \ \$file" and "emacs +\$line \$file" respectively. $pref(filetypes) The choice of file suffixes you want to see in the file open and save dialogs \ A list of the form { {{All Files} *} {{Text Files} .txt} } and so on. $pref(geometry) This defines the default size, in characters of the two text windows. The \ format should be WIDTHxHEIGHT. For example, "80x40". $pref(fancyButtons) If set, toolbar buttons will mimic the visual behavior of typical Microsoft \ Windows applications. Buttons will initially be flat until the cursor \ moves over them, at which time they will be raised. If unset, toolbar buttons will always appear raised. This feature is not supported in MacOSX. $pref(toolbarIcons) If set, the toolbar buttons will use icons instead of text labels. If unset, the toolbar buttons will use text labels instead of icons. $pref(autocenter) If set, whenever a new diff record becomes the current diff record (for \ example, when pressing the next or previous buttons), the diff record \ will be automatically centered on the screen. If unset, no automatic scrolling will occur. $pref(syncscroll) If set, scrolling either text window will result in both windows scrolling. If not set, the windows will scroll independent of each other. $pref(autoselect) If set, automatically select the nearest visible diff region when scrolling. If not set, the current diff region will not change during scrolling. This only takes effect if $pref(syncscroll) is set. Display $pref(showln) If set, line numbers will be displayed alongside each line of each file. If not set, no line numbers will appear. $pref(tagln) If set, line numbers are highlighted with the options defined in the \ Appearance section of the preferences. If not set, line numbers won\'t be highlighted. $pref(showcbs) If set, change bars will be displayed alongside each line of each file. If not set, no change bars will appear. $pref(tagcbs) If set, change indicators will be highlighted. If $pref(colorcbs) \ is set they will appear as solid colored bars that match the colors \ used in the diff map. If $pref(colorcbs)\ is not set, the change indicators will be highlighted according to the \ options defined in the Appearance section of preferences. $pref(showmap) If set, colorized, graphical "diff map" will be displayed between the two \ files, showing regions that have changed. Red is used to show deleted \ lines, green for added lines, blue for changed\ lines, and yellow for overlapping lines during a 3-way merge. If not set, the diff map will not be shown. $pref(showlineview) If set, show a window at the bottom of the display that shows the current \ line from each file, one on top of the other. This window is most \ useful to do a byte-by-byte comparison of a line that has\ changed. If not set, the window will not be shown. $pref(showinline1) If set, show inline diffs in the main window. This is useful to see what the \ actual diffs are within a large diff region. \ If not set, the inline diffs are neither computed nor shown. This is the \ simpler approach, where byte-by-byte comparisons \ are used. $pref(showinline2) If set, show inline diffs in the main window. This is useful to see what the \ actual diffs are within a large diff region. \ If not set, the inline diffs are neither computed nor shown. This approach \ is more complex, but should give more pleasing \ results for source code and written text files. This is the \ Ratcliff/Obershelp pattern matching algorithm which recursively \ finds the largest common substring, and recursively repeats on the left and \ right remainders. $pref(tagtext) If set, the file contents will be highlighted with the options defined in the \ Appearance section of the preferences. If not set, the file contents won\'t be highlighted. $pref(colorcbs) If set, the change bars will display as solid bars of color that match the \ colors used by the diff map. If not set, the change bars will display a "+" for lines that exist in only \ one file, a "-" for lines that are missing from only one file, and \ "!" for lines that are different between the two files. Appearance $pref(textopt) This is a list of Tk text widget options that are applied to each of the two \ text windows in the main display. If you have Tk installed on your \ machine these will be documented in the "Text.n" man\ page. $pref(difftag) This is a list of Tk text widget tag options that are applied to all diff \ regions. Use this option to make diff regions stand out from regular text. $pref(deltag) This is a list of Tk text widget tag options that are applied to the current \ diff region. These options have a higher priority than those for all \ diff regions. So, for example, if you set the\ forground for all diff regions to be black and set the foreground for \ the current diff region to be blue, the current diff region foreground \ color will be used. $pref(instag) This is a list of Tk text widget tag options that are applied to regions that \ have been inserted. These options have a higher priority than those for \ all diff regions. $pref(chgtag) This is a list of Tk text widget tag options that are applied to regions that \ have been changed. These options have a higher priority than those for \ all diff regions. $pref(currtag) This is a list of Tk text widget tag options that are applied to the current \ diff region. These tags have a higher priority than those for all diff \ regions, and a higher priority than the change,\ inserted and deleted diff regions. $pref(inlinetag) This is a list of Tk text widget tag options that are applied to differences \ within lines in a diff region. These tags have a higher priority than \ those for all diff regions, and a higher priority than the change,\ inserted and deleted diff regions. $pref(bytetag) This is a list of Tk text widget tag options that are applied to individual \ characters in the line view. These options do not affect the main text \ displays. $pref(tabstops) This defines the number of characters for each tabstop in the main display \ windows. The default is 8. } # since we have embedded references to the preference labels in # the text, we need to perform substitutions. Because of this, if # you edit the above text, be sure to properly escape any dollar # signs that are not meant to be treated as a variable reference set text [subst -nocommands $text] do-text-info .help-preferences $title $text } ###################################################################### # # text formatting routines derived from Klondike # Reproduced here with permission from their author. # # Copyright (C) 1993,1994 by John Heidemann # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of John Heidemann may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY JOHN HEIDEMANN ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL JOHN HEIDEMANN BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # ###################################################################### proc put-text {tw txt} { $tw configure -font {Fixed 12} $tw configure -font -*-Times-Medium-R-Normal-*-14-* $tw tag configure bld -font -*-Times-Bold-R-Normal-*-14-* $tw tag configure cmp -font -*-Courier-Medium-R-Normal-*-12-* $tw tag configure hdr -font -*-Helvetica-Bold-R-Normal-*-16-* -underline 1 $tw tag configure itl -font -*-Times-Medium-I-Normal-*-14-* $tw tag configure ttl -font -*-Helvetica-Bold-R-Normal-*-18-* #$tw tag configure h3 -font -*-Helvetica-Bold-R-Normal-*-14-* #$tw tag configure itl -font -*-Times-Medium-I-Normal-*-14-* #$tw tag configure rev -foreground white -background black $tw mark set insert 0.0 set t $txt while {[regexp -indices {<([^@>]*)>} $t match inds] == 1} { set start [lindex $inds 0] set end [lindex $inds 1] set keyword [string range $t $start $end] set oldend [$tw index end] $tw insert end [string range $t 0 [expr {$start - 2}]] purge-all-tags $tw $oldend insert if {[string range $keyword 0 0] == "/"} { set keyword [string trimleft $keyword "/"] if {[info exists tags($keyword)] == 0} { error "end tag $keyword without beginning" } $tw tag add $keyword $tags($keyword) insert unset tags($keyword) } else { if {[info exists tags($keyword)] == 1} { error "nesting of begin tag $keyword" } set tags($keyword) [$tw index insert] } set t [string range $t [expr {$end + 2}] end] } set oldend [$tw index end] $tw insert end $t purge-all-tags $tw $oldend insert } proc purge-all-tags {w start end} { foreach tag [$w tag names $start] { $w tag remove $tag $start $end } } # Open one of the diffed files in an editor if possible proc do-edit {} { debug-info "do-edit ()" global g global opts global finfo global w if {$g(activeWindow) == $w(LeftText)} { set fileno 1 } elseif {$g(activeWindow) == $w(RightText)} { set fileno 2 } else { set fileno 1 } if {$finfo(tmp,$fileno)} { do-error "This file is not editable" } else { if {[string length [string trim $opts(editor)]] == 0} { simpleEd open $finfo(pth,$fileno) } elseif {[regexp "\\\$file" "$opts(editor)"] == 1} { set line [lindex [extract $g(currdiff)] [expr {($fileno-1) *2+1}]] set file $finfo(pth,$fileno) eval set commandline \"$opts(editor) &\" debug-info "exec $commandline" eval exec $commandline } else { debug-info "exec $opts(editor) \"{$finfo(pth,$fileno)}\" &" eval exec $opts(editor) "{$finfo(pth,$fileno)}" & } } } ########################################################################## # platform-specific stuff ########################################################################## proc setAquaDialogStyle {toplevel} { if {[catch {tk::unsupported::MacWindowStyle style $toplevel moveableModal}] } { tk::unsupported::MacWindowStyle style $toplevel movableDBoxProc } } ########################################################################## # A simple editor, from Bryan Oakley. ########################################################################## proc simpleEd {command args} { debug-info "simpleEd ($command $args)" global textfont switch -- $command { open { set filename [lindex $args 0] set w .editor set count 0 while {[winfo exists ${w}$count]} { incr count 1 } set w ${w}$count toplevel $w -borderwidth 2 -relief sunken wm title $w "$filename - Simple Editor" wm group $w . menu $w.menubar $w configure -menu $w.menubar $w.menubar add cascade -label "File" -menu $w.menubar.fileMenu $w.menubar add cascade -label "Edit" -menu $w.menubar.editMenu menu $w.menubar.fileMenu menu $w.menubar.editMenu $w.menubar.fileMenu add command -label "Save" -underline 1 \ -command [list simpleEd save $filename $w] $w.menubar.fileMenu add command -label "Save As..." -underline 1 \ -command [list simpleEd saveAs $filename $w] $w.menubar.fileMenu add separator $w.menubar.fileMenu add command -label "Exit" -underline 1 \ -command [list simpleEd exit $w] $w.menubar.editMenu add command -label "Cut" -command [list event \ generate $w.text <>] $w.menubar.editMenu add command -label "Copy" -command \ [list event generate $w.text <>] $w.menubar.editMenu add command -label "Paste" -command \ [list event generate $w.text <>] text $w.text -wrap none -xscrollcommand [list $w.hsb set] \ -yscrollcommand [list $w.vsb set] -borderwidth 0 -font $textfont scrollbar $w.vsb -orient vertical -command [list $w.text yview] scrollbar $w.hsb -orient horizontal -command [list $w.text xview] grid $w.text -row 0 -column 0 -sticky nsew grid $w.vsb -row 0 -column 1 -sticky ns grid $w.hsb -row 1 -column 0 -sticky ew grid columnconfigure $w 0 -weight 1 grid columnconfigure $w 1 -weight 0 grid rowconfigure $w 0 -weight 1 grid rowconfigure $w 1 -weight 0 set fd [open $filename] $w.text insert 1.0 [read $fd] close $fd } save { set filename [lindex $args 0] set w [lindex $args 1] set fd [open $filename w] puts $fd [$w.text get 1.0 "end-1c"] close $fd } saveAs { set filename [lindex $args 0] set w [lindex $args 1] set filename [tk_getSaveFile -initialfile $filename] if {$filename != ""} { simpleEd save $filename $w } } exit { set w [lindex $args 0] destroy $w } } } # end of simpleEd # Copyright (c) 1998-2005, Bryan Oakley # All Rights Reservered # # Bryan Oakley # oakley@bardo.clearlight.com # # combobox v2.2.2 September 22, 2002 # # a combobox / dropdown listbox (pick your favorite name) widget # written in pure tcl # # this code is freely distributable without restriction, but is # provided as-is with no warranty expressed or implied. # # thanks to the following people who provided beta test support or # patches to the code (in no particular order): # # Scott Beasley Alexandre Ferrieux Todd Helfter # Matt Gushee Laurent Duperval John Jackson # Fred Rapp Christopher Nelson # Eric Galluzzo Jean-Francois Moine # # A special thanks to Martin M. Hunt who provided several good ideas, # and always with a patch to implement them. Jean-Francois Moine, # Todd Helfter and John Jackson were also kind enough to send in some # code patches. # # ... and many others over the years. package provide combobox 2.2.2 namespace eval ::combobox { # this is the public interface namespace export combobox # these contain references to available options variable widgetOptions # these contain references to available commands and subcommands variable widgetCommands variable scanCommands variable listCommands } # ::combobox::combobox -- # # This is the command that gets exported. It creates a new # combobox widget. # # Arguments: # # w path of new widget to create # args additional option/value pairs (eg: -background white, etc.) # # Results: # # It creates the widget and sets up all of the default bindings # # Returns: # # The name of the newly create widget proc ::combobox::combobox {w args} { variable widgetOptions variable widgetCommands variable scanCommands variable listCommands # perform a one time initialization if {![info exists widgetOptions]} { Init } # build it... eval Build $w $args # set some bindings... SetBindings $w # and we are done! return $w } # ::combobox::Init -- # # Initialize the namespace variables. This should only be called # once, immediately prior to creating the first instance of the # widget # # Arguments: # # none # # Results: # # All state variables are set to their default values; all of # the option database entries will exist. # # Returns: # # empty string proc ::combobox::Init {} { variable widgetOptions variable widgetCommands variable scanCommands variable listCommands variable defaultEntryCursor array set widgetOptions [list -background \ {background Background} -bd -borderwidth -bg -background \ -borderwidth {borderWidth BorderWidth} -command \ {command Command} -commandstate {commandState State} \ -cursor {cursor Cursor} \ -disabledbackground {disabledBackground DisabledBackground} \ -disabledforeground {disabledForeground DisabledForeground} \ -dropdownwidth {dropdownWidth DropdownWidth} -editable \ {editable Editable} -fg -foreground -font \ {font Font} -foreground {foreground Foreground} \ -height {height Height} \ -highlightbackground {highlightBackground HighlightBackground} \ -highlightcolor {highlightColor HighlightColor} \ -highlightthickness {highlightThickness HighlightThickness} \ -image {image Image} -maxheight \ {maxHeight Height} -opencommand {opencommand Command} \ -relief {relief Relief} \ -selectbackground {selectBackground Foreground} \ -selectborderwidth {selectBorderWidth BorderWidth} \ -selectforeground {selectForeground Background} -state \ {state State} -takefocus {takeFocus TakeFocus} \ -textvariable {textVariable Variable} -value \ {value Value} -width {width Width} \ -xscrollcommand {xScrollCommand ScrollCommand}] set widgetCommands [list bbox cget configure curselection delete get \ icursor index insert list scan selection xview select toggle open close] set listCommands [list delete get index insert size] set scanCommands [list mark dragto] # why check for the Tk package? This lets us be sourced into # an interpreter that doesn't have Tk loaded, such as the slave # interpreter used by pkg_mkIndex. In theory it should have no # side effects when run if {[lsearch -exact [package names] "Tk"] != -1} { ################################################################## #- this initializes the option database. Kinda gross, but it works #- (I think). ################################################################## # the image used for the button... if {$::tcl_platform(platform) == "windows"} { image create bitmap ::combobox::bimage -data { #define down_arrow_width 12 #define down_arrow_height 12 static char down_arrow_bits[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xfc,0xf1,0xf8,0xf0,0x70,0xf0,0x20,0xf0, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00; } } } else { image create bitmap ::combobox::bimage -data { #define down_arrow_width 15 #define down_arrow_height 15 static char down_arrow_bits[] = { 0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80, 0x00,0x80,0xf8,0x8f,0xf0,0x87,0xe0,0x83, 0xc0,0x81,0x80,0x80,0x00,0x80,0x00,0x80, 0x00,0x80,0x00,0x80,0x00,0x80 } } } # compute a widget name we can use to create a temporary widget set tmpWidget ".__tmp__" set count 0 while {[winfo exists $tmpWidget] == 1} { set tmpWidget ".__tmp__$count" incr count } # get the scrollbar width. Because we try to be clever and draw our # own button instead of using a tk widget, we need to know what size # button to create. This little hack tells us the width of a scroll # bar. # # NB: we need to be sure and pick a window that doesn't already # exist... scrollbar $tmpWidget set sb_width [winfo reqwidth $tmpWidget] destroy $tmpWidget # steal options from the entry widget # we want darn near all options, so we'll go ahead and do # them all. No harm done in adding the one or two that we # don't use. entry $tmpWidget foreach foo [$tmpWidget configure] { # the cursor option is special, so we'll save it in # a special way if {[lindex $foo 0] == "-cursor"} { set defaultEntryCursor [lindex $foo 4] } if {[llength $foo] == 5} { set option [lindex $foo 1] set value [lindex $foo 4] option add *Combobox.$option $value widgetDefault # these options also apply to the dropdown listbox if {[string compare $option "foreground"] == 0 || \ [string compare $option "background"] == 0 || \ [string compare $option "font"] == 0} { option add *Combobox*ComboboxListbox.$option $value \ widgetDefault } } } destroy $tmpWidget # these are unique to us... option add *Combobox.dropdownWidth {} widgetDefault option add *Combobox.openCommand {} widgetDefault option add *Combobox.cursor {} widgetDefault option add *Combobox.commandState normal widgetDefault option add *Combobox.editable 1 widgetDefault option add *Combobox.maxHeight 10 widgetDefault option add *Combobox.height 0 } # set class bindings SetClassBindings } # ::combobox::SetClassBindings -- # # Sets up the default bindings for the widget class # # this proc exists since it's The Right Thing To Do, but # I haven't had the time to figure out how to do all the # binding stuff on a class level. The main problem is that # the entry widget must have focus for the insertion cursor # to be visible. So, I either have to have the entry widget # have the Combobox bindtag, or do some fancy juggling of # events or some such. What a pain. # # Arguments: # # none # # Returns: # # empty string proc ::combobox::SetClassBindings {} { # make sure we clean up after ourselves... bind Combobox [list ::combobox::DestroyHandler %W] # this will (hopefully) close (and lose the grab on) the # listbox if the user clicks anywhere outside of it. Note # that on Windows, you can click on some other app and # the listbox will still be there, because tcl won't see # that button click set this {[::combobox::convert %W -W]} bind Combobox "$this close" bind Combobox "$this close" # this helps (but doesn't fully solve) focus issues. The general # idea is, whenever the frame gets focus it gets passed on to # the entry widget #bind Combobox {::combobox::tkTabToWindow \ #[::combobox::convert %W -W].entry} # this closes the listbox if we get hidden bind Combobox {[::combobox::convert %W -W] close} return "" } # ::combobox::SetBindings -- # # here's where we do most of the binding foo. I think there's probably # a few bindings I ought to add that I just haven't thought # about... # # I'm not convinced these are the proper bindings. Ideally all # bindings should be on "Combobox", but because of my juggling of # bindtags I'm not convinced thats what I want to do. But, it all # seems to work, its just not as robust as it could be. # # Arguments: # # w widget pathname # # Returns: # # empty string proc ::combobox::SetBindings {w} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # juggle the bindtags. The basic idea here is to associate the # widget name with the entry widget, so if a user does a bind # on the combobox it will get handled properly since it is # the entry widget that has keyboard focus. bindtags $widgets(entry) [concat $widgets(this) [bindtags $widgets(entry)]] bindtags $widgets(button) [concat $widgets(this) \ [bindtags $widgets(button)]] # override the default bindings for tab and shift-tab. The # focus procs take a widget as their only parameter and we # want to make sure the right window gets used (for shift- # tab we want it to appear as if the event was generated # on the frame rather than the entry. #bind $widgets(entry) "::combobox::tkTabToWindow \[tk_focusNext \ #$widgets(entry)\]; break" #bind $widgets(entry) \ #"::combobox::tkTabToWindow \[tk_focusPrev $widgets(this)\]; break" # this makes our "button" (which is actually a label) # do the right thing bind $widgets(button) [list $widgets(this) toggle] # this lets the autoscan of the listbox work, even if they # move the cursor over the entry widget. bind $widgets(entry) "break" bind $widgets(listbox) "::combobox::Select \ [list $widgets(this)] \[$widgets(listbox) nearest %y\]; break" bind $widgets(vsb) {continue} bind $widgets(vsb) {continue} bind $widgets(listbox) { %W selection clear 0 end %W activate @%x,%y %W selection anchor @%x,%y %W selection set @%x,%y @%x,%y # need to do a yview if the cursor goes off the top # or bottom of the window... (or do we?) } # these events need to be passed from the entry widget # to the listbox, or otherwise need some sort of special # handling. foreach event [list \ <1> ] { bind $widgets(entry) $event [list ::combobox::HandleEvent \ $widgets(this) $event] } # like the other events, needs to be passed from # the entry widget to the listbox. However, in this case we # need to add an additional parameter catch { bind $widgets(entry) [list ::combobox::HandleEvent \ $widgets(this) %D] } } # ::combobox::Build -- # # This does all of the work necessary to create the basic # combobox. # # Arguments: # # w widget name # args additional option/value pairs # # Results: # # Creates a new widget with the given name. Also creates a new # namespace patterened after the widget name, as a child namespace # to ::combobox # # Returns: # # the name of the widget proc ::combobox::Build {w args} { variable widgetOptions if {[winfo exists $w]} { error "window name \"$w\" already exists" } # create the namespace for this instance, and define a few # variables namespace eval ::combobox::$w { variable ignoreTrace 0 variable oldFocus {} variable oldGrab {} variable oldValue {} variable options variable this variable widgets set widgets(foo) foo ;# coerce into an array set options(foo) foo ;# coerce into an array unset widgets(foo) unset options(foo) } # import the widgets and options arrays into this proc so # we don't have to use fully qualified names, which is a # pain. upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # this is our widget -- a frame of class Combobox. Naturally, # it will contain other widgets. We create it here because # we need it in order to set some default options. set widgets(this) [frame $w -class Combobox -takefocus 0] set widgets(entry) [entry $w.entry -takefocus 1] set widgets(button) [label $w.button -takefocus 0] # this defines all of the default options. We get the # values from the option database. Note that if an array # value is a list of length one it is an alias to another # option, so we just ignore it foreach name [array names widgetOptions] { if {[llength $widgetOptions($name)] == 1} continue set optName [lindex $widgetOptions($name) 0] set optClass [lindex $widgetOptions($name) 1] set value [option get $w $optName $optClass] set options($name) $value } # a couple options aren't available in earlier versions of # tcl, so we'll set them to sane values. For that matter, if # they exist but are empty, set them to sane values. if {[string length $options(-disabledforeground)] == 0} { set options(-disabledforeground) $options(-foreground) } if {[string length $options(-disabledbackground)] == 0} { set options(-disabledbackground) $options(-background) } # if -value is set to null, we'll remove it from our # local array. The assumption is, if the user sets it from # the option database, they will set it to something other # than null (since it's impossible to determine the difference # between a null value and no value at all). if {[info exists options(-value)] && [string length $options(-value)] == \ 0} { unset options(-value) } # we will later rename the frame's widget proc to be our # own custom widget proc. We need to keep track of this # new name, so we'll define and store it here... set widgets(frame) ::combobox::${w}::$w # gotta do this sooner or later. Might as well do it now pack $widgets(entry) -side left -fill both -expand yes pack $widgets(button) -side right -fill y -expand no # I should probably do this in a catch, but for now it's # good enough... What it does, obviously, is put all of # the option/values pairs into an array. Make them easier # to handle later on... array set options $args # now, the dropdown list... the same renaming nonsense # must go on here as well... set widgets(dropdown) [toplevel $w.top] set widgets(listbox) [listbox $w.top.list] set widgets(vsb) [scrollbar $w.top.vsb] pack $widgets(listbox) -side left -fill both -expand y # fine tune the widgets based on the options (and a few # arbitrary values...) # NB: we are going to use the frame to handle the relief # of the widget as a whole, so the entry widget will be # flat. This makes the button which drops down the list # to appear "inside" the entry widget. $widgets(vsb) configure -command "$widgets(listbox) yview" \ -highlightthickness 0 $widgets(button) configure -highlightthickness 0 -borderwidth 1 \ -relief raised -width [expr {[winfo reqwidth $widgets(vsb)] - 2}] $widgets(entry) configure -borderwidth 0 -relief flat -highlightthickness 0 $widgets(dropdown) configure -borderwidth 1 -relief sunken $widgets(listbox) configure -selectmode browse \ -background [$widgets(entry) cget -bg] -yscrollcommand \ "$widgets(vsb) set" -exportselection false -borderwidth 0 # do some window management foo on the dropdown window # There seems to be some order dependency here on some platforms wm transient $widgets(dropdown) [winfo toplevel $w] wm group $widgets(dropdown) [winfo parent $w] wm resizable $widgets(dropdown) 0 0 wm overrideredirect $widgets(dropdown) 1 wm withdraw $widgets(dropdown) # this moves the original frame widget proc into our # namespace and gives it a handy name rename ::$w $widgets(frame) # now, create our widget proc. Obviously (?) it goes in # the global namespace. All combobox widgets will actually # share the same widget proc to cut down on the amount of # bloat. proc ::$w {command args} "eval ::combobox::WidgetProc $w \$command \$args" # ok, the thing exists... let's do a bit more configuration. if {[catch "::combobox::Configure [list $widgets(this)] [array get \ options]" error]} { catch {destroy $w} error "internal error: $error" } return "" } # ::combobox::HandleEvent -- # # this proc handles events from the entry widget that we want # handled specially (typically, to allow navigation of the list # even though the focus is in the entry widget) # # Arguments: # # w widget pathname # event a string representing the event (not necessarily an # actual event) # args additional arguments required by particular events proc ::combobox::HandleEvent {w event args} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options upvar ::combobox::${w}::oldValue oldValue # for all of these events, if we have a special action we'll # do that and do a "return -code break" to keep additional # bindings from firing. Otherwise we'll let the event fall # on through. switch -- $event { "" { if {[winfo ismapped $widgets(dropdown)]} { set D [lindex $args 0] # the '120' number in the following expression has # it's genesis in the tk bind manpage, which suggests # that the smallest value of %D for mousewheel events # will be 120. The intent is to scroll one line at a time. $widgets(listbox) yview scroll [expr {-($D/120)}] units } } "" { # if the widget is editable, clear the selection. # this makes it more obvious what will happen if the # user presses (and helps our code know what # to do if the user presses return) if {$options(-editable)} { $widgets(listbox) see 0 $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor 0 $widgets(listbox) activate 0 } } "" { set oldValue [$widgets(entry) get] } "" { if {![winfo ismapped $widgets(dropdown)]} { # did the value change? set newValue [$widgets(entry) get] if {$oldValue != $newValue} { CallCommand $widgets(this) $newValue } } } "<1>" { set editable [::combobox::GetBoolean $options(-editable)] if {!$editable} { if {[winfo ismapped $widgets(dropdown)]} { $widgets(this) close return -code break } else { if {$options(-state) != "disabled"} { $widgets(this) open return -code break } } } } "" { if {$options(-state) != "disabled"} { $widgets(this) toggle return -code break } } "" { if {[winfo ismapped $widgets(dropdown)]} { ::combobox::Find $widgets(this) 0 return -code break } else { ::combobox::SetValue $widgets(this) [$widgets(this) get] } } "" { # $widgets(entry) delete 0 end # $widgets(entry) insert 0 $oldValue if {[winfo ismapped $widgets(dropdown)]} { $widgets(this) close return -code break } } "" { # did the value change? set newValue [$widgets(entry) get] if {$oldValue != $newValue} { CallCommand $widgets(this) $newValue } if {[winfo ismapped $widgets(dropdown)]} { ::combobox::Select $widgets(this) \ [$widgets(listbox) curselection] return -code break } } "" { $widgets(listbox) yview scroll 1 pages set index [$widgets(listbox) index @0,0] $widgets(listbox) see $index $widgets(listbox) activate $index $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor $index $widgets(listbox) selection set $index } "" { $widgets(listbox) yview scroll -1 pages set index [$widgets(listbox) index @0,0] $widgets(listbox) activate $index $widgets(listbox) see $index $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor $index $widgets(listbox) selection set $index } "" { if {[winfo ismapped $widgets(dropdown)]} { ::combobox::tkListboxUpDown $widgets(listbox) 1 return -code break } else { if {$options(-state) != "disabled"} { $widgets(this) open return -code break } } } "" { if {[winfo ismapped $widgets(dropdown)]} { ::combobox::tkListboxUpDown $widgets(listbox) -1 return -code break } else { if {$options(-state) != "disabled"} { $widgets(this) open return -code break } } } } return "" } # ::combobox::DestroyHandler {w} -- # # Cleans up after a combobox widget is destroyed # # Arguments: # # w widget pathname # # Results: # # The namespace that was created for the widget is deleted, # and the widget proc is removed. proc ::combobox::DestroyHandler {w} { # if the widget actually being destroyed is of class Combobox, # crush the namespace and kill the proc. Get it? Crush. Kill. # Destroy. Heh. Danger Will Robinson! Oh, man! I'm so funny it # brings tears to my eyes. if {[string compare [winfo class $w] "Combobox"] == 0} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # delete the namespace and the proc which represents # our widget namespace delete ::combobox::$w rename $w {} } return "" } # ::combobox::Find # # finds something in the listbox that matches the pattern in the # entry widget and selects it # # N.B. I'm not convinced this is working the way it ought to. It # works, but is the behavior what is expected? I've also got a gut # feeling that there's a better way to do this, but I'm too lazy to # figure it out... # # Arguments: # # w widget pathname # exact boolean; if true an exact match is desired # # Returns: # # Empty string proc ::combobox::Find {w {exact 0}} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options ## *sigh* this logic is rather gross and convoluted. Surely ## there is a more simple, straight-forward way to implement ## all this. As the saying goes, I lack the time to make it ## shorter... # use what is already in the entry widget as a pattern set pattern [$widgets(entry) get] if {[string length $pattern] == 0} { # clear the current selection $widgets(listbox) see 0 $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor 0 $widgets(listbox) activate 0 return } # we're going to be searching this list... set list [$widgets(listbox) get 0 end] # if we are doing an exact match, try to find, # well, an exact match set exactMatch -1 if {$exact} { set exactMatch [lsearch -exact $list $pattern] } # search for it. We'll try to be clever and not only # search for a match for what they typed, but a match for # something close to what they typed. We'll keep removing one # character at a time from the pattern until we find a match # of some sort. set index -1 while {$index == -1 && [string length $pattern]} { set index [lsearch -glob $list "$pattern*"] if {$index == -1} { regsub {.$} $pattern {} pattern } } # this is the item that most closely matches... set thisItem [lindex $list $index] # did we find a match? If so, do some additional munging... if {$index != -1} { # we need to find the part of the first item that is # unique WRT the second... I know there's probably a # simpler way to do this... set nextIndex [expr {$index + 1}] set nextItem [lindex $list $nextIndex] # we don't really need to do much if the next # item doesn't match our pattern... if {[string match $pattern* $nextItem]} { # ok, the next item matches our pattern, too # now the trick is to find the first character # where they *don't* match... set marker [string length $pattern] while {$marker <= [string length $pattern]} { set a [string index $thisItem $marker] set b [string index $nextItem $marker] if {[string compare $a $b] == 0} { append pattern $a incr marker } else { break } } } else { set marker [string length $pattern] } } else { set marker end set index 0 } # ok, we know the pattern and what part is unique; # update the entry widget and listbox appropriately if {$exact && $exactMatch == -1} { # this means we didn't find an exact match $widgets(listbox) selection clear 0 end $widgets(listbox) see $index } elseif {!$exact} { # this means we found something, but it isn't an exact # match. If we find something that *is* an exact match we # don't need to do the following, since it would merely # be replacing the data in the entry widget with itself set oldstate [$widgets(entry) cget -state] $widgets(entry) configure -state normal $widgets(entry) delete 0 end $widgets(entry) insert end $thisItem $widgets(entry) selection clear $widgets(entry) selection range $marker end $widgets(listbox) activate $index $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor $index $widgets(listbox) selection set $index $widgets(listbox) see $index $widgets(entry) configure -state $oldstate } } # ::combobox::Select -- # # selects an item from the list and sets the value of the combobox # to that value # # Arguments: # # w widget pathname # index listbox index of item to be selected # # Returns: # # empty string proc ::combobox::Select {w index} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # the catch is because I'm sloppy -- presumably, the only time # an error will be caught is if there is no selection. if {![catch {set data [$widgets(listbox) get [lindex $index 0]]}]} { ::combobox::SetValue $widgets(this) $data $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor $index $widgets(listbox) selection set $index } $widgets(entry) selection range 0 end $widgets(this) close return "" } # ::combobox::HandleScrollbar -- # # causes the scrollbar of the dropdown list to appear or disappear # based on the contents of the dropdown listbox # # Arguments: # # w widget pathname # action the action to perform on the scrollbar # # Returns: # # an empty string proc ::combobox::HandleScrollbar {w {action "unknown"}} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options if {$options(-height) == 0} { set hlimit $options(-maxheight) } else { set hlimit $options(-height) } switch -- $action { "grow" { if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} { pack $widgets(vsb) -side right -fill y -expand n } } "shrink" { if {$hlimit > 0 && [$widgets(listbox) size] <= $hlimit} { pack forget $widgets(vsb) } } "crop" { # this means the window was cropped and we definitely # need a scrollbar no matter what the user wants pack $widgets(vsb) -side right -fill y -expand n } default { if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} { pack $widgets(vsb) -side right -fill y -expand n } else { pack forget $widgets(vsb) } } } return "" } # ::combobox::ComputeGeometry -- # # computes the geometry of the dropdown list based on the size of the # combobox... # # Arguments: # # w widget pathname # # Returns: # # the desired geometry of the listbox proc ::combobox::ComputeGeometry {w} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options if {$options(-height) == 0 && $options(-maxheight) != "0"} { # if this is the case, count the items and see if # it exceeds our maxheight. If so, set the listbox # size to maxheight... set nitems [$widgets(listbox) size] if {$nitems > $options(-maxheight)} { # tweak the height of the listbox $widgets(listbox) configure -height $options(-maxheight) } else { # un-tweak the height of the listbox $widgets(listbox) configure -height 0 } update idletasks } # compute height and width of the dropdown list set bd [$widgets(dropdown) cget -borderwidth] set height [expr {[winfo reqheight $widgets(dropdown)] + $bd + $bd}] if {[string length $options(-dropdownwidth)] == 0 || \ $options(-dropdownwidth) == 0} { set width [winfo width $widgets(this)] } else { set m [font measure [$widgets(listbox) cget -font] "m"] set width [expr {$options(-dropdownwidth) * $m}] } # figure out where to place it on the screen, trying to take into # account we may be running under some virtual window manager set screenWidth [winfo screenwidth $widgets(this)] set screenHeight [winfo screenheight $widgets(this)] set rootx [winfo rootx $widgets(this)] set rooty [winfo rooty $widgets(this)] set vrootx [winfo vrootx $widgets(this)] set vrooty [winfo vrooty $widgets(this)] # the x coordinate is simply the rootx of our widget, adjusted for # the virtual window. We won't worry about whether the window will # be offscreen to the left or right -- we want the illusion that it # is part of the entry widget, so if part of the entry widget is off- # screen, so will the list. If you want to change the behavior, # simply change the if statement... (and be sure to update this # comment!) set x [expr {$rootx + $vrootx}] if {0} { set rightEdge [expr {$x + $width}] if {$rightEdge > $screenWidth} { set x [expr {$screenWidth - $width}] } if {$x < 0} { set x 0 } } # the y coordinate is the rooty plus vrooty offset plus # the height of the static part of the widget plus 1 for a # tiny bit of visual separation... set y [expr {$rooty + $vrooty + [winfo reqheight $widgets(this)] + 1}] set bottomEdge [expr {$y + $height}] if {$bottomEdge >= $screenHeight} { # ok. Fine. Pop it up above the entry widget isntead of # below. set y [expr {($rooty - $height - 1) + $vrooty}] if {$y < 0} { # this means it extends beyond our screen. How annoying. # Now we'll try to be real clever and either pop it up or # down, depending on which way gives us the biggest list. # then, we'll trim the list to fit and force the use of # a scrollbar # (sadly, for windows users this measurement doesn't # take into consideration the height of the taskbar, # but don't blame me -- there isn't any way to detect # it or figure out its dimensions. The same probably # applies to any window manager with some magic windows # glued to the top or bottom of the screen) if {$rooty > [expr {$screenHeight / 2}]} { # we are in the lower half of the screen -- # pop it up. Y is zero; that parts easy. The height # is simply the y coordinate of our widget, minus # a pixel for some visual separation. The y coordinate # will be the topof the screen. set y 1 set height [expr {$rooty - 1 - $y}] } else { # we are in the upper half of the screen -- # pop it down set y [expr {$rooty + $vrooty + [winfo reqheight \ $widgets(this)] + 1}] set height [expr {$screenHeight - $y}] } # force a scrollbar HandleScrollbar $widgets(this) crop } } if {$y < 0} { # hmmm. Bummer. set y 0 set height $screenheight } set geometry [format "=%dx%d+%d+%d" $width $height $x $y] return $geometry } # ::combobox::DoInternalWidgetCommand -- # # perform an internal widget command, then mung any error results # to look like it came from our megawidget. A lot of work just to # give the illusion that our megawidget is an atomic widget # # Arguments: # # w widget pathname # subwidget pathname of the subwidget # command subwidget command to be executed # args arguments to the command # # Returns: # # The result of the subwidget command, or an error proc ::combobox::DoInternalWidgetCommand {w subwidget command args} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options set subcommand $command set command [concat $widgets($subwidget) $command $args] if {[catch $command result]} { # replace the subwidget name with the megawidget name regsub $widgets($subwidget) $result $widgets(this) result # replace specific instances of the subwidget command # with our megawidget command switch -- $subwidget,$subcommand { listbox,index { regsub "index" $result "list index" result } listbox,insert { regsub "insert" $result "list insert" result } listbox,delete { regsub "delete" $result "list delete" result } listbox,get { regsub "get" $result "list get" result } listbox,size { regsub "size" $result "list size" result } } error $result } else { return $result } } # ::combobox::WidgetProc -- # # This gets uses as the widgetproc for an combobox widget. # Notice where the widget is created and you'll see that the # actual widget proc merely evals this proc with all of the # arguments intact. # # Note that some widget commands are defined "inline" (ie: # within this proc), and some do most of their work in # separate procs. This is merely because sometimes it was # easier to do it one way or the other. # # Arguments: # # w widget pathname # command widget subcommand # args additional arguments; varies with the subcommand # # Results: # # Performs the requested widget command proc ::combobox::WidgetProc {w command args} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options upvar ::combobox::${w}::oldFocus oldFocus upvar ::combobox::${w}::oldFocus oldGrab set command [::combobox::Canonize $w command $command] # this is just shorthand notation... set doWidgetCommand [list ::combobox::DoInternalWidgetCommand \ $widgets(this)] if {$command == "list"} { # ok, the next argument is a list command; we'll # rip it from args and append it to command to # create a unique internal command # # NB: because of the sloppy way we are doing this, # we'll also let the user enter our secret command # directly (eg: listinsert, listdelete), but we # won't document that fact set command "list-[lindex $args 0]" set args [lrange $args 1 end] } set result "" # many of these commands are just synonyms for specific # commands in one of the subwidgets. We'll get them out # of the way first, then do the custom commands. switch -- $command { bbox - delete - get - icursor - index - insert - scan - selection - xview { set result [eval $doWidgetCommand entry $command $args] } list-get { set result [eval $doWidgetCommand listbox get $args] } list-index { set result [eval $doWidgetCommand listbox index $args] } list-size { set result [eval $doWidgetCommand listbox size $args] } select { if {[llength $args] == 1} { set index [lindex $args 0] set result [Select $widgets(this) $index] } else { error "usage: $w select index" } } subwidget { set knownWidgets [list button entry listbox dropdown vsb] if {[llength $args] == 0} { return $knownWidgets } set name [lindex $args 0] if {[lsearch $knownWidgets $name] != -1} { set result $widgets($name) } else { error "unknown subwidget $name" } } curselection { set result [eval $doWidgetCommand listbox curselection] } list-insert { eval $doWidgetCommand listbox insert $args set result [HandleScrollbar $w "grow"] } list-delete { eval $doWidgetCommand listbox delete $args set result [HandleScrollbar $w "shrink"] } toggle { # ignore this command if the widget is disabled... if {$options(-state) == "disabled"} return # pops down the list if it is not, hides it # if it is... if {[winfo ismapped $widgets(dropdown)]} { set result [$widgets(this) close] } else { set result [$widgets(this) open] } } open { # if this is an editable combobox, the focus should # be set to the entry widget if {$options(-editable)} { focus $widgets(entry) $widgets(entry) select range 0 end $widgets(entry) icur end } # if we are disabled, we won't allow this to happen if {$options(-state) == "disabled"} { return 0 } # if there is a -opencommand, execute it now if {[string length $options(-opencommand)] > 0} { # hmmm... should I do a catch, or just let the normal # error handling handle any errors? For now, the latter... uplevel \#0 $options(-opencommand) } # compute the geometry of the window to pop up, and set # it, and force the window manager to take notice # (even if it is not presently visible). # # this isn't strictly necessary if the window is already # mapped, but we'll go ahead and set the geometry here # since its harmless and *may* actually reset the geometry # to something better in some weird case. set geometry [::combobox::ComputeGeometry $widgets(this)] wm geometry $widgets(dropdown) $geometry update idletasks # if we are already open, there's nothing else to do if {[winfo ismapped $widgets(dropdown)]} { return 0 } # save the widget that currently has the focus; we'll restore # the focus there when we're done set oldFocus [focus] # ok, tweak the visual appearance of things and # make the list pop up $widgets(button) configure -relief sunken raise $widgets(dropdown) wm deiconify $widgets(dropdown) tkwait visibility $widgets(dropdown) focus -force $widgets(dropdown) # force focus to the entry widget so we can handle keypress # events for traversal focus -force $widgets(entry) # select something by default, but only if its an # exact match... ::combobox::Find $widgets(this) 1 # save the current grab state for the display containing # this widget. We'll restore it when we close the dropdown # list set status "none" set grab [grab current $widgets(this)] if {$grab != ""} { set status [grab status $grab] } set oldGrab [list $grab $status] unset grab status # *gasp* do a global grab!!! Mom always told me not to # do things like this, but sometimes a man's gotta do # what a man's gotta do. raise $widgets(dropdown) grab -global $widgets(this) # fake the listbox into thinking it has focus. This is # necessary to get scanning initialized properly in the # listbox. event generate $widgets(listbox) return 1 } close { # if we are already closed, don't do anything... if {![winfo ismapped $widgets(dropdown)]} { return 0 } # restore the focus and grab, but ignore any errors... # we're going to be paranoid and release the grab before # trying to set any other grab because we really really # really want to make sure the grab is released. catch {focus $oldFocus} result catch {grab release $widgets(this)} catch { set status [lindex $oldGrab 1] if {$status == "global"} { grab -global [lindex $oldGrab 0] } elseif {$status == "local"} { grab [lindex $oldGrab 0] } unset status } # hides the listbox $widgets(button) configure -relief raised wm withdraw $widgets(dropdown) # select the data in the entry widget. Not sure # why, other than observation seems to suggest that's # what windows widgets do. set editable [::combobox::GetBoolean $options(-editable)] if {$editable} { $widgets(entry) selection range 0 end $widgets(button) configure -relief raised } # magic tcl stuff (see tk.tcl in the distribution # lib directory) ::combobox::tkCancelRepeat return 1 } cget { if {[llength $args] != 1} { error "wrong # args: should be $w cget option" } set opt [::combobox::Canonize $w option [lindex $args 0]] if {$opt == "-value"} { set result [$widgets(entry) get] } else { set result $options($opt) } } configure { set result [eval ::combobox::Configure {$w} $args] } default { error "bad option \"$command\"" } } return $result } # ::combobox::Configure -- # # Implements the "configure" widget subcommand # # Arguments: # # w widget pathname # args zero or more option/value pairs (or a single option) # # Results: # # Performs typcial "configure" type requests on the widget proc ::combobox::Configure {w args} { variable widgetOptions variable defaultEntryCursor upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options if {[llength $args] == 0} { # hmmm. User must be wanting all configuration information # note that if the value of an array element is of length # one it is an alias, which needs to be handled slightly # differently set results {} foreach opt [lsort [array names widgetOptions]] { if {[llength $widgetOptions($opt)] == 1} { set alias $widgetOptions($opt) set optName $widgetOptions($alias) lappend results [list $opt $optName] } else { set optName [lindex $widgetOptions($opt) 0] set optClass [lindex $widgetOptions($opt) 1] set default [option get $w $optName $optClass] if {[info exists options($opt)]} { lappend results [list $opt $optName $optClass $default \ $options($opt)] } else { lappend results [list $opt $optName $optClass $default ""] } } } return $results } # one argument means we are looking for configuration # information on a single option if {[llength $args] == 1} { set opt [::combobox::Canonize $w option [lindex $args 0]] set optName [lindex $widgetOptions($opt) 0] set optClass [lindex $widgetOptions($opt) 1] set default [option get $w $optName $optClass] set results [list $opt $optName $optClass $default $options($opt)] return $results } # if we have an odd number of values, bail. if {[expr {[llength $args]%2}] == 1} { # hmmm. An odd number of elements in args error "value for \"[lindex $args end]\" missing" } # Great. An even number of options. Let's make sure they # are all valid before we do anything. Note that Canonize # will generate an error if it finds a bogus option; otherwise # it returns the canonical option name foreach {name value} $args { set name [::combobox::Canonize $w option $name] set opts($name) $value } # process all of the configuration options # some (actually, most) options require us to # do something, like change the attributes of # a widget or two. Here's where we do that... # # note that the handling of disabledforeground and # disabledbackground is a little wonky. First, we have # to deal with backwards compatibility (ie: tk 8.3 and below # didn't have such options for the entry widget), and # we have to deal with the fact we might want to disable # the entry widget but use the normal foreground/background # for when the combobox is not disabled, but not editable either. set updateVisual 0 foreach option [array names opts] { set newValue $opts($option) if {[info exists options($option)]} { set oldValue $options($option) } switch -- $option { -background { set updateVisual 1 set options($option) $newValue } -borderwidth { $widgets(frame) configure -borderwidth $newValue set options($option) $newValue } -command { # nothing else to do... set options($option) $newValue } -commandstate { # do some value checking... if {$newValue != "normal" && $newValue != "disabled"} { set options($option) $oldValue set message "bad state value \"$newValue\";" append message " must be normal or disabled" error $message } set options($option) $newValue } -cursor { $widgets(frame) configure -cursor $newValue $widgets(entry) configure -cursor $newValue $widgets(listbox) configure -cursor $newValue set options($option) $newValue } -disabledforeground { set updateVisual 1 set options($option) $newValue } -disabledbackground { set updateVisual 1 set options($option) $newValue } -dropdownwidth { set options($option) $newValue } -editable { set updateVisual 1 if {$newValue} { # it's editable... $widgets(entry) configure -state normal \ -cursor $defaultEntryCursor } else { $widgets(entry) configure -state disabled \ -cursor $options(-cursor) } set options($option) $newValue } -font { $widgets(entry) configure -font $newValue $widgets(listbox) configure -font $newValue set options($option) $newValue } -foreground { set updateVisual 1 set options($option) $newValue } -height { $widgets(listbox) configure -height $newValue HandleScrollbar $w set options($option) $newValue } -highlightbackground { $widgets(frame) configure -highlightbackground $newValue set options($option) $newValue } -highlightcolor { $widgets(frame) configure -highlightcolor $newValue set options($option) $newValue } -highlightthickness { $widgets(frame) configure -highlightthickness $newValue set options($option) $newValue } -image { if {[string length $newValue] > 0} { $widgets(button) configure -image $newValue } else { $widgets(button) configure -image ::combobox::bimage } set options($option) $newValue } -maxheight { # ComputeGeometry may dork with the actual height # of the listbox, so let's undork it $widgets(listbox) configure -height $options(-height) HandleScrollbar $w set options($option) $newValue } -opencommand { # nothing else to do... set options($option) $newValue } -relief { $widgets(frame) configure -relief $newValue set options($option) $newValue } -selectbackground { $widgets(entry) configure -selectbackground $newValue $widgets(listbox) configure -selectbackground $newValue set options($option) $newValue } -selectborderwidth { $widgets(entry) configure -selectborderwidth $newValue $widgets(listbox) configure -selectborderwidth $newValue set options($option) $newValue } -selectforeground { $widgets(entry) configure -selectforeground $newValue $widgets(listbox) configure -selectforeground $newValue set options($option) $newValue } -state { if {$newValue == "normal"} { set updateVisual 1 # it's enabled set editable [::combobox::GetBoolean $options(-editable)] if {$editable} { $widgets(entry) configure -state normal $widgets(entry) configure -takefocus 1 } # note that $widgets(button) is actually a label, # not a button. And being able to disable labels # wasn't possible until tk 8.3. (makes me wonder # why I chose to use a label, but that answer is # lost to antiquity) if {[info patchlevel] >= 8.3} { $widgets(button) configure -state normal } } elseif {$newValue == "disabled"} { set updateVisual 1 # it's disabled $widgets(entry) configure -state disabled $widgets(entry) configure -takefocus 0 # note that $widgets(button) is actually a label, # not a button. And being able to disable labels # wasn't possible until tk 8.3. (makes me wonder # why I chose to use a label, but that answer is # lost to antiquity) if {$::tcl_version >= 8.3} { $widgets(button) configure -state disabled } } else { set options($option) $oldValue set message "bad state value \"$newValue\";" append message " must be normal or disabled" error $message } set options($option) $newValue } -takefocus { $widgets(entry) configure -takefocus $newValue set options($option) $newValue } -textvariable { $widgets(entry) configure -textvariable $newValue set options($option) $newValue } -value { ::combobox::SetValue $widgets(this) $newValue set options($option) $newValue } -width { $widgets(entry) configure -width $newValue $widgets(listbox) configure -width $newValue set options($option) $newValue } -xscrollcommand { $widgets(entry) configure -xscrollcommand $newValue set options($option) $newValue } } if {$updateVisual} { UpdateVisualAttributes $w } } } # ::combobox::UpdateVisualAttributes -- # # sets the visual attributes (foreground, background mostly) # based on the current state of the widget (normal/disabled, # editable/non-editable) # # why a proc for such a simple thing? Well, in addition to the # various states of the widget, we also have to consider the # version of tk being used -- versions from 8.4 and beyond have # the notion of disabled foreground/background options for various # widgets. All of the permutations can get nasty, so we encapsulate # it all in one spot. # # note also that we don't handle all visual attributes here; just # the ones that depend on the state of the widget. The rest are # handled on a case by case basis # # Arguments: # w widget pathname # # Returns: # empty string proc ::combobox::UpdateVisualAttributes {w} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options if {$options(-state) == "normal"} { set foreground $options(-foreground) set background $options(-background) } elseif {$options(-state) == "disabled"} { set foreground $options(-disabledforeground) set background $options(-disabledbackground) } $widgets(entry) configure -foreground $foreground -background $background $widgets(listbox) configure -foreground $foreground -background $background $widgets(button) configure -foreground $foreground $widgets(vsb) configure -background $background -troughcolor $background $widgets(frame) configure -background $background # we need to set the disabled colors in case our widget is disabled. # We could actually check for disabled-ness, but we also need to # check whether we're enabled but not editable, in which case the # entry widget is disabled but we still want the enabled colors. It's # easier just to set everything and be done with it. if {$::tcl_version >= 8.4} { $widgets(entry) configure -disabledforeground $foreground \ -disabledbackground $background $widgets(button) configure -disabledforeground $foreground $widgets(listbox) configure -disabledforeground $foreground } } # ::combobox::SetValue -- # # sets the value of the combobox and calls the -command, # if defined # # Arguments: # # w widget pathname # newValue the new value of the combobox # # Returns # # Empty string proc ::combobox::SetValue {w newValue} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options upvar ::combobox::${w}::ignoreTrace ignoreTrace upvar ::combobox::${w}::oldValue oldValue if {[info exists options(-textvariable)] && [string length \ $options(-textvariable)] > 0} { set variable ::$options(-textvariable) set $variable $newValue } else { set oldstate [$widgets(entry) cget -state] $widgets(entry) configure -state normal $widgets(entry) delete 0 end $widgets(entry) insert 0 $newValue $widgets(entry) configure -state $oldstate } # set our internal textvariable; this will cause any public # textvariable (ie: defined by the user) to be updated as # well # set ::combobox::${w}::entryTextVariable $newValue # redefine our concept of the "old value". Do it before running # any associated command so we can be sure it happens even # if the command somehow fails. set oldValue $newValue # call the associated command. The proc will handle whether or # not to actually call it, and with what args CallCommand $w $newValue return "" } # ::combobox::CallCommand -- # # calls the associated command, if any, appending the new # value to the command to be called. # # Arguments: # # w widget pathname # newValue the new value of the combobox # # Returns # # empty string proc ::combobox::CallCommand {w newValue} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # call the associated command, if defined and -commandstate is # set to "normal" if {$options(-commandstate) == "normal" && [string length \ $options(-command)] > 0} { set args [list $widgets(this) $newValue] uplevel \#0 $options(-command) $args } } # ::combobox::GetBoolean -- # # returns the value of a (presumably) boolean string (ie: it should # do the right thing if the string is "yes", "no", "true", 1, etc # # Arguments: # # value value to be converted # errorValue a default value to be returned in case of an error # # Returns: # # a 1 or zero, or the value of errorValue if the string isn't # a proper boolean value proc ::combobox::GetBoolean {value {errorValue 1}} { if {[catch {expr {([string trim $value]) ?1:0}} res]} { return $errorValue } else { return $res } } # ::combobox::convert -- # # public routine to convert %x, %y and %W binding substitutions. # Given an x, y and or %W value relative to a given widget, this # routine will convert the values to be relative to the combobox # widget. For example, it could be used in a binding like this: # # bind .combobox {doSomething [::combobox::convert %W -x %x]} # # Note that this procedure is *not* exported, but is intended for # public use. It is not exported because the name could easily # clash with existing commands. # # Arguments: # # w a widget path; typically the actual result of a %W # substitution in a binding. It should be either a # combobox widget or one of its subwidgets # # args should one or more of the following arguments or # pairs of arguments: # # -x will convert the value ; typically will # be the result of a %x substitution # -y will convert the value ; typically will # be the result of a %y substitution # -W (or -w) will return the name of the combobox widget # which is the parent of $w # # Returns: # # a list of the requested values. For example, a single -w will # result in a list of one items, the name of the combobox widget. # Supplying "-x 10 -y 20 -W" (in any order) will return a list of # three values: the converted x and y values, and the name of # the combobox widget. proc ::combobox::convert {w args} { set result {} if {![winfo exists $w]} { error "window \"$w\" doesn't exist" } while {[llength $args] > 0} { set option [lindex $args 0] set args [lrange $args 1 end] switch -exact -- $option { -x { set value [lindex $args 0] set args [lrange $args 1 end] set win $w while {[winfo class $win] != "Combobox"} { incr value [winfo x $win] set win [winfo parent $win] if {$win == "."} break } lappend result $value } -y { set value [lindex $args 0] set args [lrange $args 1 end] set win $w while {[winfo class $win] != "Combobox"} { incr value [winfo y $win] set win [winfo parent $win] if {$win == "."} break } lappend result $value } -w - -W { set win $w while {[winfo class $win] != "Combobox"} { set win [winfo parent $win] if {$win == "."} break } lappend result $win } } } return $result } # ::combobox::Canonize -- # # takes a (possibly abbreviated) option or command name and either # returns the canonical name or an error # # Arguments: # # w widget pathname # object type of object to canonize; must be one of "command", # "option", "scan command" or "list command" # opt the option (or command) to be canonized # # Returns: # # Returns either the canonical form of an option or command, # or raises an error if the option or command is unknown or # ambiguous. proc ::combobox::Canonize {w object opt} { variable widgetOptions variable columnOptions variable widgetCommands variable listCommands variable scanCommands switch -- $object { command { if {[lsearch -exact $widgetCommands $opt] >= 0} { return $opt } # command names aren't stored in an array, and there # isn't a way to get all the matches in a list, so # we'll stuff the commands in a temporary array so # we can use [array names] set list $widgetCommands foreach element $list { set tmp($element) "" } set matches [array names tmp ${opt}*] } {list command} { if {[lsearch -exact $listCommands $opt] >= 0} { return $opt } # command names aren't stored in an array, and there # isn't a way to get all the matches in a list, so # we'll stuff the commands in a temporary array so # we can use [array names] set list $listCommands foreach element $list { set tmp($element) "" } set matches [array names tmp ${opt}*] } {scan command} { if {[lsearch -exact $scanCommands $opt] >= 0} { return $opt } # command names aren't stored in an array, and there # isn't a way to get all the matches in a list, so # we'll stuff the commands in a temporary array so # we can use [array names] set list $scanCommands foreach element $list { set tmp($element) "" } set matches [array names tmp ${opt}*] } option { if {[info exists widgetOptions($opt)] && \ [llength $widgetOptions($opt)] == 2} { return $opt } set list [array names widgetOptions] set matches [array names widgetOptions ${opt}*] } } if {[llength $matches] == 0} { set choices [HumanizeList $list] error "unknown $object \"$opt\"; must be one of $choices" } elseif {[llength $matches] == 1} { set opt [lindex $matches 0] # deal with option aliases switch -- $object { option { set opt [lindex $matches 0] if {[llength $widgetOptions($opt)] == 1} { set opt $widgetOptions($opt) } } } return $opt } else { set choices [HumanizeList $list] error "ambiguous $object \"$opt\"; must be one of $choices" } } # ::combobox::HumanizeList -- # # Returns a human-readable form of a list by separating items # by columns, but separating the last two elements with "or" # (eg: foo, bar or baz) # # Arguments: # # list a valid tcl list # # Results: # # A string which as all of the elements joined with ", " or # the word " or " proc ::combobox::HumanizeList {list} { if {[llength $list] == 1} { return [lindex $list 0] } else { set list [lsort $list] set secondToLast [expr {[llength $list] -2}] set most [lrange $list 0 $secondToLast] set last [lindex $list end] return "[join $most {, }] or $last" } } # This is some backwards-compatibility code to handle TIP 44 # (http://purl.org/tcl/tip/44.html). For all private tk commands # used by this widget, we'll make duplicates of the procs in the # combobox namespace. # # I'm not entirely convinced this is the right thing to do. I probably # shouldn't even be using the private commands. Then again, maybe the # private commands really should be public. Oh well; it works so it # must be OK... foreach command {TabToWindow CancelRepeat ListboxUpDown} { if {[llength [info commands ::combobox::tk$command]] == 1} break set tmp [info commands tk$command] set proc ::combobox::tk$command if {[llength [info commands tk$command]] == 1} { set command [namespace which [lindex $tmp 0]] proc $proc {args} "uplevel $command \$args" } else { if {[llength [info commands ::tk::$command]] == 1} { proc $proc {args} "uplevel ::tk::$command \$args" } } } # end of combobox.tcl ###################################################################### # icon image data. ###################################################################### image create bitmap delta48 -data { #define delta48_width 48 #define delta48_height 48 static char delta48_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x13, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x11, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x11, 0x68, 0x00, 0x00, 0x00, 0x80, 0x10, 0xc8, 0x00, 0x00, 0x00, 0x80, 0x10, 0xa8, 0x01, 0x00, 0x00, 0x80, 0x08, 0x08, 0x01, 0x00, 0x00, 0x80, 0x08, 0xac, 0x03, 0x00, 0x00, 0x80, 0x09, 0x06, 0x02, 0x00, 0x00, 0xc0, 0x09, 0xaa, 0x06, 0x00, 0x00, 0x40, 0x09, 0x01, 0x04, 0x00, 0x00, 0xe0, 0x93, 0xae, 0x0a, 0x00, 0x00, 0x30, 0x92, 0x06, 0x18, 0x00, 0x00, 0xb0, 0x92, 0xad, 0x1a, 0x00, 0x00, 0x18, 0x53, 0x04, 0x30, 0x00, 0x00, 0xa8, 0x11, 0xac, 0x2a, 0x00, 0x00, 0x0c, 0x12, 0x04, 0x60, 0x00, 0x00, 0xac, 0x12, 0xac, 0x6a, 0x00, 0x00, 0x02, 0x14, 0x04, 0x80, 0x00, 0x00, 0xab, 0x0a, 0xae, 0xaa, 0x01, 0x00, 0x01, 0x28, 0x02, 0x00, 0x01, 0x80, 0xab, 0x3a, 0xaf, 0xaa, 0x03, 0x80, 0x00, 0x70, 0x0c, 0x00, 0x02, 0xc0, 0xaa, 0x5a, 0xa8, 0xaa, 0x06, 0x40, 0x00, 0xa0, 0x08, 0x00, 0x0c, 0xa0, 0xaa, 0xea, 0xac, 0xaa, 0x0a, 0x30, 0x00, 0x80, 0x05, 0x00, 0x18, 0xb0, 0xaa, 0xaa, 0xab, 0xaa, 0x1a, 0x08, 0x00, 0x00, 0x04, 0x00, 0x30, 0xfc, 0xff, 0xff, 0xbe, 0xff, 0x7f, 0xfc, 0xff, 0xff, 0xbd, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } } image create photo deltaGif -format gif -data { R0lGODlhMAAwAOcAAAIyRsQWGJ4eIIYrK0aK4jZ2yXIkKOJ2hhpKgqZ+isJW Y1IuNpBebKJifi5qumpKUsQ0OE4ySkJiip5SXoZWXnpWahZGfp5GUideptrG yopSWtpOVnp+kiY6XiVaoIJGTphKVjxMcJ45Pso6PiFWmgYyZt5SWroeHrau tmaClu4eJsoeHlJmhts+Q3JCSi5Kepo2PpKGkiBSlqIiIuaWllY6QrQmJmJy kjp6zk6W9h5Ojx4yPh1Cc5YeIspaatoiJvJcaE5eekpihpR2gMIiItteZz6C 2nZqfuY2Pu9MVG5aYq4iIutGTqJOXkY+SuKKkgc6c4YyMkqO6cZCUgI2Vipi rV4iInI2Pq5yepIiIsIrK6ErLBpKhrY+RsozM052uhIuTsJqhrJebu5UWLRM WopWbr4iIl5mjtpCRE6G2u5eatVibqwcHjZGVoAoKr52ftoSEmo2SsYmKlaS 5pYiItZWbrpucppialKO3m5GUscqKyBGfudKUc8yMyZKeh46YiZGag42Ts46 PjZyxKcqK+JCSMpCSu7Gxh5Skp5CRtU+Px9OiUI+YropKUSC2ppOWm4eHpIp KbV/h85qcsYuLnZOYospKoZmcqlaY5ZWWnZiZuYoLl5Scm4qLlSW8jZCXpgm KIJCStpibAI2TrYuMK4pKp4yNtpaZr58hk6O5t1GSNE2N85CRopSciJKfuY+ Rgo2UuJOWj5+1CpmsLJkcn4uNi5QgEWG4ZI+RBpGgNU6PEZOcpAyNL5OWoBQ ZJJKZqpSYi5eovKWnmpaflea+CZSjjFuvU2K3couMLpkcnIqLuJaXnpyhupa Zj5Gar4uLpIuMo4uLuFLT84qKk6S7JZZZ6klJ/ZKUuJmdjZGah5WorUqKqY2 QudOUpkqK4ZabnJaegI2XiNGdspqguJGS7IaHuJeZuJaYlKS7SpGbkyK5Op0 fN5CRY5mctZESDZShtZmbhpKir4qKi5mtAIyUtpGUuBYbh5KgIJ6im5+ltIe IlpmgnpCSuIiJgY4Tv///yH5BAEKAP8ALAAAAAAwADAAAAj+AP8JHEiwoMGD CBMqXJgwFMOHEA0usIJpSMSLC10oMzCAFsaPBx/0yOJmwBuQKAXmccNmgBte WFKCrFFLgIABlkRckvmRwoByloJO4PmxUxY2lgww+kI04hVILGdAiyPDGLWm DD8M2FoOFLcLw4LJIoZVIS6c0U54AwWjQrFZUsoi1MDLUqQlS7ZYYxDiHQkc 6OQafBQtkrcA1rZ0wQQi2b0q6gQXTBTJMBxr1kpZs1FHCIlBniQLZCDCmzdr AQiV2rbtmah8izDcCiwZk6ktW2ysWL1NHqU1N7ggchBXMjDchIiY2dZIXiNk ayRwiecBB1m5+MhsUa2vUXM9lFb+jVuEQDhkwczIqN72Q54WLZT0CEJ1LxeC eCQK0MZ6ptfqRv3IAx4llLByBAL2caGDbGVRw4ICpDQihwrwUYIMBJeIg8CG uXBRVQ5YGWELLRA0oscmyCDjBSXV8MAhhzp4YAQeRKEzzyKVGKLFNJtYiEwy QQCSzYZECjfPVTJ5IgsJ8YSggBd9IOGFF2vEkIIkkyBo34b4yZKKTARUoUMu PHyzQR+veLGKKKjYMQI2RCJYng5V3JKSFIOQwEUuEfCjSB98rLKKIl4go0sR RNqX4GfFfeQIBotwcQ8IpOixyhiCrqKLLoogumWiCzoCEjUOILInAmW404cX mG7KqS7+9myp6IbCOfDlRcfI4kGkG3JyiqBJbKpICy3oYk6csk7ngSzHXCTF LDqcmosrDUjThzRocKoIGvU88WKcc84SGUTEFMDkt9mEUY8qfCiyLTvL7APu rPfldx1Dt2Aw5ry7iMJOEu/qQoO89M6qIIMMoWOMDNK+mAs4zSTBDhrsrLOP OOJ8imx5Ve2XkBEeRJtoos6okQQ57MQiTDoJSJKOnDDTGqMRzSZEzTymahyn M+e4wwTK6xQRywEvjCxngjrMM+5BqSwZj6ww27eHL7FcQw450rRQCCp7OLxx l0gadAu0XHzrtTitAMGENFgXMoTZZis4i50GEZNn2RrrjMCzHmXwwUc30sBj y8h50yrDIHMUlEa+vG589NG/MKGKCRzo/TiCXMR2SxoESWEMIhaELvropIf+ BzdqNMFD6ayLPlyjnuCACBQl1F57OLbnrnsHU/gAhu7A6w4FIjjULIs2UNAz CgCjNN8888s7Lz0ATtijhD/RQy/9883TA4U2svzjCPLhUGH++einnz4sl4ji yw6B+KP+/OF8LypRbWgiBjwKVPOJaA+5Ay4yAcACGhAiAQEAOw== } image create photo findImage -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAjUAAMIHEiwoEF3 AOQpXMiQIQB3ARC6a6fO3buHAiVWfAcPYwB1AN6pa/fQnUkAIy+qEwiy3bp07DqaPPmS3TqS Kz/SA8ATQDyB8XoCoJczI4B2F+VBjCjvocyBCNOVS9cxAE+rUqliRHhznbunEY96dbl15kyC Zs8OrDgzJ1uTRVnSYzcO5M8AQeu6I0oQ5DukAOAJlglPJVR5gBMifNjUqTyoAM6NK1f1auTJ YDuuOxdTKM/NneGFHVkRLEKKE0GeFGzRdODWMhd7Xipb6FKDuAsGBAA7 } image create photo centerDiffsImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiUAAMIHBjAHYCD ANwRHHjOncOHBgkRSgjRYUOEGAEYMpQRoUMA/8SJFGdwY0JyKFFSBGCuZcuSHN25bLmyo0aO Nj+GJAkg0caNiU6q/DjToE9DQWW6rNkxUdCcBneONHhy5FCDM106zErzo82vB3XuTEm27Equ aJd6BQsVpFSRZcmeTYuWKduM7hpW3Lv33MK/gAUGBAA7 } image create photo firstDiffImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiUAAMIdFevoMGD Bd0JXBig3j9ChAxJnDixHkOBDilqlGjxIkGEIBVevHjOnbtzI1MKLAkAwEmVJN0BIKTIJUqY AVgS+neo5kuVOv9J7Gkzpc5BFIn+XHg06SGlN1fKbDlTYiKqRRmWNFnV0FWTS7XqtGoz6six XrMClRkxbdizbMm+jQngUKK7ao1OxTo3JliTZgUGBAA7 } image create photo prevDiffImage -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAiGAAMIHCjwnDt3 5wgqLHjQHQBChgwlAtAw4cIABh9GnIjwIsOH/yIeUkTR4sWMECWW9DgQJcmOJx0SGhRR5KGR Kxei3JjT406VMH06BECUaFCWGXsilfkP51GCKGnWdGryY9GUE4s+xfiT47mqCrsq1SmT51ao ZYGCDevwUKK3Y8k2PLg2IAA7 } image create photo nextDiffImage -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAiGAAMIHHjOncGD 5wYqVFgQACFDhhIBcJdwIUN3DgsdUjSxokWBDR9G7PixIYCTIiWeJGmx4T9ChA6x/BggJESJ FGnWtDmSoseLGSFC3DizJMaiNE2uRLrQ5U2mQFNCJYhRak6dPHH+vGjQ4VOETasWEmrokFmO V6OOLYt2a1iHbXWGTbswIAA7 } image create photo lastDiffImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiTAAMIHHjOncGD 5wYqVFgQgMOH7hIuZOgOwD9ChA4BiDiRokVDhhJtlNgxQENCIEVyLGmyIsqQI1meO5lyJEmK BgG8VGnwZsuHOmtCvHmyEEiQh5IqiumRkNGjh5auXFgUqVSfTQtFZSrT5VWWHrmCFVhwakl3 9dKqXZvW3cR6F18enVvv7b+5eEHWXYiWrV+3AgMCADs= } image create photo rediffImage -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQCrPQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAicAAMIHEiwoMF0 7AD0euVKl8OHrhjqAgDvnDsAGDOmG2jR3TmDIAVaxFiRoMJXKF/1ypgR5UqPIWOCTIfQnc2b ABpS/Bgg3cmUQIOqBHBxIUpYADYKLEqUp8ynUKMatFgy5LmrWEdOrDoQIcuvrnSWPJfQqFCg YhPCAtqrrduUL8/9fIWUJs2LQ2EGmFt34MWmBNPdvKlUquEAAQEAOw== } image create photo markSetImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1Pjisd/UjtHJ a8O4SL2qJcWqAK+SAJN6AGJiAEpKADIyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiZAAMIHEhQoLqD CAsqFAigIQB3Dd0tNKjOXSxXrmABWBABgLqCByECuAir5EYJHimKvOgqFqxXrzZ2lBhgJUaY LV/GOpkSIqybOF3ClPlQIEShMF/lfLVzAcqPRhsKXRqTY1GCFaUy1ckTKkiRGhtapTkxa82u ExUSJZs2qtOUbQ2ujTsQ4luvbdXNpRtA712+UeEC7ou3YEAAADt= } image create photo markClearImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1Pjisd/UjtHJ a8O4SL2qJcWqAK+SAJN6AGJiAEpKADIyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiwAAMIHEhQoLqD CAsCWKhwIbyFANwNXBiD4UF3sVw9rLhQXQCKNTguzLgxZMePMWqo5OgqVkmVNwAIXHhDpUl3 7gCkhMkwJ02bHHfWiCkzQM5YP1cKJepRoM+kNoculEhQXc6cNW3GzNm0oFWdUSviLDgRbFST RRsuzYpWrVaoHMsujYgVKMOPUYkCWPCQbY2iP/UuiACgr9S0NDvulQBAXd+7ZYv6bPowLdmB By8LDAgAOw== } image create photo mergeChoice1Image -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAiIAAMIHEiwYMFz 7gAQ+meoIaGHECEeAuDuoDt35wxqFIgQAMWMGzkmVHRooseTKD1WPAgy5MCOhAZRvEizJsaR hxrq3LkzEcWXIz+eG0qUqMujSJMixJg0AEyhRYuKVDjIUMqrMxUy5MnVkM+bAEgaOpSorNmz X6eSnGmzZkunCT825fh2btKAADt= } image create photo mergeChoice2Image -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAiNAAMIHEiwYEF3 AP79GzSIkMOHhAwZKkQIgLtzBguec3cxo8eNACxiHIgwpMmTIQ8dUiTSo8aRBDdynEkTIcWW ARBGlMizJ8+VFgOcG0q0KEKWHV0qXcp0qUyYA4tKBVkxaU6UWAFMrIoR4SCfYCXe5AjgUKKz aNMeMgT0osyaNMsihfqxpNWmQ5s2DQgAOw== } image create photo mergeChoice12Image -format gif -data { R0lGODlhFAAUAPMHAAAAAAB6uQCS3CWq/0i4/47U/7Hi/////729vQAAAAAAAAAAAAAAAAAAAAAA AAAAACH5BAEAAAgALAAAAAAUABQAAAT+ECGEECgAIYQQggghhBBCCIFiAEQIIYQQQgghhCACxRAA AAAAAAABAAghUA4hpBRYSimllAEQAuVAQgghhBBCCCECAoRAGIQQQgghkBBCiAAIIRAGgUMIIYQQ QggBEEQIgTAGAAAAACAAAACEEEIgDAARQgghhBBCCCGIEAIBIIQQQghBhBBCCCGEEEIIIgQKQAgh hBBCECGEEEIImAIQggghAAAAAAAAAATEFIAQQmCUUmAppZRCCDkFIAQREIQQQgghhBBIyCkAISAI IYRAQgghhJARAEIACiGEEEIIIQYZMACEEAAAAAAAgACAMQJACCGEEEQIIYQQAiMAhCAPQgghhBBC CCEEQQAIIYQiADs= } image create photo mergeChoice21Image -format gif -data { R0lGODlhFAAUAPMHAAAAAAB6uQCS3CWq/0i4/47U/7Hi/////729vQAAAAAAAAAAAAAAAAAAAAAA AAAAACH5BAEAAAgALAAAAAAUABQAAAT+ECGEEEIIIYRAgQAhhBBCCCGEEEQIIWAKQAghBCAAAAAA AACAmAIBQgiBUUoppRRYCiHkFIAQAoJAQgghhBBCCDkFAoSAIIQQQgghkBBCRgAIASGEgEIIIYQY ZASAEEQAAAAAAAAAMOAIACGEEEIIIQQRQgiMABBCCCGIEEIIIYQQCABBhBBCCCEECkAIIoQQQggh hBBCEBQDEEIIIYQQggghhEAxBAAAAAQAAAAAQgiUQyAhpZRSSillAAQRKIcQQgghhBBICBEAIRAG IYRAQgghhBAiAEIIgjDIEEIIIYQQUAiAEEIgjAEAgAAAAAAAACGEEARhAIQQQgghhCAPQgghhEAA CCEEEUIIIYQiADs= } image create photo nullImage image create bitmap resize -data { #define resize_width 14 #define resize_height 11 static char resize_bits[] = { 0x20, 0x01, 0x30, 0x03, 0x38, 0x07, 0x3c, 0x0f, 0x3e, 0x1f, 0x3f, 0x3f, 0x3e, 0x1f, 0x3c, 0x0f, 0x38, 0x07, 0x30, 0x03, 0x20, 0x01 } } # Tooltip popups # # tooltips version 0.1 # Paul Boyer # Science Applications International Corp. # ############################## # set_tooltips gets a button's name and the tooltip string as # arguments and creates the proper bindings for entering # and leaving the button ############################## proc set_tooltips {widget name} { global g bind $widget " catch { after 500 { internal_tooltips_PopUp %W $name } } g(tooltip_id) " bind $widget "internal_tooltips_PopDown" bind $widget "internal_tooltips_PopDown" } ############################## # internal_tooltips_PopUp is used to activate the tooltip window ############################## proc internal_tooltips_PopUp {wid name} { global g # get rid of other existing tooltips catch {destroy .tooltips_wind} toplevel .tooltips_wind -class ToolTip set size_changed 0 set bg [option get .tooltips_wind background background] set fg [option get .tooltips_wind foreground foreground] # get the cursor position set X [winfo pointerx $wid] set Y [winfo pointery $wid] # add a slight offset to make tooltips fall below cursor set Y [expr {$Y + 20}] # Now pop up the new widgetLabel wm overrideredirect .tooltips_wind 1 wm geometry .tooltips_wind +$X+$Y label .tooltips_wind.l -text $name -border 2 -relief raised \ -background $bg -foreground $fg pack .tooltips_wind.l # make invisible wm withdraw .tooltips_wind update idletasks # adjust for bottom of screen if {($Y + [winfo reqheight .tooltips_wind]) > [winfo screenheight .]} { set Y [expr {$Y - [winfo reqheight .tooltips_wind] - 25}] set size_changed 1 } # adjust for right border of screen if {($X + [winfo reqwidth .tooltips_wind]) > [winfo screenwidth .]} { set X [expr {[winfo screenwidth .] - [winfo reqwidth .tooltips_wind]}] set size_changed 1 } # reset position if {$size_changed == 1} { wm geometry .tooltips_wind +$X+$Y } # make visible wm deiconify .tooltips_wind # make tooltip dissappear after 5 sec set g(tooltip_id) [after 5000 { internal_tooltips_PopDown }] } proc internal_tooltips_PopDown {} { global g after cancel $g(tooltip_id) catch {destroy .tooltips_wind} } proc get_gtk_params { } { global w global tk_version if {! [llength [auto_execok xrdb]]} { return 0 } set pipe [open "|xrdb -q" r] while {[gets $pipe ln] > -1} { switch -glob -- $ln { {\*Toplevel.background:*} { #puts $ln set bg [lindex $ln 1] } {\*Toplevel.foreground:*} { #puts $ln set fg [lindex $ln 1] } {\*Text.background:*} { #puts $ln set textbg [lindex $ln 1] } {\*Text.foreground:*} { #puts $ln set textfg [lindex $ln 1] } {\*Text.selectBackground:*} { #puts $ln set hlbg [lindex $ln 1] } {\*Text.selectForeground:*} { #puts $ln set hlfg [lindex $ln 1] } } } close $pipe if {! [info exists bg] || ! [info exists fg]} { return 0 } set w(selcolor) $hlbg option add *Entry.Background $textbg option add *Entry.Foreground $textfg option add *Entry.selectBackground $hlbg option add *Entry.selectForeground $hlfg option add *Entry.readonlyBackground $bg option add *Listbox.background $textbg option add *Listbox.selectBackground $hlbg option add *Listbox.selectForeground $hlfg option add *Text.Background $textbg option add *Text.Foreground $textfg option add *Text.selectBackground $hlbg option add *Text.selectForeground $hlfg # Menu checkboxes if {$tk_version >= 8.5} { option add *Menu.selectColor $fg option add *Checkbutton.selectColor "" option add *Radiobutton.selectColor "" } else { option add *selectColor $w(selcolor) } return 1 } proc get_cde_params {} { global tk_version global w # Set defaults for all the necessary things set bg [option get . background background] set fg [option get . foreground foreground] set guifont [option get . buttonFontList buttonFontList] set txtfont [option get . FontSet FontSet] set listfont [option get . textFontList textFontList] set textbg white set textfg black # If any of these aren't set, I don't think we're in CDE after all if {![string length $fg]} { return 0 } if {![string length $bg]} { return 0 } if {![string length $guifont]} { # For AIX set guifont [option get . FontList FontList] } if {![string length $guifont]} { return 0 } if {![string length $txtfont]} { return 0 } set guifont [string trimright $guifont ":"] set txtfont [string trimright $txtfont ":"] set listfont [string trimright $txtfont ":"] regsub {medium} $txtfont "bold" dlgfont # They don't tell us the slightly darker color they use for the # scrollbar backgrounds and graphics backgrounds, so we'll make # one up. set rgb_bg [winfo rgb . $bg] set shadow [format #%02x%02x%02x [expr {(9*[lindex $rgb_bg 0]) /2560}] \ [expr {(9*[lindex $rgb_bg 1]) /2560}] [expr {(9*[lindex $rgb_bg 2]) \ /2560}]] # If we can find the user's dt.resources file, we can find out the # palette and background/foreground colors set fh "" set palette "" set cur_rsrc ~/.dt/sessions/current/dt.resources set hom_rsrc ~/.dt/sessions/home/dt.resources if {[file readable $cur_rsrc] && [file readable $hom_rsrc]} { # Both exist. Use whichever is newer if {[file mtime $cur_rsrc] > [file mtime $hom_rsrc]} { if {[catch {open $cur_rsrc r} fh]} { set fh "" } } else { if {[catch {open $hom_rsrc r} fh]} { set fh "" } } } elseif {[file readable $cur_rsrc]} { if {[catch {open $cur_rsrc r} fh]} { set fh "" } } elseif {[file readable $hom_rsrc]} { if {[catch {open $hom_rsrc r} fh]} { set fh "" } } if {[string length $fh]} { set palf "" while {[gets $fh ln] != -1} { regexp "^\\*background:\[ \t]*(.*)\$" $ln nil textbg regexp "^\\*foreground:\[ \t]*(.*)\$" $ln nil textfg regexp "^\\*0\\*ColorPalette:\[ \t]*(.*)\$" $ln nil palette regexp "^Window.Color.Background:\[ \t]*(.*)\$" $ln nil textbg regexp "^Window.Color.Foreground:\[ \t]*(.*)\$" $ln nil textfg } catch {close $fh} # # If the *0*ColorPalette setting was found above, try to find the # indicated file in ~/.dt, $DTHOME, or /usr/dt. # if {[string length $palette]} { foreach dtdir {/usr/dt /etc/dt ~/.dt} { # This uses the last palette that we find if {[file readable [file join $dtdir palettes $palette]]} { set palf [file join $dtdir palettes $palette] } } # debug-info "Using palette $palf" if {[string length $palf]} { if {![catch {open $palf r} fh]} { gets $fh activetitle gets $fh inactivetitle gets $fh wkspc1 gets $fh textbg gets $fh guibg ;#(*.background) - default for tk under cde gets $fh menubg gets $fh wkspc4 gets $fh iconbg ;#control panel bg too close $fh option add *Text.highlightColor $wkspc4 option add *Dialog.Background $menubg option add *Menu.Background $menubg option add *Menu.activeBackground $menubg option add *Menu.activeForeground $fg option add *Menubutton.Background $menubg option add *Menubutton.activeBackground $menubg option add *Menubutton.activeForeground $fg } } } } else { puts stderr "Neither ~/.dt/sessions/current/dt.resources nor" puts stderr " ~/.dt/sessions/home/dt.resources was readable" puts stderr " Falling back to plain X" return 0 } if {[info exists activetitle]} { set hlbg $activetitle } else { set hlbg "#b24d7a" } set w(selcolor) $hlbg option add *Button.activeBackground $bg option add *Button.activeForeground $fg option add *Canvas.Background $shadow option add *Canvas.Foreground black option add *Entry.Background $textbg option add *Entry.Foreground $textfg option add *Entry.readonlyBackground $bg option add *Entry.highlightBackground $bg option add *Entry.highlightColor $hlbg option add *Listbox.background $textbg option add *Listbox.selectBackground $w(selcolor) option add *Listbox.selectForeground $fg option add *Menu.borderWidth 1 option add *Scrollbar.activeBackground $bg option add *Scrollbar.troughColor $shadow option add *Text.Background $textbg option add *Text.Foreground $textfg option add *Text.highlightBackground $bg # Menu checkboxes if {$tk_version >= 8.5} { # This makes it look like the native CDE checkbox option add *Menu.selectColor $fg option add *Checkbutton.offRelief sunken option add *Checkbutton.selectColor "" option add *Radiobutton.selectColor "" option add *Checkbutton.activeBackground $bg option add *Checkbutton.activeForeground $fg } else { option add *selectColor $w(selcolor) } # Suppress the border option add *HighlightThickness 0 userDefault # Add it back for text and entry widgets option add *Text.HighlightThickness 2 userDefault option add *Entry.HighlightThickness 1 userDefault return 1 } # Maybe this could be enhanced to get configs from themes and so on? # Right now it just sets colors so everything isn't blinding white. proc get_aqua_params {} { global w # button highlightbackground has to be the same as background # or else there are little white boxes around the button "pill" option add *background #ebebeb userDefault option add *Button.highlightBackground #ebebeb userDefault # That totally screws up the menus unless you fix it with this option add *Menu.Background white option add *Menu.Foreground black option add *Entry.HighlightThickness 2 userDefault option add *Entry.highlightBackground $w(selcolor) userDefault option add *Canvas.background #eeeeee userDefault option add *Entry.background #ffffff userDefault option add *Text.background white userDefault } ############################################################################### # run the main proc main tkcvs-8.2.3.orig/tkdiff/CHANGELOG.txt0000644000175000017500000001342611664612512015272 0ustar timtim4.2 (2011): * Works with Subversion 1.7 * Make opening file dialog know where it started from, and start in the same directory as the first file when looking for the second one. * You can now specify a preference for filetypes for the file open/save dialogs. * Detect PVCS by environment variable (patch 1839361 by nafmo) * Update BitKeeper support (patch 3053551 by wscott) * Mercurial support (patch 1867700 by damonmc) * Rudimentary Git support (patch 1836293 by cecilh3) * add help menu items to report versions of wish and diff * Fix location of temp files on MacOS X * Gave it a debug (-d) option 4.1.4 (11/15/2007): * Ignore -u option from svn for usage "svn diff --diff-cmd=tkdiff" * Perforce support for P4CONFIG environment variable * Remove an old font work-around for Mac, but add a new one for tk8.5 on Windows * Fix duplicate keyboard accelerator for Preferences 4.1.3 (2/20/2006): * Fixed incompatibility with older versions of Tcl/Tk ("-state disabled"). * Applied Warren Jones' subversion patch, which prevents the svn error that occurs when you omit a revision number. 4.1.2 (2/1/2006): * Can now do "tkdiff OLD-URL[@OLDREV] NEW-URL[@NEWREV]" in Subversion 4.1.1 (12/20/2005): * Security patch for temp files (CVE-2005-4434) 4.1 (7/4/2005): * Clearcase support * Better Subversion support * -L command line option allows you to control the labels above the diff file panes. * tkdiff --help or -h print a usage message for command line without invoking the GUI. * The New Diff Dialog is expanded so you can specify all the options. * The two panes can be resized relative to each other using a sash widget. Tk8.4 is recommended, since the implemetation is much better. 4.0.2 changes (11/08/2004): * Made sure all the prefs are saved (Bug# 878340) * Put all the Aqua stuff in one place 4.0.1 changes (4/27/2004): * Fix detection of a ,v file in the current directory for RCS 4.0 changes (3/10/2004): * Inline diff highlighting * Current line comparison window * Support for Subversion * better tolerance of Windows filenames * CDE, Windows, and MacOSX aware 3.09 changes (5/24/2001): * Removed use of the user name in temp file path. This removed a problem on Windows when the user name had a space in it. * Made diff "edge detection" smarter by using the new smarter overlap code for cases where diff decided to treat whitespace slightly different on missing-EOL files when doing 3-way diffs. * Added the "..." back to the Save options in the merge result window when appropriate. 3.08 changes (1/15/2001): * Made overlap detection smarter 3.07 changes (12/17/2000): * Highlight overlaps in bright yellow during 3-way merge * Added "Exit" and "Save & Exit" buttons to merge preview window * Removed error that popped up when doing a merge that was not necessary. 3.06 changes (unreleased): * Minor changes for AccuRev integration 3.05 changes (11/30/1999): * Added Jean-Francois' 3-way diff/merge feature * Added Bryan Oakley's tabstop feature * Added full support for Accurev 3.04 changes (8/5/1999): * Basic support for AccuRev. Graphical equivalent of 'accurev diff foo.c' * Fixes as a result of running Scriptics' error checker (see archives for details) * 'q' is quit 3.03m changes: * Added -o to set the name of the merge output file 3.03 changes: * includes the new line-by-line comparison window * bug fix for line numbers not being sync'd initially * slight modification for how change bars are colored * bug fix for up/down arrow keys (they were moving by two lines instead of one) * bug fix for Alt-V not opening the View menu (ditto for a few other alt- combinations) * fixed a couple of -underline options for menus * new preferences for enabling the display of the line comparison window, and for configuring how individual characters are highlighted in that window * added " vs. " to the window title (eg: foo-old.txt vs. foo.txt - TkDiff 3.03) -- this more closely aligns with Windows standards, and is equally useful on other platforms, I suppose * online help has been updated to reflect these changes + As of 3.00: (11/06/1998) A new GUI. No more reliance on perl... It's 100% Tcl/Tk now. Built-in editor. A "find" facility. Lots more... And all of this thanks to Bryan Oakley! + GPL'ed (as of 2.03). + RCS, CVS and SCCS support (auto-detected)... Even more SCM systems are supported in 3.00. + Highlighted difference regions, randomly accessible, with a quick overview/navigation bar. + Side-by-side viewing and linked (synchronized) scrolling of files. + On-line help and extensive customization (also much-improved in 3.00). + File-merge and change-summary facilities. + Line-number toggling (for easier cut & paste), with extra goodies as of 3.00. + Windows NT support. ...and more stuff I can't even remember, probably. Much work has been done by Bryan Oakley for this release (in fact, 99% of it). :-) Thanks, Bryan! Keep an eye on the tkdiff home page at http://www.ipass.net/~klassa/tkdiff for developments... I don't think there'll be many, at this point -- Bryan has turned this thing into just about all you could ask for! If you have problems, though, please send bug reports (better yet, patches :-)) against 3.00 to klassa@ipass.net. tkcvs-8.2.3.orig/tkdiff/README.txt0000644000175000017500000000207411664612512014735 0ustar timtimTkDiff 4.1.4 TkDiff is a Tcl/Tk front-end to diff for Unix and Windows, and is Copyright (C) 1994-2005 by John M. Klassa. Many of the toolbar icons were created by Dean S. Jones and used with his permission. The icons have the following copyright: Copyright(C) 1998 by Dean S. Jones dean@gallant.com http://www.gallant.com/icons.htm http://www.javalobby.org/jfa/projects/icons/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA tkcvs-8.2.3.orig/tkdiff/tkdiff.tcl0000755000175000017500000123257411664612512015230 0ustar timtim#!/bin/sh #-*-tcl-*- # the next line restarts using wish \ exec wish "$0" -- ${1+"$@"} ############################################################################### # # TkDiff -- A graphical front-end to diff for Unix and Windows. # Copyright (C) 1994-1998 by John M. Klassa. # Copyright (C) 1999-2001 by AccuRev Inc. # Copyright (C) 2002-2005 by John M. Klassa. # # TkDiff Home Page: http://tkdiff.sourceforge.net # # Usage: see "tkdiff -h" or "tkdiff --help" # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################### package require Tk 8.0 # Change to t for trace info on stderr set g(debug) f # get this out of the way -- we want to draw the whole user interface # behind the scenes, then pop up in all of its well-laid-out glory set screenWidth [winfo vrootwidth .] set screenHeight [winfo vrootheight .] wm withdraw . # set a couple o' globals that we might need sooner than later set g(name) "TkDiff" set g(version) "4.2" set g(started) 0 # FIXME - move to preferences option add "*TearOff" false 100 option add "*BorderWidth" 1 100 option add "*ToolTip.background" LightGoldenrod1 option add "*ToolTip.foreground" black # determine the windowing platform, since there are different ways to # do this for different versions of tcl if {[catch {tk windowingsystem} g(windowingSystem)]} { if {"$::tcl_platform(platform)" == "windows"} { set g(windowingSystem) "win32" } elseif {"$::tcl_platform(platform)" == "unix"} { set g(windowingSystem) "x11" } elseif {"$::tcl_platform(platform)" == "macintosh"} { set g(windowingSystem) "classic" } else { # this should never happen, but just to be sure... set g(windowingSystem) "x11" } } # determine the name of the temporary directory and the name of # the rc file, both of which are dependent on the platform. # This is overridden by the preference in .tkdiffrc except for the very first # time you run switch -- $::tcl_platform(platform) { windows { if {[info exists ::env(TEMP)]} { set opts(tmpdir) [file nativename $::env(TEMP)] } else { set opts(tmpdir) C:/temp } set basercfile "_tkdiff.rc" # Native look for toolbar set opts(fancyButtons) 1 set opts(relief) flat } default { if {[info exists ::env(TMPDIR)] && $g(windowingSystem) != "aqua"} { # MacOS X sets TMPDIR to something like /var/folders/uC/uCFr1z6qESSEYkTuOsevX++++yw/-Tmp-/ # don't let it do that set opts(tmpdir) $::env(TMPDIR) } else { set opts(tmpdir) /tmp } set basercfile ".tkdiffrc" # Native look for toolbar set opts(fancyButtons) 0 set opts(relief) raised } } # compute preferences file location. Note that TKDIFFRC can hold either # a directory or a file, though we document it as being a file name if {[info exists ::env(TKDIFFRC)]} { set rcfile $::env(TKDIFFRC) if {[file isdirectory $rcfile]} { set rcfile [file join $rcfile $basercfile] } } elseif {[info exists ::env(HOME)]} { set rcfile [file join $::env(HOME) $basercfile] } else { set rcfile [file join "/" $basercfile] } # Where should we start? MacOSX apps want to start in / which is obnoxious if {[pwd] == "/"} { if {[info exists ::env(HOME)]} { catch {cd $::env(HOME)} } } # Try to find a pleasing native look for each platform. # Fonts. set sysfont [font actual system] #debug-info "system font: $sysfont" # See what the native menu font is . configure -menu .native menu .native set menufont [lindex [.native configure -font] 3] destroy .native # Find out what the tk default is label .testlbl -text "LABEL" set w(background) [lindex [.testlbl cget -background] 0] set w(foreground) [lindex [.testlbl cget -foreground] 0] set labelfont [lindex [.testlbl configure -font] 3] destroy .testlbl text .testtext set textfont [lindex [.testtext configure -font] 3] destroy .testtext entry .testent set w(selcolor) [lindex [.testent configure -selectbackground] 4] set entryfont [lindex [.testent configure -font] 3] destroy .testent #debug-info "menufont $menufont" #debug-info "labelfont $labelfont" #debug-info "textfont $textfont" #debug-info "entryfont $entryfont" set fs [lindex $textfont 1] if {$fs == ""} { # This happens on Windows in tk8.5 # You get {TkDefaultFont} instead of {fixed 12} or whatever # Then when you add "bold" to it you have a bad spec set fa [font actual $textfont] #puts " actual font: $fa" set fm [lindex $fa 1] set fs [lindex $fa 3] set textfont [list $fm $fs] } set font [list $textfont] set bold [list [concat $textfont bold]] #debug-info "font: $font" #debug-info "bold: $bold\n" option add *Label.font $labelfont userDefault option add *Button.font $labelfont userDefault option add *Menu.font $menufont userDefault option add *Entry.font $entryfont userDefault # This makes tk_messageBox use our font. The default tends to be terrible # no matter what platform option add *Dialog.msg.font $labelfont userDefault # Initialize arrays array set g { ancfileset 0 conflictset 0 ancfile "" changefile "tkdiff-change-bars.out" destroy "" ignore_event,1 0 ignore_event,2 0 ignore_hevent,1 0 ignore_hevent,2 0 initOK 0 mapborder 0 mapheight 0 mergefile "" returnValue 0 showmerge 0 started 0 mergefileset 0 tempfiles "" thumbMinHeight 10 thumbHeight 10 thumbDeltaY 0 } array set finfo { f,1 "" f,2 "" pth,1 "" pth,2 "" revs,1 "" revs,2 "" lbl,1 "" lbl,2 "" userlbl,1 "" userlbl,2 "" title {} tmp,1 0 tmp,2 0 } set uniq 0 # These options may be changed at runtime array set opts { autocenter 1 autoselect 0 colorcbs 0 customCode {} diffcmd "diff" ignoreblanksopt "-b" ignoreblanks 0 editor "" filetypes {{{All Files} *} {{Text Files} .txt} {{TclFiles} .tcl}} geometry "80x30" showcbs 1 showln 1 showmap 1 showlineview 0 showinline1 0 showinline2 1 syncscroll 1 toolbarIcons 1 tagcbs 0 tagln 0 tagtext 1 tabstops 8 } # reporting options array set report { doSideLeft 0 doLineNumbersLeft 1 doChangeMarkersLeft 1 doTextLeft 1 doSideRight 1 doLineNumbersRight 1 doChangeMarkersRight 1 doTextRight 1 filename "tkdiff.out" } if {[string first "color" [winfo visual .]] >= 0} { # We have color # (but, let's not go crazy...) set colordel Tomato set colorins PaleGreen set colorchg DodgerBlue array set opts [subst { textopt "-background white -foreground black -font $font" currtag "-background Khaki" difftag "-background gray" deltag "-background $colordel -font $bold" instag "-background $colorins -font $bold" chgtag "-background LightSteelBlue" overlaptag "-background yellow" bytetag "-background blue -foreground white" inlinetag "-background $colorchg -font $bold" - "-background $colordel -foreground $colorins" + "-background $colorins -foreground $colordel" ! "-background $colorchg -foreground $colorchg" ? "-background yellow -foreground yellow" mapins "$colorins" mapdel "$colordel" mapchg "$colorchg" }] } else { # Assume only black and white set bg "black" array set opts [subst { textopt "-background white -foreground black -font $font" currtag "-background black -foreground white" difftag "-background white -foreground black -font $bold" deltag "-background black -foreground white" instag "-background black -foreground white" chgtag "-background black -foreground white" overlaptag "-background black -foreground white" bytetag "-underline 1" inlinetag "-underline 1" - "-background black -foreground white" + "-background black -foreground white" ! "-background black -foreground white" ? "-background black -foreground white" mapins "black" mapdel "black" mapchg "black" }] } # make sure wrapping is turned off. This might piss off a few people, # but it would screw up the display to have things wrap set opts(textopt) "$opts(textopt) -wrap none" # This proc is used in the rc file proc define {name value} { global opts set opts($name) $value } # Source the rc file, which may override some of the defaults # Any errors will be reported. Before doing so, we need to define the # "define" proc, which lets the rc file have a slightly more human-friendly # interface. Old-style .rc files should still load just fine for now, though # it ought to be noted new .rc files won't be able to be processed by older # versions of TkDiff. That shouldn't be a problem. if {[file exists $rcfile]} { if {[catch {source $rcfile} error]} { set startupError [join [list "There was an error in processing your \ startup file." "\n$g(name) will still run, but some of your \ preferences" "\nmay not be in effect." "\n\nFile: $rcfile" \ "\nError: $error"] " "] } } # a hack to handle older preferences files... # if the user has a diffopt defined in their rc file, we'll magically # convert that to diffcmd... if {[info exists opts(diffopt)]} { set opts(diffcmd) "diff $opts(diffopt)" } # Work-around for bad font approximations, # as suggested by Don Libes (libes@nist.gov). catch {tk scaling [expr {100.0 / 72}]} ############################################################################### # # HERE BEGIN THE PROCS ############################################################################### ############################################################################### # Exit with proper code ############################################################################### proc do-exit {{returncode {}}} { debug-info "do-exit ($returncode)" global g # we don't particularly care if del-tmp fails. catch {del-tmp} if {$returncode == ""} { set returncode $g(returnValue) } # exit with an appropriate return value exit $returncode } ############################################################################### # Modal error dialog. ############################################################################### proc do-error {msg} { debug-info "do-error ($msg)" global g tk_messageBox -message "$msg" -title "$g(name): Error" -icon error -type ok } ############################################################################### # Throw up a modal error dialog or print a message to stderr. For # Unix we print to stderr and exit if the main window hasn't been # created, otherwise put up a dialog and throw an exception. ############################################################################### proc fatal-error {msg} { debug-info "fatal-error ($msg)" global g tcl_platform if {$g(started)} { tk_messageBox -title "Error" -icon error -type ok -message $msg do-exit 2 } else { puts stderr $msg del-tmp do-exit 2 } } ############################################################################### # Return the name of a temporary file ############################################################################### proc tmpfile {n} { #debug-info "tmpfile ($n)" global g opts global uniq set uniq [expr ($uniq + 1) ] set tmpdir [file nativename $opts(tmpdir)] set tmpfile [file join $tmpdir "[pid]-$n-$uniq"] set access [list RDWR CREAT EXCL TRUNC] set perm 0600 if {[catch {open $tmpfile $access $perm} fid ]} { # something went wrong error "Failed creating temporary file: $fid" } close $fid lappend g(tempfiles) $tmpfile debug-info "temp file $tmpfile" return $tmpfile } ############################################################################### # Execute a command. # Returns "$stdout $stderr $exitcode" if exit code != 0 ############################################################################### proc run-command {cmd} { #debug-info "run-command ($cmd)" global opts errorCode set stderr "" set exitcode 0 set errfile [tmpfile "r"] debug-info "$cmd" set failed [catch "$cmd \"2>$errfile\"" stdout] # Read stderr output catch { set hndl [open "$errfile" r] set stderr [read $hndl] close $hndl } if {$failed} { switch -- [lindex $errorCode 0] { "CHILDSTATUS" { set exitcode [lindex $errorCode 2] } "POSIX" { if {$stderr == ""} { set stderr $stdout } set exitcode -1 } default { set exitcode -1 } } } catch {file delete $errfile} return [list "$stdout" "$stderr" "$exitcode"] } ############################################################################### # Execute a command. Die if unsuccessful. ############################################################################### proc die-unless {cmd file} { #debug-info "die-unless ($cmd $file)" global opts errorCode set file [string trim $file "\""] set result [run-command "$cmd \">$file\""] set stdout [lindex $result 0] set stderr [lindex $result 1] set exitcode [lindex $result 2] if {$exitcode != 0} { fatal-error "$stderr\n$stdout" } } ############################################################################### # Filter PVCS output files that have CR-CR-LF end-of-lines ############################################################################### proc filterCRCRLF {file} { debug-info "filterCRCLF ($file)" set outfile [tmpfile 9] set inp [open $file r] set out [open $outfile w] fconfigure $inp -translation binary fconfigure $out -translation binary set CR [format %c 13] while {![eof $inp]} { set line [gets $inp] if {[string length $line] && ![eof $inp]} { regsub -all "$CR$CR" $line $CR line puts $out $line } } close $inp close $out file rename -force $outfile $file } ############################################################################### # Return the smallest of two values ############################################################################### proc min {a b} { return [expr {$a < $b ? $a : $b}] } ############################################################################### # Return the largest of two values ############################################################################### proc max {a b} { return [expr {$a > $b ? $a : $b}] } ############################################################################### # Toggle change bars ############################################################################### proc do-show-changebars {{show {}}} { global opts global w if {$show != {}} { set opts(showcbs) $show } if {$opts(showcbs)} { grid $w(LeftCB) -row 0 -column 2 -sticky ns grid $w(RightCB) -row 0 -column 1 -sticky ns } else { grid forget $w(LeftCB) grid forget $w(RightCB) } } ############################################################################### # Toggle ignore white spaces ############################################################################### proc do-show-ignoreblanks {{showIgn {}}} { global opts global finfo if {$showIgn != {}} { set opts(ignoreblanks) $showIgn } if {$finfo(pth,1) != {} && $finfo(pth,2) != {}} { recompute-diff } } ############################################################################### # Toggle line numbers. ############################################################################### proc do-show-linenumbers {{showLn {}}} { global opts global w if {$showLn != {}} { set opts(showln) $showLn } if {$opts(showln)} { grid $w(LeftInfo) -row 0 -column 1 -sticky nsew grid $w(RightInfo) -row 0 -column 0 -sticky nsew } else { grid forget $w(LeftInfo) grid forget $w(RightInfo) } } ############################################################################### # Show line numbers in info windows ############################################################################### proc draw-line-numbers {} { debug-info "draw-line-numbers ()" global g global w $w(LeftInfo) configure -state normal $w(RightInfo) configure -state normal $w(LeftCB) configure -state normal $w(RightCB) configure -state normal set lines(Left) [lindex [split [$w(LeftText) index end-1lines] .] 0] set lines(Right) [lindex [split [$w(RightText) index end-1lines] .] 0] # Smallest line count set minlines [min $lines(Left) $lines(Right)] # cache all the blank lines for the info and cb windows, and do # one big insert after we're done. This seems to be much quicker # than inserting them in the widgets one line at a time. set linestuff {} set cbstuff {} for {set i 1} {$i < $minlines} {incr i} { append linestuff "$i\n" append cbstuff " \n" ;# for now, just put in place holders... } $w(LeftInfo) insert end $linestuff $w(RightInfo) insert end $linestuff $w(LeftCB) insert end $cbstuff $w(RightCB) insert end $cbstuff # Insert remaining line numbers. We'll cache the stuff to be # inserted so we can do just one call in to the widget. This # should be much faster, relatively speaking, then inserting # data one line at a time. foreach mod {Left Right} { set linestuff {} set cbstuff {} for {set i $minlines} {$i < $lines($mod)} {incr i} { append linestuff "$i\n" append cbstuff " \n" ;# for now, just put in place holders... } $w(${mod}Info) insert end $linestuff $w(${mod}CB) insert end $cbstuff } $w(LeftCB) configure -state disabled $w(RightCB) configure -state disabled $w(LeftInfo) configure -state disabled $w(RightInfo) configure -state disabled } ############################################################################### # Pop up a window for file merge. ############################################################################### proc popup-merge {{writeproc merge-write-file}} { debug-info "popup-merge ($writeproc)" global g global w global opts if {$g(mergefileset)} { $writeproc return } set path [tk_getSaveFile -defaultextension "" \ -filetypes $opts(filetypes) \ -initialfile [file nativename $g(mergefile)]] if {[string length $path] > 0} { set g(mergefile) $path $writeproc } } ############################################################################### # Split a file containing CVS conflict markers into two temporary files # name Name of file containing conflict markers # Returns the names of the two temporary files and the names of the # files that were merged ############################################################################### proc split-conflictfile {name} { debug-info "conflicts ($name)" global g opts set first ${name}.1 set second ${name}.2 set temp1 [tmpfile 1] set temp2 [tmpfile 2] if {[catch {set input [open $name r]}]} { fatal-error "Couldn't open file '$name'" } set first [open $temp1 w] set second [open $temp2 w] set firstname "" set secondname "" set output 3 set firstMatch "" set secondMatch "" set thirdMatch "" while {[gets $input line] >= 0} { if {$firstMatch == ""} { if {[regexp {^<<<<<<<* +(.*)} $line]} { set firstMatch {^<<<<<<<* +(.*)} set secondMatch {^=======*} set thirdMatch {^>>>>>>>* +(.*)} } elseif {[regexp {^>>>>>>>* +(.*)} $line]} { set firstMatch {^>>>>>>>* +(.*)} set secondMatch {^<<<<<<<* +(.*)} set thirdMatch {^=======*} } } if {$firstMatch != ""} { if {[regexp $firstMatch $line]} { set output 2 if {$secondname == ""} { regexp $firstMatch $line all secondname } } elseif {[regexp $secondMatch $line]} { set output 1 if {$firstname == ""} { regexp $secondMatch $line all firstname } } elseif {[regexp $thirdMatch $line]} { set output 3 if {$firstname == ""} { regexp $thirdMatch $line all firstname } } else { if {$output & 1} { puts $first $line } if {$output & 2} { puts $second $line } } } else { puts $first $line puts $second $line } } close $input close $first close $second if {$firstname == ""} { set firstname "old" } if {$secondname == ""} { set secondname "new" } return "{$temp1} {$temp2} {$firstname} {$secondname}" } ############################################################################### # Get a revision of a file # f file name # index index in finfo array # r revision, "" for head revision ############################################################################### proc get-file-rev {f index {r ""}} { debug-info "get-file-rev ($f $index \"$r\")" global finfo global opts global tcl_platform if {"$r" == ""} { set rev "HEAD" set acrev "HEAD" set acopt "" set cvsopt "" set svnopt "" set gitopt "" set rcsopt "" set sccsopt "" set bkopt "" set pvcsopt "" set p4file "$f" set hgopt "" } else { set rev "r$r" set acrev "\"$r\"" set acopt "-v \"$r\"" set cvsopt "-r $r" set svnopt "-r $r" set gitopt "$r:" set rcsopt "$r" set sccsopt "-r$r" set bkopt "-r$r" set pvcsopt "-r$r" set p4file "$f#$r" set hgopt "-r$r" } set finfo(pth,$index) [tmpfile $index] set finfo(tmp,$index) 1 # NB: it would probably be a Good Thing to move the definition # of the various command to exec, to the preferences dialog. regsub -all {\$} $f {\$} f set dirname [file dirname $f] set tailname [file tail $f] debug-info " $f" # For CVS, if it isn't checked out there is neither a CVS nor RCS # directory. It will however have a ,v suffix just like rcs. # There is not necessarily a RCS directory for RCS, either. The file # always has a ,v suffix. if {[file isdirectory [file join $dirname CVS]]} { set cmd "cvs" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (CVS $rev)" die-unless "exec $cmd update -p $cvsopt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {! [catch {eval "exec svn info"}]} { set cmd "svn" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } if {"$r" == "" || "$rev" == "rBASE"} { set finfo(lbl,$index) "$f (SVN BASE)" } else { set finfo(lbl,$index) "$f (SVN $rev)" } die-unless "exec $cmd cat $svnopt \"$f\"" $finfo(pth,$index) } elseif {[file isdirectory [file join $dirname .git]]} { set cmd "git" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } # Won't work if you aren't actually in the work tree debug-info "exec $cmd rev-parse --is-inside-work-tree" if {[catch {eval "exec $cmd rev-parse --is-inside-work-tree"} err] } { puts stderr "$err" puts stderr "Please start in a git work tree." exit 1 } debug-info "exec $cmd rev-parse --show-prefix" set prefix [exec $cmd rev-parse --show-prefix] if {"$r" == "" || "$rev" == "rBASE"} { set finfo(lbl,$index) "$f (GIT BASE)" die-unless "exec $cmd show \":$prefix$f\"" $finfo(pth,$index) } else { set finfo(lbl,$index) "$f (GIT $rev)" die-unless "exec $cmd show \"$gitopt$prefix$f\"" $finfo(pth,$index) } } elseif {[regexp {://} $f]} { # Subversion command can have the form # svn diff OLD-URL[@OLDREV] NEW-URL[@NEWREV] if {![regsub {^.*@} $f {} rev]} { set rev "HEAD" } regsub {@\d+$} $f {} path set finfo(lbl,$index) "$f" set cmd "svn" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } if {"$rev" == ""} { set command "$cmd cat $path" } else { set command "$cmd cat -r$rev $path" } die-unless "exec $command" $finfo(pth,$index) } elseif {[sccs-is-bk]} { set cmd "bk" set opt $bkopt set finfo(lbl,$index) "$f (bitkeeper $rev)" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } die-unless "exec $cmd get -p $opt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {[file isdirectory [file join $dirname SCCS]]} { set finfo(lbl,$index) "$f (SCCS $rev)" set opt $sccsopt set cmd "sccs" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } die-unless "exec $cmd get -p $opt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {[file isdirectory [file join $dirname RCS]]} { set cmd "co" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (RCS $rev)" die-unless "exec $cmd -p$rcsopt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {[file exists [file join $dirname $tailname,v]]} { set cmd "co" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (RCS $rev)" die-unless "exec $cmd -p$rcsopt \"$f\"" \""$finfo(pth,$index)\"" } elseif {[file exists [file join $dirname vcs.cfg]] || [info exists ::env(VCSCFG)]} { set cmd "get" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (PVCS $rev)" die-unless "exec $cmd -p $pvcsopt \"$f\"" "\"$finfo(pth,$index)\"" filterCRCRLF $finfo(pth,$index) } elseif {[info exists ::env(P4CLIENT)] || [info exists ::env(P4CONFIG)]} { set cmd "p4" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f (Perforce $rev)" die-unless "exec $cmd print -q \"$p4file\"" "\"$finfo(pth,$index)\"" } elseif {[info exists ::env(ACCUREV_BIN)]} { set cmd "accurev" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } set finfo(lbl,$index) "$f ($acrev)" die-unless "exec $cmd cat $acopt \"$f\"" "\"$finfo(pth,$index)\"" } elseif {[info exists ::env(CLEARCASE_ROOT)]} { set cmd "cleartool" set finfo(lbl,$index) "$f (ClearCase $rev)" debug-info "exec $cmd ls -s $f" catch {exec $cmd ls -s $f} ctls # get the path name to file minus the revision info # (either CHECKEDOUT or a number) if {![regexp {(\S+)/([^/]+)$} $ctls dummy path checkedout]} { puts "Couldn't parse ct ls output '$ctls'" exit } debug-info "exec $cmd lshistory -last 50 $f" catch {exec $cmd lshistory -last 50 $f} ctlshistory set lines [split $ctlshistory "\n"] set predecessor "" # find the previous version if {$checkedout == "CHECKEDOUT" || $checkedout == 0} { if {$checkedout == 0} { set path [file dirname $path] } set pattern "create version \"($path/\[^/\]+)\"" } else { incr checkedout -1 set pattern "create version \"($path/$checkedout)\"" } # search the history of the file for the latest version on our branch foreach l $lines { if {[regexp $pattern $l dummy predecessor]} { break } } if {$predecessor != ""} { set finfo(pth,$index) $predecessor debug-info " Setting lbl from predecessor $finfo(lbl,$index)" } else { puts "Couldn't deal with $f, exiting..." exit } } elseif {[is-mercurial-repository $dirname]} { # mercurial support set cmd "hg" if {$::tcl_platform(platform) == "windows"} { append cmd ".exe" } if {"$r" == "" || "$rev" == "PARENT"} { # in hg, the revision for cat defaults to the parent revision # of the working directory set finfo(lbl,$index) "$f (HG PARENT)" die-unless "exec $cmd cat $f" $finfo(pth,$index) } else { set finfo(lbl,$index) "$f (HG $rev)" die-unless "exec $cmd cat $hgopt $f" $finfo(pth,$index) } } else { fatal-error "File '$f' is not part of a revision control system" } # Header above each file - if user has specified -L, override #debug-info " $finfo(lbl,$index)" if {$finfo(userlbl,$index) != {}} { set finfo(lbl,$index) $finfo(userlbl,$index) debug-info " User label: $finfo(lbl,$index)" } } proc is-mercurial-repository {dirname} { debug-info "is-mercurial-repository ($dirname)" # check for a .hg directory in all parent directories set dirname [file normalize $dirname] set prevdir {} while {$dirname != $prevdir} { set hgfilename [file join $dirname .hg] if {[file isdirectory $hgfilename]} { return true } set prevdir $dirname set dirname [file dirname $dirname] } return false } proc sccs-is-bk {} { debug-info "sccs-is-bk ()" set cmd [auto_execok "bk"] set result 0 if {[string length $cmd] > 0} { debug-info "exec bk root" if {![catch {exec bk root} error]} { set result 1 } } return $result } ############################################################################### # Setup ordinary file # f file name # index index in finfo array ############################################################################### proc get-file {f index} { debug-info "get-file ($f $index)" global finfo #set finfo(f,$index) $f if {[file exists $f] != 1} { fatal-error "File '$f' does not exist" return 1 } if {[file isdirectory $f]} { fatal-error "'$f' is a directory" return 1 } # Header above each file - use filename unless # user has specified one with -L set finfo(lbl,$index) "$f" if {$finfo(userlbl,$index) != {}} { set finfo(lbl,$index) $finfo(userlbl,$index) debug-info " User label: $finfo(lbl,$index)" } set finfo(pth,$index) "$f" set finfo(tmp,$index) 0 return 0 } ############################################################################### # Read the commandline ############################################################################### proc commandline {} { debug-info "commandline" global argv global argc debug-info " argv: $argv" global finfo global opts global g set g(initOK) 0 set argindex 0 set revs 0 set pths 0 set lbls 0 # Loop through argv, storing revision args in rev and file args in # finfo. revs and pths are counters. while {$argindex < $argc} { set arg [lindex $argv $argindex] switch -regexp -- $arg { "^-h" - "^--help" { do-usage cline exit 0 } "^-a$" { incr argindex set g(ancfile) [lindex $argv $argindex] } "^-a.*" { set g(ancfile) [string range $arg 2 end] } "^-v$" - "^-r$" { incr argindex incr revs set finfo(revs,$revs) [lindex $argv $argindex] } "^-v.*" - "^-r.*" { incr revs set finfo(revs,$revs) [string range $arg 2 end] } "^-L$" { incr argindex incr lbls set finfo(userlbl,$lbls) [lindex $argv $argindex] } "^-L.*" { incr lbls set finfo(userlbl,$lbls) [string range $arg 2 end] } "^-conflict$" { set g(conflictset) 1 } "^-o$" { incr argindex set g(mergefile) [lindex $argv $argindex] } "^-o.*" { set g(mergefile) [string range $arg 2 end] } "^-u$" { # Ignore flag from "svn diff --diff-cmd=tkdiff" } "^-d$" { set g(debug) t } "^-psn" { # Ignore the Carbon Process Serial Number set argv [lreplace $argv $argindex $argindex] incr argc -1 incr argindex } "^-" { append opts(diffcmd) " $arg " } default { incr pths set finfo(pth,$pths) $arg set finfo(f,$pths) $arg } } incr argindex } # Add our counters to the global array # Now check how many revision and file args we have. #debug-info " $pths files, $revs revisions" # Maybe adjustment is needed if {$revs == 1 && $pths == 0} { # tkdiff -r FILE; same as tkdiff FILE set finfo(pths,1) $finfo(revs,1) set finfo(f,1) $finfo(revs,1) incr pths 1 incr revs -1 unset finfo(revs,1) } elseif {$revs == 2 && $pths == 0} { # tkdiff -rREV -r FILE; same as tkdiff -rREV FILE set finfo(pths,1) $finfo(revs,2) set finfo(f,1) $finfo(revs,2) incr pths 1 incr revs -1 unset finfo(revs,2) } # What have we got now? debug-info " $pths files, $revs revisions" if {$revs == 0 && $pths == 0} { # Return "empty" flag, and we'll do a pop-up return 1 } elseif {$revs > 1 && $pths != 1} { puts stderr "Error: you specified $pths file(s) and $revs revision(s)" do-usage cline exit 1 } if {$g(mergefile) != ""} { set g(mergefileset) 1 } return 0 } ############################################################################### # Process the arguments, whether from the command line or from the dialog ############################################################################### proc assemble-args {} { debug-info "assemble-args ()" global finfo global opts global g if {$g(ancfile) != ""} { set g(ancfileset) 1 } #debug-info " conflict: $g(conflictset)" #debug-info " ancestor: $g(ancfileset) $g(ancfile)" #debug-info " mergefile set: $g(mergefileset) $g(mergefile)" #debug-info " diff command: $opts(diffcmd) " # Count up how many files and revs we got from the GUI or commandline set pths 0 foreach p [array names finfo f,*] { if {$finfo($p) != ""} { incr pths } } set revs 0 foreach r [array names finfo revs,*] { if {$finfo($r) != ""} { incr revs } } debug-info " $pths files, $revs revisions" if {$revs == 0 && $pths == 0} { return } if {$g(conflictset)} { if {$revs == 0 && $pths == 1} { ############################################################ # tkdiff -conflict FILE ############################################################ set files [split-conflictfile "$finfo(f,1)"] if {[get-file [lindex "$files" 0] 1]} {return} if {[get-file [lindex "$files" 1] 2]} {return} # A conflict file may come from merge, cvs, or vmrg. The # names of the files/revisions depend on how it was made and # are taken from the <<<<<<< and >>>>>>> lines inside it. set finfo(lbl,1) [lindex "$files" 2] set finfo(lbl,2) [lindex "$files" 3] } else { fatal-error "Usage: tkdiff -conflict FILE" } } else { if {$revs == 2 && $pths == 1} { ############################################################ # tkdiff -rREV1 -rREV2 FILE ############################################################ set f $finfo(f,1) get-file-rev "$f" 1 "$finfo(revs,1)" get-file-rev "$f" 2 "$finfo(revs,2)" } elseif {$revs == 1 && $pths == 1} { ############################################################ # tkdiff -rREV FILE ############################################################ set f $finfo(f,1) get-file-rev "$f" 1 "$finfo(revs,1)" if {[get-file "$f" 2]} {return} } elseif {$revs == 0 && $pths == 2} { ############################################################ # tkdiff FILE1 FILE2 ############################################################ set f1 $finfo(f,1) set f2 $finfo(f,2) if {[file isdirectory $f1] && [file isdirectory $f2]} { fatal-error "Cannot diff two directories" } if {[file isdirectory $f1]} { set f1 [file join $f1 [file tail $f2]] } elseif {[file isdirectory $f2]} { set f2 [file join $f2 [file tail $f1]] } # Maybe they're Subversion URL paths, not local files if {[regexp {://} $f1]} { get-file-rev "$f1" 1 } else { if {[get-file "$f1" 1]} {return} } if {[regexp {://} $f2]} { get-file-rev "$f2" 2 } else { if {[get-file "$f2" 2]} {return} } } elseif {$revs == 0 && $pths == 1} { ############################################################ # tkdiff FILE ############################################################ set f $finfo(f,1) get-file-rev "$f" 1 if {[get-file "$f" 2]} {return} } else { if {[winfo exists .toolbar]} { do-error "Error: you specified $pths file(s) and $revs revision(s)" do-usage gui tkwait window .usage return 1 } else { puts stderr "Error: you specified $pths file(s) and $revs revision(s)" do-usage cline exit 1 } } } set finfo(title) "[file tail $finfo(lbl,1)] vs. [file tail $finfo(lbl,2)]" #debug-info " Setting title $finfo(title)" set rootname [file rootname $finfo(pth,1)] # set path [file dirname $finfo(pth,1)] set path [pwd] set suffix [file extension $finfo(pth,1)] if {! $g(mergefileset)} { set g(mergefile) [file join $path "${rootname}-merge$suffix"] } set g(initOK) 1 #debug-info " $revs revs $pths files" wm title . "$finfo(title) - $g(name) $g(version)" return 0 } ############################################################################### # Set up the display ############################################################################### proc create-display {} { debug-info "create-display ()" global g opts bg tk_version global w global tmpopts # these are the four major areas of the GUI: # menubar - the menubar (duh) # toolbar - the toolbar (duh, again) # client - the area with the text widgets and the graphical map # status us - a bottom status line # this block of destroys is only for stand-alone testing of # the GUI code, and can be blown away (or not, if we want to # be able to call this routine to recreate the display...) catch { destroy .menubar destroy .toolbar destroy .client destroy .map destroy .status } # create the top level frames and store them in a global # array.. set w(client) .client set w(menubar) .menubar set w(toolbar) .toolbar set w(status) .status # other random windows... set w(preferences) .pref set w(findDialog) .findDialog set w(popupMenu) .popupMenu # now, simply build all the pieces build-menubar build-toolbar build-client build-status build-popupMenu frame .separator1 -height 2 -borderwidth 2 -relief groove frame .separator2 -height 2 -borderwidth 2 -relief groove # ... and fit it all together... . configure -menu $w(menubar) pack $w(toolbar) -side top -fill x -expand n pack .separator1 -side top -fill x -expand n pack $w(client) -side top -fill both -expand y pack .separator2 -side top -fill x -expand n pack $w(status) -side bottom -fill x -expand n # apply user preferences by calling the proc that gets # called when the user presses "Apply" from the preferences # window. That proc uses a global variable named "tmpopts" # which should have the values from the dialog. Since we # aren't using the dialog, we need to populate this array # manually foreach key [array names opts] { set ::tmpopts($key) $opts($key) } apply 0 # Make sure temporary files get deleted #bind . {del-tmp} # other misc. bindings common-navigation $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB) # normally, keyboard traversal using tab and shift-tab isn't # enabled for text widgets, since the default binding for these # keys is to actually insert the tab character. Because all of # our text widgets are for display only, let's redefine the # default binding so the global and bindings # are used. bind Text {continue} bind Text {continue} # if the user toggles scrollbar syncing, we want to make sure # they sync up immediately trace variable opts(syncscroll) w toggleSyncScroll wm deiconify . focus -force $w(RightText) update idletasks # Need this to make the pane-resizing behave grid propagate $w(client) f } ############################################################################### # when the user changes the "sync scrollbars" option, we want to # sync up the left scrollbar with the right if they turn the option on ############################################################################### proc toggleSyncScroll {args} { global opts global w if {$opts(syncscroll) == 1} { eval vscroll-sync {{}} 2 [$w(RightText) yview] } } ############################################################################### # show the popup menu, optionally changing some of the entries based on # where the user clicked ############################################################################### proc show-popupMenu {x y} { global w global g set window [winfo containing $x $y] if {[winfo class $window] == "Text"} { $w(popupMenu) entryconfigure "Find..." -state normal $w(popupMenu) entryconfigure "Find Nearest*" -state normal $w(popupMenu) entryconfigure "Edit*" -state normal if {$window == $w(LeftText) || $window == $w(LeftInfo) || $window == \ $w(LeftCB)} { $w(popupMenu) configure -title "File 1" set g(activeWindow) $w(LeftText) } else { $w(popupMenu) configure -title "File 2" set g(activeWindow) $w(RightText) } } else { $w(popupMenu) entryconfigure "Find..." -state disabled $w(popupMenu) entryconfigure "Find Nearest*" -state disabled $w(popupMenu) entryconfigure "Edit*" -state disabled } tk_popup $w(popupMenu) $x $y } ############################################################################### # build the right-click popup menu ############################################################################### proc build-popupMenu {} { debug-info "build-popupMenu ()" global w g # this routine assumes the other windows already exist... menu $w(popupMenu) foreach win [list LeftText RightText LeftInfo RightInfo LeftCB RightCB \ mapCanvas] { bind $w($win) <3> {show-popupMenu %X %Y} } set m $w(popupMenu) $m add command -label "First Diff" -underline 0 -command [list popupMenu \ first] -accelerator "f" $m add command -label "Previous Diff" -underline 0 -command \ [list popupMenu previous] -accelerator "p" $m add command -label "Center Current Diff" -underline 0 -command \ [list popupMenu center] -accelerator "c" $m add command -label "Next Diff" -underline 0 -command [list popupMenu \ next] -accelerator "n" $m add command -label "Last Diff" -underline 0 -command [list popupMenu \ last] -accelerator "l" $m add separator $m add command -label "Find Nearest Diff" -underline 0 -command \ [list popupMenu nearest] -accelerator "Double-Click" $m add separator $m add command -label "Find..." -underline 0 -command [list popupMenu find] $m add command -label "Edit" -underline 0 -command [list popupMenu edit] } ############################################################################### # handle popup menu commands ############################################################################### proc popupMenu {command args} { debug-info "popupMenu ($command $args)" global g global w switch -- $command { center { center } edit { do-edit } find { do-find } first { move first } last { move last } next { move 1 } previous { move -1 } nearest { moveNearest $g(activeWindow) xy [winfo pointerx $g(activeWindow)] \ [winfo pointery $g(activeWindow)] } } } # Resize the text windows relative to each other. The 8.4 method works # much better. proc pane_drag {win x} { global w global finfo global tk_version set relX [expr $x - [winfo rootx $win]] set maxX [winfo width $win] set frac [expr int((double($relX) / $maxX) * 100)] if {$tk_version < 8.4} { if {$frac < 15} { set frac 15 } if {$frac > 85} { set frac 85 } #debug-info "frac $frac" set L $frac set R [expr 100 - $frac] .client.leftlabel configure -width [expr $L * 2] .client.rightlabel configure -width [expr $R * 2] } else { if {$frac < 5} { set frac 5 } if {$frac > 95} { set frac 95 } #debug-info "frac $frac" set L $frac set R [expr 100 - $frac] grid columnconfigure $win 0 -weight $L grid columnconfigure $win 2 -weight $R } #debug-info " new: $L $R" } ############################################################################### # build the main client display (the text widgets, scrollbars, that # sort of fluff) ############################################################################### proc build-client {} { debug-info "build-client ()" global g global w global opts global map global tk_version frame $w(client) -bd 2 -relief flat # set up global variables to reference the widgets, so # we don't have to use hardcoded widget paths elsewhere # in the code # # Text - holds the text of the file # Info - sort-of "invisible" text widget which is kept in sync # with the text widget and holds line numbers # CB - contains changebars or status or something like that... # VSB - vertical scrollbar # HSB - horizontal scrollbar # Label - label to hold the name of the file set w(LeftText) $w(client).left.text set w(LeftInfo) $w(client).left.info set w(LeftCB) $w(client).left.changeBars set w(LeftVSB) $w(client).left.vsb set w(LeftHSB) $w(client).left.hsb set w(LeftLabel) $w(client).leftlabel set w(RightText) $w(client).right.text set w(RightInfo) $w(client).right.info set w(RightCB) $w(client).right.changeBars set w(RightVSB) $w(client).right.vsb set w(RightHSB) $w(client).right.hsb set w(RightLabel) $w(client).rightlabel set w(BottomText) $w(client).bottomtext set w(map) $w(client).map set w(mapCanvas) $w(map).canvas # these don't need to be global... set leftFrame $w(client).left set rightFrame $w(client).right # we'll create each widget twice; once for the left side # and once for the right. debug-info " Assigning labels to headers" scan $opts(geometry) "%dx%d" width height label $w(LeftLabel) -bd 1 -relief flat -textvariable finfo(lbl,1) -width $width label $w(RightLabel) -bd 1 -relief flat -textvariable finfo(lbl,2) -width $width # this holds the text widgets and the scrollbars. The reason # for the frame is purely for aesthetics. It just looks # nicer, IMHO, to "embed" the scrollbars within the text # widget frame $leftFrame -bd 1 -relief sunken frame $rightFrame -bd 1 -relief sunken scrollbar $w(LeftHSB) -borderwidth 1 -orient horizontal -command \ [list $w(LeftText) xview] scrollbar $w(RightHSB) -borderwidth 1 -orient horizontal -command \ [list $w(RightText) xview] scrollbar $w(LeftVSB) -borderwidth 1 -orient vertical -command \ [list $w(LeftText) yview] scrollbar $w(RightVSB) -borderwidth 1 -orient vertical -command \ [list $w(RightText) yview] text $w(LeftText) -padx 0 -wrap none -width $width -height $height \ -borderwidth 0 -setgrid 1 -yscrollcommand [list vscroll-sync \ "$w(LeftInfo) $w(LeftCB)" 1] -xscrollcommand [list hscroll-sync 1] text $w(RightText) -padx 0 -wrap none -width $width -height $height \ -borderwidth 0 -setgrid 1 -yscrollcommand [list vscroll-sync \ "$w(RightInfo) $w(RightCB)" 2] -xscrollcommand [list hscroll-sync 2] text $w(LeftInfo) -height 0 -padx 0 -width 6 -borderwidth 0 -setgrid 1 \ -yscrollcommand [list vscroll-sync "$w(LeftCB) $w(LeftText)" 1] text $w(RightInfo) -height 0 -padx 0 -width 6 -borderwidth 0 -setgrid 1 \ -yscrollcommand [list vscroll-sync "$w(RightCB) $w(RightText)" 2] # each and every line in a text window will have a corresponding line # in this widget. And each line in this widget will be composed of # a single character (either "+", "-" or "!" for insertion, deletion # or change, respectively text $w(LeftCB) -height 0 -padx 0 -highlightthickness 0 -wrap none \ -foreground white -width 1 -borderwidth 0 -yscrollcommand \ [list vscroll-sync "$w(LeftInfo) $w(LeftText)" 1] text $w(RightCB) -height 0 -padx 0 -highlightthickness 0 -wrap none \ -background white -foreground white -width 1 -borderwidth 0 \ -yscrollcommand [list vscroll-sync "$w(RightInfo) $w(RightText)" 2] # this widget is the two line display showing the current line, so # one can compare character by character if necessary. text $w(BottomText) -wrap none -borderwidth 1 -height 2 -width 0 # this is how we highlight bytes that are different... # the bottom window (lineview) uses reverse video to highlight # diffs, so we need to figure out what reverse video is, and # define the tag appropriately eval $w(BottomText) tag configure diff $opts(bytetag) # Set up text tags for the 'current diff' (the one chosen by the 'next' # and 'prev' buttons) and any ol' diff region. All diff regions are # given the 'diff' tag initially... As 'next' and 'prev' are \ pressed, # to scroll through the differences, one particular diff region is # always chosen as the 'current diff', and is set off from the others # via the 'diff' tag -- in particular, so that it's obvious which diffs # in the left and right-hand text widgets match. foreach widget [list $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB)] { eval "$widget configure $opts(textopt)" foreach tag {difftag currtag inlinetag deltag instag chgtag \ overlaptag + - ! ?} { eval "$widget tag configure $tag $opts($tag)" } } # adjust the tag priorities a bit... foreach window [list LeftText RightText LeftCB RightCB LeftInfo RightInfo] { $w($window) tag raise deltag currtag $w($window) tag raise chgtag currtag $w($window) tag raise instag currtag $w($window) tag raise currtag difftag $w($window) tag raise inlinetag } # these tags are specific to change bars foreach widget [list $w(LeftCB) $w(RightCB)] { eval "$widget tag configure + $opts(+)" eval "$widget tag configure - $opts(-)" eval "$widget tag configure ! $opts(!)" eval "$widget tag configure ? $opts(?)" } # build the map... # we want the map to be the same width as a scrollbar, so we'll # steal some information from one of the scrollbars we just # created... set cwidth [winfo reqwidth $w(LeftVSB)] set ht [$w(LeftVSB) cget -highlightthickness] set cwidth [expr {$cwidth -($ht*2)}] set color [$w(LeftVSB) cget -troughcolor] set map [frame $w(client).map -bd 1 -relief sunken -takefocus 0 \ -highlightthickness 0] # now for the real map... image create photo map canvas $w(mapCanvas) -width [expr {$cwidth + 1}] \ -yscrollcommand map-resize -background $color -borderwidth 0 \ -relief sunken -highlightthickness 0 $w(mapCanvas) create image 1 1 -image map -anchor nw pack $w(mapCanvas) -side top -fill both -expand y # I'm not too pleased with these bindings -- it results in a rather # jerky, cpu-intensive maneuver since with each move of the mouse # we are finding and tagging the nearest diff. But, what *should* # it do? # # I think what I *want* it to do is update the combobox and status # bar so the user can see where in the scheme of things they are, # but not actually select anything until they release the mouse. bind $w(mapCanvas) [list handleMapEvent B1-Press %y] bind $w(mapCanvas) [list handleMapEvent B1-Motion %y] bind $w(mapCanvas) [list handleMapEvent B1-Release %y] # this is a grip for resizing the sides relative to each other. button $w(client).grip -borderwidth 3 -relief raised \ -cursor sb_h_double_arrow -image resize bind $w(client).grip {pane_drag $w(client) %X} # use grid to manage the widgets in the left side frame grid $w(LeftVSB) -row 0 -column 0 -sticky ns grid $w(LeftInfo) -row 0 -column 1 -sticky nsew grid $w(LeftCB) -row 0 -column 2 -sticky ns grid $w(LeftText) -row 0 -column 3 -sticky nsew grid $w(LeftHSB) -row 1 -column 1 -sticky ew -columnspan 3 grid rowconfigure $leftFrame 0 -weight 1 grid rowconfigure $leftFrame 1 -weight 0 grid columnconfigure $leftFrame 0 -weight 0 grid columnconfigure $leftFrame 1 -weight 0 grid columnconfigure $leftFrame 2 -weight 0 grid columnconfigure $leftFrame 3 -weight 1 # likewise for the right... grid $w(RightVSB) -row 0 -column 3 -sticky ns grid $w(RightInfo) -row 0 -column 0 -sticky nsew grid $w(RightCB) -row 0 -column 1 -sticky ns grid $w(RightText) -row 0 -column 2 -sticky nsew grid $w(RightHSB) -row 1 -column 0 -sticky ew -columnspan 3 grid rowconfigure $rightFrame 0 -weight 1 grid rowconfigure $rightFrame 1 -weight 0 grid columnconfigure $rightFrame 0 -weight 0 grid columnconfigure $rightFrame 1 -weight 0 grid columnconfigure $rightFrame 2 -weight 1 grid columnconfigure $rightFrame 3 -weight 0 # use grid to manage the labels, frames and map. We're going to # toss in an extra row just for the benefit of our dummy frame. # the intent is that the dummy frame will match the height of # the horizontal scrollbars so the map stops at the right place... grid $w(LeftLabel) -row 0 -column 0 -sticky ew grid $w(RightLabel) -row 0 -column 2 -sticky ew grid $leftFrame -row 1 -column 0 -sticky nsew -rowspan 2 grid $map -row 1 -column 1 -stick ns grid $w(client).grip -row 2 -column 1 grid $rightFrame -row 1 -column 2 -sticky nsew -rowspan 2 grid rowconfigure $w(client) 0 -weight 0 grid rowconfigure $w(client) 1 -weight 1 grid rowconfigure $w(client) 2 -weight 0 grid rowconfigure $w(client) 3 -weight 0 if {$tk_version < 8.4} { grid columnconfigure $w(client) 0 -weight 1 grid columnconfigure $w(client) 2 -weight 1 } else { grid columnconfigure $w(client) 0 -weight 100 -uniform a grid columnconfigure $w(client) 2 -weight 100 -uniform a } grid columnconfigure $w(client) 1 -weight 0 # this adjusts the variable g(activeWindow) to be whatever text # widget has the focus... bind $w(LeftText) <1> {set g(activeWindow) $w(LeftText)} bind $w(RightText) <1> {set g(activeWindow) $w(RightText)} set g(activeWindow) $w(LeftText) ;# establish a default rename $w(RightText) $w(RightText)_ rename $w(LeftText) $w(LeftText)_ proc $w(RightText) {command args} $::text_widget_proc proc $w(LeftText) {command args} $::text_widget_proc } ############################################################################### # Functionality: Inline diffs # Athr: Michael D. Beynon : mdb - beynon@yahoo.com # Date: 04/08/2003 : mdb - Added inline character diffs. # 04/16/2003 : mdb - Rewrote longest-common-substring to be faster. # - Added byte-by-byte algorithm. # # the recursive version is derived from the Ratcliff/Obershelp pattern # recognition algorithm (Dr Dobbs July 1988), where we search for a # longest common substring between two strings. This match is used as # an archor, around which we recursively do the same for the two left # and two right remaining pieces (omitting the anchor). This # precisely determines the location of the intraline tags. ################################################################################# proc longest-common-substring {s1 off1 len1 s2 off2 len2 lcsoff1_ref \ lcsoff2_ref} { upvar $lcsoff1_ref lcsoff1 upvar $lcsoff2_ref lcsoff2 set snippet "" set snippetlen 0 set longestlen 0 # extract just the search regions for efficiency in string searching set s1 [string range $s1 $off1 [expr $off1+$len1-1]] set s2 [string range $s2 $off2 [expr $off2+$len2-1]] set j 0 while {1} { # increase size of matching snippet while {$snippetlen < $len2-$j} { set tmp "$snippet[string index $s2 [expr $j+$snippetlen]]" if {[string first $tmp $s1] == -1} { break } set snippet $tmp incr snippetlen } if {$snippetlen == 0} { # nothing starting at this position incr j if {$snippetlen >= $len2-$j} { break } } else { set tmpoff [string first $snippet $s1] if {$tmpoff != -1 && $snippetlen > $longestlen} { # new longest? set longest $snippet set longestlen $snippetlen set lcsoff1 [expr $off1+$tmpoff] set lcsoff2 [expr $off2+$j] } # drop 1st char of prefix, but keep size the same as longest if {$snippetlen >= $len2-$j} { break } set snippet "[string range $snippet 1 end][string index $s2 \ [expr $j+$snippetlen]]" incr j } } return $longestlen } proc fid-ratcliff-aux {pos l1 l2 s1 off1 len1 s2 off2 len2} { global g if {$len1 <= 0 || $len2 <= 0} { if {$len1 == 0} { set g(scrinline,$pos,$g(scrinline,$pos)) [list r $l2 $off2 \ [expr $off2+$len2]] incr g(scrinline,$pos) } elseif {$len2 == 0} { set g(scrinline,$pos,$g(scrinline,$pos)) [list l $l1 $off1 \ [expr $off1+$len1]] incr g(scrinline,$pos) } return 0 } set cnt 0 set lcsoff1 -1 set lcsoff2 -1 set ret [longest-common-substring $s1 $off1 $len1 $s2 $off2 $len2 lcsoff1 \ lcsoff2] if {$ret > 0} { set rightoff1 [expr $lcsoff1+$ret] set rightoff2 [expr $lcsoff2+$ret] incr cnt [expr 2*$ret] if {$lcsoff1 > $off1 || $lcsoff2 > $off2} { # left incr cnt [fid-ratcliff-aux $pos $l1 $l2 $s1 $off1 \ [expr $lcsoff1-$off1] $s2 $off2 [expr $lcsoff2-$off2]] } if {$rightoff1<$off1+$len1 || $rightoff2<$off2+$len2} { # right incr cnt [fid-ratcliff-aux $pos $l1 $l2 $s1 $rightoff1 \ [expr $off1+$len1-$rightoff1] $s2 $rightoff2 \ [expr $off2+$len2-$rightoff2]] } } else { set g(scrinline,$pos,$g(scrinline,$pos)) [list r $l2 $off2 \ [expr $off2+$len2]] incr g(scrinline,$pos) set g(scrinline,$pos,$g(scrinline,$pos)) [list l $l1 $off1 \ [expr $off1+$len1]] incr g(scrinline,$pos) } return $cnt } proc find-inline-diff-ratcliff {pos l1 l2 s1 s2} { global g set len1 [string length $s1] set len2 [string length $s2] if {$len1 == 0 || $len2 == 0} { return 0 } return [fid-ratcliff-aux $pos $l1 $l2 $s1 0 $len1 $s2 0 $len2] } proc find-inline-diff-byte {pos l1 l2 s1 s2} { global g set len1 [string length $s1] set len2 [string length $s2] if {$len1 == 0 || $len2 == 0} { return 0 } set cnt 0 set lenmin [min $len1 $len2] set size 0 for {set i 0} {$i < $lenmin} {incr i} { if {$size > 0} { # in a diff section if {[string index $s1 $i] == [string index $s2 $i]} { # end of diff region set g(scrinline,$pos,$g(scrinline,$pos)) [list r $l2 \ [expr $i-$size] $i] incr g(scrinline,$pos) set g(scrinline,$pos,$g(scrinline,$pos)) [list l $l1 \ [expr $i-$size] $i] incr g(scrinline,$pos) set size 0 incr cnt } else { incr size } } else { if {[string index $s1 $i] != [string index $s2 $i]} { set size 1 } } } if {$size > 0} { # end of diff region set g(scrinline,$pos,$g(scrinline,$pos)) [list r $l2 [expr $i-$size] \ $len2] incr g(scrinline,$pos) set g(scrinline,$pos,$g(scrinline,$pos)) [list l $l1 [expr $i-$size] \ $len1] incr g(scrinline,$pos) incr cnt } return $cnt } ############################################################################### # the following code is used as the replacement body for the left and # right widget procs. The purpose is to catch when the insertion point # changes so we can update the line comparison window ############################################################################### set text_widget_proc { global w set real "[lindex [info level [info level]] 0]_" set result [eval $real $command $args] if {$command == "mark"} { if {[lindex $args 0] == "set" && [lindex $args 1] == "insert"} { set i [lindex $args 2] set i0 "$i linestart" set i1 "$i lineend" set left [$w(LeftText)_ get $i0 $i1] set right [$w(RightText)_ get $i0 $i1] $w(BottomText) delete 1.0 end $w(BottomText) insert end "< $left\n> $right" # find characters that are different, and underline them if {$left != $right} { set left [split $left {}] set right [split $right {}] # n.b. we set c to an offset equal to whatever we have # prepended to the data... set c 2 foreach l $left r $right { if {[string compare $l $r] != 0} { $w(BottomText) tag add diff 1.$c "1.$c+1c" $w(BottomText) tag add diff 2.$c "2.$c+1c" } incr c } $w(BottomText) tag remove diff "1.0 lineend" $w(BottomText) tag remove diff "2.0 lineend" } } } return $result } ############################################################################### # create (if necessary) and show the find dialog ############################################################################### proc show-find {} { debug-info "show-find ()" global w g global tcl_platform if {![winfo exists $w(findDialog)]} { toplevel $w(findDialog) wm group $w(findDialog) . wm transient $w(findDialog) . wm title $w(findDialog) "$g(name) Find" if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w(findDialog) } # we don't want the window to be deleted, just hidden from view wm protocol $w(findDialog) WM_DELETE_WINDOW [list wm withdraw \ $w(findDialog)] wm withdraw $w(findDialog) update idletasks frame $w(findDialog).content -bd 2 -relief groove pack $w(findDialog).content -side top -fill both -expand y -padx 0 \ -pady 5 frame $w(findDialog).buttons pack $w(findDialog).buttons -side bottom -fill x -expand n button $w(findDialog).buttons.doit -text "Find Next" -command do-find button $w(findDialog).buttons.dismiss -text "Dismiss" -command \ "wm withdraw $w(findDialog)" pack $w(findDialog).buttons.dismiss -side right -pady 5 -padx 0 pack $w(findDialog).buttons.doit -side right -pady 5 -padx 1 set ff $w(findDialog).content.findFrame frame $ff -height 100 -bd 2 -relief flat pack $ff -side top -fill x -expand n -padx 0 -pady 5 label $ff.label -text "Find what:" -underline 2 entry $ff.entry -textvariable g(findString) checkbutton $ff.searchCase -text "Ignore Case" -offvalue 0 -onvalue 1 \ -indicatoron true -variable g(findIgnoreCase) grid $ff.label -row 0 -column 0 -sticky e grid $ff.entry -row 0 -column 1 -sticky ew grid $ff.searchCase -row 0 -column 2 -sticky w grid columnconfigure $ff 0 -weight 0 grid columnconfigure $ff 1 -weight 1 grid columnconfigure $ff 2 -weight 0 # we need this in other places... set w(findEntry) $ff.entry bind $ff.entry do-find set of $w(findDialog).content.optionsFrame frame $of -bd 2 -relief flat pack $of -side top -fill y -expand y -padx 10 -pady 10 label $of.directionLabel -text "Search Direction:" -anchor e radiobutton $of.directionForward -indicatoron true -text "Down" \ -value "-forward" -variable g(findDirection) radiobutton $of.directionBackward -text "Up" -value "-backward" \ -indicatoron true -variable g(findDirection) label $of.windowLabel -text "Window:" -anchor e radiobutton $of.windowLeft -indicatoron true -text "Left" \ -value $w(LeftText) -variable g(activeWindow) radiobutton $of.windowRight -indicatoron true -text "Right" \ -value $w(RightText) -variable g(activeWindow) label $of.searchLabel -text "Search Type:" -anchor e radiobutton $of.searchExact -indicatoron true -text "Exact" \ -value "-exact" -variable g(findType) radiobutton $of.searchRegexp -text "Regexp" -value "-regexp" \ -indicatoron true -variable g(findType) grid $of.directionLabel -row 1 -column 0 -sticky w grid $of.directionForward -row 1 -column 1 -sticky w grid $of.directionBackward -row 1 -column 2 -sticky w grid $of.windowLabel -row 0 -column 0 -sticky w grid $of.windowLeft -row 0 -column 1 -sticky w grid $of.windowRight -row 0 -column 2 -sticky w grid $of.searchLabel -row 2 -column 0 -sticky w grid $of.searchExact -row 2 -column 1 -sticky w grid $of.searchRegexp -row 2 -column 2 -sticky w grid columnconfigure $of 0 -weight 0 grid columnconfigure $of 1 -weight 0 grid columnconfigure $of 2 -weight 1 set g(findDirection) "-forward" set g(findType) "-exact" set g(findIgnoreCase) 1 set g(lastSearch) "" if {$g(activeWindow) == ""} { set g(activeWindow) [focus] if {$g(activeWindow) != $w(LeftText) && $g(activeWindow) != \ $w(RightText)} { set g(activeWindow) $w(LeftText) } } } centerWindow $w(findDialog) wm deiconify $w(findDialog) raise $w(findDialog) after idle focus $w(findEntry) } ############################################################################### # do the "Edit->Copy" functionality, by copying the current selection # to the clipboard ############################################################################### proc do-copy {} { clipboard clear -displayof . # figure out which window has the selection... catch { clipboard append [selection get -displayof .] } } ############################################################################### # search for the text in the find dialog ############################################################################### proc do-find {} { debug-info "do-find ()" global g global w if {![winfo exists $w(findDialog)] || ![winfo ismapped $w(findDialog)]} { show-find return } set win $g(activeWindow) if {$win == ""} { set win $w(LeftText) } if {$g(lastSearch) != ""} { if {$g(findDirection) == "-forward"} { set start [$win index "insert +1c"] } else { set start insert } } else { set start 1.0 } if {$g(findIgnoreCase)} { set result [$win search $g(findDirection) $g(findType) -nocase \ -- $g(findString) $start] } else { set result [$win search $g(findDirection) $g(findType) \ -- $g(findString) $start] } if {[string length $result] > 0} { # if this is a regular expression search, get the whole line and try # to figure out exactly what matched; otherwise we know we must # have matched the whole string... if {$g(findType) == "-regexp"} { set line [$win get $result "$result lineend"] regexp $g(findString) $line matchVar set length [string length $matchVar] } else { set length [string length $g(findString)] } set g(lastSearch) $result $win mark set insert $result $win tag remove sel 1.0 end $win tag add sel $result "$result + ${length}c" $win see $result focus $win # should I somehow snap to the nearest diff? Probably not... } else { bell } } ############################################################################### # Build the menu bar ############################################################################### proc build-menubar {} { debug-info "build-menubar ()" global g global opts global w menu $w(menubar) # this is just temporary shorthand ... set menubar $w(menubar) # First, the menu buttons... set fileMenu $w(menubar).file set viewMenu $w(menubar).view set helpMenu $w(menubar).help set editMenu $w(menubar).edit set mergeMenu $w(menubar).window set markMenu $w(menubar).marks $w(menubar) add cascade -label "File" -menu $fileMenu -underline 0 $w(menubar) add cascade -label "Edit" -menu $editMenu -underline 0 $w(menubar) add cascade -label "View" -menu $viewMenu -underline 0 $w(menubar) add cascade -label "Mark" -menu $markMenu -underline 3 $w(menubar) add cascade -label "Merge" -menu $mergeMenu -underline 0 $w(menubar) add cascade -label "Help" -menu $helpMenu -underline 0 # these, however, are used in other places.. set w(fileMenu) $fileMenu set w(viewMenu) $viewMenu set w(helpMenu) $helpMenu set w(editMenu) $editMenu set w(mergeMenu) $mergeMenu set w(markMenu) $markMenu # Now, the menus... # Mark menu... menu $markMenu $markMenu add command -label "Mark Current Diff" -command [list diffmark \ mark] -underline 0 $markMenu add command -label "Clear Current Diff Mark" -command \ [list diffmark clear] -underline 0 set "g(tooltip,Mark Current Diff)" "Create a marker for the current \ difference record" set "g(tooltip,Clear Current Diff Mark)" "Clear the marker for the \ current difference record" # File menu... menu $fileMenu $fileMenu add command -label "New..." -underline 0 -command {do-new-diff} $fileMenu add separator $fileMenu add command -label "Recompute Diffs" -underline 0 \ -accelerator r -command recompute-diff $fileMenu add command -label "Write Report..." -command \ [list write-report popup] -underline 0 $fileMenu add separator $fileMenu add command -label "Exit" -underline 1 -accelerator q \ -command do-exit # Edit menu... If you change, add or remove labels, be sure and # update the tooltips. menu $editMenu $editMenu add command -label "Copy" -underline 0 -command do-copy $editMenu add separator $editMenu add command -label "Find..." -underline 0 -command show-find $editMenu add separator $editMenu add command -label "Edit File 1" -command { set g(activeWindow) $w(LeftText) do-edit } -underline 10 $editMenu add command -label "Edit File 2" -command { set g(activeWindow) $w(RightText) do-edit } -underline 10 $editMenu add separator $editMenu add command -label "Preferences..." -underline 0 \ -command customize set "g(tooltip,Copy)" "Copy the currently selected text to the clipboard" set "g(tooltip,Find...)" "Pop up a dialog to search for a string within \ either file" set "g(tooltip,Edit File 1)" "Launch an editor on the file on the left \ side of the window" set "g(tooltip,Edit File 2)" "Launch an editor on the file on the right \ side of the window" set "g(tooltip,Preferences...)" "Pop up a window to customize $g(name)" # View menu... If you change, add or remove labels, be sure and # update the tooltips. menu $viewMenu $viewMenu add checkbutton -label "Ignore White Spaces" -underline 8 \ -variable opts(ignoreblanks) \ -command do-show-ignoreblanks $viewMenu add checkbutton -label "Show Line Numbers" -underline 12 \ -variable opts(showln) \ -command do-show-linenumbers $viewMenu add checkbutton -label "Show Change Bars" -underline 12 \ -variable opts(showcbs) \ -command do-show-changebars $viewMenu add checkbutton -label "Show Diff Map" -underline 5 \ -variable opts(showmap) -command do-show-map $viewMenu add checkbutton -label "Show Line Comparison Window" \ -underline 11 -variable opts(showlineview) \ -command do-show-lineview $viewMenu add checkbutton -label "Show Inline Comparison (byte)" \ -variable opts(showinline1) \ -command do-show-inline1 $viewMenu add checkbutton -label "Show Inline Comparison (recursive)" \ -variable opts(showinline2) \ -command do-show-inline2 $viewMenu add separator $viewMenu add checkbutton -label "Synchronize Scrollbars" -underline 0 \ -variable opts(syncscroll) $viewMenu add checkbutton -label "Auto Center" -underline 0 \ -variable opts(autocenter) -command {if \ {$opts(autocenter)} {center}} $viewMenu add checkbutton -label "Auto Select" -underline 1 \ -variable opts(autoselect) $viewMenu add separator $viewMenu add command -label "First Diff" -underline 0 -command \ {move first} -accelerator "F" $viewMenu add command -label "Previous Diff" -underline 0 -command {move \ -1} -accelerator "P" $viewMenu add command -label "Center Current Diff" -underline 0 \ -command {center} -accelerator "C" $viewMenu add command -label "Next Diff" -underline 0 -command {move 1} \ -accelerator "N" $viewMenu add command -label "Last Diff" -underline 0 -command \ {move last} -accelerator "L" set "g(tooltip,Show Change Bars)" "If set, show the changebar column for \ each line of each file" set "g(tooltip,Show Line Numbers)" "If set, show line numbers beside each \ line of each file" set "g(tooltip,Synchronize Scrollbars)" "If set, scrolling either window \ will scroll both windows" set "g(tooltip,Diff Map)" "If set, display the graphical \"Difference \ Map\" in the center of the display" set "g(tooltip,Show Line Comparison Window)" "If set, display the window \ with byte-by-byte differences" set "g(tooltip,Show Inline Comparison (byte))" "If set, display inline \ byte-by-byte differences" set "g(tooltip,Show Inline Comparison (recursive))" "If set, display \ inline differences based on recursive matching regions" set "g(tooltip,Auto Select)" "If set, automatically selects the nearest \ diff record while scrolling" set "g(tooltip,Auto Center)" "If set, moving to another diff record will \ center the diff on the screen" set "g(tooltip,Center Current Diff)" "Center the display around the \ current diff record" set "g(tooltip,First Diff)" "Go to the first difference" set "g(tooltip,Last Diff)" "Go to the last difference" set "g(tooltip,Previous Diff)" "Go to the diff record just prior to the \ current diff record" set "g(tooltip,Next Diff)" "Go to the diff record just after the current \ diff record" set "g(tooltip,Ignore White Spaces)" "If set, changes in whitespaces are \ ignored" # Merge menu. If you change, add or remove labels, be sure and # update the tooltips. menu $mergeMenu $mergeMenu add checkbutton -label "Show Merge Window" -underline 9 \ -variable g(showmerge) -command "do-show-merge 1" $mergeMenu add command -label "Write Merge File..." -underline 6 \ -command popup-merge set "g(tooltip,Show Merge Window)" "Pops up a window showing the current \ merge results" set "g(tooltip,Write Merge File)" "Write the merge file to disk. You will \ be prompted for a filename" # Help menu. If you change, add or remove labels, be sure and # update the tooltips. menu $helpMenu $helpMenu add command -label "On GUI" -underline 3 -command do-help $helpMenu add command -label "On Command Line" -underline 3 \ -command "do-usage gui" $helpMenu add command -label "On Preferences" -underline 3 \ -command do-help-preferences $helpMenu add separator $helpMenu add command -label "About $g(name)" -underline 0 -command do-about $helpMenu add command -label "About Wish" -underline 0 -command about_wish $helpMenu add command -label "About Diff" -underline 0 -command about_diff bind $fileMenu <> {showTooltip menu %W} bind $editMenu <> {showTooltip menu %W} bind $viewMenu <> {showTooltip menu %W} bind $markMenu <> {showTooltip menu %W} bind $mergeMenu <> {showTooltip menu %W} bind $helpMenu <> {showTooltip menu %W} set "g(tooltip,On Preferences)" "Show help on the user-settable preferences" set "g(tooltip,On GUI)" "Show help on how to use the Graphical User \ Interface" set "g(tooltip,On Command Line)" "Show help on the command line arguments" set "g(tooltip,About $g(name))" "Show information about this application" set "g(tooltip,About Wish)" "Show information about Wish" set "g(tooltip,About Diff)" "Show information about diff" } ############################################################################### # Show explanation of item in the status bar at the bottom. # Now used only for menu items ############################################################################### proc showTooltip {which w} { global tooltip global g switch -- $which { menu { if {[catch {$w entrycget active -label} label]} { set label "" } if {[info exists g(tooltip,$label)]} { set g(statusInfo) $g(tooltip,$label) } else { set g(statusInfo) $label } update idletasks } button { if {[info exists g(tooltip,$w)]} { set g(statusInfo) $g(tooltip,$w) } else { set g(statusInfo) "" } update idletasks } } } ############################################################################### # Build the toolbar, in text or image mode ############################################################################### proc build-toolbar {} { debug-info "build-toolbar ()" global w g global opts frame $w(toolbar) -bd 0 set toolbar $w(toolbar) # these are used in other places.. set w(combo) $toolbar.combo set w(rediff_im) $toolbar.rediff_im set w(rediff_tx) $toolbar.rediff_tx set w(find_im) $toolbar.find_im set w(find_tx) $toolbar.find_tx set w(mergeChoiceLabel) $toolbar.mergechoicelbl set w(mergeChoice1_im) $toolbar.m1_im set w(mergeChoice1_tx) $toolbar.m1_tx set w(mergeChoice2_im) $toolbar.m2_im set w(mergeChoice2_tx) $toolbar.m2_tx set w(mergeChoice12_im) $toolbar.m12_im set w(mergeChoice12_tx) $toolbar.m12_tx set w(mergeChoice21_im) $toolbar.m21_im set w(mergeChoice21_tx) $toolbar.m21_tx set w(diffNavLabel) $toolbar.diffnavlbl set w(prevDiff_im) $toolbar.prev_im set w(prevDiff_tx) $toolbar.prev_tx set w(firstDiff_im) $toolbar.first_im set w(firstDiff_tx) $toolbar.first_tx set w(lastDiff_im) $toolbar.last_im set w(lastDiff_tx) $toolbar.last_tx set w(nextDiff_im) $toolbar.next_im set w(nextDiff_tx) $toolbar.next_tx set w(centerDiffs_im) $toolbar.center_im set w(centerDiffs_tx) $toolbar.center_tx set w(markLabel) $toolbar.bkmklbl set w(markSet_im) $toolbar.bkmkset_im set w(markSet_tx) $toolbar.bkmkset_tx set w(markClear_im) $toolbar.bkmkclear_im set w(markClear_tx) $toolbar.bkmkclear_tx # separators toolsep $toolbar.sep1 toolsep $toolbar.sep2 toolsep $toolbar.sep3 toolsep $toolbar.sep4 toolsep $toolbar.sep5 toolsep $toolbar.sep6 # The combo box ::combobox::combobox $toolbar.combo -borderwidth 1 -editable false \ -command moveTo -width 20 # rediff... toolbutton $toolbar.rediff_im -image rediffImage -command recompute-diff \ -bd 1 toolbutton $toolbar.rediff_tx -text "Rediff" -command recompute-diff \ -bd 1 -pady 1 # find... toolbutton $toolbar.find_im -image findImage -command do-find -bd 1 toolbutton $toolbar.find_tx -text "Find" -command do-find -bd 1 -pady 1 # navigation widgets label $toolbar.diffnavlbl -text "Diff:" -pady 0 -bd 2 -relief groove toolbutton $toolbar.prev_im -image prevDiffImage -command [list move -1] \ -bd 1 toolbutton $toolbar.prev_tx -text "Prev" -command [list move -1] -bd 1 \ -pady 1 toolbutton $toolbar.next_im -image nextDiffImage -command [list move 1] \ -bd 1 toolbutton $toolbar.next_tx -text "Next" -command [list move 1] -bd 1 \ -pady 1 toolbutton $toolbar.first_im -image firstDiffImage -command [list move \ first] -bd 1 toolbutton $toolbar.first_tx -text "First" -command [list move first] \ -bd 1 -pady 1 toolbutton $toolbar.last_im -image lastDiffImage -command [list move \ last] -bd 1 toolbutton $toolbar.last_tx -text "Last" -command [list move last] -bd 1 \ -pady 1 toolbutton $toolbar.center_im -image centerDiffsImage -command center -bd 1 toolbutton $toolbar.center_tx -text "Center" -command center -bd 1 -pady 1 # the merge widgets label $toolbar.mergechoicelbl -text "Merge:" -pady 0 -bd 2 -relief groove radiobutton $toolbar.m2_im -borderwidth 1 -indicatoron false \ -selectcolor $w(selcolor) \ -image mergeChoice2Image -value 2 -variable g(toggle) -command \ [list do-merge-choice 2] -takefocus 0 radiobutton $toolbar.m2_tx -borderwidth 1 -indicatoron true -text "R" \ -value 2 -variable g(toggle) -command [list do-merge-choice 2] \ -takefocus 0 radiobutton $toolbar.m1_im -borderwidth 1 -indicatoron false \ -selectcolor $w(selcolor) \ -image mergeChoice1Image -value 1 -variable g(toggle) -command \ [list do-merge-choice 1] -takefocus 0 radiobutton $toolbar.m1_tx -borderwidth 1 -indicatoron true -text "L" \ -value 1 -variable g(toggle) -command [list do-merge-choice 1] \ -takefocus 0 radiobutton $toolbar.m12_im -borderwidth 1 -indicatoron false \ -selectcolor $w(selcolor) \ -image mergeChoice12Image -value 12 -variable g(toggle) -command \ [list do-merge-choice 12] -takefocus 0 radiobutton $toolbar.m12_tx -borderwidth 1 -indicatoron true -text "LR" \ -value 12 -variable g(toggle) -command [list do-merge-choice 12] \ -takefocus 0 radiobutton $toolbar.m21_im -borderwidth 1 -indicatoron false \ -selectcolor $w(selcolor) \ -image mergeChoice21Image -value 21 -variable g(toggle) -command \ [list do-merge-choice 21] -takefocus 0 radiobutton $toolbar.m21_tx -borderwidth 1 -indicatoron true -text "RL" \ -value 21 -variable g(toggle) -command [list do-merge-choice 21] \ -takefocus 0 # The bookmarks label $toolbar.bkmklbl -text "Mark:" -pady 0 -bd 2 -relief groove toolbutton $toolbar.bkmkset_im -image markSetImage -command \ [list diffmark mark] -bd 1 toolbutton $toolbar.bkmkset_tx -text "Set" -command [list diffmark mark] \ -bd 1 -pady 1 toolbutton $toolbar.bkmkclear_im -image markClearImage -command \ [list diffmark clear] -bd 1 toolbutton $toolbar.bkmkclear_tx -text "Clear" -command [list diffmark \ clear] -bd 1 -pady 1 set_tooltips $w(find_im) {"Pop up a dialog to search for a string within \ either file"} set_tooltips $w(find_tx) {"Pop up a dialog to search for a string within \ either file"} set_tooltips $w(rediff_im) {"Recompute and redisplay the difference \ records"} set_tooltips $w(rediff_tx) {"Recompute and redisplay the difference \ records"} set_tooltips $w(mergeChoice12_im) {"select the diff on the left then \ right for merging"} set_tooltips $w(mergeChoice12_tx) {"select the diff on the left then \ right for merging"} set_tooltips $w(mergeChoice1_im) {"select the diff on the left for merging"} set_tooltips $w(mergeChoice1_tx) {"select the diff on the left for merging"} set_tooltips $w(mergeChoice2_im) {"select the diff on the right for \ merging"} set_tooltips $w(mergeChoice2_tx) {"select the diff on the right for \ merging"} set_tooltips $w(mergeChoice21_im) {"select the diff on the right then \ left for merging"} set_tooltips $w(mergeChoice21_tx) {"select the diff on the right then \ left for merging"} set_tooltips $w(prevDiff_im) {"Previous Diff"} set_tooltips $w(prevDiff_tx) {"Previous Diff"} set_tooltips $w(nextDiff_im) {"Next Diff"} set_tooltips $w(nextDiff_tx) {"Next Diff"} set_tooltips $w(firstDiff_im) {"First Diff"} set_tooltips $w(firstDiff_tx) {"First Diff"} set_tooltips $w(lastDiff_im) {"Last Diff"} set_tooltips $w(lastDiff_tx) {"Last Diff"} set_tooltips $w(markSet_im) {"Mark current diff"} set_tooltips $w(markSet_tx) {"Mark current diff"} set_tooltips $w(markClear_im) {"Clear current diff mark"} set_tooltips $w(markClear_tx) {"Clear current diff mark"} set_tooltips $w(centerDiffs_im) {"Center Current Diff"} set_tooltips $w(centerDiffs_tx) {"Center Current Diff"} pack-toolbuttons $toolbar } proc pack-toolbuttons {toolbar} { global opts if {$opts(toolbarIcons)} { set bp "im" } else { set bp "tx" } pack $toolbar.combo -side left -padx 2 pack $toolbar.sep1 -side left -fill y -pady 2 -padx 2 pack $toolbar.rediff_$bp -side left -padx 2 pack $toolbar.find_$bp -side left -padx 2 pack $toolbar.sep2 -side left -fill y -pady 2 -padx 2 pack $toolbar.mergechoicelbl -side left -padx 2 pack $toolbar.m12_$bp $toolbar.m1_$bp $toolbar.m2_$bp $toolbar.m21_$bp \ -side left -padx 2 pack $toolbar.sep3 -side left -fill y -pady 2 -padx 2 pack $toolbar.diffnavlbl -side left -pady 2 -padx 2 pack $toolbar.first_$bp $toolbar.last_$bp $toolbar.prev_$bp \ $toolbar.next_$bp -side left -pady 2 -padx 2 pack $toolbar.sep4 -side left -fill y -pady 2 -padx 2 pack $toolbar.center_$bp -side left -pady 2 -padx 1 pack $toolbar.sep5 -side left -fill y -pady 2 -padx 2 pack $toolbar.bkmklbl -side left -padx 2 pack $toolbar.bkmkset_$bp $toolbar.bkmkclear_$bp -side left -pady 2 -padx 2 pack $toolbar.sep6 -side left -fill y -pady 2 -padx 2 foreach b [info commands $toolbar.mark*] { pack $b -side left -fill y -pady 2 -padx 2 } foreach b [info commands $toolbar.mark*] { $b configure -relief $opts(relief) } foreach b [info commands $toolbar.*_$bp] { $b configure -relief $opts(relief) } # Radiobuttons ignore relief configuration if they have an image, so we # set their borderwidth to 0 if we want them flat. if {$opts(relief) == "flat" && $opts(toolbarIcons)} { set bord 0 } else { set bord 1 } foreach b [info commands $toolbar.m1*] { $b configure -bd $bord } foreach b [info commands $toolbar.m2*] { $b configure -bd $bord } } proc reconfigure-toolbar {} { debug-info "reconfigure-toolbar ()" global w foreach button [winfo children $w(toolbar)] { pack forget $button } pack-toolbuttons $w(toolbar) } proc build-status {} { debug-info "build-status ()" global w global g frame $w(status) -bd 0 set w(statusLabel) $w(status).label set w(statusCurrent) $w(status).current # MacOS has a resize handle in the bottom right which will sit # on top of whatever is placed there. So, we'll add a little bit # of whitespace there. It's harmless, so we'll do it on all of the # platforms. label $w(status).blank -image nullImage -width 16 -bd 1 -relief sunken label $w(statusCurrent) -textvariable g(statusCurrent) -anchor e \ -width 14 -borderwidth 1 -relief sunken -padx 4 -pady 2 label $w(statusLabel) -textvariable g(statusInfo) -anchor w -width 1 \ -borderwidth 1 -relief sunken -pady 2 pack $w(status).blank -side right -fill y pack $w(statusCurrent) -side right -fill y -expand n pack $w(statusLabel) -side left -fill both -expand y } ############################################################################### # handles events over the map ############################################################################### proc handleMapEvent {event y} { global opts global w global g #debug-info "handleMapEvent $event $y" switch -- $event { B1-Press { set ty1 [lindex $g(thumbBbox) 1] set ty2 [lindex $g(thumbBbox) 3] if {$y >= $ty1 && $y <= $ty2} { set g(mapScrolling) 1 # this captures the negative delta between the mouse press \ and the top # of the thumbbox. It's used so when we scroll by moving the # mouse, we can keep this distance constant. This is how all # scrollbars work, and it's what the user expects. set g(thumbDeltaY) [expr -1 * ($y - $ty1 - 2)] } } B1-Motion { if {[info exists g(mapScrolling)]} { incr y $g(thumbDeltaY) map-seek $y } } B1-Release { show-info "" set ty1 [lindex $g(thumbBbox) 1] set ty2 [lindex $g(thumbBbox) 3] # if we release over the trough (*not* over the thumb) # just scroll by the size of the thumb if {$y < $ty1 || $y > $ty2} { if {$y < $ty1} { # if vertical scrollbar syncing is turned on, # all the other windows should toe the line # appropriately... $w(RightText) yview scroll -1 pages } else { $w(RightText) yview scroll 1 pages } } else { # do nothing } catch {unset g(mapScrolling)} } } } # makes a toolbar "separator" proc toolsep {w} { label $w -image [image create photo] -highlightthickness 0 -bd 1 -width 0 \ -relief groove return $w } proc toolbutton {w args} { global tcl_platform global opts global g # create the button eval button $w $args # add minimal tooltip-like support bind $w [list toolbutton:handleEvent %W] bind $w [list toolbutton:handleEvent %W] bind $w [list toolbutton:handleEvent %W] bind $w [list toolbutton:handleEvent %W] $w configure -relief $opts(relief) return $w } # handle events in our fancy toolbuttons... proc toolbutton:handleEvent {event w {isToolbutton 1}} { global g global opts switch -- $event { "" { showTooltip button $w if {$opts(fancyButtons) && $isToolbutton && [$w cget -state] == \ "normal"} { $w configure -relief raised } } "" { set g(statusInfo) "" if {$opts(fancyButtons) && $isToolbutton} { $w configure -relief flat } } "" { showTooltip button $w if {$opts(fancyButtons) && $isToolbutton && [$w cget -state] == \ "normal"} { $w configure -relief raised } } "" { set g(statusInfo) "" if {$opts(fancyButtons) && $isToolbutton} { $w configure -relief flat } } } } ############################################################################### # move the map thumb to correspond to current shown merge... ############################################################################### proc map-move-thumb {y1 y2} { global g global w set thumbheight [expr {($y2 - $y1) * $g(mapheight)}] if {$thumbheight < $g(thumbMinHeight)} { set thumbheight $g(thumbMinHeight) } if {![info exists g(mapwidth)]} { set g(mapwidth) 0 } set x1 1 set x2 [expr {$g(mapwidth) - 3}] # why -2? it's the thickness of our border... set y1 [expr {int(($y1 * $g(mapheight)) - 2)}] if {$y1 < 0} { set y1 0 } set y2 [expr {$y1 + $thumbheight}] if {$y2 > $g(mapheight)} { set y2 $g(mapheight) set y1 [expr {$y2 - $thumbheight}] } set dx1 [expr {$x1 + 1}] set dx2 [expr {$x2 - 1}] set dy1 [expr {$y1 + 1}] set dy2 [expr {$y2 - 1}] $w(mapCanvas) coords thumbUL $x1 $y2 $x1 $y1 $x2 $y1 $dx2 $dy1 $dx1 $dy1 \ $dx1 $dy2 $w(mapCanvas) coords thumbLR $dx1 $y2 $x2 $y2 $x2 $dy1 $dx2 $dy1 $dx2 \ $dy2 $dx1 $dy2 set g(thumbBbox) [list $x1 $y1 $x2 $y2] set g(thumbHeight) $thumbheight } ############################################################################### # Bind keys for Next, Prev, Center, Merge choices 1 and 2 # # N.B. This is GROSS! It might have been necessary in earlier versions, # but now I think it needs a serious rewriite. We are now overriding # the text widget, so we can probably just disable the insert and delete # commands, and use something like insert_ and delete_ internally. ############################################################################### proc common-navigation {args} { global w bind . do-find foreach widget $args { # this effectively disables the widget, without having to # resort to actually disabling the widget (the latter which # has some annoying side effects). What we really want is to # only disable keys that get inserted, but that's difficult # to do, and this works almost as well... bind $widget {break} bind $widget {continue} bind $widget <> {break} # ... but now we need to restore some navigation key bindings # which got lost because we disable all keys. Since we are # attaching bindings that duplicate class bindings, we need # to be sure and include the break, so the events don't fire # twice (once for the widget, once for the class). There is # probably a much better way to do all this, but I'm too # lazy to figure it out... foreach event [list Next Prior Up Down Left Right Home End] { foreach modifier [list {} Shift Control Shift-Control] { set binding [bind Text <${modifier}${event}>] if {[string length $binding] > 0} { bind $widget "<${modifier}${event}>" " ${binding} break " } } } # these bindings allow control-f, tab and shift-tab to work # in spite of the fact we bound Any-KeyPress to a null action bind $widget continue bind $widget continue bind $widget continue bind $widget " center break " bind $widget " move 1 break " bind $widget

" move -1 break " bind $widget " move first break " bind $widget " move last break " bind $widget " do-exit break " bind $widget " recompute-diff break " bind $widget " moveNearest $widget mark insert break " # these bindings keep Alt- modified keys from triggering # the above actions. This way, any Alt combinations that # should open a menu will... foreach key [list c n p f l] { bind $widget {continue} } bind $widget " moveNearest $widget xy %x %y break " bind $widget " do-merge-choice 1 break " bind $widget " do-merge-choice 2 break " bind $widget " do-merge-choice 12 break " bind $widget " do-merge-choice 21 break " } } ############################################################################### # set or clear a "diff mark" -- a hot button to move to a particular diff ############################################################################### proc diffmark {option {diff -1}} { debug-info "diffmark ($option $diff)" global g global w if {$diff == -1} { set diff $g(pos) } set widget $w(toolbar).mark$diff switch -- $option { activate { move $diff 0 1 } mark { if {![winfo exists $widget]} { toolbutton $widget -text "\[$diff\]" -command [list diffmark \ activate $diff] -bd 1 -pady 1 pack $widget -side left -padx 2 set g(tooltip,$widget) "Diff Marker: Jump to diff record \ number $diff" } update-display } clear { if {[winfo exists $widget]} { destroy $widget catch {unset g(tooltip,$widget)} } update-display } clearall { set bookmarks [info commands $w(toolbar).mark*] if {[llength $bookmarks] > 0} { foreach widget $bookmarks { destroy $widget catch {unset g(tooltip,$widget)} } } update-display } } } ############################################################################### # Customize the display (among other things). ############################################################################### proc customize {} { debug-info "customize ()" global pref global g global w global opts global tmpopts global tcl_platform catch {destroy $w(preferences)} toplevel $w(preferences) wm title $w(preferences) "$g(name) Preferences" wm transient $w(preferences) . wm group $w(preferences) . if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w(preferences) } wm withdraw $w(preferences) # the button frame... frame $w(preferences).buttons -bd 0 button $w(preferences).buttons.dismiss -width 8 -text "Dismiss" \ -command {destroy $w(preferences)} button $w(preferences).buttons.apply -width 8 -text "Apply" \ -command {apply 1} button $w(preferences).buttons.save -width 8 -text "Save" -command save button $w(preferences).buttons.help -width 8 -text "Help" \ -command do-help-preferences pack $w(preferences).buttons -side bottom -fill x pack $w(preferences).buttons.dismiss -side right -padx 10 -pady 5 pack $w(preferences).buttons.help -side right -padx 10 -pady 5 pack $w(preferences).buttons.save -side right -padx 1 -pady 5 pack $w(preferences).buttons.apply -side right -padx 1 -pady 5 # a series of checkbuttons to act as a poor mans notebook tab frame $w(preferences).notebook -bd 0 pack $w(preferences).notebook -side top -fill x -pady 4 set pagelist {} # The relief makes these work, so we don't need to use the selcolor # Radiobuttons without indicators look rather sucky on MacOSX, so # we'll tweak the style for that platform if {$::tcl_platform(os) == "Darwin"} { set indicatoron true } else { set indicatoron false } foreach page [list General Display Appearance] { set frame $w(preferences).f$page lappend pagelist $frame set rb $w(preferences).notebook.f$page radiobutton $rb -command "customize-selectPage $frame" \ -selectcolor $w(background) \ -variable g(prefPage) -value $frame -height 2 -text $page \ -indicatoron $indicatoron -borderwidth 1 pack $rb -side left frame $frame -bd 2 -relief groove -width 400 -height 300 } set g(prefPage) $w(preferences).fGeneral # make sure our labels are defined customize-initLabels # this is an option that we support internally, but don't give # the user a way to directly edit (right now, anyway). But we # need to make sure tmpopts knows about it set tmpopts(customCode) $opts(customCode) # General set count 0 set frame $w(preferences).fGeneral foreach key {diffcmd ignoreblanksopt tmpdir editor filetypes geometry } { label $frame.l$count -text "$pref($key): " -anchor w set tmpopts($key) $opts($key) entry $frame.e$count -textvariable tmpopts($key) -width 50 -bd 2 \ -relief sunken grid $frame.l$count -row $count -column 0 -sticky w -padx 5 -pady 2 grid $frame.e$count -row $count -column 1 -sticky ew -padx 5 -pady 2 incr count } # this is just for filler... label $frame.filler -text {} grid $frame.filler -row $count incr count foreach key {fancyButtons toolbarIcons autocenter syncscroll autoselect} { label $frame.l$count -text "$pref($key): " -anchor w set tmpopts($key) $opts($key) checkbutton $frame.c$count -indicatoron true -text "$pref($key)" \ -justify left -onvalue 1 -offvalue 0 -variable tmpopts($key) set tmpopts($key) $opts($key) if {$key == "fancyButtons" && $g(windowingSystem) == "aqua"} { # Skipit - nothing to do incr count continue } grid $frame.c$count -row $count -column 0 -columnspan 2 -sticky w \ -padx 5 incr count } grid columnconfigure $frame 0 -weight 0 grid columnconfigure $frame 1 -weight 1 # this, in effect, adds a hidden row at the bottom which takes # up any extra room grid rowconfigure $frame $count -weight 1 # pack this window for a brief moment, and compute the window # size. We'll do this for each "page" and find the largest # size to be the size of the dialog pack $frame -side right -fill both -expand y update idletasks set maxwidth [winfo reqwidth $w(preferences)] set maxheight [winfo reqheight $w(preferences)] pack forget $frame # Appearance set frame $w(preferences).fAppearance set count 0 foreach key {textopt difftag deltag instag chgtag currtag bytetag \ inlinetag overlaptag} { label $frame.l$count -text "$pref($key): " -anchor w set tmpopts($key) $opts($key) entry $frame.e$count -textvariable tmpopts($key) -bd 2 -relief sunken grid $frame.l$count -row $count -column 0 -sticky w -padx 5 -pady 2 grid $frame.e$count -row $count -column 1 -sticky ew -padx 5 -pady 2 incr count } grid columnconfigure $frame 0 -weight 0 grid columnconfigure $frame 1 -weight 1 # tabstops are placed after a little extra whitespace, since it is # slightly different than all of the other options (ie: it's not # a list of widget options) frame $frame.sep$count -bd 0 -height 4 grid $frame.sep$count -row $count -column 0 -stick ew -columnspan 2 \ -padx 5 -pady 2 incr count set key "tabstops" set tmpopts($key) $opts($key) label $frame.l$count -text "$pref($key):" -anchor w set tmpopts($key) $opts($key) entry $frame.e$count -textvariable tmpopts($key) -bd 2 -relief sunken \ -width 3 grid $frame.l$count -row $count -column 0 -sticky w -padx 5 -pady 2 grid $frame.e$count -row $count -column 1 -sticky w -padx 5 -pady 2 incr count # add a tiny bit of validation, so the user can only enter numbers trace variable tmpopts($key) w [list validate integer] # this, in effect, adds a hidden row at the bottom which takes # up any extra room grid rowconfigure $frame $count -weight 1 pack $frame -side right -fill both -expand y update idletasks set maxwidth [max $maxwidth [winfo reqwidth $w(preferences)]] set maxheight [max $maxheight [winfo reqheight $w(preferences)]] pack forget $frame # Display set frame $w(preferences).fDisplay set row 0 # Option fields # Note that the order of the list is used to determine # the layout. So, if you add something to the list pay # attention to how it affects things. # # an x means an empty column; a - means an empty row set col 0 foreach key [list showln tagln showcbs tagcbs showmap colorcbs \ showlineview tagtext ignoreblanks showinline1 x showinline2 x] { if {$key == "x"} { set col [expr {$col ? 0 : 1}] if {$col == 0} { incr row } continue } if {$key == "-"} { frame $frame.f${row} -bd 0 -height 4 grid $frame.f${row} -row $row -column 0 -columnspan 2 -padx 20 \ -pady 4 -sticky nsew set col 1 ;# will force next column to zero and incr row } else { checkbutton $frame.c${row}${col} -indicatoron true \ -text "$pref($key)" -onvalue 1 -offvalue 0 -variable tmpopts($key) set tmpopts($key) $opts($key) grid $frame.c${row}$col -row $row -column $col -sticky w -padx 5 } set col [expr {$col ? 0 : 1}] if {$col == 0} { incr row } } grid columnconfigure $frame 0 -weight 0 grid columnconfigure $frame 1 -weight 0 grid columnconfigure $frame 2 -weight 0 grid columnconfigure $frame 3 -weight 0 grid columnconfigure $frame 4 -weight 1 # add validation to make only one of the showinline# options are set trace variable tmpopts(showinline1) w [list validate-inline showinline1] trace variable tmpopts(showinline2) w [list validate-inline showinline2] # this, in effect, adds a hidden row at the bottom which takes # up any extra room grid rowconfigure $frame $row -weight 1 pack $frame -side right -fill both -expand y update idletasks set maxwidth [max $maxwidth [winfo reqwidth $w(preferences)]] set maxheight [max $maxheight [winfo reqheight $w(preferences)]] pack forget $frame customize-selectPage # compute a reasonable location for the window... centerWindow $w(preferences) [list $maxwidth $maxheight] wm deiconify $w(preferences) } proc validate {type name index op} { global tmpopts # if we fail the check, attempt to do something clever if {![string is $type $tmpopts($index)]} { bell switch -- $type { integer { regsub -all {[^0-9]} $tmpopts($index) {} tmpopts($index) } default { # this should never happen. If you use this routine, # make sure you add cases to handle all possible # values of $type used by this program. set tmpopts($index) "" } } } } proc validate-inline {option name index op} { global tmpopts if {$tmpopts($index)} { if {$index == "showinline1"} { set tmpopts(showinline2) 0 } elseif {$index == "showinline2"} { set tmpopts(showinline1) 0 } } } proc customize-selectPage {{frame {}}} { global g w if {$frame == ""} { set frame $g(prefPage) } pack forget $w(preferences).fGeneral pack forget $w(preferences).fAppearance pack forget $w(preferences).fDisplay pack forget $w(preferences).fBehavior pack $frame -side right -fill both -expand y } ############################################################################### # define the labels for the preferences. This is done outside of # the customize proc since the labels are used in the help text. ############################################################################### proc customize-initLabels {} { global pref set pref(diffcmd) {diff command} set pref(ignoreblanksopt) {Ignore blanks option} set pref(ignoreblanks) {Ignore blanks when diffing} set pref(textopt) {Text widget options} set pref(bytetag) {Tag options for characters in line view} set pref(difftag) {Tag options for diff regions} set pref(currtag) {Tag options for the current diff region} set pref(inlinetag) {Tag options for diff region inline differences} set pref(deltag) {Tag options for deleted diff region} set pref(instag) {Tag options for inserted diff region} set pref(chgtag) {Tag options for changed diff region} set pref(overlaptag) {Tag options for overlap diff region} set pref(geometry) {Text window size} set pref(tmpdir) {Directory for scratch files} set pref(editor) {Program for editing files} set pref(filetypes) {Choice of file suffixes for file dialogs} set pref(fancyButtons) {Windows-style toolbar buttons} set pref(showlineview) {Show current line comparison window} set pref(showinline1) {Show inline diffs (byte comparisons)} set pref(showinline2) {Show inline diffs (recursive matching algorithm)} set pref(showmap) {Show graphical map of diffs} set pref(showln) {Show line numbers} set pref(showcbs) {Show change bars} set pref(autocenter) {Automatically center current diff region} set pref(syncscroll) {Synchronize scrollbars} set pref(toolbarIcons) {Use icons instead of labels in the toolbar} set pref(colorcbs) {Color change bars to match the diff map} set pref(tagtext) {Highlight file contents} set pref(tagcbs) {Highlight change bars} set pref(tagln) {Highlight line numbers} set pref(tabstops) {Tab stops} set pref(autoselect) "Automatically select the nearest diff region while \ scrolling" } ############################################################################### # Apply customization changes. ############################################################################### proc apply {{remark 0}} { debug-info "apply ($remark)" global opts global tmpopts global w global pref global screenWidth global screenHeight global tk_version grid propagate $w(client) t if {! [file isdirectory $tmpopts(tmpdir)]} { do-error "Invalid temporary directory $tmpopts(tmpdir)" } if {[catch " $w(LeftText) configure $tmpopts(textopt) $w(RightText) configure $tmpopts(textopt) $w(BottomText) configure $tmpopts(textopt) "]} { do-error "Invalid text widget setting: \n\n'$tmpopts(textopt)'" eval "$w(LeftText) configure $opts(textopt)" eval "$w(RightText) configure $opts(textopt)" eval "$w(BottomText) configure $opts(textopt)" return } # the text options must be ok. Configure the other text widgets # similarly eval "$w(LeftCB) configure $tmpopts(textopt)" eval "$w(LeftInfo) configure $tmpopts(textopt)" eval "$w(RightCB) configure $tmpopts(textopt)" eval "$w(RightInfo) configure $tmpopts(textopt)" set gridsize [wm grid .] set gridx [lindex $gridsize 2] set gridy [lindex $gridsize 3] #debug-info " wm grid is $gridx x $gridy" set maxunitsx [expr {$screenWidth / $gridx}] set maxunitsy [expr {$screenHeight / $gridy}] #debug-info " max X is $maxunitsx units" #debug-info " max Y is $maxunitsy units" set halfmax [expr {$maxunitsx / 2}] if {$tmpopts(geometry) == "" || [catch {scan $tmpopts(geometry) \ "%dx%d" width height} result]} { do-error "invalid geometry setting: $tmpopts(geometry)" return } #debug-info " width $width halfmax $halfmax" set maxw [expr {$halfmax - 18}] #debug-info " maxw $maxw" if {$width > $maxw} { set width $maxw } # re-center map if {$tk_version < 8.4} { grid columnconfigure $w(client) 0 -weight 1 grid columnconfigure $w(client) 2 -weight 1 } else { grid columnconfigure $w(client) 0 -weight 100 -uniform a grid columnconfigure $w(client) 2 -weight 100 -uniform a } if {[catch {$w(LeftText) configure -width $width -height $height} result]} { do-error "invalid geometry setting: $tmpopts(geometry)" return } $w(RightText) configure -width $width -height $height $w(LeftLabel) configure -width $width $w(RightLabel) configure -width $width grid forget $w(LeftLabel) grid forget $w(RightLabel) grid $w(LeftLabel) -row 0 -column 0 -sticky ew grid $w(RightLabel) -row 0 -column 2 -sticky ew foreach tag {difftag currtag inlinetag deltag instag chgtag overlaptag} { foreach win [list $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB)] { if {[catch "$win tag configure $tag $tmpopts($tag)"]} { do-error "Invalid settings for \"$pref($tag)\": \ \n\n'$tmpopts($tag)' is not a valid option string" eval "$win tag configure $tag $opts($tag)" return } } } if {[catch "$w(BottomText) tag configure diff $tmpopts(bytetag)"]} { do-error "Invalid settings for \"$pref(bytetag)\": \ \n\n'$tmpopts(bytetag)' is not a valid option string" eval "$w(BottomText) tag configure diff $opts(bytetag)" return } # tabstops require a little extra work. We need to figure out # the width of an "m" in the widget's font, then multiply that # by the tab stop width. For the bottom text widget the first tabstop # is adjusted by two to take into consideration the fact that we # add two bytes to each line (ie: "< " or "> "). set cwidth [font measure [$w(LeftText) cget -font] "m"] set tabstops [expr {$cwidth * $tmpopts(tabstops)}] $w(LeftText) configure -tabs $tabstops $w(RightText) configure -tabs $tabstops $w(BottomText) configure -tabs [list [expr {$tabstops +($cwidth * 2)}] \ [expr {2*$tabstops +($cwidth * 2)}]] if {[info exists w(mergeText)] && [winfo exists $w(mergeText)]} { $w(mergeText) configure -tabs $tabstops } # set opts to the values from tmpopts foreach key {autocenter autoselect chgtag colorcbs currtag deltag diffcmd \ difftag inlinetag editor fancyButtons filetypes geometry ignoreblanks \ ignoreblanksopt instag overlaptag showcbs showlineview showln showmap \ syncscroll tabstops tagcbs tagln tagtext textopt tmpdir toolbarIcons} { set opts($key) $tmpopts($key) } if {$opts(fancyButtons)} { set opts(relief) flat } else { set opts(relief) raised } # determine if we need to redo the inline diffs to avoid needless rediff if {$opts(showinline1) != $tmpopts(showinline1) || $opts(showinline2) != \ $tmpopts(showinline2)} { set opts(showinline1) $tmpopts(showinline1) set opts(showinline2) $tmpopts(showinline2) recompute-diff } # reconfigure the toolbar buttons reconfigure-toolbar # remark all the diff regions, show (or hide) the line numbers, # change bars and diff map, and we are done if {$remark} { remark-diffs } do-show-linenumbers do-show-changebars do-show-map do-show-lineview do-show-ignoreblanks grid propagate $w(client) f } ############################################################################### # Save customization changes. ############################################################################### proc save {} { debug-info "save ()" global g global tmpopts rcfile tcl_platform global pref if {[file exists $rcfile]} { file rename -force $rcfile "$rcfile~" } set fid [open $rcfile w] # put the tkdiff version in the file. It might be handy later puts $fid "# This file was generated by $g(name) $g(version)" puts $fid "# [clock format [clock seconds]]\n" puts $fid "set prefsFileVersion {$g(version)}\n" # now, put all of the preferences in the file foreach key [lsort [array names pref]] { regsub "\n" $pref($key) "\n# " comment puts $fid "# $comment" puts $fid "define $key {$tmpopts($key)}\n" } # ... and any custom code puts $fid "# custom code" puts $fid "# put any custom code you want to be executed in the" puts $fid "# following block. This code will be automatically executed" puts $fid "# after the GUI has been set up but before the diff is " puts $fid "# performed. Use this code to customize the interface if" puts $fid "# you so desire." puts $fid "# " puts $fid "# Even though you can't (as of version 3.09) edit this " puts $fid "# code via the preferences dialog, it will be automatically" puts $fid "# saved and restored if you do a SAVE from that dialog." puts $fid "" puts $fid "# Unless you really know what you are doing, it is probably" puts $fid "# wise to leave this unmodified." puts $fid "" puts $fid "define customCode {\n[string trim $tmpopts(customCode) \n]\n}\n" close $fid if {$::tcl_platform(platform) == "windows"} { file attribute $rcfile -hidden 1 } } ############################################################################### # Text has scrolled, update scrollbars and synchronize windows ############################################################################### proc hscroll-sync {id args} { global g opts global w # If ignore_event is true, we've already taken care of scrolling. # We're only interested in the first event. if {$g(ignore_hevent,$id)} { return } # Scrollbar sizes set size1 [expr {[lindex [$w(LeftText) xview] 1] - [lindex \ [$w(LeftText) xview] 0]}] set size2 [expr {[lindex [$w(RightText) xview] 1] - [lindex \ [$w(RightText) xview] 0]}] if {$opts(syncscroll) || $id == 1} { set start [lindex $args 0] if {$id != 1} { set start [expr {$start * $size2 / $size1}] } $w(LeftHSB) set $start [expr {$start + $size1}] $w(LeftText) xview moveto $start set g(ignore_hevent,1) 1 } if {$opts(syncscroll) || $id == 2} { set start [lindex $args 0] if {$id != 2} { set start [expr {$start * $size1 / $size2}] } $w(RightHSB) set $start [expr {$start + $size2}] $w(RightText) xview moveto $start set g(ignore_hevent,2) 1 } # This forces all the event handlers for the view alterations # above to trigger, and we lock out the recursive (redundant) # events using ignore_event. update idletasks # Restore to normal set g(ignore_hevent,1) 0 set g(ignore_hevent,2) 0 } ############################################################################### # Text has scrolled, update scrollbars and synchronize windows ############################################################################### proc vscroll-sync {windowlist id y0 y1} { global g opts global w if {$id == 1} { $w(LeftVSB) set $y0 $y1 } else { $w(RightVSB) set $y0 $y1 } # if syncing is disabled, we're done. This prevents a nasty # set of recursive calls if {[info exists g(disableSyncing)]} { return } # set the flag; this makes sure we only get called once set g(disableSyncing) 1 # scroll the other windows on the same side as this window foreach window $windowlist { $window yview moveto $y0 } eval map-move-thumb $y0 $y1 # Select nearest visible diff region, if the appropriate # options are set if {$opts(syncscroll) && $opts(autoselect) && $g(count) > 0} { set winhalf [expr {[winfo height $w(RightText)] / 2}] set result [find-diff [expr {int([$w(RightText) index @1,$winhalf])}]] set i [lindex $result 0] # have we found a diff other than the current diff? if {$i != $g(pos)} { # Also, make sure the diff is visible. If not, we won't # change the current diff region... set topline [$w(RightText) index @0,0] set bottomline [$w(RightText) index @0,10000] foreach {line s1 e1 s2 e2 type} $g(scrdiff,$i) { } if {$s1 >= $topline && $s1 <= $bottomline} { move $i 0 1 } } } # if syncing is turned on, scroll other windows. # Annoyingly, sometimes the *Text windows won't scroll properly, # at least under windows. And I can't for the life of me figure # out why. Maybe a bug in tk? if {$opts(syncscroll)} { if {$id == 1} { $w(RightText) yview moveto $y0 $w(RightInfo) yview moveto $y0 $w(RightCB) yview moveto $y0 $w(RightVSB) set $y0 $y1 } else { $w(LeftText) yview moveto $y0 $w(LeftInfo) yview moveto $y0 $w(LeftCB) yview moveto $y0 $w(LeftVSB) set $y0 $y1 } } # we apparently automatically process idle events after this # proc is called. Once that is done we'll unset our flag after idle {catch {unset g(disableSyncing)}} } ############################################################################### # Make a miniature map of the diff regions ############################################################################### proc create-map {name mapwidth mapheight} { global g global w global map global opts set map $name # Text widget always contains blank line at the end set lines [expr {double([$w(LeftText) index end]) - 2}] set factor [expr {$mapheight / $lines}] # We add some transparent stuff to make the map fill the canvas # in order to receive mouse events at the very bottom. $map blank $map put \#000 -to 0 $mapheight $mapwidth $mapheight # Line numbers start at 1, not at 0. for {set i 1} {$i <= $g(count)} {incr i} { # scan $g(scrdiff,$i) "%s %d %d %d %d %s" line s1 e1 s2 e2 type foreach {line s1 e1 s2 e2 type} $g(scrdiff,$i) { } set y [expr {int(($s2 - 1) * $factor) + $g(mapborder)}] set size [expr {round(($e2 - $s2 + 1) * $factor)}] if {$size < 1} { set size 1 } switch -- $type { "d" { set color $opts(mapdel) } "a" { set color $opts(mapins) } "c" { set color $opts(mapchg) } } if {[info exists g(overlap$i)]} { set color yellow } $map put $color -to 0 $y $mapwidth [expr {$y + $size}] } # let's draw a rectangle to simulate a scrollbar thumb. The size # isn't important since it will get resized when map-move-thumb # is called... $w(mapCanvas) create line 0 0 0 0 -width 1 -tags thumbUL -fill white $w(mapCanvas) create line 1 1 1 1 -width 1 -tags thumbLR -fill black $w(mapCanvas) raise thumb # now, move the thumb eval map-move-thumb [$w(LeftText) yview] } ############################################################################### # Resize map to fit window size ############################################################################### proc map-resize {args} { global g opts global w set mapwidth [winfo width $w(map)] set g(mapborder) [expr {[$w(map) cget -borderwidth] + [$w(map) cget \ -highlightthickness]}] set mapheight [expr {[winfo height $w(map)] - $g(mapborder) * 2}] # We'll get a couple of "resize" events, so don't draw a map # unless we've got the diffs and the map size has changed if {$g(count) == 0 || $mapheight == $g(mapheight)} { return } # If we don't have a map and don't want one, don't make one if {$g(mapheight) == 0 && $opts(showmap) == 0} { return } # This seems to happen on Windows!? _After_ the map is drawn the first time # another event triggers and [winfo height $w(map)] is then 0... if {$mapheight < 1} { return } set g(mapheight) $mapheight set g(mapwidth) $mapwidth create-map map $mapwidth $mapheight } ############################################################################### # scroll to diff region nearest to y ############################################################################### proc map-scroll {y} { global g global w global opts set yview [expr {double($y) / double($g(mapheight))}] # Show text corresponding to map catch {$w(RightText) yview moveto $yview} result update idletasks # Select the diff region closest to the middle of the screen set winhalf [expr {[winfo height $w(RightText)] / 2}] set result [find-diff [expr {int([$w(RightText) index @1,$winhalf])}]] move [lindex $result 0] 0 0 if {$opts(autocenter)} { center } if {$g(showmerge)} { merge-center } } ############################################################################### # Toggle showing the line comparison window ############################################################################### proc do-show-lineview {{showLineview {}}} { global opts global w if {$showLineview != {}} { set opts(showlineview) $showLineview } if {$opts(showlineview)} { grid $w(BottomText) -row 3 -column 0 -sticky ew -columnspan 4 } else { grid forget $w(BottomText) } } ############################################################################### # Toggle showing inline comparison ############################################################################### proc do-show-inline1 {{showInline1 {}}} { global opts if {$showInline1 != {}} { puts "passed in value=$showInline1" set opts(showinline1) $showInline1 } # mutually disjoint options if {$opts(showinline1)} { set opts(showinline2) 0 } recompute-diff } proc do-show-inline2 {{showInline2 {}}} { global opts if {$showInline2 != {}} { set opts(showinline2) $showInline2 } # mutually disjoint options if {$opts(showinline2)} { set opts(showinline1) 0 } recompute-diff } ############################################################################### # Toggle showing map or not ############################################################################### proc do-show-map {{showMap {}}} { global opts global w if {$showMap != {}} { set opts(showmap) $showMap } if {$opts(showmap)} { grid $w(map) -row 1 -column 1 -stick ns } else { grid forget $w(map) } } ############################################################################### # Find the diff nearest to $line. # Returns "$i $newtop" where $i is the index of the diff region # and $newtop is the new top line in the window to the right. ############################################################################### proc find-diff {line} { global g global w set top $line set newtop [expr {$top - int([$w(LeftText) index end]) + \ int([$w(RightText) index end])}] for {set low 1; set high $g(count); set i [expr {($low + $high) / 2}]} \ {$i >= $low} {set i [expr {($low + $high) / 2}]} { foreach {line s1 e1 s2 e2 type} $g(scrdiff,$i) { } if {$s1 > $top} { set newtop [expr {$top - $s1 + $s2}] set high [expr {$i-1}] } else { set low [expr {$i+1}] } } # do some range checking... set i [max 1 [min $i $g(count)]] # If next diff is closer than the one found, use it instead if {$i > 0 && $i < $g(count)} { set nexts1 [lindex $g(scrdiff,[expr {$i + 1}]) 1] set e1 [lindex $g(scrdiff,$i) 2] if {$nexts1 - $top < $top - $e1} { incr i } } return [list $i $newtop] } ############################################################################### # Calculate number of lines in diff region # pos Diff number # version 1 or 2, left or right window version # screen 1 for screen size, 0 for original diff size ############################################################################### proc diff-size {pos version {screen 0}} { global g if {$screen} { set diff scrdiff } else { set diff pdiff } foreach {thisdiff s(1) e(1) s(2) e(2) type} $g($diff,$pos) { } switch -- $version { 1 { set lines [expr {$e(1) - $s(1) + 1}] if {$type == "a"} { incr lines -1 } } 2 { set lines [expr {$e(2) - $s(2) + 1}] if {$type == "d"} { incr lines -1 } } 12 - 21 { set lines [expr {$e(1) - $s(1) + $e(2) - $s(2) + 1}] } } return $lines } ############################################################################### # Toggle showing merge preview or not ############################################################################### proc do-show-merge {{showMerge ""}} { #debug-info "do-show-merge ($showMerge)" global g global w if {$showMerge != ""} { set g(showmerge) $showMerge } if {$g(showmerge)} { watch-cursor if {! [info exists w(mergeText]} { merge-read-file merge-add-marks } wm deiconify .merge $w(mergeText) configure -state disabled focus -force $w(mergeText) merge-center } else { wm withdraw $w(merge) } #debug-info " ...restore-cursor from do-show-merge" restore-cursor } ############################################################################### # Create Merge preview window ############################################################################### proc merge-create-window {} { debug-info "merge-create-window ()" global opts global w global g set top .merge set w(merge) $top catch {destroy $top} toplevel $top set rx [winfo rootx .] set ry [winfo rooty .] set px [winfo width .] set py [winfo height .] #debug-info " rx $rx ry $ry px $px py $py" set x [expr {$rx + $px / 4}] set y [expr {$ry + $py / 2}] wm geometry $top "+${x}+$y" wm group $top . wm title $top "$g(name) Merge Preview" frame $top.frame -bd 1 -relief sunken pack $top.frame -side top -fill both -expand y -padx 10 -pady 10 set w(mergeText) $top.frame.text set w(mergeVSB) $top.frame.vsb set w(mergeHSB) $top.frame.hsb set w(mergeDismiss) $top.dismiss set w(mergeWrite) $top.mergeWrite set w(mergeWriteAndExit) $top.mergeWriteAndExit set w(mergeExit) $top.mergeExit set w(mergeRecenter) $top.mergeRecenter # Window and scrollbars scrollbar $w(mergeHSB) -orient horizontal -command [list $w(mergeText) \ xview] scrollbar $w(mergeVSB) -orient vertical -command [list $w(mergeText) yview] text $w(mergeText) -bd 0 -takefocus 1 -yscrollcommand [list $w(mergeVSB) \ set] -xscrollcommand [list $w(mergeHSB) set] grid $w(mergeText) -row 0 -column 0 -sticky nsew grid $w(mergeVSB) -row 0 -column 1 -sticky ns grid $w(mergeHSB) -row 1 -column 0 -sticky ew grid rowconfigure $top.frame 0 -weight 1 grid rowconfigure $top.frame 1 -weight 0 grid columnconfigure $top.frame 0 -weight 1 grid columnconfigure $top.frame 1 -weight 0 # buttons button $w(mergeRecenter) -width 8 -text "ReCenter" -underline 0 \ -command merge-center button $w(mergeDismiss) -width 8 -text "Dismiss" -underline 0 \ -command "do-show-merge 0" if {$g(mergefileset)} { button $w(mergeWrite) -width 8 -text "Save" -underline 0 \ -command [list popup-merge merge-write-file] button $w(mergeWriteAndExit) -width 8 -text "Save & Exit" \ -underline 8 -command { popup-merge merge-write-file exit } } else { button $w(mergeWrite) -width 8 -text "Save..." -underline 0 \ -command [list popup-merge merge-write-file] button $w(mergeWriteAndExit) -width 10 -text "Save & Exit..." \ -underline 8 -command { popup-merge merge-write-file exit } } button $w(mergeExit) -width 8 -text "Exit $g(name)" -underline 0 \ -command {exit} pack $w(mergeDismiss) -side right -pady 5 -padx 10 pack $w(mergeRecenter) -side right -pady 5 -padx 1 pack $w(mergeWrite) -side right -pady 5 -padx 1 pack $w(mergeWriteAndExit) -side right -pady 5 -padx 1 pack $w(mergeExit) -side right -pady 5 -padx 1 eval $w(mergeText) configure $opts(textopt) foreach tag {difftag currtag} { eval $w(mergeText) tag configure $tag $opts($tag) } # adjust the tabstops set cwidth [font measure [$w(mergeText) cget -font] "m"] set tabstops [expr {$cwidth * $opts(tabstops)}] $w(mergeText) configure -tabs $tabstops wm protocol $w(merge) WM_DELETE_WINDOW {do-show-merge 0} # adjust the tag priorities a bit... $w(mergeText) tag raise sel $w(mergeText) tag raise currtag difftag common-navigation $w(mergeText) if {! $g(showmerge)} { wm withdraw $w(merge) } } ############################################################################### # Read original file (Left window file) into merge preview window. # Not so good if it has changed. ############################################################################### proc merge-read-file {} { debug-info "merge-read-file ()" global finfo global w # hack; need to find a cleaner way... catch {destroy .merge} merge-create-window set hndl [open "$finfo(pth,1)" r] $w(mergeText) configure -state normal $w(mergeText) delete 1.0 end $w(mergeText) insert 1.0 [read $hndl] close $hndl # If last line doesn't end with a newline, add one. Important when # writing out the merge preview. if {![regexp {\.0$} [$w(mergeText) index "end-1lines lineend"]]} { $w(mergeText) insert end "\n" } $w(mergeText) configure -state disabled } ############################################################################### # Write merge preview to file ############################################################################### proc merge-write-file {} { global g global w set hndl [open "$g(mergefile)" w] set text [$w(mergeText) get 1.0 end-1lines] puts -nonewline $hndl $text close $hndl } ############################################################################### # Add a mark where each diff begins and tag diff regions so they are visible. # Assumes text is initially the bare original (Left) version. ############################################################################### proc merge-add-marks {} { global g global w # mark all lines first, so selection won't mess up line numbers for {set i 1} {$i <= $g(count)} {incr i} { foreach [list thisdiff s1 e1 s2 e2 type] $g(pdiff,$i) { } # set delta [expr {$type == "a" ? 1 : 0}] # $w(mergeText) mark set mark$i $s1.0+${delta}lines if {$type == "a"} { incr s1 } $w(mergeText) mark set mark$i $s1.0 $w(mergeText) mark gravity mark$i left } # if a 3-way merge, select right window as needed if {$g(ancfileset) && $g(count) > 0} { # # If there was something different between file1 # and the ancestor, pick the left window, but... # for {set i 1} {$i <= $g(count)} {incr i} { set s1 [lindex $g(pdiff,$i) 1] set s2 [lindex $g(pdiff,$i) 2] for {set p $s1} {$p <= $s2} {incr p} { if {[info exists g(diff3l$p)]} { set g(merge$i) 1 break } } } # # ... if there was a diff between file2 and the ancestor, # then file2 takes precedence # for {set i 1} {$i <= $g(count)} {incr i} { set s1 [lindex $g(pdiff,$i) 3] set s2 [lindex $g(pdiff,$i) 4] for {set p $s1} {$p <= $s2} {incr p} { if {[info exists g(diff3r$p)]} { set g(merge$i) 2 break } } } } # select merged lines for {set i 1} {$i <= $g(count)} {incr i} { foreach [list thisdiff s1 e1 s2 e2 type] $g(pdiff,$i) { } if {$g(merge$i) == 1} { # (If it's an insert it's not visible) if {$type != "a"} { set lines [expr {$e1 - $s1 + 1}] $w(mergeText) tag add difftag mark$i mark$i+${lines}lines } } else { # Insert right window version merge-select-version $i 1 2 } } # Tag current if {$g(count) > 0} { set pos $g(pos) set lines [diff-size $pos $g(merge$pos)] $w(mergeText) tag add currtag mark$pos "mark$pos+${lines}lines" } } ############################################################################### # Add a mark where each diff begins # pos diff index # oldversion 1 or 2, previous merge choice # newversion 1 or 2, new merge choice ############################################################################### proc merge-select-version {pos oldversion newversion} { global g global w catch { switch -- $oldversion { 1 - 2 {set oldlines [diff-size $pos $oldversion]} 12 - 21 {set oldlines [expr {[diff-size $pos 1] + [diff-size $pos 2]}]} } $w(mergeText) configure -state normal $w(mergeText) delete mark$pos "mark${pos}+${oldlines}lines" $w(mergeText) configure -state disabled } # Screen coordinates foreach {thisdiff s(1) e(1) s(2) e(2) type} $g(scrdiff,$pos) { } # Get the text directly from window switch -- $newversion { 1 { set newlines [diff-size $pos 1] set newtext [$w(LeftText) get $s(1).0 $s(1).0+${newlines}lines] } 2 { set newlines [diff-size $pos 2] set newtext [$w(RightText) get $s(2).0 $s(2).0+${newlines}lines] } 12 { set newlines [diff-size $pos 1] set newtext [$w(LeftText) get $s(1).0 $s(1).0+${newlines}lines] set newlines [diff-size $pos 2] append newtext [$w(RightText) get $s(2).0 $s(2).0+${newlines}lines] incr newlines [diff-size $pos 1] } 21 { set newlines [diff-size $pos 2] set newtext [$w(RightText) get $s(2).0 $s(2).0+${newlines}lines] set newlines [diff-size $pos 1] append newtext [$w(LeftText) get $s(1).0 $s(1).0+${newlines}lines] incr newlines [diff-size $pos 2] } } # Insert it $w(mergeText) configure -state normal $w(mergeText) insert mark$pos $newtext diff $w(mergeText) configure -state disabled if {$pos == $g(pos)} { $w(mergeText) tag add currtag mark$pos "mark${pos}+${newlines}lines" } } ############################################################################### # Center the merge region in the merge window ############################################################################### proc merge-center {} { global g global w # bail if there are no diffs if {$g(count) == 0} { return } # Size of diff in lines of text set difflines [diff-size $g(pos) $g(merge$g(pos))] set yview [$w(mergeText) yview] # Window height in percent set ywindow [expr {[lindex $yview 1] - [lindex $yview 0]}] # First line of diff set firstline [$w(mergeText) index mark$g(pos)] # Total number of lines in window set totallines [$w(mergeText) index end] if {$difflines / $totallines < $ywindow} { # Diff fits in window, center it $w(mergeText) yview moveto [expr {($firstline + $difflines / 2) / \ $totallines - $ywindow / 2}] } else { # Diff too big, show top part $w(mergeText) yview moveto [expr {($firstline - 1) / $totallines}] } } ############################################################################### # Update the merge preview window with the current merge choice # newversion 1 or 2, new merge choice ############################################################################### proc do-merge-choice {newversion} { debug-info "do-merge-choice ($newversion)" global g opts global w if {! [info exists w(mergeText)] || ! [winfo exists $w(mergeText)]} { return } $w(mergeText) configure -state normal merge-select-version $g(pos) $g(merge$g(pos)) $newversion $w(mergeText) configure -state disabled set g(merge$g(pos)) $newversion if {$g(showmerge) && $opts(autocenter)} { merge-center } set g(toggle) $newversion } ############################################################################### # Extract the start and end lines for file1 and file2 from the diff # stored in "line". ############################################################################### proc extract {line} { # the line darn well better be of the form , # where op is one of "a","c" or "d". range will either be a # single number or two numbers separated by a comma. # is this a cool regular expression, or what? :-) regexp {([0-9]*)(,([0-9]*))?([a-z])([0-9]*)(,([0-9]*))?} $line matchvar \ s1 x e1 op s2 x e2 if {[string length $e1] == 0} { set e1 $s1 } if {[string length $e2] == 0} { set e2 $s2 } if {[info exists s1] && [info exists s2]} { # return "$line $s1 $e1 $s2 $e2 $op" return [list $line $s1 $e1 $s2 $e2 $op] } else { fatal-error "Cannot parse output from diff:\n$line" } } ############################################################################### # Insert blank lines to match added/deleted lines in other file ############################################################################### proc add-lines {pos} { global g global w global opts # Figure out which lines we need to address... foreach [list thisdiff s1 e1 s2 e2 type] $g(pdiff,$pos) { } set size(1) [expr {$e1 - $s1}] set size(2) [expr {$e2 - $s2}] incr s1 $g(delta,1) incr s2 $g(delta,2) # Figure out what kind of diff we're dealing with switch -- $type { "a" { set lefttext " " ;# insert set righttext "+" set idx 1 set count [expr {$size(2) + 1}] incr s1 incr size(2) } "d" { set lefttext "-" ;# delete set righttext " " set idx 2 set count [expr {$size(1) + 1}] incr s2 incr size(1) } "c" { set lefttext "!" ;# change set righttext "!" ;# change if {$g(ancfileset)} { set change $g(pdiff,$g(count)) set leftBegin [lindex $change 1] set leftEnd [lindex $change 2] set rightBegin [lindex $change 3] set rightEnd [lindex $change 4] set changeLeft 0 set changeRight 0 for {set i $leftBegin} {$i <= $leftEnd} {incr i} { if {[info exists g(diff3l$i)]} { set changeLeft 1 break } } if {$changeLeft} { for {set i $rightBegin} {$i <= $rightEnd} {incr i} { if {[info exists g(diff3r$i)]} { set changeRight 1 break } } } if {$changeLeft && $changeRight} { set lefttext "?" ;# overlap set righttext "?" ;# overlap set g(overlap$pos) 1 } } set idx [expr {$size(1) < $size(2) ? 1 : 2}] set count [expr {abs($size(1) - $size(2))}] incr size(1) incr size(2) } } # Put plus signs in left info column if {$idx == 1} { set textWidget $w(LeftText) set infoWidget $w(LeftInfo) set cbWidget $w(LeftCB) # set blank "++++++\n" set blank " \n" } else { set textWidget $w(RightText) set infoWidget $w(RightInfo) set cbWidget $w(RightCB) set blank " \n" } # Insert blank lines to match other window set line [expr {$s1 + $size($idx)}] for {set i 0} {$i < $count} {incr i} { $textWidget insert $line.0 "\n" $infoWidget insert $line.0 $blank $cbWidget insert $line.0 "\n" } incr size($idx) $count set e1 [expr {$s1 + $size(1) - 1}] set e2 [expr {$s2 + $size(2) - 1}] incr g(delta,$idx) $count # Insert change bars or text to show what has changed. $w(RightCB) configure -state normal $w(LeftCB) configure -state normal for {set i $s1} {$i <= $e1} {incr i} { $w(LeftCB) insert $i.0 $lefttext $w(RightCB) insert $i.0 $righttext } # Save the diff block in window coordinates set g(scrdiff,$g(count)) [list $thisdiff $s1 $e1 $s2 $e2 $type] set g(scrinline,$pos) 0 if {$opts(showinline1) || $opts(showinline2)} { if {$type == "c"} { set numlines [max [expr {$e1-$s1+1}] [expr {$e2-$s2+1}]] for {set i 0} {$i < $numlines} {incr i} { set l1 [expr $s1+$i] set l2 [expr $s2+$i] if {$opts(showinline1)} { find-inline-diff-byte $pos $l1 $l2 [$w(LeftText) get \ $l1.0 $l1.end] [$w(RightText) get $l2.0 $l2.end] } else { find-inline-diff-ratcliff $pos $l1 $l2 [$w(LeftText) get \ $l1.0 $l1.end] [$w(RightText) get $l2.0 $l2.end] } } } } } ############################################################################### # Add a tag to a region. ############################################################################### proc add-tag {wgt tag start end type new {exact 0}} { global g $wgt tag add $tag $start.0 [expr {$end + 1}].0 } proc add-inline-tag {wgt tag line startcol endcol} { $wgt tag add $tag $line.$startcol $line.$endcol } ############################################################################### # Change the tag for a diff region. # 'pos' is the index in the diff array # If 'oldtag' is present, first remove it from the region # If 'setpos' is non-zero, make sure the region is visible. # Returns the diff expression. ############################################################################### proc set-tag {pos newtag {oldtag ""} {setpos 0}} { global g opts global w # Figure out which lines we need to address... if {![info exists g(scrdiff,$pos)]} { return } foreach {thisdiff s1 e1 s2 e2 dt} $g(scrdiff,$pos) { } # Remove old tag if {"$oldtag" != ""} { set e1next "[expr {$e1 + 1}].0" set e2next "[expr {$e2 + 1}].0" $w(LeftText) tag remove $oldtag $s1.0 $e1next $w(LeftInfo) tag remove $oldtag $s1.0 $e1next $w(RightText) tag remove $oldtag $s2.0 $e2next $w(RightInfo) tag remove $oldtag $s2.0 $e2next $w(LeftCB) tag remove $oldtag $s1.0 $e1next $w(RightCB) tag remove $oldtag $s2.0 $e2next catch { set lines [diff-size $pos $g(merge$pos)] $w(mergeText) tag remove $oldtag mark$pos "mark${pos}+${lines}lines" } } switch -- $dt { "d" { set coltag deltag set rcbtag " " set lcbtag "-" } "a" { set coltag instag set rcbtag "+" set lcbtag " " } "c" { set coltag chgtag set rcbtag "!" set lcbtag "!" } } if {[info exists g(overlap$pos)]} { set coltag overlaptag set rcbtag "?" set lcbtag "?" } # Add new tag if {$opts(tagtext)} { add-tag $w(LeftText) $newtag $s1 $e1 $dt 1 add-tag $w(RightText) $newtag $s2 $e2 $dt 1 add-tag $w(RightText) $coltag $s2 $e2 $dt 1 } if {$opts(tagcbs)} { if {$opts(colorcbs)} { add-tag $w(LeftCB) $lcbtag $s1 $e1 $dt 1 add-tag $w(RightCB) $rcbtag $s2 $e2 $dt 1 } else { add-tag $w(LeftCB) $newtag $s1 $e1 $dt 1 add-tag $w(RightCB) $newtag $s2 $e2 $dt 1 add-tag $w(RightCB) $coltag $s2 $e2 $dt 1 } } if {$opts(tagln)} { add-tag $w(LeftInfo) $newtag $s1 $e1 $dt 1 add-tag $w(RightInfo) $newtag $s2 $e2 $dt 1 add-tag $w(RightInfo) $coltag $s2 $e2 $dt 1 } catch { set lines [diff-size $pos $g(merge$pos)] $w(mergeText) tag add $newtag mark$pos "mark${pos}+${lines}lines" } # Move the view on both text widgets so that the new region is # visible. if {$setpos} { if {$opts(autocenter)} { center } else { $w(LeftText) see $s1.0 $w(RightText) see $s2.0 $w(LeftText) mark set insert $s1.0 $w(RightText) mark set insert $s2.0 if {$g(showmerge)} { $w(mergeText) see mark$pos } } } # make sure the sel tag has the highest priority foreach window [list LeftText RightText LeftCB RightCB LeftInfo RightInfo] { $w($window) tag raise sel $w($window) tag raise inlinetag } return $thisdiff } ############################################################################### # moves to the diff nearest the insertion cursor or the mouse click, # depending on $mode (which should be either "xy" or "mark") ############################################################################### proc moveNearest {window mode args} { switch -- $mode { "xy" { set x [lindex $args 0] set y [lindex $args 1] set index [$window index @$x,$y] set line [expr {int($index)}] set diff [find-diff $line] } "mark" { set index [$window index [lindex $args 0]] set line [expr {int($index)}] set diff [find-diff $line] } } # ok, we have an index move [lindex $diff 0] 0 1 } ############################################################################### ############################################################################### proc moveTo {window value} { global w global g # we know that the value is prefixed by the nunber/index of # the diff the user wants. So, just grab that out of the string regexp {([0-9]+) *:} $value matchVar index move $index 0 1 } ############################################################################### # this is called when the user scrolls the map thumb interactively. ############################################################################### proc map-seek {y} { global g global w set yview [expr {(double($y) / double($g(mapheight)))}] # Show text corresponding to map; $w(RightText) yview moveto $yview } ############################################################################### # Move the "current" diff indicator (i.e. go to the next or previous diff # region if "relative" is 1; go to an absolute diff number if "relative" # is 0). ############################################################################### proc move {value {relative 1} {setpos 1}} { #debug-info "move ($value $relative $setpos)" global g global w if {$value == "first"} { set value 1 set relative 0 } if {$value == "last"} { set value $g(count) set relative 0 } # Remove old 'curr' tag set-tag $g(pos) difftag currtag # Bump 'pos' (one way or the other). if {$relative} { set g(pos) [expr {$g(pos) + $value}] } else { set g(pos) $value } # Range check 'pos'. set g(pos) [max $g(pos) 1] set g(pos) [min $g(pos) $g(count)] # Set new 'curr' tag set g(currdiff) [set-tag $g(pos) currtag "" $setpos] # update the buttons.. #debug-info " ...update-display from move" update-display } proc update-display {} { debug-info "update-display ()" global g global w #debug-info " init_OK $g(initOK)" #debug-info " started $g(started)" #if {!$g(started)} return if {!$g(initOK)} { # disable darn near everything foreach b [list rediff find prevDiff firstDiff nextDiff lastDiff \ centerDiffs mergeChoice1 mergeChoice2 mergeChoice12 mergeChoice21] { $w(${b}_im) configure -state disabled $w(${b}_tx) configure -state disabled } foreach menu [list $w(popupMenu) $w(viewMenu)] { $menu entryconfigure "Previous*" -state disabled $menu entryconfigure "First*" -state disabled $menu entryconfigure "Next*" -state disabled $menu entryconfigure "Last*" -state disabled $menu entryconfigure "Center*" -state disabled } $w(popupMenu) entryconfigure "Find..." -state disabled $w(popupMenu) entryconfigure "Find Nearest*" -state disabled $w(popupMenu) entryconfigure "Edit*" -state disabled $w(editMenu) entryconfigure "Find*" -state disabled $w(editMenu) entryconfigure "Edit File 1" -state disabled $w(editMenu) entryconfigure "Edit File 2" -state disabled $w(fileMenu) entryconfigure "Write*" -state disabled $w(fileMenu) entryconfigure "Recompute*" -state disabled $w(mergeMenu) entryconfigure "Show*" -state disabled $w(mergeMenu) entryconfigure "Write*" -state disabled $w(markMenu) entryconfigure "Mark*" -state disabled $w(markMenu) entryconfigure "Clear*" -state disabled } else { # these are always enabled, assuming we have properly # diffed a couple of files $w(popupMenu) entryconfigure "Find..." -state normal $w(popupMenu) entryconfigure "Find Nearest*" -state normal $w(popupMenu) entryconfigure "Edit*" -state normal foreach b [list rediff find prevDiff firstDiff nextDiff lastDiff \ centerDiffs] { $w(${b}_im) configure -state normal $w(${b}_tx) configure -state normal } foreach b [list mergeChoice1 mergeChoice2 mergeChoice12 mergeChoice21] { $w(${b}_im) configure -state normal $w(${b}_tx) configure -state normal } $w(editMenu) entryconfigure "Find*" -state normal $w(editMenu) entryconfigure "Edit File 1" -state normal $w(editMenu) entryconfigure "Edit File 2" -state normal $w(fileMenu) entryconfigure "Write*" -state normal $w(fileMenu) entryconfigure "Recompute*" -state normal $w(mergeMenu) entryconfigure "Show*" -state normal $w(mergeMenu) entryconfigure "Write*" -state normal $w(find_im) configure -state normal $w(find_tx) configure -state normal # Hmmm.... on my Mac the combobox flashes if we don't add this # check. Is this a bug in AquaTk, or in my combobox... :-| if {[$w(combo) cget -state] != "normal"} { $w(combo) configure -state normal } } # Update the toggles. if {$g(count)} { set g(toggle) $g(merge$g(pos)) } # update the status line set g(statusCurrent) "$g(pos) of $g(count)" show-info $g(statusCurrent) # update the combobox. We don't want its command to fire, so # we'll disable it temporarily $w(combo) configure -commandstate "disabled" set i [expr {$g(pos) - 1}] $w(combo) configure -value [lindex [$w(combo) list get 0 end] $i] $w(combo) selection clear $w(combo) configure -commandstate "normal" # update the widgets if {$g(pos) <= 1} { foreach buttonpref {im tx} { $w(prevDiff_$buttonpref) configure -state disabled $w(firstDiff_$buttonpref) configure -state disabled } $w(popupMenu) entryconfigure "Previous*" -state disabled $w(popupMenu) entryconfigure "First*" -state disabled $w(viewMenu) entryconfigure "Previous*" -state disabled $w(viewMenu) entryconfigure "First*" -state disabled } else { foreach buttonpref {im tx} { $w(prevDiff_$buttonpref) configure -state normal $w(firstDiff_$buttonpref) configure -state normal } $w(popupMenu) entryconfigure "Previous*" -state normal $w(popupMenu) entryconfigure "First*" -state normal $w(viewMenu) entryconfigure "Previous*" -state normal $w(viewMenu) entryconfigure "First*" -state normal } if {$g(pos) >= $g(count)} { foreach buttonpref {im tx} { $w(nextDiff_$buttonpref) configure -state disabled $w(lastDiff_$buttonpref) configure -state disabled } $w(popupMenu) entryconfigure "Next*" -state disabled $w(popupMenu) entryconfigure "Last*" -state disabled $w(viewMenu) entryconfigure "Next*" -state disabled $w(viewMenu) entryconfigure "Last*" -state disabled } else { foreach buttonpref {im tx} { $w(nextDiff_$buttonpref) configure -state normal $w(lastDiff_$buttonpref) configure -state normal } $w(popupMenu) entryconfigure "Next*" -state normal $w(popupMenu) entryconfigure "Last*" -state normal $w(viewMenu) entryconfigure "Next*" -state normal $w(viewMenu) entryconfigure "Last*" -state normal } if {$g(count) > 0} { $w(popupMenu) entryconfigure "Center*" -state normal $w(viewMenu) entryconfigure "Center*" -state normal $w(markMenu) entryconfigure "Mark*" -state normal foreach buttonpref {im tx} { $w(centerDiffs_$buttonpref) configure -state normal $w(mergeChoice1_$buttonpref) configure -state normal $w(mergeChoice2_$buttonpref) configure -state normal $w(mergeChoice12_$buttonpref) configure -state normal $w(mergeChoice21_$buttonpref) configure -state normal } catch { $w(mergeChoiceLabel) configure -state normal } } else { foreach buttonpref {im tx} { $w(centerDiffs_$buttonpref) configure -state disabled $w(mergeChoice1_$buttonpref) configure -state disabled $w(mergeChoice2_$buttonpref) configure -state disabled $w(mergeChoice12_$buttonpref) configure -state disabled $w(mergeChoice21_$buttonpref) configure -state disabled } catch { $w(mergeChoiceLabel) configure -state disabled } $w(popupMenu) entryconfigure "Center*" -state disabled $w(viewMenu) entryconfigure "Center*" -state disabled $w(markMenu) entryconfigure "Mark*" -state disabled } # the mark clear button should only be enabled if there is # presently a mark at the current diff record set widget $w(toolbar).mark$g(pos) if {[winfo exists $widget]} { $w(markMenu) entryconfigure "Clear*" -state normal $w(markMenu) entryconfigure "Mark*" -state disabled foreach buttonpref {im tx} { $w(markClear_$buttonpref) configure -state normal $w(markSet_$buttonpref) configure -state disabled } } else { $w(markMenu) entryconfigure "Clear*" -state disabled $w(markMenu) entryconfigure "Mark*" -state normal foreach buttonpref {im tx} { $w(markClear_$buttonpref) configure -state disabled $w(markSet_$buttonpref) configure -state normal } } } ############################################################################### # Center the top line of the CDR in each window. ############################################################################### proc center {} { global g global w if {! [info exists g(scrdiff,$g(pos))]} {return} #scan $g(scrdiff,$g(pos)) "%s %d %d %d %d %s" dummy s1 e1 s2 e2 dt foreach {dummy s1 e1 s2 e2 dt} $g(scrdiff,$g(pos)) { } # Window requested height in pixels set opix [winfo reqheight $w(LeftText)] # Window requested lines set olin [$w(LeftText) cget -height] # Current window height in pixels set npix [winfo height $w(LeftText)] # Visible lines set winlines [expr {$npix * $olin / $opix}] # Lines in diff set diffsize [max [expr {$e1 - $s1 + 1}] [expr {$e2 - $s2 + 1}]] if {$diffsize < $winlines} { set h [expr {($winlines - $diffsize) / 2}] } else { set h 2 } set o [expr {$s1 - $h}] if {$o < 0} { set o 0 } set n [expr {$s2 - $h}] if {$n < 0} { set n 0 } $w(LeftText) mark set insert $s1.0 $w(RightText) mark set insert $s2.0 $w(LeftText) yview $o $w(RightText) yview $n if {$g(showmerge)} { merge-center } } ############################################################################### # Change the state on all of the diff-sensitive buttons. ############################################################################### proc buttons {{newstate "normal"}} { global w $w(combo) configure -state $newstate foreach buttonpref {im tx} { $w(prevDiff_$buttonpref) configure -state $newstate $w(nextDiff_$buttonpref) configure -state $newstate $w(firstDiff_$buttonpref) configure -state $newstate $w(lastDiff_$buttonpref) configure -state $newstate $w(centerDiffs_$buttonpref) configure -state $newstate } } ############################################################################### # Wipe the slate clean... ############################################################################### proc wipe {} { debug-info "wipe ()" global g set g(pos) 0 set g(count) 0 set g(diff) "" set g(currdiff) "" set g(delta,1) 0 set g(delta,2) 0 } ############################################################################### # Wipe all data and all windows ############################################################################### proc wipe-window {} { debug-info "wipe-window ()" global g global w wipe foreach mod {Left Right} { $w(${mod}Text) configure -state normal $w(${mod}Text) tag remove difftag 1.0 end $w(${mod}Text) tag remove currtag 1.0 end $w(${mod}Text) tag remove inlinetag 1.0 end $w(${mod}Text) delete 1.0 end $w(${mod}Info) configure -state normal $w(${mod}Info) delete 1.0 end $w(${mod}CB) configure -state normal $w(${mod}CB) delete 1.0 end } catch { $w(mergeText) configure -state normal $w(mergeText) delete 1.0 end eval $w(mergeText) tag delete [$w(mergeText) tag names] $w(mergeText) configure -state disabled } if {[string length $g(destroy)] > 0} { eval $g(destroy) set g(destroy) "" } $w(combo) list delete 0 end buttons disabled diffmark clearall } ############################################################################### # Mark difference regions and build up the combobox ############################################################################### proc mark-diffs {} { debug-info "mark-diffs ()" global g global w set numdiff [llength "$g(diff)"] set g(count) 0 # ain't this clever? We want to update the display as soon as # we've marked enough diffs to fill the display so the user will # have the impression we're fast. But, we don't want this # code to slow us down too much, so we'll put the # code in a variable and delete it when its no longer needed. set hack { # for now, just pick a number out of thin air. Ideally # we'd compute the number of lines that are visible and # use that, but I'm too lazy today... if {$g(count) > 25} { update idletasks set hack {} } } foreach d $g(diff) { set result [extract $d] if {$result != ""} { incr g(count) set g(merge$g(count)) 1 set g(pdiff,$g(count)) "$result" add-lines $g(count) $w(combo) list insert end [format "%-6d: %s" $g(count) $d] eval $hack } } remark-diffs return $g(count) } ############################################################################### # start a new diff from the popup dialog ############################################################################### proc do-new-diff {} { debug-info "do-new-diff ()" global g global finfo set g(mergefileset) 0 set g(mergefile) "" set finfo(pth,1) "" set finfo(pth,2) "" set finfo(tmp,1) 0 set finfo(tmp,2) 0 #foreach inf [lsort [array names finfo]] { debug-info " $inf: \ $finfo($inf)" } # Pop up the dialog to collect the args newDiffDialog # Put them together into a command if {[assemble-args] != 0} return #foreach inf [lsort [array names finfo]] { #debug-info " $inf: $finfo($inf)" #} set g(disableSyncing) 1 ;# turn off syncing until things settle down # remove all evidence of previous diff #wipe-window #update idletasks watch-cursor # do the diff do-diff #debug-info " move first 1 1 from do-new-diff" move first 1 1 #debug-info " ...restore-cursor from do-new-diff" restore-cursor #debug-info " ...update-display from do-new-diff" update-display catch {unset g(disableSyncing)} } ############################################################################### # Remark difference regions... ############################################################################### proc remark-diffs {} { debug-info "remark-diffs ()" global g global w global opts global pref # delete all known tags. foreach window [list $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB)] { eval $window tag delete [$window tag names] } if {[winfo exists .merge]} { eval $window tag delete [$w(mergeText) tag names] } # reconfigure all the tags based on the current options # first, the common tags: foreach tag {difftag currtag inlinetag deltag instag chgtag overlaptag} { foreach win [list $w(LeftText) $w(LeftInfo) $w(LeftCB) $w(RightText) \ $w(RightInfo) $w(RightCB)] { if {[catch "$win tag configure $tag $opts($tag)"]} { do-error "Invalid settings for \"$pref($tag)\": \ \n\n'$opts($tag)' is not a valid option string." eval "$win tag configure $tag $opts($tag)" return } } } # next, changebar-specific tags foreach widget [list $w(LeftCB) $w(RightCB)] { eval $widget tag configure + $opts(+) eval $widget tag configure - $opts(-) eval $widget tag configure ! $opts(!) eval $widget tag configure ? $opts(?) } # ... and the merge text window if {[winfo exists .merge]} { foreach tag {difftag currtag} { eval $w(mergeText) tag configure $tag $opts($tag) } } # now, reapply the tags to all the diff regions for {set i 1} {$i <= $g(count)} {incr i} { set-tag $i difftag # add the inline annotation for {set j 0} {$j < $g(scrinline,$i)} {incr j} { foreach {side line startcol endcol} $g(scrinline,$i,$j) { } if {$side == "l"} { add-inline-tag $w(LeftText) inlinetag $line $startcol $endcol } else { add-inline-tag $w(RightText) inlinetag $line $startcol $endcol } } } # finally, reset the current diff set-tag $g(pos) currtag "" 0 } ############################################################################### # Put up some informational text. ############################################################################### proc show-info {message} { global g set g(statusInfo) $message update idletasks } ############################################################################### # Trace output, enabled by a global variable ############################################################################### proc debug-info {message} { global g if {$g(debug)} { puts "$message" } } ############################################################################### # Compute differences (start over, basically). ############################################################################### proc rediff {} { debug-info "rediff ()" global g global opts global finfo global w buttons disabled # Read the files into their respective widgets & add line numbers. foreach mod {1 2} { if {$mod == 1} { set text $w(LeftText) } else { set text $w(RightText) } show-info "reading $finfo(pth,$mod)..." if {[catch {set hndl [open "$finfo(pth,$mod)" r]}]} { fatal-error "Failed to open file: $finfo(pth,$mod)" } $text insert 1.0 [read $hndl] close $hndl } # Diff the two files and store the summary lines into 'g(diff)'. if {$opts(ignoreblanks) == 1} { set diffcmd "$opts(diffcmd) $opts(ignoreblanksopt) {$finfo(pth,1)} \ {$finfo(pth,2)}" } else { set diffcmd "$opts(diffcmd) {$finfo(pth,1)} {$finfo(pth,2)}" } show-info "Executing \"$diffcmd\"" set result [run-command "exec $diffcmd"] set stdout [lindex $result 0] set stderr [lindex $result 1] set exitcode [lindex $result 2] set g(returnValue) $exitcode # The exit code is 0 if there are no differences and 1 if there # are differences. Any other exit code means trouble if {$exitcode < 0 || $exitcode > 1 || $stderr != ""} { do-error "diff failed:\n$stderr" } set g(diff) {} set lines [split $stdout "\n"] # If there is no output and we got this far the files are equal, # otherwise check if the first line begins with a line number. If # not there was trouble and we abort. For instance, using a binary # file results in the message "Binary files ..." etc on stdout, # exit code 1. The message may wary depending on locale. if {$lines != "" && [string match {[0-9]*} $lines] != 1} { fatal-error "diff failed:\n$stdout" } # Collect all lines containing line numbers foreach line $lines { if {[string match {[0-9]*} $line]} { lappend g(diff) $line } } if {$g(ancfileset)} { # 3-way merge - compare 1st file (left: diff3l) with ancestor if {$opts(ignoreblanks) == 1} { set diffcmd "$opts(diffcmd) $opts(ignoreblanksopt) \ {$finfo(pth,1)} {$g(ancfile)}" } else { set diffcmd "$opts(diffcmd) {$finfo(pth,1)} {$g(ancfile)}" } show-info "Executing \"$diffcmd\"" set result [run-command "exec $diffcmd"] set stdout [lindex $result 0] set stderr [lindex $result 1] set exitcode [lindex $result 2] if {$exitcode < 0 || $exitcode > 1 || $stderr != ""} { fatal-error "diff3 failed:\n$stderr" } set lines [split $stdout "\n"] set g(diff3l) {} foreach line $lines { if {[string match {[0-9]*} $line]} { if {[regexp {^[0-9]*,} $line match]} { regexp {([0-9]*),([0-9]*).*} $line matchvar s1 s2 } else { regexp {([0-9]*).*} $line matchvar s1 set s2 $s1 } lappend g(diff3l) $s1 for {set i $s1} {$i <= $s2} {incr i} { set g(diff3l$i) 1 } } } # 3-way merge - compare 2nd file (right: diff3r) with ancestor if {$opts(ignoreblanks) == 1} { set diffcmd "$opts(diffcmd) $opts(ignoreblanksopt) \ {$finfo(pth,2)} {$g(ancfile)}" } else { set diffcmd "$opts(diffcmd) {$finfo(pth,2)} {$g(ancfile)}" } show-info "Executing \"$diffcmd\"" set result [run-command "exec $diffcmd"] set stdout [lindex $result 0] set stderr [lindex $result 1] set exitcode [lindex $result 2] if {$exitcode < 0 || $exitcode > 1 || $stderr != ""} { fatal-error "diff3 failed:\n$stderr" } set lines [split $stdout "\n"] set g(diff3r) {} foreach line $lines { if {[string match {[0-9]*} $line]} { if {[regexp {^[0-9]*,} $line match]} { regexp {([0-9]*),([0-9]*).*} $line matchvar s1 s2 } else { regexp {([0-9]*).*} $line matchvar s1 set s2 $s1 } lappend g(diff3r) $s1 for {set i $s1} {$i <= $s2} {incr i} { set g(diff3r$i) 1 } } } } # Mark up the two text widgets and go to the first diff (if there is one). draw-line-numbers show-info "Marking differences..." $w(LeftInfo) configure -state normal $w(RightInfo) configure -state normal $w(LeftCB) configure -state normal $w(RightCB) configure -state normal if {[mark-diffs]} { set g(pos) 1 move 1 0 1 buttons normal } else { after idle {show-info "Files are identical"} buttons disabled } # Prevent tampering in the line number widgets. The text # widgets are already taken care of $w(LeftInfo) configure -state disabled $w(RightInfo) configure -state disabled $w(LeftCB) configure -state disabled $w(RightCB) configure -state disabled } ############################################################################### # Set the X cursor to "watch" for a window and all of its descendants. ############################################################################### proc watch-cursor {args} { #debug-info "watch-cursor ($args)" global current global w . configure -cursor watch $w(LeftText) configure -cursor watch $w(RightText) configure -cursor watch $w(combo) configure -cursor watch update idletasks } ############################################################################### # Restore the X cursor for a window and all of its descendants. ############################################################################### proc restore-cursor {args} { #debug-info "restore-cursor ($args)" global current global w . configure -cursor {} $w(LeftText) configure -cursor {} $w(RightText) configure -cursor {} $w(combo) configure -cursor {} show-info "" update idletasks } ############################################################################### # Check if error was thrown by us or unexpected ############################################################################### proc check-error {result output} { global g errorInfo if {$result && $output != "Fatal"} { error $result $errorInfo } } ############################################################################### # redo the current diff. Attempt to return to the same diff region, # numerically speaking. ############################################################################### proc recompute-diff {} { debug-info "recompute-diff ()" global g set current $g(pos) debug-info "current position $g(pos)" do-diff move $current 0 1 center } ############################################################################### # Flash the "rediff" button and then kick off a rediff. ############################################################################### proc do-diff {} { debug-info "do-diff ()" global g finfo map errorInfo global opts wipe-window update idletasks set result [catch { if {$g(mapheight)} { ## FIXME this could better a catch catch {$map blank} } rediff merge-read-file merge-add-marks # If a map exists, recreate it if {$opts(showmap)} { set g(mapheight) -1 map-resize } } output] #debug-info " result: $result outptut: $output" check-error $result $output if {$g(mergefileset)} { do-show-merge 1 } } ############################################################################### # Get things going... ############################################################################### proc main {} { debug-info "main" global w global g errorInfo global startupError global opts global waitvar global tk_version wm withdraw . wm protocol . WM_DELETE_WINDOW do-exit wm title . "$g(name) $g(version)" if {![catch {set windowingsystem [tk windowingsystem]}]} { if {$windowingsystem == "x11"} { # All this nonsense is necessary to use an icon bitmap that's # not in a separate file. toplevel .icw if {[string first "color" [winfo visual .]] >= 0} { label .icw.l -image deltaGif } else { label .icw.l -image delta48 } pack .icw.l bind .icw "wm deiconify ." wm iconwindow . .icw } } if {$g(windowingSystem) == "x11"} { if {[get_gtk_params]} { debug-info "gtk" } elseif {[get_cde_params]} { debug-info "cde" } else { debug-info "x11 fallback" set hlbg "#4a6984" set hlfg "#ffffff" #set w(selcolor) $hlbg if {$tk_version >= 8.5} { option add *Menu.selectColor $w(foreground) option add *Checkbutton.selectColor "" option add *Radiobutton.selectColor "" } else { option add *selectColor $hlbg } } } if {$g(windowingSystem) == "aqua"} { get_aqua_params } set g(started) 1 wipe set cmd_empty [commandline] if {! $cmd_empty} { assemble-args } else { newDiffDialog # If they cancel the dialog before doing any diffs, exit if {[assemble-args] != 0} { return } set cmd_empty 0 } create-display update do-show-linenumbers do-show-map # evaluate any custom code the user has if {[info exists opts(customCode)]} { if {[catch [list uplevel \#0 $opts(customCode)] error]} { set startupError "Error in custom code: \n\n$error" } else { update } } if {$cmd_empty} { do-new-diff } move first 1 1 # this forces all of the various scrolling windows (line numbers, # change bars, etc) to get in sync. set yview [$w(RightText) yview] vscroll-sync [list $w(RightInfo) $w(LeftInfo)] 2 [lindex $yview 0] \ [lindex $yview 1] wm deiconify . update idletasks if {[info exists startupError]} { tk_messageBox -icon warning -type ok -title "$g(name) - Error in \ Startup File" -message $startupError } } ############################################################################### # Erase tmp files (if necessary) and destroy the application. ############################################################################### proc del-tmp {} { global g foreach f $g(tempfiles) { file delete $f } } ############################################################################### # Put up a window with formatted text ############################################################################### proc do-text-info {w title text} { global g catch "destroy $w" toplevel $w wm group $w . wm transient $w . wm title $w "$g(name) Help - $title" if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w } set width 64 set height 32 frame $w.f -bd 2 -relief sunken pack $w.f -side top -fill both -expand y text $w.f.title -highlightthickness 0 -bd 0 -height 2 -wrap word \ -width 50 -background white -foreground black text $w.f.text -wrap word -setgrid true -padx 20 -highlightthickness 0 \ -bd 0 -width $width -height $height -yscroll [list $w.f.vsb set] \ -background white -foreground black scrollbar $w.f.vsb -borderwidth 1 -command [list $w.f.text yview] \ -orient vertical pack $w.f.vsb -side right -fill y -expand n pack $w.f.title -side top -fill x -expand n pack $w.f.text -side left -fill both -expand y focus $w.f.text button $w.done -text Dismiss -command "destroy $w" pack $w.done -side right -fill none -pady 5 -padx 5 put-text $w.f.title "$title" put-text $w.f.text $text $w.f.text configure -state disabled wm geometry $w ${width}x${height} update idletasks raise $w } ############################################################################### # centers window w over parent ############################################################################### proc centerWindow {w {size {}}} { update set parent . if {[llength $size] > 0} { set wWidth [lindex $size 0] set wHeight [lindex $size 1] } else { set wWidth [winfo reqwidth $w] set wHeight [winfo reqheight $w] } set pWidth [winfo reqwidth $parent] set pHeight [winfo reqheight $parent] set pX [winfo rootx $parent] set pY [winfo rooty $parent] set centerX [expr {$pX +($pWidth / 2)}] set centerY [expr {$pY +($pHeight / 2)}] set x [expr {$centerX -($wWidth / 2)}] set y [expr {$centerY -($wHeight / 2)}] if {[llength $size] > 0} { wm geometry $w "=${wWidth}x${wHeight}+${x}+${y}" } else { wm geometry $w "=+${x}+${y}" } update } ############################################################################### # The "New Diff" dialog # In order to be able to enter only one filename if it's a revision-controlled # file, the dialog now collects the arguments and sends them through the # command line parser. ############################################################################### proc newDiffDialog {} { debug-info "newDiffDialog" global g w global finfo set g(mergefile) "" set g(mergefileset) 0 set waitvar {} set w(newDiffPopup) .newDiffPopup if {[winfo exists $w(newDiffPopup)]} { debug-info " $w(newDiffPopup) already exists, just centering" } else { debug-info " creating $w(newDiffPopup)" toplevel $w(newDiffPopup) wm group $w(newDiffPopup) . # Won't start as the first window on Windows if it's transient if {[winfo exists .client]} { wm transient $w(newDiffPopup) . } wm title $w(newDiffPopup) "New Diff" if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w(newDiffPopup) } wm protocol $w(newDiffPopup) WM_DELETE_WINDOW {wm withdraw \ $w(newDiffPopup)} wm withdraw $w(newDiffPopup) set simple [frame $w(newDiffPopup).simple -borderwidth 2 -relief groove] label $simple.l1 -text "File 1:" label $simple.l2 -text "File 2:" entry $simple.e1 -textvariable finfo(f,1) entry $simple.e2 -textvariable finfo(f,2) label $simple.lr1 -text "-r" label $simple.lr2 -text "-r" entry $simple.er1 -textvariable finfo(revs,1) entry $simple.er2 -textvariable finfo(revs,2) set w(newDiffPopup,entry1) $simple.e1 set w(newDiffPopup,entry2) $simple.e2 # we want these buttons to be the same height as # the entry, so we'll try to force the issue... button $simple.b1 -borderwidth 1 -highlightthickness 0 \ -text "Browse..." -command [list newDiffBrowse "File 1" $simple.e1] button $simple.b2 -borderwidth 1 -highlightthickness 0 \ -text "Browse..." -command [list newDiffBrowse "File 2" $simple.e2] # we'll use the grid geometry manager to get things lined up right... grid $simple.l1 -row 0 -column 0 -sticky e grid $simple.e1 -row 0 -column 1 -columnspan 4 -sticky nsew -pady 4 grid $simple.b1 -row 0 -column 5 -sticky nsew -padx 4 -pady 4 grid $simple.lr1 -row 1 -column 1 grid $simple.er1 -row 1 -column 2 grid $simple.lr2 -row 1 -column 3 grid $simple.er2 -row 1 -column 4 grid $simple.l2 -row 2 -column 0 -sticky e grid $simple.e2 -row 2 -column 1 -columnspan 4 -sticky nsew -pady 4 grid $simple.b2 -row 2 -column 5 -sticky nsew -padx 4 -pady 4 grid columnconfigure $simple 0 -weight 0 set options [frame $w(newDiffPopup).options -borderwidth 2 \ -relief groove] button $options.more -text "More" -command open-more-options label $options.ml -text "Merge Output" entry $options.me -textvariable g(mergefile) label $options.al -text "Ancestor" entry $options.ae -textvariable g(ancfile) label $options.l1l -text "Label for File 1" entry $options.l1e -textvariable finfo(userlbl,1) label $options.l2l -text "Label for File 2" entry $options.l2e -textvariable finfo(userlbl,2) grid $options.more -column 0 -row 0 -sticky nw grid columnconfigure $options -0 -weight 0 # here are the buttons for this dialog... set commands [frame $w(newDiffPopup).buttons] button $commands.ok -text "Ok" -width 5 -default active -command { if {$g(mergefile) == ""} { set g(mergefileset) 0 } else { set g(mergefileset) 1 } if {$g(ancfile) == ""} { set g(ancfileset) 0 } else { set g(ancfileset) 1 } set waitvar 1 } button $commands.cancel -text "Cancel" -width 5 -default normal \ -command { if {! [winfo exists .client]} {do-exit} wm withdraw $w(newDiffPopup); set waitvar 0 } pack $commands.ok $commands.cancel -side left -fill none -expand y \ -pady 4 catch {$commands.ok -default 1} # pack this crud in... pack $commands -side bottom -fill x -expand n pack $simple -side top -fill both -ipady 20 -ipadx 20 -padx 5 -pady 5 pack $options -side top -fill both -ipady 5 -ipadx 5 -padx 5 -pady 5 bind $w(newDiffPopup) [list $commands.ok invoke] bind $w(newDiffPopup) [list $commands.cancel invoke] } if {[winfo exists .client]} { centerWindow $w(newDiffPopup) } else { update } wm deiconify $w(newDiffPopup) raise $w(newDiffPopup) focus $w(newDiffPopup,entry1) tkwait variable waitvar wm withdraw $w(newDiffPopup) } proc open-more-options {} { global w grid $w(newDiffPopup).options.ml -row 0 -column 1 -sticky e grid $w(newDiffPopup).options.me -row 0 -column 2 -sticky nsew -pady 4 grid $w(newDiffPopup).options.al -row 1 -column 1 -sticky e grid $w(newDiffPopup).options.ae -row 1 -column 2 -sticky nsew -pady 4 grid $w(newDiffPopup).options.l1l -row 2 -column 1 -sticky e grid $w(newDiffPopup).options.l1e -row 2 -column 2 -sticky nsew -pady 4 grid $w(newDiffPopup).options.l2l -row 3 -column 1 -sticky e grid $w(newDiffPopup).options.l2e -row 3 -column 2 -sticky nsew -pady 4 $w(newDiffPopup).options.more configure -text "Less" \ -command close-more-options set x [winfo width $w(newDiffPopup)] set y [winfo height $w(newDiffPopup)] set yi [winfo reqheight $w(newDiffPopup).options] set newy [expr $y + $yi] if {[winfo exists .client]} { centerWindow $w(newDiffPopup) } else { update } } proc close-more-options {} { global w global finfo grid remove $w(newDiffPopup).options.ml grid remove $w(newDiffPopup).options.me grid remove $w(newDiffPopup).options.al grid remove $w(newDiffPopup).options.ae grid remove $w(newDiffPopup).options.l1l grid remove $w(newDiffPopup).options.l1e grid remove $w(newDiffPopup).options.l2l grid remove $w(newDiffPopup).options.l2e set g(mergefileset) "" set g(conflictset) "" set g(ancfileset) "" set g(ancfile) "" set finfo(userlbl,1) "" set finfo(userlbl,2) "" $w(newDiffPopup).options.more configure -text "More" \ -command open-more-options } ############################################################################### # File browser for the "New Diff" dialog ############################################################################### proc newDiffBrowse {title widget} { global w global opts debug-info "newDiffBrowse($title $widget)" set n [string index $widget end] set widgroot [string range $widget 0 end-1] if {$n == 1} { set other 2 } elseif { $n == 2} { set other 1 } set entrystuff [$widget get] debug-info "$n: entrystuff $entrystuff" if {$entrystuff != ""} { set initialdir [file dirname $entrystuff] } else { set otherentrystuff [${widgroot}$other get] if {$otherentrystuff != ""} { set initialdir [file dirname $otherentrystuff] } else { set initialdir [pwd] } } debug-info "initialdir $initialdir" set initialfile [file tail $entrystuff] set filename [tk_getOpenFile -title $title \ -filetypes $opts(filetypes) \ -initialfile $initialfile \ -initialdir $initialdir] if {[string length $filename] > 0} { $widget delete 0 end $widget insert 0 $filename $widget selection range 0 end $widget xview end focus $widget return $filename } else { after idle {raise $w(newDiffPopup)} return {} } } ############################################################################### # all the code to handle the report writing dialog. ############################################################################### proc write-report {command args} { global g global w global report global finfo set w(reportPopup) .reportPopup switch -- $command { popup { if {![winfo exists $w(reportPopup)]} { write-report build } set report(filename) [file join [pwd] $report(filename)] write-report update centerWindow $w(reportPopup) wm deiconify $w(reportPopup) raise $w(reportPopup) } cancel { wm withdraw $w(reportPopup) } update { set stateLeft "disabled" set stateRight "disabled" if {$report(doSideLeft)} { set stateLeft "normal" } if {$report(doSideRight)} { set stateRight "normal" } $w(reportLinenumLeft) configure -state $stateLeft $w(reportCMLeft) configure -state $stateLeft $w(reportTextLeft) configure -state $stateLeft $w(reportLinenumRight) configure -state $stateRight $w(reportCMRight) configure -state $stateRight $w(reportTextRight) configure -state $stateRight } save { set leftLines [lindex [split [$w(LeftText) index end-1lines] .] 0] set rightLines [lindex [split [$w(RightText) index end-1lines] .] 0] # number of lines of the largest window... set maxlines [max $leftLines $rightLines] # probably ought to catch this, in case it fails. Maybe later... set handle [open $report(filename) w] puts $handle "$g(name) $g(version) report" # write the file names if {$report(doSideLeft) == 1 && $report(doSideRight) == 1} { puts $handle "\nFile A: $finfo(lbl,1)\nFile B: $finfo(lbl,2)\n" } elseif {$report(doSideLeft) == 1} { puts $handle "\nFile: $finfo(lbl,1)" } else { puts $handle "\nFile: $finfo(lbl,2)" } puts $handle "number of diffs: $g(count)" set acount [set ccount [set dcount 0]] for {set i 1} {$i <= $g(count)} {incr i} { foreach {line s1 e1 s2 e2 type} $g(scrdiff,$i) { } switch -- $type { "d" { incr dcount } "a" { incr acount } "c" { incr ccount } } } puts $handle [format " %6d regions were deleted" $dcount] puts $handle [format " %6d regions were added" $acount] puts $handle [format " %6d regions were changed" $ccount] puts $handle "\n" for {set i 1} {$i <= $maxlines} {incr i} { set out(Left) [set out(Right) ""] foreach side {Left Right} { if {$side == "Left" && $i > $leftLines} break if {$side == "Right" && $i > $rightLines} break if {$report(doLineNumbers$side)} { set widget $w(${side}Info) set number [string trimright [$widget get "$i.0" \ "$i.0 lineend"]] append out($side) [format "%6s " $number] } if {$report(doChangeMarkers$side)} { set widget $w(${side}CB) set data [$widget get "$i.0" "$i.1"] append out($side) "$data " } if {$report(doText$side)} { set widget $w(${side}Text) append out($side) [string trimright [$widget get \ "$i.0" "$i.0 lineend"]] } } if {$report(doSideLeft) == 1 && $report(doSideRight) == 1} { set output [format "%-90s%-90s" $out(Left) $out(Right)] } elseif {$report(doSideRight) == 1} { set output $out(Right) } elseif {$report(doSideLeft) == 1} { set output $out(Left) } else { # what a wasted effort! set output "" } puts $handle [string trimright $output] } close $handle wm withdraw $w(reportPopup) } browse { set path [tk_getSaveFile \ -initialfile $report(filename)] if {[string length $path] > 0} { set report(filename) $path } } build { catch {destroy $w(reportPopup)} toplevel $w(reportPopup) wm group $w(reportPopup) . wm transient $w(reportPopup) . wm title $w(reportPopup) "$g(name) - Generate Report" wm protocol $w(reportPopup) WM_DELETE_WINDOW [list write-report \ cancel] wm withdraw $w(reportPopup) if {$g(windowingSystem) == "aqua"} { setAquaDialogStyle $w(reportPopup) } set cf [frame $w(reportPopup).clientFrame -bd 2 -relief groove] set bf [frame $w(reportPopup).buttonFrame -bd 0] pack $cf -side top -fill both -expand y -padx 5 -pady 5 pack $bf -side bottom -fill x -expand n # buttons... set w(reportSave) $bf.save set w(reportCancel) $bf.cancel button $w(reportSave) -text "Save" -underline 0 -command \ [list write-report save] -width 6 button $w(reportCancel) -text "Cancel" -underline 0 \ -command [list write-report cancel] -width 6 pack $w(reportCancel) -side right -pady 5 -padx 5 pack $w(reportSave) -side right -pady 5 # client area. set col(Left) 0 set col(Right) 1 foreach side [list Left Right] { set choose [checkbutton $cf.choose$side] set linenum [checkbutton $cf.linenum$side] set cm [checkbutton $cf.changemarkers$side] set text [checkbutton $cf.text$side] $choose configure -text "$side Side" \ -variable report(doSide$side) -command [list write-report \ update] $linenum configure -text "Line Numbers" \ -variable report(doLineNumbers$side) $cm configure -text "Change Markers" \ -variable report(doChangeMarkers$side) $text configure -text "Text" -variable report(doText$side) grid $choose -row 0 -column $col($side) -sticky w grid $linenum -row 1 -column $col($side) -sticky w -padx 10 grid $cm -row 2 -column $col($side) -sticky w -padx 10 grid $text -row 3 -column $col($side) -sticky w -padx 10 # save the widget paths for later use... set w(reportChoose$side) $choose set w(reportLinenum$side) $linenum set w(reportCM$side) $cm set w(reportText$side) $text } # the entry, label and button for the filename will get # stuffed into a frame for convenience... frame $cf.fileFrame -bd 0 grid $cf.fileFrame -row 4 -columnspan 2 -sticky ew -padx 5 label $cf.fileFrame.l -text "File:" entry $cf.fileFrame.e -textvariable report(filename) -width 30 button $cf.fileFrame.b -text "Browse..." -pady 0 \ -highlightthickness 0 -borderwidth 1 -command \ [list write-report browse] pack $cf.fileFrame.l -side left -pady 4 pack $cf.fileFrame.b -side right -pady 4 -padx 2 pack $cf.fileFrame.e -side left -fill x -expand y -pady 4 grid rowconfigure $cf 0 -weight 0 grid rowconfigure $cf 1 -weight 0 grid rowconfigure $cf 2 -weight 0 grid rowconfigure $cf 3 -weight 0 grid columnconfigure $cf 0 -weight 1 grid columnconfigure $cf 1 -weight 1 # make sure the widgets are in the proper state write-report update } } } ############################################################################### # Report the version of wish ############################################################################### proc about_wish {} { global tk_patchLevel set version $tk_patchLevel set whichwish [info nameofexecutable] set about_string "$whichwish\n\n" append about_string "Tk version $version" tk_messageBox -title "About Wish" \ -message $about_string \ -parent . \ -type ok } ############################################################################### # Report the version of diff ############################################################################### proc about_diff {} { set whichdiff [auto_execok diff] if {[llength $whichdiff]} { set whichdiff [join $whichdiff] set commandline "diff -v" catch {eval "exec $commandline"} output set message "$whichdiff\n$output" } else { set message "diff was not found in your path!" } tk_messageBox -title "About Diff" \ -message $message \ -parent . \ -type ok } ############################################################################### # Throw up an "about" window. ############################################################################### proc do-about {} { global g set title "About $g(name)" set text { $g(name) $g(version) $g(name) is a Tcl/Tk front-end to diff for Unix and \ Windows, and is Copyright (C) 1994-2005 by John M. Klassa. Many of the toolbar icons were created by Dean S. Jones and used with his \ permission. The icons have the following copyright: Copyright(C) 1998 by Dean S. Jones dean@gallant.com http://www.gallant.com/icons.htm http://www.javalobby.org/jfa/projects/icons/ This program is free software; you can redistribute it and/or modify it \ under the terms of the GNU General Public License as published by the \ Free Software Foundation; either version 2 of the License, or (at your \ option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT \ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License \ for more details. You should have received a copy of the GNU General Public License along with \ this program; if not, write to the Free Software Foundation, Inc., 59 \ Temple Place, Suite 330, Boston, MA 02111-1307 USA } set text [subst -nobackslashes -nocommands $text] do-text-info .about $title $text } ############################################################################### # Throw up a "command line usage" window. ############################################################################### proc do-usage {mode} { debug-info "do-usage ($mode)" global g set usage { $g(name) may be started in any of the following ways: Interactive selection of files to compare: tkdiff Plain files: tkdiff FILE1 FILE2 Plain file with conflict markers: tkdiff -conflict FILE Source control (AccuRev, BitKeeper, ClearCase, CVS, Git, Mercurial, \ Perforce, PVCS, RCS, SCCS, Subversion) tkdiff FILE tkdiff -rREV FILE tkdiff -rREV1 -rREV2 FILE tkdiff OLD-URL[@OLDREV] NEW-URL[@NEWREV] (Subversion) Additional optional parameters: -a ANCESTORFILE -o MERGEOUTPUTFILE -L LEFT_FILE_LABEL [-L RIGHT_FILE_LABEL] -d debug } set usage [subst -nobackslashes -nocommands $usage] set text { $g(name) detects and supports RCS, CVS, Subversion and SCCS by looking for a \ directory with the same name. It detects and supports PVCS by looking \ for a vcs.cfg file. It detects and supports AccuRev, Perforce and \ ClearCase by looking for the environment variables named ACCUREV_BIN, \ P4CLIENT, and CLEARCASE_ROOT respectively. It detects Git by looking \ for a .git directory, but will only work when started in Git work-tree. \ Mercurial is supported by looking for a directory named ".hg" in the current \ directory or any of its ancestor directories. In the first form, tkdiff will present a dialog to allow you to choose the \ files to diff interactively. At present this dialog only supports a \ diff between two files that already exist. There is no way to do a diff \ against a file under souce code control (unless the previous versions \ can be found on disk via a standard file selection dialog). In the second form, at least one of the arguments must be the name of a plain \ text file. Symbolic links are acceptable, but at least one of the \ filename arguments must point to a real file rather than to a directory. In the remaining forms, REV (or REV1 and \ REV2) must be a valid revision number for FILE. \ When a source control system (RCS, CVS, etc.) is detected (see above) \ but no revision number is specified, FILE is compared with \ the revision most recently checked in. Git REV (see man git-rev-parse): -r FILE [compare with HEAD] -r HEAD FILE [compare with HEAD] -r HEAD^ FILE [compare with parent of HEAD] -r HEAD~5 FILE [compare with 5th parent of HEAD] -r HEAD~20 -r HEAD^ FILE [compare 20th parent and parent of HEAD] -r 29329e FILE [compare with commit 29329e (full/partial SHA1)] -r v1.2.3 FILE [compare with tag (UNTESTED)] To merge a file with conflict markers generated by "merge", \ "cvs", or "vmrg", use \ "tkdiff -conflict FILE". The file is split into two temporary \ files which you can merge as usual (see below). If the merge output filename is not specified, tkdiff will present a dialog \ to allow you to choose the name of the merge output file. Note further that anything with a leading dash that isn\'t recognized as a \ $g(name) option is passed through to diff. This permits you to \ temporarily alter the way diff is called, without resorting to a change \ in your preferences file. } if {$mode == "cline"} { puts $usage exit 0 } set text [subst -nobackslashes -nocommands $text] append usage $text do-text-info .usage "$g(name) Usage" $usage } ############################################################################### # Throw up a help window. ############################################################################### proc do-help {} { global g set title "How to use the $g(name) GUI" set text { Layout The top row contains the File, Edit, View, Mark, Merge and Help menus. The \ second row contains the labels which identify the contents of each text \ window. Below that is a toolbar which contains\ navigation and merge selection tools. The left-most text widget displays the contents of FILE1, the most \ recently checked-in revision, REV or REV1, \ respectively (as per the startup options described in\ the "On Command Line" help). The right-most widget displays the \ contents of FILE2, FILE or REV2, \ respectively. Clicking the right mouse button over either of\ these windows will give you a context sensitive menu with actions that \ will act on the window you clicked over. For example, if you click \ right over the right hand window and select\ "Edit", the file displayed on the right hand side will be loaded into a \ text editor. At the bottom of the display is a two line window called the \ "Line Comparison" window. This will show the "current line" from the \ left and right windows, one on top of the other. The "current line"\ is defined by the line that has the blinking insertion cursor, which \ can be set by merely clicking on any line in the display. This window \ may be hidden if the View menu item\ Show Line Comparison is deselected. All difference regions (DRs) are highlighted to set them apart from the \ surrounding text. The current difference region, or \ CDR, is further set apart so that it can be\ correlated to its partner in the other text widget (that is, the CDR on \ the left matches the CDR on the right). Changing the CDR The CDR can be changed in a sequential manner by means of the Next \ and Previous buttons. The First and \ Last buttons allow you to quickly navigate to the\ first or last CDR, respectively. For random access to the DRs, use the \ dropdown listbox in the toolbar or the diff map, described below. By clicking right over a window and using the popup menu you can select \ Find Nearest Diff to find the diff record nearest the point \ where you clicked. You may also select any highlighted diff region as the current diff region by \ double-clicking on it. Operations 1. From the File menu: The New... button displays a dialog where you may choose two files \ to compare. Selecting "Ok" from the dialog will diff the two files. The \ Recompute Diffs button recomputes the\ differences between the two files whose names appear at the top of the \ $g(name) window. The Write Report... lets you \ create a report file that contains the information\ visible in the windows. Lastly, the Exit button terminates \ $g(name). 2. From the Edit menu: Copy copies the currently selected text to the system clipboard. \ Find pops up a dialog to let you search either text window \ for a specified text string. Edit File 1 and Edit File \ 2 launch an editor on the files displayed in the left- and \ right-hand panes. Preferences pops up a dialog box from \ which display (and other) options can be changed and saved. 3. From the View menu: Show Line Numbers toggles the display of line numbers in the text \ widgets. If Synchronize Scrollbars is on, the left and right \ text widgets are synchronized i.e. scrolling one\ of the windows scrolls the other. If Auto Center is on, \ pressing the Next or Prev buttons centers the new CDR automatically. \ Show Diff Map toggles the display of the diff\ map (see below) on or off. Show Merge Preview shows or hides \ the merge preview (see below). Show Line Comparison toggles \ the display of the "line comparison" window at\ the bottom of the display. 4. From the Mark menu: The Mark Current Diff creates a new toolbar button that will jump \ to the current diff region. The Clear Current Diff Mark will \ remove the toolbar mark button associated with\ the current diff region, if one exists. 5. From the Merge menu: The Show Merge Window button pops up a window with the current \ merged version of the two files. The Write Merge File button \ will allow you to save the contents of that window\ to a file. 6. From the Help menu: The About $g(name) button displays copyright and author \ information. The On GUI button generates this window. The \ On Command Line button displays help on the\ $g(name) command line options. The On Preferences button \ displays help on the user-settable preferences. 7. From the toolbar: The first tool is a dropdown list of all of the differences in a standard \ diff-type format. You may use this list to go directly to any diff \ record. The Next and Previous\ buttons take you to the "next" and "previous" DR, respectively. The \ First and Last buttons take you to the \ "first" and "last" DR. The Center button centers the\ CDRs in their respective text windows. You can set Auto \ Center in Preferences to do this automatically for you \ as you navigate through the diff records. Keyboard Navigation When a text widget has the focus, you may use the following shortcut keys: f First diff c Center current diff l Last diff n Next diff p Previous diff 1 Merge Choice 1 2 Merge Choice 2 The cursor, Home, End, PageUp and PageDown keys work as expected, adjusting \ the view in whichever text window has the focus. Note that if \ Synchronize Scrollbars is set in\ Preferences, both windows will scroll at the same time. Scrolling To scroll the text widgets independently, make sure Synchronize \ Scrollbars in Preferences is off. If it is on, \ scrolling any text widget scrolls all others. Scrolling does not\ change the current diff record (CDR). Diff Marks You can set "markers" at specific diff regions for easier navigation. To do \ this, click on the Set Mark button. It will create a new \ toolbar button that will jump back to this diff\ region. To clear a diff mark, go to that diff record and click on the \ Clear Mark button. Diff Map The diff map is a map of all the diff regions. It is shown in the middle of \ the main window if "Diff Map" on the View menu is on. The map is a \ miniature of the file's diff regions from top to\ bottom. Each diff region is rendered as a patch of color, Delete as \ red, Insert as green and Change as blue. In the case of a 3-way merge, \ overlap regions are marked in yellow. The height of each patch \ corresponds to the relative size of the diff region. A\ thumb lets you interact with the map as if it were a scrollbar. All diff regions are drawn on the map even if too small to be visible. For \ large files with small diff regions, this may result in patches \ overwriting each other. Merging To merge the two files, go through the difference regions (via "Next", \ "Prev" or whatever other means you prefer) and select "Left" or \ "Right" (next to the "Merge Choice:" label) for each. Selecting\ "Left" means that the the left-most file's version of the difference \ will be used in creating the final result; choosing "Right" means that \ the right-most file's difference will be used. Each\ choice is recorded, and can be changed arbitrarily many times. To \ commit the final, merged result to disk, choose "Write Merge File..." \ from the Merge menu. Merge Preview To see a preview of the file that would be written by "Write Merge File...", \ select "Show Merge Window" in the View menu. A separate window is shown \ containing the preview. It is updated as you\ change merge choices. It is synchronized with the other text widgets if \ "Synchronize Scrollbars" is on. Author John M. Klassa Comments Questions and comments should be sent to the TkDiff mailing list at \ tkdiff-discuss@lists.sourceforge.net. } set text [subst -nobackslashes -nocommands $text] do-text-info .help $title $text } ###################################################################### # display help on the preferences ###################################################################### proc do-help-preferences {} { global g global pref customize-initLabels set title "$g(name) Preferences" set text { Overview Preferences are stored in a file in your home directory (identified by the \ environment variable HOME.) If the environment variable \ HOME is not set the platform-specific variant\ of "/" will be used. If you are on a Windows platform the file will be \ named _tkdiff.rc and will have the attribute "hidden". For \ all other platforms the file will be named\ ".tkdiffrc". You may override the name and location of this file by \ setting the environment variable TKDIFFRC to whatever \ filename you wish. Preferences are organized into three categories: General, Display and \ Appearance. General $pref(diffcmd) This is the command to run to generate a diff of the two files. Typically \ this will be "diff". When this command is run, the ignore-blanks \ options and the names of two files to be diffed will be added as the \ last to arguments on the command line. $pref(ignoreblanksopt) Arguments to send with the diff command to tell it how to ignore whitespace. \ If you are using gnu diff, "-b" or "--ignore-space-change" ignores \ changes in the amount of whitespace, while "-w" or \ "--ignore-all-space" ignores all white space. $pref(tmpdir) The name of a directory for files that are temporarily created while $g(name) \ is running. $pref(editor) The name of an external editor program to use when editing a file (ie: when \ you select "Edit" from the popup menu). If this value is blank, a \ simple editor built in to $g(name) will be used. For\ windows users you might want to set this to "notepad". Unix users may \ want to set this to "xterm -e vi" or perhaps "gnuclient". When run, the \ name of the file to edit will be appened as the\ last argument on the command line. If the supplied string contains the string "\$file", it\'s treated as a whole \ command line, where the following parameters can be used: \$file: the file of your choice \$line: the starting line of the current diff For example, in the case of NEdit or Emacs you can use "nc -line \$line \ \$file" and "emacs +\$line \$file" respectively. $pref(filetypes) The choice of file suffixes you want to see in the file open and save dialogs \ A list of the form { {{All Files} *} {{Text Files} .txt} } and so on. $pref(geometry) This defines the default size, in characters of the two text windows. The \ format should be WIDTHxHEIGHT. For example, "80x40". $pref(fancyButtons) If set, toolbar buttons will mimic the visual behavior of typical Microsoft \ Windows applications. Buttons will initially be flat until the cursor \ moves over them, at which time they will be raised. If unset, toolbar buttons will always appear raised. This feature is not supported in MacOSX. $pref(toolbarIcons) If set, the toolbar buttons will use icons instead of text labels. If unset, the toolbar buttons will use text labels instead of icons. $pref(autocenter) If set, whenever a new diff record becomes the current diff record (for \ example, when pressing the next or previous buttons), the diff record \ will be automatically centered on the screen. If unset, no automatic scrolling will occur. $pref(syncscroll) If set, scrolling either text window will result in both windows scrolling. If not set, the windows will scroll independent of each other. $pref(autoselect) If set, automatically select the nearest visible diff region when scrolling. If not set, the current diff region will not change during scrolling. This only takes effect if $pref(syncscroll) is set. Display $pref(showln) If set, line numbers will be displayed alongside each line of each file. If not set, no line numbers will appear. $pref(tagln) If set, line numbers are highlighted with the options defined in the \ Appearance section of the preferences. If not set, line numbers won\'t be highlighted. $pref(showcbs) If set, change bars will be displayed alongside each line of each file. If not set, no change bars will appear. $pref(tagcbs) If set, change indicators will be highlighted. If $pref(colorcbs) \ is set they will appear as solid colored bars that match the colors \ used in the diff map. If $pref(colorcbs)\ is not set, the change indicators will be highlighted according to the \ options defined in the Appearance section of preferences. $pref(showmap) If set, colorized, graphical "diff map" will be displayed between the two \ files, showing regions that have changed. Red is used to show deleted \ lines, green for added lines, blue for changed\ lines, and yellow for overlapping lines during a 3-way merge. If not set, the diff map will not be shown. $pref(showlineview) If set, show a window at the bottom of the display that shows the current \ line from each file, one on top of the other. This window is most \ useful to do a byte-by-byte comparison of a line that has\ changed. If not set, the window will not be shown. $pref(showinline1) If set, show inline diffs in the main window. This is useful to see what the \ actual diffs are within a large diff region. \ If not set, the inline diffs are neither computed nor shown. This is the \ simpler approach, where byte-by-byte comparisons \ are used. $pref(showinline2) If set, show inline diffs in the main window. This is useful to see what the \ actual diffs are within a large diff region. \ If not set, the inline diffs are neither computed nor shown. This approach \ is more complex, but should give more pleasing \ results for source code and written text files. This is the \ Ratcliff/Obershelp pattern matching algorithm which recursively \ finds the largest common substring, and recursively repeats on the left and \ right remainders. $pref(tagtext) If set, the file contents will be highlighted with the options defined in the \ Appearance section of the preferences. If not set, the file contents won\'t be highlighted. $pref(colorcbs) If set, the change bars will display as solid bars of color that match the \ colors used by the diff map. If not set, the change bars will display a "+" for lines that exist in only \ one file, a "-" for lines that are missing from only one file, and \ "!" for lines that are different between the two files. Appearance $pref(textopt) This is a list of Tk text widget options that are applied to each of the two \ text windows in the main display. If you have Tk installed on your \ machine these will be documented in the "Text.n" man\ page. $pref(difftag) This is a list of Tk text widget tag options that are applied to all diff \ regions. Use this option to make diff regions stand out from regular text. $pref(deltag) This is a list of Tk text widget tag options that are applied to the current \ diff region. These options have a higher priority than those for all \ diff regions. So, for example, if you set the\ forground for all diff regions to be black and set the foreground for \ the current diff region to be blue, the current diff region foreground \ color will be used. $pref(instag) This is a list of Tk text widget tag options that are applied to regions that \ have been inserted. These options have a higher priority than those for \ all diff regions. $pref(chgtag) This is a list of Tk text widget tag options that are applied to regions that \ have been changed. These options have a higher priority than those for \ all diff regions. $pref(currtag) This is a list of Tk text widget tag options that are applied to the current \ diff region. These tags have a higher priority than those for all diff \ regions, and a higher priority than the change,\ inserted and deleted diff regions. $pref(inlinetag) This is a list of Tk text widget tag options that are applied to differences \ within lines in a diff region. These tags have a higher priority than \ those for all diff regions, and a higher priority than the change,\ inserted and deleted diff regions. $pref(bytetag) This is a list of Tk text widget tag options that are applied to individual \ characters in the line view. These options do not affect the main text \ displays. $pref(tabstops) This defines the number of characters for each tabstop in the main display \ windows. The default is 8. } # since we have embedded references to the preference labels in # the text, we need to perform substitutions. Because of this, if # you edit the above text, be sure to properly escape any dollar # signs that are not meant to be treated as a variable reference set text [subst -nocommands $text] do-text-info .help-preferences $title $text } ###################################################################### # # text formatting routines derived from Klondike # Reproduced here with permission from their author. # # Copyright (C) 1993,1994 by John Heidemann # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of John Heidemann may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY JOHN HEIDEMANN ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL JOHN HEIDEMANN BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # ###################################################################### proc put-text {tw txt} { $tw configure -font {Fixed 12} $tw configure -font -*-Times-Medium-R-Normal-*-14-* $tw tag configure bld -font -*-Times-Bold-R-Normal-*-14-* $tw tag configure cmp -font -*-Courier-Medium-R-Normal-*-12-* $tw tag configure hdr -font -*-Helvetica-Bold-R-Normal-*-16-* -underline 1 $tw tag configure itl -font -*-Times-Medium-I-Normal-*-14-* $tw tag configure ttl -font -*-Helvetica-Bold-R-Normal-*-18-* #$tw tag configure h3 -font -*-Helvetica-Bold-R-Normal-*-14-* #$tw tag configure itl -font -*-Times-Medium-I-Normal-*-14-* #$tw tag configure rev -foreground white -background black $tw mark set insert 0.0 set t $txt while {[regexp -indices {<([^@>]*)>} $t match inds] == 1} { set start [lindex $inds 0] set end [lindex $inds 1] set keyword [string range $t $start $end] set oldend [$tw index end] $tw insert end [string range $t 0 [expr {$start - 2}]] purge-all-tags $tw $oldend insert if {[string range $keyword 0 0] == "/"} { set keyword [string trimleft $keyword "/"] if {[info exists tags($keyword)] == 0} { error "end tag $keyword without beginning" } $tw tag add $keyword $tags($keyword) insert unset tags($keyword) } else { if {[info exists tags($keyword)] == 1} { error "nesting of begin tag $keyword" } set tags($keyword) [$tw index insert] } set t [string range $t [expr {$end + 2}] end] } set oldend [$tw index end] $tw insert end $t purge-all-tags $tw $oldend insert } proc purge-all-tags {w start end} { foreach tag [$w tag names $start] { $w tag remove $tag $start $end } } # Open one of the diffed files in an editor if possible proc do-edit {} { debug-info "do-edit ()" global g global opts global finfo global w if {$g(activeWindow) == $w(LeftText)} { set fileno 1 } elseif {$g(activeWindow) == $w(RightText)} { set fileno 2 } else { set fileno 1 } if {$finfo(tmp,$fileno)} { do-error "This file is not editable" } else { if {[string length [string trim $opts(editor)]] == 0} { simpleEd open $finfo(pth,$fileno) } elseif {[regexp "\\\$file" "$opts(editor)"] == 1} { set line [lindex [extract $g(currdiff)] [expr {($fileno-1) *2+1}]] set file $finfo(pth,$fileno) eval set commandline \"$opts(editor) &\" debug-info "exec $commandline" eval exec $commandline } else { debug-info "exec $opts(editor) \"{$finfo(pth,$fileno)}\" &" eval exec $opts(editor) "{$finfo(pth,$fileno)}" & } } } ########################################################################## # platform-specific stuff ########################################################################## proc setAquaDialogStyle {toplevel} { if {[catch {tk::unsupported::MacWindowStyle style $toplevel moveableModal}] } { tk::unsupported::MacWindowStyle style $toplevel movableDBoxProc } } ########################################################################## # A simple editor, from Bryan Oakley. ########################################################################## proc simpleEd {command args} { debug-info "simpleEd ($command $args)" global textfont switch -- $command { open { set filename [lindex $args 0] set w .editor set count 0 while {[winfo exists ${w}$count]} { incr count 1 } set w ${w}$count toplevel $w -borderwidth 2 -relief sunken wm title $w "$filename - Simple Editor" wm group $w . menu $w.menubar $w configure -menu $w.menubar $w.menubar add cascade -label "File" -menu $w.menubar.fileMenu $w.menubar add cascade -label "Edit" -menu $w.menubar.editMenu menu $w.menubar.fileMenu menu $w.menubar.editMenu $w.menubar.fileMenu add command -label "Save" -underline 1 \ -command [list simpleEd save $filename $w] $w.menubar.fileMenu add command -label "Save As..." -underline 1 \ -command [list simpleEd saveAs $filename $w] $w.menubar.fileMenu add separator $w.menubar.fileMenu add command -label "Exit" -underline 1 \ -command [list simpleEd exit $w] $w.menubar.editMenu add command -label "Cut" -command [list event \ generate $w.text <>] $w.menubar.editMenu add command -label "Copy" -command \ [list event generate $w.text <>] $w.menubar.editMenu add command -label "Paste" -command \ [list event generate $w.text <>] text $w.text -wrap none -xscrollcommand [list $w.hsb set] \ -yscrollcommand [list $w.vsb set] -borderwidth 0 -font $textfont scrollbar $w.vsb -orient vertical -command [list $w.text yview] scrollbar $w.hsb -orient horizontal -command [list $w.text xview] grid $w.text -row 0 -column 0 -sticky nsew grid $w.vsb -row 0 -column 1 -sticky ns grid $w.hsb -row 1 -column 0 -sticky ew grid columnconfigure $w 0 -weight 1 grid columnconfigure $w 1 -weight 0 grid rowconfigure $w 0 -weight 1 grid rowconfigure $w 1 -weight 0 set fd [open $filename] $w.text insert 1.0 [read $fd] close $fd } save { set filename [lindex $args 0] set w [lindex $args 1] set fd [open $filename w] puts $fd [$w.text get 1.0 "end-1c"] close $fd } saveAs { set filename [lindex $args 0] set w [lindex $args 1] set filename [tk_getSaveFile -initialfile $filename] if {$filename != ""} { simpleEd save $filename $w } } exit { set w [lindex $args 0] destroy $w } } } # end of simpleEd # Copyright (c) 1998-2005, Bryan Oakley # All Rights Reservered # # Bryan Oakley # oakley@bardo.clearlight.com # # combobox v2.2.2 September 22, 2002 # # a combobox / dropdown listbox (pick your favorite name) widget # written in pure tcl # # this code is freely distributable without restriction, but is # provided as-is with no warranty expressed or implied. # # thanks to the following people who provided beta test support or # patches to the code (in no particular order): # # Scott Beasley Alexandre Ferrieux Todd Helfter # Matt Gushee Laurent Duperval John Jackson # Fred Rapp Christopher Nelson # Eric Galluzzo Jean-Francois Moine # # A special thanks to Martin M. Hunt who provided several good ideas, # and always with a patch to implement them. Jean-Francois Moine, # Todd Helfter and John Jackson were also kind enough to send in some # code patches. # # ... and many others over the years. package provide combobox 2.2.2 namespace eval ::combobox { # this is the public interface namespace export combobox # these contain references to available options variable widgetOptions # these contain references to available commands and subcommands variable widgetCommands variable scanCommands variable listCommands } # ::combobox::combobox -- # # This is the command that gets exported. It creates a new # combobox widget. # # Arguments: # # w path of new widget to create # args additional option/value pairs (eg: -background white, etc.) # # Results: # # It creates the widget and sets up all of the default bindings # # Returns: # # The name of the newly create widget proc ::combobox::combobox {w args} { variable widgetOptions variable widgetCommands variable scanCommands variable listCommands # perform a one time initialization if {![info exists widgetOptions]} { Init } # build it... eval Build $w $args # set some bindings... SetBindings $w # and we are done! return $w } # ::combobox::Init -- # # Initialize the namespace variables. This should only be called # once, immediately prior to creating the first instance of the # widget # # Arguments: # # none # # Results: # # All state variables are set to their default values; all of # the option database entries will exist. # # Returns: # # empty string proc ::combobox::Init {} { variable widgetOptions variable widgetCommands variable scanCommands variable listCommands variable defaultEntryCursor array set widgetOptions [list -background \ {background Background} -bd -borderwidth -bg -background \ -borderwidth {borderWidth BorderWidth} -command \ {command Command} -commandstate {commandState State} \ -cursor {cursor Cursor} \ -disabledbackground {disabledBackground DisabledBackground} \ -disabledforeground {disabledForeground DisabledForeground} \ -dropdownwidth {dropdownWidth DropdownWidth} -editable \ {editable Editable} -fg -foreground -font \ {font Font} -foreground {foreground Foreground} \ -height {height Height} \ -highlightbackground {highlightBackground HighlightBackground} \ -highlightcolor {highlightColor HighlightColor} \ -highlightthickness {highlightThickness HighlightThickness} \ -image {image Image} -maxheight \ {maxHeight Height} -opencommand {opencommand Command} \ -relief {relief Relief} \ -selectbackground {selectBackground Foreground} \ -selectborderwidth {selectBorderWidth BorderWidth} \ -selectforeground {selectForeground Background} -state \ {state State} -takefocus {takeFocus TakeFocus} \ -textvariable {textVariable Variable} -value \ {value Value} -width {width Width} \ -xscrollcommand {xScrollCommand ScrollCommand}] set widgetCommands [list bbox cget configure curselection delete get \ icursor index insert list scan selection xview select toggle open close] set listCommands [list delete get index insert size] set scanCommands [list mark dragto] # why check for the Tk package? This lets us be sourced into # an interpreter that doesn't have Tk loaded, such as the slave # interpreter used by pkg_mkIndex. In theory it should have no # side effects when run if {[lsearch -exact [package names] "Tk"] != -1} { ################################################################## #- this initializes the option database. Kinda gross, but it works #- (I think). ################################################################## # the image used for the button... if {$::tcl_platform(platform) == "windows"} { image create bitmap ::combobox::bimage -data { #define down_arrow_width 12 #define down_arrow_height 12 static char down_arrow_bits[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xfc,0xf1,0xf8,0xf0,0x70,0xf0,0x20,0xf0, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00; } } } else { image create bitmap ::combobox::bimage -data { #define down_arrow_width 15 #define down_arrow_height 15 static char down_arrow_bits[] = { 0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80, 0x00,0x80,0xf8,0x8f,0xf0,0x87,0xe0,0x83, 0xc0,0x81,0x80,0x80,0x00,0x80,0x00,0x80, 0x00,0x80,0x00,0x80,0x00,0x80 } } } # compute a widget name we can use to create a temporary widget set tmpWidget ".__tmp__" set count 0 while {[winfo exists $tmpWidget] == 1} { set tmpWidget ".__tmp__$count" incr count } # get the scrollbar width. Because we try to be clever and draw our # own button instead of using a tk widget, we need to know what size # button to create. This little hack tells us the width of a scroll # bar. # # NB: we need to be sure and pick a window that doesn't already # exist... scrollbar $tmpWidget set sb_width [winfo reqwidth $tmpWidget] destroy $tmpWidget # steal options from the entry widget # we want darn near all options, so we'll go ahead and do # them all. No harm done in adding the one or two that we # don't use. entry $tmpWidget foreach foo [$tmpWidget configure] { # the cursor option is special, so we'll save it in # a special way if {[lindex $foo 0] == "-cursor"} { set defaultEntryCursor [lindex $foo 4] } if {[llength $foo] == 5} { set option [lindex $foo 1] set value [lindex $foo 4] option add *Combobox.$option $value widgetDefault # these options also apply to the dropdown listbox if {[string compare $option "foreground"] == 0 || \ [string compare $option "background"] == 0 || \ [string compare $option "font"] == 0} { option add *Combobox*ComboboxListbox.$option $value \ widgetDefault } } } destroy $tmpWidget # these are unique to us... option add *Combobox.dropdownWidth {} widgetDefault option add *Combobox.openCommand {} widgetDefault option add *Combobox.cursor {} widgetDefault option add *Combobox.commandState normal widgetDefault option add *Combobox.editable 1 widgetDefault option add *Combobox.maxHeight 10 widgetDefault option add *Combobox.height 0 } # set class bindings SetClassBindings } # ::combobox::SetClassBindings -- # # Sets up the default bindings for the widget class # # this proc exists since it's The Right Thing To Do, but # I haven't had the time to figure out how to do all the # binding stuff on a class level. The main problem is that # the entry widget must have focus for the insertion cursor # to be visible. So, I either have to have the entry widget # have the Combobox bindtag, or do some fancy juggling of # events or some such. What a pain. # # Arguments: # # none # # Returns: # # empty string proc ::combobox::SetClassBindings {} { # make sure we clean up after ourselves... bind Combobox [list ::combobox::DestroyHandler %W] # this will (hopefully) close (and lose the grab on) the # listbox if the user clicks anywhere outside of it. Note # that on Windows, you can click on some other app and # the listbox will still be there, because tcl won't see # that button click set this {[::combobox::convert %W -W]} bind Combobox "$this close" bind Combobox "$this close" # this helps (but doesn't fully solve) focus issues. The general # idea is, whenever the frame gets focus it gets passed on to # the entry widget #bind Combobox {::combobox::tkTabToWindow \ #[::combobox::convert %W -W].entry} # this closes the listbox if we get hidden bind Combobox {[::combobox::convert %W -W] close} return "" } # ::combobox::SetBindings -- # # here's where we do most of the binding foo. I think there's probably # a few bindings I ought to add that I just haven't thought # about... # # I'm not convinced these are the proper bindings. Ideally all # bindings should be on "Combobox", but because of my juggling of # bindtags I'm not convinced thats what I want to do. But, it all # seems to work, its just not as robust as it could be. # # Arguments: # # w widget pathname # # Returns: # # empty string proc ::combobox::SetBindings {w} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # juggle the bindtags. The basic idea here is to associate the # widget name with the entry widget, so if a user does a bind # on the combobox it will get handled properly since it is # the entry widget that has keyboard focus. bindtags $widgets(entry) [concat $widgets(this) [bindtags $widgets(entry)]] bindtags $widgets(button) [concat $widgets(this) \ [bindtags $widgets(button)]] # override the default bindings for tab and shift-tab. The # focus procs take a widget as their only parameter and we # want to make sure the right window gets used (for shift- # tab we want it to appear as if the event was generated # on the frame rather than the entry. #bind $widgets(entry) "::combobox::tkTabToWindow \[tk_focusNext \ #$widgets(entry)\]; break" #bind $widgets(entry) \ #"::combobox::tkTabToWindow \[tk_focusPrev $widgets(this)\]; break" # this makes our "button" (which is actually a label) # do the right thing bind $widgets(button) [list $widgets(this) toggle] # this lets the autoscan of the listbox work, even if they # move the cursor over the entry widget. bind $widgets(entry) "break" bind $widgets(listbox) "::combobox::Select \ [list $widgets(this)] \[$widgets(listbox) nearest %y\]; break" bind $widgets(vsb) {continue} bind $widgets(vsb) {continue} bind $widgets(listbox) { %W selection clear 0 end %W activate @%x,%y %W selection anchor @%x,%y %W selection set @%x,%y @%x,%y # need to do a yview if the cursor goes off the top # or bottom of the window... (or do we?) } # these events need to be passed from the entry widget # to the listbox, or otherwise need some sort of special # handling. foreach event [list \ <1> ] { bind $widgets(entry) $event [list ::combobox::HandleEvent \ $widgets(this) $event] } # like the other events, needs to be passed from # the entry widget to the listbox. However, in this case we # need to add an additional parameter catch { bind $widgets(entry) [list ::combobox::HandleEvent \ $widgets(this) %D] } } # ::combobox::Build -- # # This does all of the work necessary to create the basic # combobox. # # Arguments: # # w widget name # args additional option/value pairs # # Results: # # Creates a new widget with the given name. Also creates a new # namespace patterened after the widget name, as a child namespace # to ::combobox # # Returns: # # the name of the widget proc ::combobox::Build {w args} { variable widgetOptions if {[winfo exists $w]} { error "window name \"$w\" already exists" } # create the namespace for this instance, and define a few # variables namespace eval ::combobox::$w { variable ignoreTrace 0 variable oldFocus {} variable oldGrab {} variable oldValue {} variable options variable this variable widgets set widgets(foo) foo ;# coerce into an array set options(foo) foo ;# coerce into an array unset widgets(foo) unset options(foo) } # import the widgets and options arrays into this proc so # we don't have to use fully qualified names, which is a # pain. upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # this is our widget -- a frame of class Combobox. Naturally, # it will contain other widgets. We create it here because # we need it in order to set some default options. set widgets(this) [frame $w -class Combobox -takefocus 0] set widgets(entry) [entry $w.entry -takefocus 1] set widgets(button) [label $w.button -takefocus 0] # this defines all of the default options. We get the # values from the option database. Note that if an array # value is a list of length one it is an alias to another # option, so we just ignore it foreach name [array names widgetOptions] { if {[llength $widgetOptions($name)] == 1} continue set optName [lindex $widgetOptions($name) 0] set optClass [lindex $widgetOptions($name) 1] set value [option get $w $optName $optClass] set options($name) $value } # a couple options aren't available in earlier versions of # tcl, so we'll set them to sane values. For that matter, if # they exist but are empty, set them to sane values. if {[string length $options(-disabledforeground)] == 0} { set options(-disabledforeground) $options(-foreground) } if {[string length $options(-disabledbackground)] == 0} { set options(-disabledbackground) $options(-background) } # if -value is set to null, we'll remove it from our # local array. The assumption is, if the user sets it from # the option database, they will set it to something other # than null (since it's impossible to determine the difference # between a null value and no value at all). if {[info exists options(-value)] && [string length $options(-value)] == \ 0} { unset options(-value) } # we will later rename the frame's widget proc to be our # own custom widget proc. We need to keep track of this # new name, so we'll define and store it here... set widgets(frame) ::combobox::${w}::$w # gotta do this sooner or later. Might as well do it now pack $widgets(entry) -side left -fill both -expand yes pack $widgets(button) -side right -fill y -expand no # I should probably do this in a catch, but for now it's # good enough... What it does, obviously, is put all of # the option/values pairs into an array. Make them easier # to handle later on... array set options $args # now, the dropdown list... the same renaming nonsense # must go on here as well... set widgets(dropdown) [toplevel $w.top] set widgets(listbox) [listbox $w.top.list] set widgets(vsb) [scrollbar $w.top.vsb] pack $widgets(listbox) -side left -fill both -expand y # fine tune the widgets based on the options (and a few # arbitrary values...) # NB: we are going to use the frame to handle the relief # of the widget as a whole, so the entry widget will be # flat. This makes the button which drops down the list # to appear "inside" the entry widget. $widgets(vsb) configure -command "$widgets(listbox) yview" \ -highlightthickness 0 $widgets(button) configure -highlightthickness 0 -borderwidth 1 \ -relief raised -width [expr {[winfo reqwidth $widgets(vsb)] - 2}] $widgets(entry) configure -borderwidth 0 -relief flat -highlightthickness 0 $widgets(dropdown) configure -borderwidth 1 -relief sunken $widgets(listbox) configure -selectmode browse \ -background [$widgets(entry) cget -bg] -yscrollcommand \ "$widgets(vsb) set" -exportselection false -borderwidth 0 # do some window management foo on the dropdown window # There seems to be some order dependency here on some platforms wm transient $widgets(dropdown) [winfo toplevel $w] wm group $widgets(dropdown) [winfo parent $w] wm resizable $widgets(dropdown) 0 0 wm overrideredirect $widgets(dropdown) 1 wm withdraw $widgets(dropdown) # this moves the original frame widget proc into our # namespace and gives it a handy name rename ::$w $widgets(frame) # now, create our widget proc. Obviously (?) it goes in # the global namespace. All combobox widgets will actually # share the same widget proc to cut down on the amount of # bloat. proc ::$w {command args} "eval ::combobox::WidgetProc $w \$command \$args" # ok, the thing exists... let's do a bit more configuration. if {[catch "::combobox::Configure [list $widgets(this)] [array get \ options]" error]} { catch {destroy $w} error "internal error: $error" } return "" } # ::combobox::HandleEvent -- # # this proc handles events from the entry widget that we want # handled specially (typically, to allow navigation of the list # even though the focus is in the entry widget) # # Arguments: # # w widget pathname # event a string representing the event (not necessarily an # actual event) # args additional arguments required by particular events proc ::combobox::HandleEvent {w event args} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options upvar ::combobox::${w}::oldValue oldValue # for all of these events, if we have a special action we'll # do that and do a "return -code break" to keep additional # bindings from firing. Otherwise we'll let the event fall # on through. switch -- $event { "" { if {[winfo ismapped $widgets(dropdown)]} { set D [lindex $args 0] # the '120' number in the following expression has # it's genesis in the tk bind manpage, which suggests # that the smallest value of %D for mousewheel events # will be 120. The intent is to scroll one line at a time. $widgets(listbox) yview scroll [expr {-($D/120)}] units } } "" { # if the widget is editable, clear the selection. # this makes it more obvious what will happen if the # user presses (and helps our code know what # to do if the user presses return) if {$options(-editable)} { $widgets(listbox) see 0 $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor 0 $widgets(listbox) activate 0 } } "" { set oldValue [$widgets(entry) get] } "" { if {![winfo ismapped $widgets(dropdown)]} { # did the value change? set newValue [$widgets(entry) get] if {$oldValue != $newValue} { CallCommand $widgets(this) $newValue } } } "<1>" { set editable [::combobox::GetBoolean $options(-editable)] if {!$editable} { if {[winfo ismapped $widgets(dropdown)]} { $widgets(this) close return -code break } else { if {$options(-state) != "disabled"} { $widgets(this) open return -code break } } } } "" { if {$options(-state) != "disabled"} { $widgets(this) toggle return -code break } } "" { if {[winfo ismapped $widgets(dropdown)]} { ::combobox::Find $widgets(this) 0 return -code break } else { ::combobox::SetValue $widgets(this) [$widgets(this) get] } } "" { # $widgets(entry) delete 0 end # $widgets(entry) insert 0 $oldValue if {[winfo ismapped $widgets(dropdown)]} { $widgets(this) close return -code break } } "" { # did the value change? set newValue [$widgets(entry) get] if {$oldValue != $newValue} { CallCommand $widgets(this) $newValue } if {[winfo ismapped $widgets(dropdown)]} { ::combobox::Select $widgets(this) \ [$widgets(listbox) curselection] return -code break } } "" { $widgets(listbox) yview scroll 1 pages set index [$widgets(listbox) index @0,0] $widgets(listbox) see $index $widgets(listbox) activate $index $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor $index $widgets(listbox) selection set $index } "" { $widgets(listbox) yview scroll -1 pages set index [$widgets(listbox) index @0,0] $widgets(listbox) activate $index $widgets(listbox) see $index $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor $index $widgets(listbox) selection set $index } "" { if {[winfo ismapped $widgets(dropdown)]} { ::combobox::tkListboxUpDown $widgets(listbox) 1 return -code break } else { if {$options(-state) != "disabled"} { $widgets(this) open return -code break } } } "" { if {[winfo ismapped $widgets(dropdown)]} { ::combobox::tkListboxUpDown $widgets(listbox) -1 return -code break } else { if {$options(-state) != "disabled"} { $widgets(this) open return -code break } } } } return "" } # ::combobox::DestroyHandler {w} -- # # Cleans up after a combobox widget is destroyed # # Arguments: # # w widget pathname # # Results: # # The namespace that was created for the widget is deleted, # and the widget proc is removed. proc ::combobox::DestroyHandler {w} { # if the widget actually being destroyed is of class Combobox, # crush the namespace and kill the proc. Get it? Crush. Kill. # Destroy. Heh. Danger Will Robinson! Oh, man! I'm so funny it # brings tears to my eyes. if {[string compare [winfo class $w] "Combobox"] == 0} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # delete the namespace and the proc which represents # our widget namespace delete ::combobox::$w rename $w {} } return "" } # ::combobox::Find # # finds something in the listbox that matches the pattern in the # entry widget and selects it # # N.B. I'm not convinced this is working the way it ought to. It # works, but is the behavior what is expected? I've also got a gut # feeling that there's a better way to do this, but I'm too lazy to # figure it out... # # Arguments: # # w widget pathname # exact boolean; if true an exact match is desired # # Returns: # # Empty string proc ::combobox::Find {w {exact 0}} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options ## *sigh* this logic is rather gross and convoluted. Surely ## there is a more simple, straight-forward way to implement ## all this. As the saying goes, I lack the time to make it ## shorter... # use what is already in the entry widget as a pattern set pattern [$widgets(entry) get] if {[string length $pattern] == 0} { # clear the current selection $widgets(listbox) see 0 $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor 0 $widgets(listbox) activate 0 return } # we're going to be searching this list... set list [$widgets(listbox) get 0 end] # if we are doing an exact match, try to find, # well, an exact match set exactMatch -1 if {$exact} { set exactMatch [lsearch -exact $list $pattern] } # search for it. We'll try to be clever and not only # search for a match for what they typed, but a match for # something close to what they typed. We'll keep removing one # character at a time from the pattern until we find a match # of some sort. set index -1 while {$index == -1 && [string length $pattern]} { set index [lsearch -glob $list "$pattern*"] if {$index == -1} { regsub {.$} $pattern {} pattern } } # this is the item that most closely matches... set thisItem [lindex $list $index] # did we find a match? If so, do some additional munging... if {$index != -1} { # we need to find the part of the first item that is # unique WRT the second... I know there's probably a # simpler way to do this... set nextIndex [expr {$index + 1}] set nextItem [lindex $list $nextIndex] # we don't really need to do much if the next # item doesn't match our pattern... if {[string match $pattern* $nextItem]} { # ok, the next item matches our pattern, too # now the trick is to find the first character # where they *don't* match... set marker [string length $pattern] while {$marker <= [string length $pattern]} { set a [string index $thisItem $marker] set b [string index $nextItem $marker] if {[string compare $a $b] == 0} { append pattern $a incr marker } else { break } } } else { set marker [string length $pattern] } } else { set marker end set index 0 } # ok, we know the pattern and what part is unique; # update the entry widget and listbox appropriately if {$exact && $exactMatch == -1} { # this means we didn't find an exact match $widgets(listbox) selection clear 0 end $widgets(listbox) see $index } elseif {!$exact} { # this means we found something, but it isn't an exact # match. If we find something that *is* an exact match we # don't need to do the following, since it would merely # be replacing the data in the entry widget with itself set oldstate [$widgets(entry) cget -state] $widgets(entry) configure -state normal $widgets(entry) delete 0 end $widgets(entry) insert end $thisItem $widgets(entry) selection clear $widgets(entry) selection range $marker end $widgets(listbox) activate $index $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor $index $widgets(listbox) selection set $index $widgets(listbox) see $index $widgets(entry) configure -state $oldstate } } # ::combobox::Select -- # # selects an item from the list and sets the value of the combobox # to that value # # Arguments: # # w widget pathname # index listbox index of item to be selected # # Returns: # # empty string proc ::combobox::Select {w index} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # the catch is because I'm sloppy -- presumably, the only time # an error will be caught is if there is no selection. if {![catch {set data [$widgets(listbox) get [lindex $index 0]]}]} { ::combobox::SetValue $widgets(this) $data $widgets(listbox) selection clear 0 end $widgets(listbox) selection anchor $index $widgets(listbox) selection set $index } $widgets(entry) selection range 0 end $widgets(this) close return "" } # ::combobox::HandleScrollbar -- # # causes the scrollbar of the dropdown list to appear or disappear # based on the contents of the dropdown listbox # # Arguments: # # w widget pathname # action the action to perform on the scrollbar # # Returns: # # an empty string proc ::combobox::HandleScrollbar {w {action "unknown"}} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options if {$options(-height) == 0} { set hlimit $options(-maxheight) } else { set hlimit $options(-height) } switch -- $action { "grow" { if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} { pack $widgets(vsb) -side right -fill y -expand n } } "shrink" { if {$hlimit > 0 && [$widgets(listbox) size] <= $hlimit} { pack forget $widgets(vsb) } } "crop" { # this means the window was cropped and we definitely # need a scrollbar no matter what the user wants pack $widgets(vsb) -side right -fill y -expand n } default { if {$hlimit > 0 && [$widgets(listbox) size] > $hlimit} { pack $widgets(vsb) -side right -fill y -expand n } else { pack forget $widgets(vsb) } } } return "" } # ::combobox::ComputeGeometry -- # # computes the geometry of the dropdown list based on the size of the # combobox... # # Arguments: # # w widget pathname # # Returns: # # the desired geometry of the listbox proc ::combobox::ComputeGeometry {w} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options if {$options(-height) == 0 && $options(-maxheight) != "0"} { # if this is the case, count the items and see if # it exceeds our maxheight. If so, set the listbox # size to maxheight... set nitems [$widgets(listbox) size] if {$nitems > $options(-maxheight)} { # tweak the height of the listbox $widgets(listbox) configure -height $options(-maxheight) } else { # un-tweak the height of the listbox $widgets(listbox) configure -height 0 } update idletasks } # compute height and width of the dropdown list set bd [$widgets(dropdown) cget -borderwidth] set height [expr {[winfo reqheight $widgets(dropdown)] + $bd + $bd}] if {[string length $options(-dropdownwidth)] == 0 || \ $options(-dropdownwidth) == 0} { set width [winfo width $widgets(this)] } else { set m [font measure [$widgets(listbox) cget -font] "m"] set width [expr {$options(-dropdownwidth) * $m}] } # figure out where to place it on the screen, trying to take into # account we may be running under some virtual window manager set screenWidth [winfo screenwidth $widgets(this)] set screenHeight [winfo screenheight $widgets(this)] set rootx [winfo rootx $widgets(this)] set rooty [winfo rooty $widgets(this)] set vrootx [winfo vrootx $widgets(this)] set vrooty [winfo vrooty $widgets(this)] # the x coordinate is simply the rootx of our widget, adjusted for # the virtual window. We won't worry about whether the window will # be offscreen to the left or right -- we want the illusion that it # is part of the entry widget, so if part of the entry widget is off- # screen, so will the list. If you want to change the behavior, # simply change the if statement... (and be sure to update this # comment!) set x [expr {$rootx + $vrootx}] if {0} { set rightEdge [expr {$x + $width}] if {$rightEdge > $screenWidth} { set x [expr {$screenWidth - $width}] } if {$x < 0} { set x 0 } } # the y coordinate is the rooty plus vrooty offset plus # the height of the static part of the widget plus 1 for a # tiny bit of visual separation... set y [expr {$rooty + $vrooty + [winfo reqheight $widgets(this)] + 1}] set bottomEdge [expr {$y + $height}] if {$bottomEdge >= $screenHeight} { # ok. Fine. Pop it up above the entry widget isntead of # below. set y [expr {($rooty - $height - 1) + $vrooty}] if {$y < 0} { # this means it extends beyond our screen. How annoying. # Now we'll try to be real clever and either pop it up or # down, depending on which way gives us the biggest list. # then, we'll trim the list to fit and force the use of # a scrollbar # (sadly, for windows users this measurement doesn't # take into consideration the height of the taskbar, # but don't blame me -- there isn't any way to detect # it or figure out its dimensions. The same probably # applies to any window manager with some magic windows # glued to the top or bottom of the screen) if {$rooty > [expr {$screenHeight / 2}]} { # we are in the lower half of the screen -- # pop it up. Y is zero; that parts easy. The height # is simply the y coordinate of our widget, minus # a pixel for some visual separation. The y coordinate # will be the topof the screen. set y 1 set height [expr {$rooty - 1 - $y}] } else { # we are in the upper half of the screen -- # pop it down set y [expr {$rooty + $vrooty + [winfo reqheight \ $widgets(this)] + 1}] set height [expr {$screenHeight - $y}] } # force a scrollbar HandleScrollbar $widgets(this) crop } } if {$y < 0} { # hmmm. Bummer. set y 0 set height $screenheight } set geometry [format "=%dx%d+%d+%d" $width $height $x $y] return $geometry } # ::combobox::DoInternalWidgetCommand -- # # perform an internal widget command, then mung any error results # to look like it came from our megawidget. A lot of work just to # give the illusion that our megawidget is an atomic widget # # Arguments: # # w widget pathname # subwidget pathname of the subwidget # command subwidget command to be executed # args arguments to the command # # Returns: # # The result of the subwidget command, or an error proc ::combobox::DoInternalWidgetCommand {w subwidget command args} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options set subcommand $command set command [concat $widgets($subwidget) $command $args] if {[catch $command result]} { # replace the subwidget name with the megawidget name regsub $widgets($subwidget) $result $widgets(this) result # replace specific instances of the subwidget command # with our megawidget command switch -- $subwidget,$subcommand { listbox,index { regsub "index" $result "list index" result } listbox,insert { regsub "insert" $result "list insert" result } listbox,delete { regsub "delete" $result "list delete" result } listbox,get { regsub "get" $result "list get" result } listbox,size { regsub "size" $result "list size" result } } error $result } else { return $result } } # ::combobox::WidgetProc -- # # This gets uses as the widgetproc for an combobox widget. # Notice where the widget is created and you'll see that the # actual widget proc merely evals this proc with all of the # arguments intact. # # Note that some widget commands are defined "inline" (ie: # within this proc), and some do most of their work in # separate procs. This is merely because sometimes it was # easier to do it one way or the other. # # Arguments: # # w widget pathname # command widget subcommand # args additional arguments; varies with the subcommand # # Results: # # Performs the requested widget command proc ::combobox::WidgetProc {w command args} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options upvar ::combobox::${w}::oldFocus oldFocus upvar ::combobox::${w}::oldFocus oldGrab set command [::combobox::Canonize $w command $command] # this is just shorthand notation... set doWidgetCommand [list ::combobox::DoInternalWidgetCommand \ $widgets(this)] if {$command == "list"} { # ok, the next argument is a list command; we'll # rip it from args and append it to command to # create a unique internal command # # NB: because of the sloppy way we are doing this, # we'll also let the user enter our secret command # directly (eg: listinsert, listdelete), but we # won't document that fact set command "list-[lindex $args 0]" set args [lrange $args 1 end] } set result "" # many of these commands are just synonyms for specific # commands in one of the subwidgets. We'll get them out # of the way first, then do the custom commands. switch -- $command { bbox - delete - get - icursor - index - insert - scan - selection - xview { set result [eval $doWidgetCommand entry $command $args] } list-get { set result [eval $doWidgetCommand listbox get $args] } list-index { set result [eval $doWidgetCommand listbox index $args] } list-size { set result [eval $doWidgetCommand listbox size $args] } select { if {[llength $args] == 1} { set index [lindex $args 0] set result [Select $widgets(this) $index] } else { error "usage: $w select index" } } subwidget { set knownWidgets [list button entry listbox dropdown vsb] if {[llength $args] == 0} { return $knownWidgets } set name [lindex $args 0] if {[lsearch $knownWidgets $name] != -1} { set result $widgets($name) } else { error "unknown subwidget $name" } } curselection { set result [eval $doWidgetCommand listbox curselection] } list-insert { eval $doWidgetCommand listbox insert $args set result [HandleScrollbar $w "grow"] } list-delete { eval $doWidgetCommand listbox delete $args set result [HandleScrollbar $w "shrink"] } toggle { # ignore this command if the widget is disabled... if {$options(-state) == "disabled"} return # pops down the list if it is not, hides it # if it is... if {[winfo ismapped $widgets(dropdown)]} { set result [$widgets(this) close] } else { set result [$widgets(this) open] } } open { # if this is an editable combobox, the focus should # be set to the entry widget if {$options(-editable)} { focus $widgets(entry) $widgets(entry) select range 0 end $widgets(entry) icur end } # if we are disabled, we won't allow this to happen if {$options(-state) == "disabled"} { return 0 } # if there is a -opencommand, execute it now if {[string length $options(-opencommand)] > 0} { # hmmm... should I do a catch, or just let the normal # error handling handle any errors? For now, the latter... uplevel \#0 $options(-opencommand) } # compute the geometry of the window to pop up, and set # it, and force the window manager to take notice # (even if it is not presently visible). # # this isn't strictly necessary if the window is already # mapped, but we'll go ahead and set the geometry here # since its harmless and *may* actually reset the geometry # to something better in some weird case. set geometry [::combobox::ComputeGeometry $widgets(this)] wm geometry $widgets(dropdown) $geometry update idletasks # if we are already open, there's nothing else to do if {[winfo ismapped $widgets(dropdown)]} { return 0 } # save the widget that currently has the focus; we'll restore # the focus there when we're done set oldFocus [focus] # ok, tweak the visual appearance of things and # make the list pop up $widgets(button) configure -relief sunken raise $widgets(dropdown) wm deiconify $widgets(dropdown) tkwait visibility $widgets(dropdown) focus -force $widgets(dropdown) # force focus to the entry widget so we can handle keypress # events for traversal focus -force $widgets(entry) # select something by default, but only if its an # exact match... ::combobox::Find $widgets(this) 1 # save the current grab state for the display containing # this widget. We'll restore it when we close the dropdown # list set status "none" set grab [grab current $widgets(this)] if {$grab != ""} { set status [grab status $grab] } set oldGrab [list $grab $status] unset grab status # *gasp* do a global grab!!! Mom always told me not to # do things like this, but sometimes a man's gotta do # what a man's gotta do. raise $widgets(dropdown) grab -global $widgets(this) # fake the listbox into thinking it has focus. This is # necessary to get scanning initialized properly in the # listbox. event generate $widgets(listbox) return 1 } close { # if we are already closed, don't do anything... if {![winfo ismapped $widgets(dropdown)]} { return 0 } # restore the focus and grab, but ignore any errors... # we're going to be paranoid and release the grab before # trying to set any other grab because we really really # really want to make sure the grab is released. catch {focus $oldFocus} result catch {grab release $widgets(this)} catch { set status [lindex $oldGrab 1] if {$status == "global"} { grab -global [lindex $oldGrab 0] } elseif {$status == "local"} { grab [lindex $oldGrab 0] } unset status } # hides the listbox $widgets(button) configure -relief raised wm withdraw $widgets(dropdown) # select the data in the entry widget. Not sure # why, other than observation seems to suggest that's # what windows widgets do. set editable [::combobox::GetBoolean $options(-editable)] if {$editable} { $widgets(entry) selection range 0 end $widgets(button) configure -relief raised } # magic tcl stuff (see tk.tcl in the distribution # lib directory) ::combobox::tkCancelRepeat return 1 } cget { if {[llength $args] != 1} { error "wrong # args: should be $w cget option" } set opt [::combobox::Canonize $w option [lindex $args 0]] if {$opt == "-value"} { set result [$widgets(entry) get] } else { set result $options($opt) } } configure { set result [eval ::combobox::Configure {$w} $args] } default { error "bad option \"$command\"" } } return $result } # ::combobox::Configure -- # # Implements the "configure" widget subcommand # # Arguments: # # w widget pathname # args zero or more option/value pairs (or a single option) # # Results: # # Performs typcial "configure" type requests on the widget proc ::combobox::Configure {w args} { variable widgetOptions variable defaultEntryCursor upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options if {[llength $args] == 0} { # hmmm. User must be wanting all configuration information # note that if the value of an array element is of length # one it is an alias, which needs to be handled slightly # differently set results {} foreach opt [lsort [array names widgetOptions]] { if {[llength $widgetOptions($opt)] == 1} { set alias $widgetOptions($opt) set optName $widgetOptions($alias) lappend results [list $opt $optName] } else { set optName [lindex $widgetOptions($opt) 0] set optClass [lindex $widgetOptions($opt) 1] set default [option get $w $optName $optClass] if {[info exists options($opt)]} { lappend results [list $opt $optName $optClass $default \ $options($opt)] } else { lappend results [list $opt $optName $optClass $default ""] } } } return $results } # one argument means we are looking for configuration # information on a single option if {[llength $args] == 1} { set opt [::combobox::Canonize $w option [lindex $args 0]] set optName [lindex $widgetOptions($opt) 0] set optClass [lindex $widgetOptions($opt) 1] set default [option get $w $optName $optClass] set results [list $opt $optName $optClass $default $options($opt)] return $results } # if we have an odd number of values, bail. if {[expr {[llength $args]%2}] == 1} { # hmmm. An odd number of elements in args error "value for \"[lindex $args end]\" missing" } # Great. An even number of options. Let's make sure they # are all valid before we do anything. Note that Canonize # will generate an error if it finds a bogus option; otherwise # it returns the canonical option name foreach {name value} $args { set name [::combobox::Canonize $w option $name] set opts($name) $value } # process all of the configuration options # some (actually, most) options require us to # do something, like change the attributes of # a widget or two. Here's where we do that... # # note that the handling of disabledforeground and # disabledbackground is a little wonky. First, we have # to deal with backwards compatibility (ie: tk 8.3 and below # didn't have such options for the entry widget), and # we have to deal with the fact we might want to disable # the entry widget but use the normal foreground/background # for when the combobox is not disabled, but not editable either. set updateVisual 0 foreach option [array names opts] { set newValue $opts($option) if {[info exists options($option)]} { set oldValue $options($option) } switch -- $option { -background { set updateVisual 1 set options($option) $newValue } -borderwidth { $widgets(frame) configure -borderwidth $newValue set options($option) $newValue } -command { # nothing else to do... set options($option) $newValue } -commandstate { # do some value checking... if {$newValue != "normal" && $newValue != "disabled"} { set options($option) $oldValue set message "bad state value \"$newValue\";" append message " must be normal or disabled" error $message } set options($option) $newValue } -cursor { $widgets(frame) configure -cursor $newValue $widgets(entry) configure -cursor $newValue $widgets(listbox) configure -cursor $newValue set options($option) $newValue } -disabledforeground { set updateVisual 1 set options($option) $newValue } -disabledbackground { set updateVisual 1 set options($option) $newValue } -dropdownwidth { set options($option) $newValue } -editable { set updateVisual 1 if {$newValue} { # it's editable... $widgets(entry) configure -state normal \ -cursor $defaultEntryCursor } else { $widgets(entry) configure -state disabled \ -cursor $options(-cursor) } set options($option) $newValue } -font { $widgets(entry) configure -font $newValue $widgets(listbox) configure -font $newValue set options($option) $newValue } -foreground { set updateVisual 1 set options($option) $newValue } -height { $widgets(listbox) configure -height $newValue HandleScrollbar $w set options($option) $newValue } -highlightbackground { $widgets(frame) configure -highlightbackground $newValue set options($option) $newValue } -highlightcolor { $widgets(frame) configure -highlightcolor $newValue set options($option) $newValue } -highlightthickness { $widgets(frame) configure -highlightthickness $newValue set options($option) $newValue } -image { if {[string length $newValue] > 0} { $widgets(button) configure -image $newValue } else { $widgets(button) configure -image ::combobox::bimage } set options($option) $newValue } -maxheight { # ComputeGeometry may dork with the actual height # of the listbox, so let's undork it $widgets(listbox) configure -height $options(-height) HandleScrollbar $w set options($option) $newValue } -opencommand { # nothing else to do... set options($option) $newValue } -relief { $widgets(frame) configure -relief $newValue set options($option) $newValue } -selectbackground { $widgets(entry) configure -selectbackground $newValue $widgets(listbox) configure -selectbackground $newValue set options($option) $newValue } -selectborderwidth { $widgets(entry) configure -selectborderwidth $newValue $widgets(listbox) configure -selectborderwidth $newValue set options($option) $newValue } -selectforeground { $widgets(entry) configure -selectforeground $newValue $widgets(listbox) configure -selectforeground $newValue set options($option) $newValue } -state { if {$newValue == "normal"} { set updateVisual 1 # it's enabled set editable [::combobox::GetBoolean $options(-editable)] if {$editable} { $widgets(entry) configure -state normal $widgets(entry) configure -takefocus 1 } # note that $widgets(button) is actually a label, # not a button. And being able to disable labels # wasn't possible until tk 8.3. (makes me wonder # why I chose to use a label, but that answer is # lost to antiquity) if {[info patchlevel] >= 8.3} { $widgets(button) configure -state normal } } elseif {$newValue == "disabled"} { set updateVisual 1 # it's disabled $widgets(entry) configure -state disabled $widgets(entry) configure -takefocus 0 # note that $widgets(button) is actually a label, # not a button. And being able to disable labels # wasn't possible until tk 8.3. (makes me wonder # why I chose to use a label, but that answer is # lost to antiquity) if {$::tcl_version >= 8.3} { $widgets(button) configure -state disabled } } else { set options($option) $oldValue set message "bad state value \"$newValue\";" append message " must be normal or disabled" error $message } set options($option) $newValue } -takefocus { $widgets(entry) configure -takefocus $newValue set options($option) $newValue } -textvariable { $widgets(entry) configure -textvariable $newValue set options($option) $newValue } -value { ::combobox::SetValue $widgets(this) $newValue set options($option) $newValue } -width { $widgets(entry) configure -width $newValue $widgets(listbox) configure -width $newValue set options($option) $newValue } -xscrollcommand { $widgets(entry) configure -xscrollcommand $newValue set options($option) $newValue } } if {$updateVisual} { UpdateVisualAttributes $w } } } # ::combobox::UpdateVisualAttributes -- # # sets the visual attributes (foreground, background mostly) # based on the current state of the widget (normal/disabled, # editable/non-editable) # # why a proc for such a simple thing? Well, in addition to the # various states of the widget, we also have to consider the # version of tk being used -- versions from 8.4 and beyond have # the notion of disabled foreground/background options for various # widgets. All of the permutations can get nasty, so we encapsulate # it all in one spot. # # note also that we don't handle all visual attributes here; just # the ones that depend on the state of the widget. The rest are # handled on a case by case basis # # Arguments: # w widget pathname # # Returns: # empty string proc ::combobox::UpdateVisualAttributes {w} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options if {$options(-state) == "normal"} { set foreground $options(-foreground) set background $options(-background) } elseif {$options(-state) == "disabled"} { set foreground $options(-disabledforeground) set background $options(-disabledbackground) } $widgets(entry) configure -foreground $foreground -background $background $widgets(listbox) configure -foreground $foreground -background $background $widgets(button) configure -foreground $foreground $widgets(vsb) configure -background $background -troughcolor $background $widgets(frame) configure -background $background # we need to set the disabled colors in case our widget is disabled. # We could actually check for disabled-ness, but we also need to # check whether we're enabled but not editable, in which case the # entry widget is disabled but we still want the enabled colors. It's # easier just to set everything and be done with it. if {$::tcl_version >= 8.4} { $widgets(entry) configure -disabledforeground $foreground \ -disabledbackground $background $widgets(button) configure -disabledforeground $foreground $widgets(listbox) configure -disabledforeground $foreground } } # ::combobox::SetValue -- # # sets the value of the combobox and calls the -command, # if defined # # Arguments: # # w widget pathname # newValue the new value of the combobox # # Returns # # Empty string proc ::combobox::SetValue {w newValue} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options upvar ::combobox::${w}::ignoreTrace ignoreTrace upvar ::combobox::${w}::oldValue oldValue if {[info exists options(-textvariable)] && [string length \ $options(-textvariable)] > 0} { set variable ::$options(-textvariable) set $variable $newValue } else { set oldstate [$widgets(entry) cget -state] $widgets(entry) configure -state normal $widgets(entry) delete 0 end $widgets(entry) insert 0 $newValue $widgets(entry) configure -state $oldstate } # set our internal textvariable; this will cause any public # textvariable (ie: defined by the user) to be updated as # well # set ::combobox::${w}::entryTextVariable $newValue # redefine our concept of the "old value". Do it before running # any associated command so we can be sure it happens even # if the command somehow fails. set oldValue $newValue # call the associated command. The proc will handle whether or # not to actually call it, and with what args CallCommand $w $newValue return "" } # ::combobox::CallCommand -- # # calls the associated command, if any, appending the new # value to the command to be called. # # Arguments: # # w widget pathname # newValue the new value of the combobox # # Returns # # empty string proc ::combobox::CallCommand {w newValue} { upvar ::combobox::${w}::widgets widgets upvar ::combobox::${w}::options options # call the associated command, if defined and -commandstate is # set to "normal" if {$options(-commandstate) == "normal" && [string length \ $options(-command)] > 0} { set args [list $widgets(this) $newValue] uplevel \#0 $options(-command) $args } } # ::combobox::GetBoolean -- # # returns the value of a (presumably) boolean string (ie: it should # do the right thing if the string is "yes", "no", "true", 1, etc # # Arguments: # # value value to be converted # errorValue a default value to be returned in case of an error # # Returns: # # a 1 or zero, or the value of errorValue if the string isn't # a proper boolean value proc ::combobox::GetBoolean {value {errorValue 1}} { if {[catch {expr {([string trim $value]) ?1:0}} res]} { return $errorValue } else { return $res } } # ::combobox::convert -- # # public routine to convert %x, %y and %W binding substitutions. # Given an x, y and or %W value relative to a given widget, this # routine will convert the values to be relative to the combobox # widget. For example, it could be used in a binding like this: # # bind .combobox {doSomething [::combobox::convert %W -x %x]} # # Note that this procedure is *not* exported, but is intended for # public use. It is not exported because the name could easily # clash with existing commands. # # Arguments: # # w a widget path; typically the actual result of a %W # substitution in a binding. It should be either a # combobox widget or one of its subwidgets # # args should one or more of the following arguments or # pairs of arguments: # # -x will convert the value ; typically will # be the result of a %x substitution # -y will convert the value ; typically will # be the result of a %y substitution # -W (or -w) will return the name of the combobox widget # which is the parent of $w # # Returns: # # a list of the requested values. For example, a single -w will # result in a list of one items, the name of the combobox widget. # Supplying "-x 10 -y 20 -W" (in any order) will return a list of # three values: the converted x and y values, and the name of # the combobox widget. proc ::combobox::convert {w args} { set result {} if {![winfo exists $w]} { error "window \"$w\" doesn't exist" } while {[llength $args] > 0} { set option [lindex $args 0] set args [lrange $args 1 end] switch -exact -- $option { -x { set value [lindex $args 0] set args [lrange $args 1 end] set win $w while {[winfo class $win] != "Combobox"} { incr value [winfo x $win] set win [winfo parent $win] if {$win == "."} break } lappend result $value } -y { set value [lindex $args 0] set args [lrange $args 1 end] set win $w while {[winfo class $win] != "Combobox"} { incr value [winfo y $win] set win [winfo parent $win] if {$win == "."} break } lappend result $value } -w - -W { set win $w while {[winfo class $win] != "Combobox"} { set win [winfo parent $win] if {$win == "."} break } lappend result $win } } } return $result } # ::combobox::Canonize -- # # takes a (possibly abbreviated) option or command name and either # returns the canonical name or an error # # Arguments: # # w widget pathname # object type of object to canonize; must be one of "command", # "option", "scan command" or "list command" # opt the option (or command) to be canonized # # Returns: # # Returns either the canonical form of an option or command, # or raises an error if the option or command is unknown or # ambiguous. proc ::combobox::Canonize {w object opt} { variable widgetOptions variable columnOptions variable widgetCommands variable listCommands variable scanCommands switch -- $object { command { if {[lsearch -exact $widgetCommands $opt] >= 0} { return $opt } # command names aren't stored in an array, and there # isn't a way to get all the matches in a list, so # we'll stuff the commands in a temporary array so # we can use [array names] set list $widgetCommands foreach element $list { set tmp($element) "" } set matches [array names tmp ${opt}*] } {list command} { if {[lsearch -exact $listCommands $opt] >= 0} { return $opt } # command names aren't stored in an array, and there # isn't a way to get all the matches in a list, so # we'll stuff the commands in a temporary array so # we can use [array names] set list $listCommands foreach element $list { set tmp($element) "" } set matches [array names tmp ${opt}*] } {scan command} { if {[lsearch -exact $scanCommands $opt] >= 0} { return $opt } # command names aren't stored in an array, and there # isn't a way to get all the matches in a list, so # we'll stuff the commands in a temporary array so # we can use [array names] set list $scanCommands foreach element $list { set tmp($element) "" } set matches [array names tmp ${opt}*] } option { if {[info exists widgetOptions($opt)] && \ [llength $widgetOptions($opt)] == 2} { return $opt } set list [array names widgetOptions] set matches [array names widgetOptions ${opt}*] } } if {[llength $matches] == 0} { set choices [HumanizeList $list] error "unknown $object \"$opt\"; must be one of $choices" } elseif {[llength $matches] == 1} { set opt [lindex $matches 0] # deal with option aliases switch -- $object { option { set opt [lindex $matches 0] if {[llength $widgetOptions($opt)] == 1} { set opt $widgetOptions($opt) } } } return $opt } else { set choices [HumanizeList $list] error "ambiguous $object \"$opt\"; must be one of $choices" } } # ::combobox::HumanizeList -- # # Returns a human-readable form of a list by separating items # by columns, but separating the last two elements with "or" # (eg: foo, bar or baz) # # Arguments: # # list a valid tcl list # # Results: # # A string which as all of the elements joined with ", " or # the word " or " proc ::combobox::HumanizeList {list} { if {[llength $list] == 1} { return [lindex $list 0] } else { set list [lsort $list] set secondToLast [expr {[llength $list] -2}] set most [lrange $list 0 $secondToLast] set last [lindex $list end] return "[join $most {, }] or $last" } } # This is some backwards-compatibility code to handle TIP 44 # (http://purl.org/tcl/tip/44.html). For all private tk commands # used by this widget, we'll make duplicates of the procs in the # combobox namespace. # # I'm not entirely convinced this is the right thing to do. I probably # shouldn't even be using the private commands. Then again, maybe the # private commands really should be public. Oh well; it works so it # must be OK... foreach command {TabToWindow CancelRepeat ListboxUpDown} { if {[llength [info commands ::combobox::tk$command]] == 1} break set tmp [info commands tk$command] set proc ::combobox::tk$command if {[llength [info commands tk$command]] == 1} { set command [namespace which [lindex $tmp 0]] proc $proc {args} "uplevel $command \$args" } else { if {[llength [info commands ::tk::$command]] == 1} { proc $proc {args} "uplevel ::tk::$command \$args" } } } # end of combobox.tcl ###################################################################### # icon image data. ###################################################################### image create bitmap delta48 -data { #define delta48_width 48 #define delta48_height 48 static char delta48_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x13, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x03, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x11, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x11, 0x68, 0x00, 0x00, 0x00, 0x80, 0x10, 0xc8, 0x00, 0x00, 0x00, 0x80, 0x10, 0xa8, 0x01, 0x00, 0x00, 0x80, 0x08, 0x08, 0x01, 0x00, 0x00, 0x80, 0x08, 0xac, 0x03, 0x00, 0x00, 0x80, 0x09, 0x06, 0x02, 0x00, 0x00, 0xc0, 0x09, 0xaa, 0x06, 0x00, 0x00, 0x40, 0x09, 0x01, 0x04, 0x00, 0x00, 0xe0, 0x93, 0xae, 0x0a, 0x00, 0x00, 0x30, 0x92, 0x06, 0x18, 0x00, 0x00, 0xb0, 0x92, 0xad, 0x1a, 0x00, 0x00, 0x18, 0x53, 0x04, 0x30, 0x00, 0x00, 0xa8, 0x11, 0xac, 0x2a, 0x00, 0x00, 0x0c, 0x12, 0x04, 0x60, 0x00, 0x00, 0xac, 0x12, 0xac, 0x6a, 0x00, 0x00, 0x02, 0x14, 0x04, 0x80, 0x00, 0x00, 0xab, 0x0a, 0xae, 0xaa, 0x01, 0x00, 0x01, 0x28, 0x02, 0x00, 0x01, 0x80, 0xab, 0x3a, 0xaf, 0xaa, 0x03, 0x80, 0x00, 0x70, 0x0c, 0x00, 0x02, 0xc0, 0xaa, 0x5a, 0xa8, 0xaa, 0x06, 0x40, 0x00, 0xa0, 0x08, 0x00, 0x0c, 0xa0, 0xaa, 0xea, 0xac, 0xaa, 0x0a, 0x30, 0x00, 0x80, 0x05, 0x00, 0x18, 0xb0, 0xaa, 0xaa, 0xab, 0xaa, 0x1a, 0x08, 0x00, 0x00, 0x04, 0x00, 0x30, 0xfc, 0xff, 0xff, 0xbe, 0xff, 0x7f, 0xfc, 0xff, 0xff, 0xbd, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } } image create photo deltaGif -format gif -data { R0lGODlhMAAwAOcAAAIyRsQWGJ4eIIYrK0aK4jZ2yXIkKOJ2hhpKgqZ+isJW Y1IuNpBebKJifi5qumpKUsQ0OE4ySkJiip5SXoZWXnpWahZGfp5GUideptrG yopSWtpOVnp+kiY6XiVaoIJGTphKVjxMcJ45Pso6PiFWmgYyZt5SWroeHrau tmaClu4eJsoeHlJmhts+Q3JCSi5Kepo2PpKGkiBSlqIiIuaWllY6QrQmJmJy kjp6zk6W9h5Ojx4yPh1Cc5YeIspaatoiJvJcaE5eekpihpR2gMIiItteZz6C 2nZqfuY2Pu9MVG5aYq4iIutGTqJOXkY+SuKKkgc6c4YyMkqO6cZCUgI2Vipi rV4iInI2Pq5yepIiIsIrK6ErLBpKhrY+RsozM052uhIuTsJqhrJebu5UWLRM WopWbr4iIl5mjtpCRE6G2u5eatVibqwcHjZGVoAoKr52ftoSEmo2SsYmKlaS 5pYiItZWbrpucppialKO3m5GUscqKyBGfudKUc8yMyZKeh46YiZGag42Ts46 PjZyxKcqK+JCSMpCSu7Gxh5Skp5CRtU+Px9OiUI+YropKUSC2ppOWm4eHpIp KbV/h85qcsYuLnZOYospKoZmcqlaY5ZWWnZiZuYoLl5Scm4qLlSW8jZCXpgm KIJCStpibAI2TrYuMK4pKp4yNtpaZr58hk6O5t1GSNE2N85CRopSciJKfuY+ Rgo2UuJOWj5+1CpmsLJkcn4uNi5QgEWG4ZI+RBpGgNU6PEZOcpAyNL5OWoBQ ZJJKZqpSYi5eovKWnmpaflea+CZSjjFuvU2K3couMLpkcnIqLuJaXnpyhupa Zj5Gar4uLpIuMo4uLuFLT84qKk6S7JZZZ6klJ/ZKUuJmdjZGah5WorUqKqY2 QudOUpkqK4ZabnJaegI2XiNGdspqguJGS7IaHuJeZuJaYlKS7SpGbkyK5Op0 fN5CRY5mctZESDZShtZmbhpKir4qKi5mtAIyUtpGUuBYbh5KgIJ6im5+ltIe IlpmgnpCSuIiJgY4Tv///yH5BAEKAP8ALAAAAAAwADAAAAj+AP8JHEiwoMGD CBMqXJgwFMOHEA0usIJpSMSLC10oMzCAFsaPBx/0yOJmwBuQKAXmccNmgBte WFKCrFFLgIABlkRckvmRwoByloJO4PmxUxY2lgww+kI04hVILGdAiyPDGLWm DD8M2FoOFLcLw4LJIoZVIS6c0U54AwWjQrFZUsoi1MDLUqQlS7ZYYxDiHQkc 6OQafBQtkrcA1rZ0wQQi2b0q6gQXTBTJMBxr1kpZs1FHCIlBniQLZCDCmzdr AQiV2rbtmah8izDcCiwZk6ktW2ysWL1NHqU1N7ggchBXMjDchIiY2dZIXiNk ayRwiecBB1m5+MhsUa2vUXM9lFb+jVuEQDhkwczIqN72Q54WLZT0CEJ1LxeC eCQK0MZ6ptfqRv3IAx4llLByBAL2caGDbGVRw4ICpDQihwrwUYIMBJeIg8CG uXBRVQ5YGWELLRA0oscmyCDjBSXV8MAhhzp4YAQeRKEzzyKVGKLFNJtYiEwy QQCSzYZECjfPVTJ5IgsJ8YSggBd9IOGFF2vEkIIkkyBo34b4yZKKTARUoUMu PHyzQR+veLGKKKjYMQI2RCJYng5V3JKSFIOQwEUuEfCjSB98rLKKIl4go0sR RNqX4GfFfeQIBotwcQ8IpOixyhiCrqKLLoogumWiCzoCEjUOILInAmW404cX mG7KqS7+9myp6IbCOfDlRcfI4kGkG3JyiqBJbKpICy3oYk6csk7ngSzHXCTF LDqcmosrDUjThzRocKoIGvU88WKcc84SGUTEFMDkt9mEUY8qfCiyLTvL7APu rPfldx1Dt2Aw5ry7iMJOEu/qQoO89M6qIIMMoWOMDNK+mAs4zSTBDhrsrLOP OOJ8imx5Ve2XkBEeRJtoos6okQQ57MQiTDoJSJKOnDDTGqMRzSZEzTymahyn M+e4wwTK6xQRywEvjCxngjrMM+5BqSwZj6ww27eHL7FcQw450rRQCCp7OLxx l0gadAu0XHzrtTitAMGENFgXMoTZZis4i50GEZNn2RrrjMCzHmXwwUc30sBj y8h50yrDIHMUlEa+vG589NG/MKGKCRzo/TiCXMR2SxoESWEMIhaELvropIf+ BzdqNMFD6ayLPlyjnuCACBQl1F57OLbnrnsHU/gAhu7A6w4FIjjULIs2UNAz CgCjNN8888s7Lz0ATtijhD/RQy/9883TA4U2svzjCPLhUGH++einnz4sl4ji yw6B+KP+/OF8LypRbWgiBjwKVPOJaA+5Ay4yAcACGhAiAQEAOw== } image create photo findImage -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAjUAAMIHEiwoEF3 AOQpXMiQIQB3ARC6a6fO3buHAiVWfAcPYwB1AN6pa/fQnUkAIy+qEwiy3bp07DqaPPmS3TqS Kz/SA8ATQDyB8XoCoJczI4B2F+VBjCjvocyBCNOVS9cxAE+rUqliRHhznbunEY96dbl15kyC Zs8OrDgzJ1uTRVnSYzcO5M8AQeu6I0oQ5DukAOAJlglPJVR5gBMifNjUqTyoAM6NK1f1auTJ YDuuOxdTKM/NneGFHVkRLEKKE0GeFGzRdODWMhd7Xipb6FKDuAsGBAA7 } image create photo centerDiffsImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiUAAMIHBjAHYCD ANwRHHjOncOHBgkRSgjRYUOEGAEYMpQRoUMA/8SJFGdwY0JyKFFSBGCuZcuSHN25bLmyo0aO Nj+GJAkg0caNiU6q/DjToE9DQWW6rNkxUdCcBneONHhy5FCDM106zErzo82vB3XuTEm27Equ aJd6BQsVpFSRZcmeTYuWKduM7hpW3Lv33MK/gAUGBAA7 } image create photo firstDiffImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiUAAMIdFevoMGD Bd0JXBig3j9ChAxJnDixHkOBDilqlGjxIkGEIBVevHjOnbtzI1MKLAkAwEmVJN0BIKTIJUqY AVgS+neo5kuVOv9J7Gkzpc5BFIn+XHg06SGlN1fKbDlTYiKqRRmWNFnV0FWTS7XqtGoz6six XrMClRkxbdizbMm+jQngUKK7ao1OxTo3JliTZgUGBAA7 } image create photo prevDiffImage -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAiGAAMIHCjwnDt3 5wgqLHjQHQBChgwlAtAw4cIABh9GnIjwIsOH/yIeUkTR4sWMECWW9DgQJcmOJx0SGhRR5KGR Kxei3JjT406VMH06BECUaFCWGXsilfkP51GCKGnWdGryY9GUE4s+xfiT47mqCrsq1SmT51ao ZYGCDevwUKK3Y8k2PLg2IAA7 } image create photo nextDiffImage -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAiGAAMIHHjOncGD 5wYqVFgQACFDhhIBcJdwIUN3DgsdUjSxokWBDR9G7PixIYCTIiWeJGmx4T9ChA6x/BggJESJ FGnWtDmSoseLGSFC3DizJMaiNE2uRLrQ5U2mQFNCJYhRak6dPHH+vGjQ4VOETasWEmrokFmO V6OOLYt2a1iHbXWGTbswIAA7 } image create photo lastDiffImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiTAAMIHHjOncGD 5wYqVFgQgMOH7hIuZOgOwD9ChA4BiDiRokVDhhJtlNgxQENCIEVyLGmyIsqQI1meO5lyJEmK BgG8VGnwZsuHOmtCvHmyEEiQh5IqiumRkNGjh5auXFgUqVSfTQtFZSrT5VWWHrmCFVhwakl3 9dKqXZvW3cR6F18enVvv7b+5eEHWXYiWrV+3AgMCADs= } image create photo rediffImage -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQCrPQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAicAAMIHEiwoMF0 7AD0euVKl8OHrhjqAgDvnDsAGDOmG2jR3TmDIAVaxFiRoMJXKF/1ypgR5UqPIWOCTIfQnc2b ABpS/Bgg3cmUQIOqBHBxIUpYADYKLEqUp8ynUKMatFgy5LmrWEdOrDoQIcuvrnSWPJfQqFCg YhPCAtqrrduUL8/9fIWUJs2LQ2EGmFt34MWmBNPdvKlUquEAAQEAOw== } image create photo markSetImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1Pjisd/UjtHJ a8O4SL2qJcWqAK+SAJN6AGJiAEpKADIyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiZAAMIHEhQoLqD CAsqFAigIQB3Dd0tNKjOXSxXrmABWBABgLqCByECuAir5EYJHimKvOgqFqxXrzZ2lBhgJUaY LV/GOpkSIqybOF3ClPlQIEShMF/lfLVzAcqPRhsKXRqTY1GCFaUy1ckTKkiRGhtapTkxa82u ExUSJZs2qtOUbQ2ujTsQ4luvbdXNpRtA712+UeEC7ou3YEAAADt= } image create photo markClearImage -format gif -data { R0lGODlhFAAUAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1Pjisd/UjtHJ a8O4SL2qJcWqAK+SAJN6AGJiAEpKADIyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAAAiwAAMIHEhQoLqD CAsCWKhwIbyFANwNXBiD4UF3sVw9rLhQXQCKNTguzLgxZMePMWqo5OgqVkmVNwAIXHhDpUl3 7gCkhMkwJ02bHHfWiCkzQM5YP1cKJepRoM+kNoculEhQXc6cNW3GzNm0oFWdUSviLDgRbFST RRsuzYpWrVaoHMsujYgVKMOPUYkCWPCQbY2iP/UuiACgr9S0NDvulQBAXd+7ZYv6bPowLdmB By8LDAgAOw== } image create photo mergeChoice1Image -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAiIAAMIHEiwYMFz 7gAQ+meoIaGHECEeAuDuoDt35wxqFIgQAMWMGzkmVHRooseTKD1WPAgy5MCOhAZRvEizJsaR hxrq3LkzEcWXIz+eG0qUqMujSJMixJg0AEyhRYuKVDjIUMqrMxUy5MnVkM+bAEgaOpSorNmz X6eSnGmzZkunCT825fh2btKAADt= } image create photo mergeChoice2Image -format gif -data { R0lGODdhFAAUAPf/AAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8P/w1P/isf/Ujv/G a/+4SP+qJf+qANySALl6AJZiAHNKAFAyAP/j1P/Hsf+rjv+Pa/9zSP9XJf9VANxJALk9AJYx AHMlAFAZAP/U1P+xsf+Ojv9ra/9ISP8lJf4AANwAALkAAJYAAHMAAFAAAP/U4/+xx/+Oq/9r j/9Ic/8lV/8AVdwASbkAPZYAMXMAJVAAGf/U8P+x4v+O1P9rxv9IuP8lqv8AqtwAkrkAepYA YnMASlAAMv/U//+x//+O//9r//9I//8l//4A/twA3LkAuZYAlnMAc1AAUPDU/+Kx/9SO/8Zr /7hI/6ol/6oA/5IA3HoAuWIAlkoAczIAUOPU/8ex/6uO/49r/3NI/1cl/1UA/0kA3D0AuTEA liUAcxkAUNTU/7Gx/46O/2tr/0hI/yUl/wAA/gAA3AAAuQAAlgAAcwAAUNTj/7HH/46r/2uP /0hz/yVX/wBV/wBJ3AA9uQAxlgAlcwAZUNTw/7Hi/47U/2vG/0i4/yWq/wCq/wCS3AB6uQBi lgBKcwAyUNT//7H//47//2v//0j//yX//wD+/gDc3AC5uQCWlgBzcwBQUNT/8LH/4o7/1Gv/ xkj/uCX/qgD/qgDckgC5egCWYgBzSgBQMtT/47H/x47/q2v/j0j/cyX/VwD/VQDcSQC5PQCW MQBzJQBQGdT/1LH/sY7/jmv/a0j/SCX/JQD+AADcAAC5AACWAABzAABQAOP/1Mf/sav/jo// a3P/SFf/JVX/AEncAD25ADGWACVzABlQAPD/1OL/sdT/jsb/a7j/SKr/Jar/AJLcAHq5AGKW AEpzADJQAP//1P//sf//jv//a///SP//Jf7+ANzcALm5AJaWAHNzAFBQAPLy8ubm5tra2s7O zsLCwra2tqqqqp6enpKSkoaGhnp6em5ubmJiYlZWVkpKSj4+PjIyMiYmJhoaGg4ODv/78KCg pICAgP8AAAD/AP//AAAA//8A/wD//////yH5BAEAAAEALAAAAAAUABQAQAiNAAMIHEiwYEF3 AP79GzSIkMOHhAwZKkQIgLtzBguec3cxo8eNACxiHIgwpMmTIQ8dUiTSo8aRBDdynEkTIcWW ARBGlMizJ8+VFgOcG0q0KEKWHV0qXcp0qUyYA4tKBVkxaU6UWAFMrIoR4SCfYCXe5AjgUKKz aNMeMgT0osyaNMsihfqxpNWmQ5s2DQgAOw== } image create photo mergeChoice12Image -format gif -data { R0lGODlhFAAUAPMHAAAAAAB6uQCS3CWq/0i4/47U/7Hi/////729vQAAAAAAAAAAAAAAAAAAAAAA AAAAACH5BAEAAAgALAAAAAAUABQAAAT+ECGEECgAIYQQggghhBBCCIFiAEQIIYQQQgghhCACxRAA AAAAAAABAAghUA4hpBRYSimllAEQAuVAQgghhBBCCCECAoRAGIQQQgghkBBCiAAIIRAGgUMIIYQQ QggBEEQIgTAGAAAAACAAAACEEEIgDAARQgghhBBCCCGIEAIBIIQQQghBhBBCCCGEEEIIIgQKQAgh hBBCECGEEEIImAIQggghAAAAAAAAAATEFIAQQmCUUmAppZRCCDkFIAQREIQQQgghhBBIyCkAISAI IYRAQgghhJARAEIACiGEEEIIIQYZMACEEAAAAAAAgACAMQJACCGEEEQIIYQQAiMAhCAPQgghhBBC CCEEQQAIIYQiADs= } image create photo mergeChoice21Image -format gif -data { R0lGODlhFAAUAPMHAAAAAAB6uQCS3CWq/0i4/47U/7Hi/////729vQAAAAAAAAAAAAAAAAAAAAAA AAAAACH5BAEAAAgALAAAAAAUABQAAAT+ECGEEEIIIYRAgQAhhBBCCCGEEEQIIWAKQAghBCAAAAAA AACAmAIBQgiBUUoppRRYCiHkFIAQAoJAQgghhBBCCDkFAoSAIIQQQgghkBBCRgAIASGEgEIIIYQY ZASAEEQAAAAAAAAAMOAIACGEEEIIIQQRQgiMABBCCCGIEEIIIYQQCABBhBBCCCEECkAIIoQQQggh hBBCEBQDEEIIIYQQggghhEAxBAAAAAQAAAAAQgiUQyAhpZRSSillAAQRKIcQQgghhBBICBEAIRAG IYRAQgghhBAiAEIIgjDIEEIIIYQQUAiAEEIgjAEAgAAAAAAAACGEEARhAIQQQgghhCAPQgghhEAA CCEEEUIIIYQiADs= } image create photo nullImage image create bitmap resize -data { #define resize_width 14 #define resize_height 11 static char resize_bits[] = { 0x20, 0x01, 0x30, 0x03, 0x38, 0x07, 0x3c, 0x0f, 0x3e, 0x1f, 0x3f, 0x3f, 0x3e, 0x1f, 0x3c, 0x0f, 0x38, 0x07, 0x30, 0x03, 0x20, 0x01 } } # Tooltip popups # # tooltips version 0.1 # Paul Boyer # Science Applications International Corp. # ############################## # set_tooltips gets a button's name and the tooltip string as # arguments and creates the proper bindings for entering # and leaving the button ############################## proc set_tooltips {widget name} { global g bind $widget " catch { after 500 { internal_tooltips_PopUp %W $name } } g(tooltip_id) " bind $widget "internal_tooltips_PopDown" bind $widget "internal_tooltips_PopDown" } ############################## # internal_tooltips_PopUp is used to activate the tooltip window ############################## proc internal_tooltips_PopUp {wid name} { global g # get rid of other existing tooltips catch {destroy .tooltips_wind} toplevel .tooltips_wind -class ToolTip set size_changed 0 set bg [option get .tooltips_wind background background] set fg [option get .tooltips_wind foreground foreground] # get the cursor position set X [winfo pointerx $wid] set Y [winfo pointery $wid] # add a slight offset to make tooltips fall below cursor set Y [expr {$Y + 20}] # Now pop up the new widgetLabel wm overrideredirect .tooltips_wind 1 wm geometry .tooltips_wind +$X+$Y label .tooltips_wind.l -text $name -border 2 -relief raised \ -background $bg -foreground $fg pack .tooltips_wind.l # make invisible wm withdraw .tooltips_wind update idletasks # adjust for bottom of screen if {($Y + [winfo reqheight .tooltips_wind]) > [winfo screenheight .]} { set Y [expr {$Y - [winfo reqheight .tooltips_wind] - 25}] set size_changed 1 } # adjust for right border of screen if {($X + [winfo reqwidth .tooltips_wind]) > [winfo screenwidth .]} { set X [expr {[winfo screenwidth .] - [winfo reqwidth .tooltips_wind]}] set size_changed 1 } # reset position if {$size_changed == 1} { wm geometry .tooltips_wind +$X+$Y } # make visible wm deiconify .tooltips_wind # make tooltip dissappear after 5 sec set g(tooltip_id) [after 5000 { internal_tooltips_PopDown }] } proc internal_tooltips_PopDown {} { global g after cancel $g(tooltip_id) catch {destroy .tooltips_wind} } proc get_gtk_params { } { global w global tk_version if {! [llength [auto_execok xrdb]]} { return 0 } set pipe [open "|xrdb -q" r] while {[gets $pipe ln] > -1} { switch -glob -- $ln { {\*Toplevel.background:*} { #puts $ln set bg [lindex $ln 1] } {\*Toplevel.foreground:*} { #puts $ln set fg [lindex $ln 1] } {\*Text.background:*} { #puts $ln set textbg [lindex $ln 1] } {\*Text.foreground:*} { #puts $ln set textfg [lindex $ln 1] } {\*Text.selectBackground:*} { #puts $ln set hlbg [lindex $ln 1] } {\*Text.selectForeground:*} { #puts $ln set hlfg [lindex $ln 1] } } } close $pipe if {! [info exists bg] || ! [info exists fg]} { return 0 } set w(selcolor) $hlbg option add *Entry.Background $textbg option add *Entry.Foreground $textfg option add *Entry.selectBackground $hlbg option add *Entry.selectForeground $hlfg option add *Entry.readonlyBackground $bg option add *Listbox.background $textbg option add *Listbox.selectBackground $hlbg option add *Listbox.selectForeground $hlfg option add *Text.Background $textbg option add *Text.Foreground $textfg option add *Text.selectBackground $hlbg option add *Text.selectForeground $hlfg # Menu checkboxes if {$tk_version >= 8.5} { option add *Menu.selectColor $fg option add *Checkbutton.selectColor "" option add *Radiobutton.selectColor "" } else { option add *selectColor $w(selcolor) } return 1 } proc get_cde_params {} { global tk_version global w # Set defaults for all the necessary things set bg [option get . background background] set fg [option get . foreground foreground] set guifont [option get . buttonFontList buttonFontList] set txtfont [option get . FontSet FontSet] set listfont [option get . textFontList textFontList] set textbg white set textfg black # If any of these aren't set, I don't think we're in CDE after all if {![string length $fg]} { return 0 } if {![string length $bg]} { return 0 } if {![string length $guifont]} { # For AIX set guifont [option get . FontList FontList] } if {![string length $guifont]} { return 0 } if {![string length $txtfont]} { return 0 } set guifont [string trimright $guifont ":"] set txtfont [string trimright $txtfont ":"] set listfont [string trimright $txtfont ":"] regsub {medium} $txtfont "bold" dlgfont # They don't tell us the slightly darker color they use for the # scrollbar backgrounds and graphics backgrounds, so we'll make # one up. set rgb_bg [winfo rgb . $bg] set shadow [format #%02x%02x%02x [expr {(9*[lindex $rgb_bg 0]) /2560}] \ [expr {(9*[lindex $rgb_bg 1]) /2560}] [expr {(9*[lindex $rgb_bg 2]) \ /2560}]] # If we can find the user's dt.resources file, we can find out the # palette and background/foreground colors set fh "" set palette "" set cur_rsrc ~/.dt/sessions/current/dt.resources set hom_rsrc ~/.dt/sessions/home/dt.resources if {[file readable $cur_rsrc] && [file readable $hom_rsrc]} { # Both exist. Use whichever is newer if {[file mtime $cur_rsrc] > [file mtime $hom_rsrc]} { if {[catch {open $cur_rsrc r} fh]} { set fh "" } } else { if {[catch {open $hom_rsrc r} fh]} { set fh "" } } } elseif {[file readable $cur_rsrc]} { if {[catch {open $cur_rsrc r} fh]} { set fh "" } } elseif {[file readable $hom_rsrc]} { if {[catch {open $hom_rsrc r} fh]} { set fh "" } } if {[string length $fh]} { set palf "" while {[gets $fh ln] != -1} { regexp "^\\*background:\[ \t]*(.*)\$" $ln nil textbg regexp "^\\*foreground:\[ \t]*(.*)\$" $ln nil textfg regexp "^\\*0\\*ColorPalette:\[ \t]*(.*)\$" $ln nil palette regexp "^Window.Color.Background:\[ \t]*(.*)\$" $ln nil textbg regexp "^Window.Color.Foreground:\[ \t]*(.*)\$" $ln nil textfg } catch {close $fh} # # If the *0*ColorPalette setting was found above, try to find the # indicated file in ~/.dt, $DTHOME, or /usr/dt. # if {[string length $palette]} { foreach dtdir {/usr/dt /etc/dt ~/.dt} { # This uses the last palette that we find if {[file readable [file join $dtdir palettes $palette]]} { set palf [file join $dtdir palettes $palette] } } # debug-info "Using palette $palf" if {[string length $palf]} { if {![catch {open $palf r} fh]} { gets $fh activetitle gets $fh inactivetitle gets $fh wkspc1 gets $fh textbg gets $fh guibg ;#(*.background) - default for tk under cde gets $fh menubg gets $fh wkspc4 gets $fh iconbg ;#control panel bg too close $fh option add *Text.highlightColor $wkspc4 option add *Dialog.Background $menubg option add *Menu.Background $menubg option add *Menu.activeBackground $menubg option add *Menu.activeForeground $fg option add *Menubutton.Background $menubg option add *Menubutton.activeBackground $menubg option add *Menubutton.activeForeground $fg } } } } else { puts stderr "Neither ~/.dt/sessions/current/dt.resources nor" puts stderr " ~/.dt/sessions/home/dt.resources was readable" puts stderr " Falling back to plain X" return 0 } if {[info exists activetitle]} { set hlbg $activetitle } else { set hlbg "#b24d7a" } set w(selcolor) $hlbg option add *Button.activeBackground $bg option add *Button.activeForeground $fg option add *Canvas.Background $shadow option add *Canvas.Foreground black option add *Entry.Background $textbg option add *Entry.Foreground $textfg option add *Entry.readonlyBackground $bg option add *Entry.highlightBackground $bg option add *Entry.highlightColor $hlbg option add *Listbox.background $textbg option add *Listbox.selectBackground $w(selcolor) option add *Listbox.selectForeground $fg option add *Menu.borderWidth 1 option add *Scrollbar.activeBackground $bg option add *Scrollbar.troughColor $shadow option add *Text.Background $textbg option add *Text.Foreground $textfg option add *Text.highlightBackground $bg # Menu checkboxes if {$tk_version >= 8.5} { # This makes it look like the native CDE checkbox option add *Menu.selectColor $fg option add *Checkbutton.offRelief sunken option add *Checkbutton.selectColor "" option add *Radiobutton.selectColor "" option add *Checkbutton.activeBackground $bg option add *Checkbutton.activeForeground $fg } else { option add *selectColor $w(selcolor) } # Suppress the border option add *HighlightThickness 0 userDefault # Add it back for text and entry widgets option add *Text.HighlightThickness 2 userDefault option add *Entry.HighlightThickness 1 userDefault return 1 } # Maybe this could be enhanced to get configs from themes and so on? # Right now it just sets colors so everything isn't blinding white. proc get_aqua_params {} { global w # button highlightbackground has to be the same as background # or else there are little white boxes around the button "pill" option add *background #ebebeb userDefault option add *Button.highlightBackground #ebebeb userDefault # That totally screws up the menus unless you fix it with this option add *Menu.Background white option add *Menu.Foreground black option add *Entry.HighlightThickness 2 userDefault option add *Entry.highlightBackground $w(selcolor) userDefault option add *Canvas.background #eeeeee userDefault option add *Entry.background #ffffff userDefault option add *Text.background white userDefault } ############################################################################### # run the main proc main tkcvs-8.2.3.orig/doinstall.tcl0000755000175000017500000001304111664612512014464 0ustar timtim#!/bin/sh #-*-tcl-*- # the next line restarts using wish \ if [ -z "$DISPLAY" -o "X$1" = "X-nox" ]; then exec tclsh "$0" -- ${1+"$@"}; else exec wish "$0" -- ${1+"$@"}; fi # # Usage: doinstall.tcl [-nox] [destination] # # For a non-interactive installation which doesn't require an X server, do ## doinstall.tcl -nox /usr/local # proc set_paths {INSTALLROOT} { global tcl_platform global LIBDIR BINDIR MANDIR if {$tcl_platform(platform) == "windows"} { set BINDIR [file join $INSTALLROOT bin] set LIBDIR [file join $INSTALLROOT lib] set MANDIR "" } else { set BINDIR [file join $INSTALLROOT bin] set LIBDIR [file join $INSTALLROOT lib] set MANDIR [file join $INSTALLROOT man man1] } } proc show_paths {INSTALLROOT} { global tcl_platform global TKCVS TKDIFF global LIBDIR BINDIR MANDIR set_paths $INSTALLROOT set msg(1) [file join $BINDIR $TKCVS] set msg(2) [file join $BINDIR $TKDIFF] set msg(3) [file join $LIBDIR tkcvs *.tcl] set msg(4) [file join $LIBDIR tkcvs bitmaps *.gif,xbm] if {$tcl_platform(platform) == "unix"} { set msg(5) [file join $MANDIR tkcvs.1] } foreach m [lsort [array names msg]] { if {[winfo exists .messages.$m]} { destroy .messages.$m } global var$m set var$m $msg($m) label .messages.$m -text $msg($m) -justify left -textvariable var$m pack .messages.$m -side top -anchor w } } proc doinstall { INSTALLROOT } { global tcl_platform global TKCVS TKDIFF global LIBDIR BINDIR MANDIR global X set_paths $INSTALLROOT # Some directories we have to create. set TCDIR [file join $LIBDIR tkcvs] set GFDIR [file join $LIBDIR tkcvs bitmaps] file mkdir $INSTALLROOT foreach dir [concat \"$BINDIR\" \"$GFDIR\" \"$TCDIR\"] { file mkdir $dir } set destfile [file join $BINDIR $TKCVS] puts "Installing $TKCVS in $BINDIR" file copy -force [file join tkcvs tkcvs.tcl] [file join $BINDIR $TKCVS] puts "Installing $TKDIFF in $BINDIR" file copy -force [file join tkdiff tkdiff] [file join $BINDIR $TKDIFF] if {$tcl_platform(platform) == "unix"} { file attributes $destfile -permissions 0755 file attributes [file join $BINDIR $TKDIFF] -permissions 0755 file mkdir $MANDIR puts "Installing manpage tkcvs.1 in $MANDIR" file copy -force [file join tkcvs tkcvs.1] $MANDIR } puts "Installing tcl files in $TCDIR" cd tkcvs foreach tclfile [glob *.tcl tclIndex] { if {$tclfile != "tkcvs.tcl"} { puts " $tclfile" file copy -force $tclfile $TCDIR } } puts "Installing icons in $GFDIR" cd bitmaps foreach pixfile [glob *.gif *.xbm] { puts " $pixfile" file copy -force $pixfile $GFDIR } cd [file join .. ..] puts "Finished!" if {$X} { destroy .bottom.do destroy .bottom.not button .bottom.done -text "Finished!" -command {destroy .} pack .bottom.done } } ################################################################################ set usage "Usage: doinstall.tcl \[-nox\] \[destination\]" set X 1 # Check Tcl/TK version if {$tcl_version < 8.3} { tk_dialog .wrongversion "Tcl/Tk too old" \ "TkCVS requires Tcl/Tk 8.3 or better!" \ error 0 {Bye Bye} exit 1 } # See if the user changed them with command-line args set ArgInstallRoot "" for {set i 0} {$i < [llength $argv]} {incr i} { set arg [lindex $argv $i] switch -exact -- $arg { -- { continue } -nox { set X 0 } --help { puts "$usage"; exit } -h { puts "$usage"; exit } -finaldir { puts "The -finaldir option is obsolete." puts "TkCVS now figures out where it is at run-time," puts "so substituting paths is unnecessary." exit 1 } default { set ArgInstallRoot $arg } } } # Do this after checking tcl version, because 7.x doesn't have it. if {[string match "*tclsh" [info nameofexecutable]]} { set X 0 } else { if {$X && [catch {frame .title} err]} { puts "\nTk can't draw the UI." puts "Something seems to be wrong with your X11 environment." set X 0 puts "You may use the -nox argument to do a command-line install:" puts "$usage" exit } } # Some rational and reasonable defaults. if {$tcl_platform(platform) == "windows"} { set INSTALLROOT "C:\\" set TKCVS "tkcvs.tcl" set TKDIFF "tkdiff.tcl" } else { set INSTALLROOT [file join /usr local] set TKCVS "tkcvs" set TKDIFF "tkdiff" } if {$ArgInstallRoot != ""} { set INSTALLROOT $ArgInstallRoot } if {$X} { # GUI installation label .title.lbl -text "TkCVS Installer" -font {Helvetica -14 bold} pack .title -side top pack .title.lbl -side top frame .entry label .entry.instlbl -text "Installation Root" entry .entry.instent -textvariable INSTALLROOT bind .entry.instent {show_paths $INSTALLROOT} bind .entry.instent {show_paths $INSTALLROOT} pack .entry -side top -pady 10 pack .entry.instlbl -side left pack .entry.instent -side left frame .messages -relief groove -bd 2 pack .messages -side top -expand y -fill x label .messages.adv -text "These files will be installed:" pack .messages.adv -side top show_paths $INSTALLROOT frame .bottom button .bottom.do -text "Install" -command {doinstall $INSTALLROOT} button .bottom.not -text "Cancel" -command {destroy .} pack .bottom -side top pack .bottom.do -side left pack .bottom.not -side left } else { # Command-line installation if {$ArgInstallRoot != ""} { set INSTALLROOT $ArgInstallRoot } else { puts "Install where? \[/usr/local\]" gets stdin IN puts "you entered $IN" } #puts "Will install in $INSTALLROOT" doinstall $INSTALLROOT } tkcvs-8.2.3.orig/contrib/0000755000175000017500000000000011664612512013425 5ustar timtimtkcvs-8.2.3.orig/contrib/tkdirdiff0000755000175000017500000024613011664612512015327 0ustar timtim#!/bin/sh # Tcl ignores the next line \ exec wish "$0" -- "${1+$@}" # Copyright (C) 1999-2002 Paul Mackerras. All rights reserved # This program is free software; it may be used, copied, modified # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. set TclExe [info nameofexecutable] set Script [info script] set ScriptTail [file tail $Script] if {[file type $Script] == "link"} { set ScriptBin [file join [file dirname $Script] [file readlink $Script]] } else { set ScriptBin $Script } set debug f proc debug-info {string} { global debug if {$debug} {puts $string} } # determine the windowing platform, since there are different ways to # do this for different versions of tcl if {[catch {tk windowingsystem} winSys]} { if {"$::tcl_platform(platform)" == "windows"} { set winSys "win32" } elseif {"$::tcl_platform(platform)" == "unix"} { set winSys "x11" } elseif {"$::tcl_platform(platform)" == "macintosh"} { set winSys "classic" } else { # this should never happen, but just to be sure... set winSys "x11" } } if {$tcl_version < 8.4 || $winSys == "aqua" } { set compound_ok 0 } else { set compound_ok 1 } set nofilecmp [catch {load libfilecmp.so.0.0}] set rcsflag {} set diffbflag {} set diffBflag {} set diffiflag {} set diffwflag {} set diffdflag {} set ctxlines 3 set showsame 0 set underlinetabs 0 set bitkeeper 0 set bkgetmode " " set redisp_immed 1 set diffnewfirst 0 set nukefiles {*.o *~ *.orig CVS *.a *.link *.old *.save .depend .*.flags SCCS} set filelistfont {Helvetica -12} set textfont {Courier -12} set maxdepth 9999999 if {$tcl_platform(platform) == "windows"} { set TclExe [file attributes $TclExe -shortname] # I don't like it any better than you do set nullfile "C:/temp/nulfile" set nf [open "$nullfile" w] close $nf } else { set nullfile "/dev/null" } set diffprogram {} set showprogram {} set multiviewer {} set numlines 20 set canvy0 0 set canvy 0 set canvx 0 set have_unidiff 1 debug-info "diff -u $nullfile $nullfile" set caught [catch "exec diff -u $nullfile $nullfile" err] if {$caught != 0} { puts "Unified diff not available. Will use context diff for patches." set have_unidiff 0 } catch {source ~/.dirdiff} proc ignorefile pat { debug-info "ignorefile ($pat)" global nukefiles if {$pat == "!"} { set nukefiles {} } else { lappend nukefiles $pat } } set linespc [font metrics $filelistfont -linespace] if {$linespc < 15} {set linespc 15} set blotw [expr $linespc-3] set bloth [expr $linespc-3] set blotspc $linespc proc usage {} { puts stderr {Usage: dirdiff [options]... dir1 dir2 ... Options: -a, --all don't exclude any files -o, --only pattern only process files matching pattern -I, --ignore pattern don't process files matching pattern -r, --rcs ignore differences in RCS strings -t, --bktag ignore differences in Bitkeeper strings -c, --context num set number of lines of context to show -b, -w, -B, -i, -d pass these on to diff(1) -S show files that are the same in the file list -K Bitkeeper support Note: dirdiff needs to be able to load the libfilecmp.so.0.0 shared library for the -r or -t flags to work.} } proc NewDirDialog {} { global d0 d1 d2 d3 d4 toplevel .newdirDlg wm transient .newdirDlg wm title .newdirDlg "Directories" set waitvar 0 frame .newdirDlg.top -borderwidth 2 -relief groove pack .newdirDlg.top -side top -fill x \ -ipadx 20 -ipady 20 -padx 5 -pady 5 button .newdirDlg.top.b0 -text "Browse..." -command { set d0 [tk_chooseDirectory -parent .newdirDlg -mustexist t] } button .newdirDlg.top.b1 -text "Browse..." -command { set d1 [tk_chooseDirectory -parent .newdirDlg -mustexist t] } button .newdirDlg.top.b2 -text "Browse..." -command { set d2 [tk_chooseDirectory -parent .newdirDlg -mustexist t] } button .newdirDlg.top.b3 -text "Browse..." -command { set d3 [tk_chooseDirectory -parent .newdirDlg -mustexist t] } button .newdirDlg.top.b4 -text "Browse..." -command { set d4 [tk_chooseDirectory -parent .newdirDlg -mustexist t] } for { set n 0 } { $n < 5 } { incr n } { set dn [expr {$n + 1}] label .newdirDlg.top.l$n -text "Directory $dn" entry .newdirDlg.top.e$n -width 25 -textvariable d$n grid .newdirDlg.top.l$n -row $n -column 0 -sticky e grid .newdirDlg.top.e$n -row $n -column 1 -sticky sew -pady 4 grid .newdirDlg.top.b$n -row $n -column 2 -sticky w } grid columnconfigure .newdirDlg.top 0 -weight 0 grid columnconfigure .newdirDlg.top 1 -weight 1 grid columnconfigure .newdirDlg.top 2 -weight 0 frame .newdirDlg.bot button .newdirDlg.bot.ok -text "OK" -width 5 -default active \ -command { set dirs [list $d0 $d1 $d2 $d3 $d4] destroy .newdirDlg set waitvar 1 } button .newdirDlg.bot.cancel -text "Cancel" -width 5 -default normal \ -command { set dirs {} destroy .newdirDlg exit 0 } pack .newdirDlg.bot -side bottom -fill x -expand n pack .newdirDlg.bot.ok .newdirDlg.bot.cancel \ -side left -fill none -expand y -pady 4 tkwait variable waitvar } proc addfiles {sd} { debug-info "addfiles ($sd)" global dirs stat onlyfiles statinfo fserial nextserial global filetype filesize filetime set dcount 0 foreach d $dirs { if {[catch {file stat $d/$sd stat}] == 0} { if {$stat(type) == "directory"} {incr dcount} } } if {$dcount <= 1} { return {} } foreach d $dirs { foreach f [lsort [glob -nocomplain $d/$sd* $d/$sd.*]] { set fs $sd[file tail $f] set wantim 0 if [notnuked $fs] { if {[catch {file lstat $f stat}] == 0} { if {$stat(type) == "file"} { if [info exists onlyfiles] { foreach o $onlyfiles { if [string match $o $fs] { set wantim 1 break } } } else { set wantim 1 } } elseif {$stat(type) == "directory"} { append fs / set wantim 1 } } } if {$wantim} { if {![info exists files($fs)]} { set fserial($fs) [incr nextserial] set files($fs) 1 } set filetype($f) $stat(type) set filesize($f) $stat(size) set filetime($f) $stat(mtime) } } } return [lsort [array names files]] } # Called to re-lstat a given file across all directories proc updatefileinfo {f} { debug-info "updatefileinfo ($f)" global dirs filetype filesize filetime foreach d $dirs { set df [joinname $d [string trimright $f /]] if {[catch {file lstat $df stat}] == 0} { set filetype($df) $stat(type) set filesize($df) $stat(size) set filetime($df) $stat(mtime) } else { catch {unset filetype($df)} } } } # Returns 1 if we are interested in this file, i.e. if it isn't # matched by something in the exclude list proc notnuked {f} { #debug-info "notnuked ($f)" global nukefiles set ft [file tail $f] if {$ft == "." || $ft == ".."} { return 0 } foreach n $nukefiles { if {[string match $n $f] || [string match $n $ft]} { return 0 } } return 1 } proc joinname {dir f} { global filemode if {$filemode} { return $dir } return [file join $dir $f] } proc fileisa {f t} { global filetype return [expr {[info exists filetype($f)] && $filetype($f) == $t}] } proc diffages {f showsame maxdepth} { #debug-info "diffages ($f $showsame $maxdepth)" global dirs nofilecmp rcsflag filesize filetime set numgroups 0 set notexist {} set doesexist {} foreach d $dirs { set sameas($d) {} set group($d) 0 set fname [joinname $d [string trimright $f /]] if {!([fileisa $fname "file"] || ($maxdepth <= 0 && [fileisa $fname "directory"]))} { set fd [file dirname $fname] if {[file dirname $f] == "." || [fileisa $fd "directory"]} { lappend notexist $d } } else { lappend doesexist $d set fsize($d) $filesize($fname) set fmtime($d) $filetime($fname) foreach d2 $dirs { if {$d2 == $d} break if {$sameas($d2) != "" || $group($d2) == 0} continue if {$rcsflag != "" || $fsize($d) == $fsize($d2)} { set fname2 [joinname $d2 [string trimright $f /]] if $nofilecmp { debug-info "cmp -s $fname $fname2" set notsame [catch {exec cmp -s $fname $fname2}] } else { set same 0 catch { set same [eval filecmp $rcsflag $fname $fname2] } set notsame [expr !$same] } } else { set notsame 1 } if {$notsame == 0} { set sameas($d) $d2 set g $group($d2) set group($d) $g lappend groupelts($g) $d if {$fmtime($d) > $gmtime($g)} { set gmtime($g) $fmtime($d) } break } } if {$sameas($d) == ""} { incr numgroups set group($d) $numgroups set groupelts($numgroups) $d set gmtime($numgroups) $fmtime($d) } } } if {!$showsame && $numgroups == 1 && $notexist == ""} { return {} } set glist {} for {set g 1} {$g <= $numgroups} {incr g} { lappend glist [list [format "%.8x" $gmtime($g)] $g] } set grank(0) 0 set rank 1 foreach xx [lsort -decreasing $glist] { set g [lindex $xx 1] set grank($g) $rank incr rank } set res {} foreach d $dirs { lappend res $grank($group($d)) } return [list $numgroups $res] } proc subdirgroups {sd} { global dirs debug-info "subdirgroups ($sd)" set nummiss 0 set groups {} foreach d $dirs { set fn [joinname $d $sd] if {![fileisa $fn "directory"]} { set pd [file dirname $sd] lappend groups 0 set fnp [joinname $d $pd] if {$pd == "." || [fileisa $fnp "directory"]} { incr nummiss } } else { lappend groups 1 } } if {$nummiss == 0} { return {} } return [list dir $groups] } set stringx 8 proc initcanv {} { global canvw canvx canvy canvy0 linespc stringx global dirs arroww blotspc blotw ycoord filelistfont $canvw delete all $canvw yview moveto 0 $canvw conf -scrollregion {0 0 0 1} catch {unset ycoord} set canvy $canvy0 if {![info exists arroww]} { set stringx [expr $blotspc + 8] return } set numdirs [llength $dirs] set stringx [expr $numdirs * $blotspc + 8] $arroww delete all set arrowh [expr ($numdirs+1) * $linespc] $arroww conf -height $arrowh set y 0 set yoff [expr $linespc / 2] set x [expr $canvx + 3 + ($blotw / 2)] set x2 [expr $stringx - 3] set horiz [expr $arrowh + 2] foreach d $dirs { set y2 [expr $y + $yoff] set t [$arroww create line $x $horiz $x $y2 $x2 $y2 \ -width 2 -arrow first] $arroww addtag arrows withtag $t set t [$arroww create text $stringx $y -text $d -anchor nw \ -font $filelistfont] #$arroww addtag strings withtag $t incr y $linespc incr x $blotspc } set dx [expr [$arroww cget -width] / 2] set dy [expr $horiz - 1] $arroww create text $dx $dy -text "Older <- " -anchor se $arroww create image $dx $dy -image paper_red -anchor sw incr dx $blotspc $arroww create image $dx $dy -image paper_orange -anchor sw incr dx $blotspc $arroww create image $dx $dy -image paper_yellow -anchor sw incr dx $blotspc $arroww create image $dx $dy -image paper_yellowgreen -anchor sw incr dx $blotspc $arroww create image $dx $dy -image paper_green -anchor sw incr dx $blotspc $arroww create text $dx $dy -text " -> Newer" -anchor sw } proc addcline {blots str} { global canvy canvx linespc stringx blotw bloth blotspc canvw ycoord global filelistfont set x [expr $canvx+1] set y [expr $canvy+1] foreach b $blots { set t [$canvw create image $x $y -image $b -anchor nw] $canvw addtag blots withtag $t incr x $blotspc } set t [$canvw create text $stringx $canvy -anchor nw -text $str \ -font $filelistfont] $canvw addtag strings withtag $t set ycoord($str) $canvy incr canvy $linespc set vis [lindex [$canvw yview] 1] $canvw conf -scrollregion "0 0 0 $canvy" if {$vis >= 1.0} { $canvw yview moveto 1 } } proc displine {groups name} { debug-info "displine ($groups $name)" global agecolors set ng [lindex $groups 0] set cols $agecolors($ng) set blots {} foreach g [lindex $groups 1] { lappend blots [lindex $cols $g] } addcline $blots $name } proc dispfilelines {groups} { debug-info "dispfilelines ($groups)" global agecolors dirs set ng [lindex $groups 0] set cols $agecolors($ng) set n 0 foreach g [lindex $groups 1] { addcline [lindex $cols $g] [lindex $dirs $n] incr n } } proc ruleoff {stopped} { global canvw canvy linespc set y [expr $canvy + $linespc/2] set color black if {$stopped} {set color red} $canvw create line 0 $y [$canvw cget -width] $y -width 2 -fill $color incr canvy $linespc set vis [lindex [$canvw yview] 1] $canvw conf -scrollregion "0 0 0 $canvy" if {$vis >= 1.0} { $canvw yview moveto 1 } set ruletype $stopped } proc updatecline {si di f} { debug-info "updatecline ($si $di $f)" global ycoord canvw blotspc bloth blotw groups global filemode dirs selitem if {$filemode} { set fs [lindex $dirs $si] set fd [lindex $dirs $di] if {![info exists ycoord($fs)] || ![info exists ycoord($fd)]} return set ys [expr $ycoord($fs) + 2] set yd [expr $ycoord($fd) + 2] set xs 2 set xd 2 } else { if {![info exists ycoord($f)]} return set ys [expr $ycoord($f) + 2] set yd $ys set xs [expr $si * $blotspc + 2] set xd [expr $di * $blotspc + 2] } set ts [$canvw find overlapping $xs $ys \ [expr $xs+$blotw-2] [expr $ys+$bloth-2]] set td [$canvw find overlapping $xd $yd \ [expr $xd+$blotw-2] [expr $yd+$bloth-2]] if {$ts != "" && $td != ""} { $canvw itemconf $td -image [$canvw itemcget $ts -image] } set ng [lindex $groups($f) 0] set g [lindex $groups($f) 1] set groups($f) [list $ng [lreplace $g $di $di [lindex $g $si]]] addsecsel $selitem refreshcline $f } proc refreshcline {f} { debug-info "refreshcline ($f)" global ycoord canvw blotspc bloth blotw groups global agecolors if {![info exists ycoord($f)]} return set y [expr $ycoord($f) + 2] set ng [lindex $groups($f) 0] if {$ng == ""} {set ng "dir"} set cols $agecolors($ng) #debug-info " $ng groups: cols $cols" set x 2 foreach g [lindex $groups($f) 1] { set rect [list [expr $x+3] [expr $y+3] [expr $x+$blotw-2] [expr $y+$bloth-2]] #debug-info " $rect" #$canvw create rectangle $rect set tag [$canvw find overlapping [expr $x+3] [expr $y+3] \ [expr $x+$bloth-2] [expr $y+$blotw+2]] set t [lindex $tag 0] $canvw itemconf $t -image [lindex $cols $g] #debug-info " t $tag, g $g: itemconf $t -image [lindex $cols $g]" incr x $blotspc } } proc makepatchmenu {base} { global dirs menu $base.p -tearoff 0 set sub1 0 foreach d1 $dirs { set any 0 incr sub1 menu $base.p.$sub1 -tearoff 0 foreach d2 $dirs { if {$d1 == $d2} continue set any 1 $base.p.$sub1 add command -label "$d2" \ -command "makepatch \"$d1\" \"$d2\"" } if {$any} { $base.p add cascade -label "$d1 ->" -menu $base.p.$sub1 } incr sub1 } $base add cascade -label "Make patch" -menu $base.p } proc maketouchmenu {base} { global dirs menu $base.t -tearoff 0 foreach d $dirs { $base.t add command -label $d -command "touchfiles \"$d\"" } $base add cascade -label "Touch" -menu $base.t } proc makebkgetmenu {base} { global dirs bitkeeper menu $base.g -tearoff 0 foreach d $dirs { $base.g add command -label $d -command "bkgetfiles \"$d\"" } $base add cascade -label "BK get" -menu $base.g \ -state [expr {$bitkeeper? "normal": "disabled"}] } proc makewins {} { global canvw numlines linespc arroww diffbut copybut filelabel nofilecmp global filemode dirs dirinterest filelistfont global rcsflag diffiflag diffwflag diffbflag diffBflag diffdflag global bitkeeper bgcolors # Native-style menubar menu .bar .bar add cascade -label "File" -menu .bar.file # File menu menu .bar.file .bar.file add command -label "Rediff" -command rediff if {!$filemode} { .bar.file add command -label "Redisplay" -command redisplay } set menubg [lindex [.bar.file configure -background] 4] set bgcolors(1) [list $menubg $menubg] set bgcolors(2) [list $menubg green "#ff8080"] set bgcolors(3) [list $menubg green yellow "#ff8080"] set bgcolors(4) [list $menubg green yellow orange "#ff8080"] set bgcolors(5) [list $menubg green "#e0ff90" yellow orange "#ff8080"] makepatchmenu .bar.file maketouchmenu .bar.file makebkgetmenu .bar.file .bar.file add command -label "Stop" -command "set stopped 1" .bar.file add separator .bar.file add command -label "Quit" -command "set stopped 1; destroy ." # Diff menu set diffbut .bar.diff menu $diffbut .bar add cascade -label "Diff" -menu $diffbut $diffbut add command -label "All" -command difffiles # Copy menu set copybut .bar.copy menu $copybut .bar add cascade -label "Copy/Del" -menu $copybut # Options menu menu .bar.options .bar add cascade -label "Options" -menu .bar.options .bar.options add radiobutton -label "Literal comparison" \ -variable rcsflag -value " " \ -state [expr {$nofilecmp? "disabled": "normal"}] .bar.options add radiobutton -label "Ignore differences in RCS strings" \ -variable rcsflag -value "-rcs" \ -state [expr {$nofilecmp? "disabled": "normal"}] .bar.options add radiobutton -label "Ignore differences in BK tags" \ -variable rcsflag -value "-bk" \ -state [expr {$nofilecmp? "disabled": "normal"}] .bar.options add checkbutton -label "Show files that are identical" \ -variable showsame .bar.options add checkbutton -label "Bitkeeper support" \ -variable bitkeeper \ -command bkchange menu .bar.options.bkmode -tearoff 0 .bar.options.bkmode add radiobutton -label "Expand keywords" \ -variable bkgetmode -value " " .bar.options.bkmode add radiobutton -label "Don't expand keywords" \ -variable bkgetmode -value "k" .bar.options.bkmode add radiobutton -label "Check out for editing" \ -variable bkgetmode -value "e" .bar.options add cascade -label "BK get mode" \ -menu .bar.options.bkmode \ -state [expr {$bitkeeper? "normal": "disabled"}] .bar.options add checkbutton -label "Redisplay immediately" \ -variable redisp_immed .bar.options add command -label "Excluded files..." -command exclfilelist .bar.options add command -label "Diff options..." -command diffoptions .bar.options add command -label "External viewers..." -command extprograms .bar.options add command -label "Save options" -command saveoptions .bar.options add separator set i 0 foreach d $dirs { set dirinterest($i) 1 .bar.options add checkbutton -label "Show file" \ -variable dirinterest($i) -command maybe_redisplay incr i } # Help menu menu .bar.help .bar add cascade -label "Help" -menu .bar.help .bar.help add command -label "About dirdiff" -command about .bar.help add command -label "About diff" -command about_diff .bar.help add command -label "Show help text" -command helptext . configure -menu .bar # make the filename display bar if {!$filemode} { frame .file -relief sunk -bd 1 set filelabel .file.name #label $filelabel -relief flat -padx 7 -text "File: " label $filelabel -relief flat -padx 7 -image paper set fileentry .file.ent entry $fileentry -relief sunk -bd 1 -textvariable selfile \ -font $filelistfont pack $filelabel -side left pack $fileentry -side left -fill x -expand yes pack .file -side top -fill x } # make the frame containing the 2 canvases (one for the top section # containing the directory names, one for the files) and the scrollbar # in file mode the top section is omitted frame .cf if {$filemode} { set numlines [llength $dirs] } canvas .cf.c -height [expr $numlines * $linespc] \ -yscrollincr $linespc -yscrollcommand ".csb set" \ -bg white -relief sunk -bd 1 set canvw .cf.c if {!$filemode} { canvas .cf.d -height [expr 3 * $linespc] \ -relief flat -bd 1 -highlightthickness 0 set arroww .cf.d pack .cf.d -side top -fill x } pack .cf.c -side bottom -fill both -expand 1 scrollbar .csb -command "$canvw yview" -highlightthickness 0 pack .csb -side right -fill y pack .cf -side left -fill both -expand 1 bind $fileentry "search_canvas" # set up event bindings on the main canvas bind $canvw {selcanvline %x %y 0} bind $canvw {selcanvline %x %y 1} bind $canvw {selcanvline %x %y 3} bind $canvw {selcanvline %x %y 2} bind $canvw "$canvw yview scroll -5 u" bind $canvw "$canvw yview scroll 5 u" bind $canvw <2> "$canvw scan mark 0 %y" bind $canvw "$canvw scan dragto 0 %y" bind $canvw "showsomediff" bind $canvw "showsomediff" $canvw conf -scrollregion {0 0 0 1} if {!$filemode} { bind . N "diffnextfile 1" bind . P "diffnextfile -1" } bind . C copydifffile bind . "$canvw yview scroll -1 p" bind . "$canvw yview scroll 1 p" bind . "$canvw yview scroll -1 p" bind . "$canvw yview scroll -1 p" bind . "$canvw yview scroll 1 p" bind . "$canvw yview scroll -1 u" bind . "$canvw yview scroll 1 u" bind . Q "set stopped 1; destroy ." # Need a way to unselect all bind . resetsel } proc bkchange {} { global bitkeeper set state [expr {$bitkeeper? "normal": "disabled"}] .bar.options entryconf "BK*" -state $state .bar.file entryconf "BK*" -state $state } proc about {} { set w .about if {[winfo exists $w]} { raise $w return } toplevel $w wm title $w "About dirdiff" message $w.m -text { Dirdiff version 1.6 Copyright Љ 1999-2001 Paul Mackerras Use and redistribute under the terms of the GNU General Public License (CVS $Revision: 1.9 $)} \ -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 button $w.ok -text Close -command "destroy $w" pack $w.ok -side bottom } proc about_diff {} { set w .about_diff if {[winfo exists $w]} { raise $w return } toplevel $w wm title $w "About diff" debug-info "diff -v" set retval [catch "exec diff -v" err] message $w.m -text $err -justify center -aspect 600 pack $w.m -side top -fill x -padx 20 -pady 20 if {$retval == 0} { text $w.t -bg white -yscrollcommand "$w.sb set" -wrap word scrollbar $w.sb -command "$w.t yview" pack $w.sb -side right -fill y pack $w.t -side left -fill both -expand 1 set fdh [open "|diff --help" r] while { [eof $fdh] == 0 } { $w.t insert end "[gets $fdh]\n" } pack $w.t -side top -fill both -expand yes } button $w.ok -text Close -command "destroy $w" pack $w.ok -side bottom } proc helptext {} { set w .help if {[winfo exists $w]} { raise $w return } toplevel $w wm title $w "Dirdiff help" text $w.t -font {Times -14} -yscrollcommand "$w.sb set" -wrap word scrollbar $w.sb -command "$w.t yview" pack $w.sb -side right -fill y pack $w.t -side left -fill both -expand 1 bind $w "$w.t yview scroll -1 p" bind $w "$w.t yview scroll -1 p" bind $w "$w.t yview scroll -1 p" bind $w b "$w.t yview scroll -1 p" bind $w B "$w.t yview scroll -1 p" bind $w "$w.t yview scroll -1 u" bind $w "$w.t yview scroll 1 u" bind $w d "$w.t yview scroll \[expr \"int(\[$w.t cget -height\]/2)\"\] u" bind $w D "$w.t yview scroll \[expr \"int(\[$w.t cget -height\]/2)\"\] u" bind $w u "$w.t yview scroll \[expr \"int(-\[$w.t cget -height\]/2)\"\] u" bind $w U "$w.t yview scroll \[expr \"int(-\[$w.t cget -height\]/2)\"\] u" bind $w q "destroy $w" bind $w Q "destroy $w" $w.t insert end {Dirdiff instructions. Dirdiff compares all the files in up to five directories. There is one \ column in the main window for each directory. Each file is shown with a coloured square indicating its status. Files \ are like leaves on a deciduous tree: the newest ones are green, and then \ they turn yellow, orange, and red as they get older. Double-click a file to show differences between two versions. By default, \ the first and last versions are compared, but this can be changed by the \ 'Diff' menu in the main window. You can select several files to copy or to make a patch by shift-clicking. You can search for a file by typing part of its name in the entry and \ pressing the key. In the diff window, check the boxes on the left margin for changes you \ want to preserve, and then choose 'Merge' to move those changes into one \ of the files. Alternatively, choose 'Copy' in the main window to copy \ across the whole file, replacing any changes. 'Make patch' produces a file describing the changes between the files that \ can be applied by the patch tool. You can edit the patch before saving, \ and may wish to add explanatory text, instructions, or patch(1) Prereq \ lines at the beginning. To save the patch, enter a filename in the patch \ window relative to the current directory, and choose 'Save'. This will \ also close the window. If you are sending out patches, then the "from" directory should be the \ original version of the source. Try to make sure that the two files have \ the same number of leading directories. See the patch(1) man page for \ more information. } $w.t conf -state disabled } proc filediffs {} { debug-info "filediffs ()" global groups selitem fserial updatefileinfo . set groups(.) [set gr [diffages . 1 1]] set fserial(.) 1 dispfilelines $gr clearsecsel debug-info "From filediffs" selcurfile } proc diffsin {sd maxdepth} { debug-info "diffsin ($sd $maxdepth)" global groups stopped showsame alllines foreach f [addfiles $sd] { if {$stopped} return lappend alllines $f set d [string trimright $f /] if {$d == $f || $maxdepth <= 0} { set groups($f) [set gr [diffages $f $showsame $maxdepth]] if [interesting_line $gr] { displine $gr $f } } else { set groups($f) [set gr [subdirgroups $d]] if [interesting_line $gr] { displine $gr $f } diffsin $f [expr $maxdepth-1] } catch update idletasks } } proc canvdiffs {} { debug-info "canvdiffs ()" global canvw groups stopped filemode alllines global filetype filetime filesize maxdepth set stopped 0 set alllines {} catch {unset filetype} catch {unset filetime} catch {unset filesize} initcanv if {$filemode} { filediffs } else { diffsin {} $maxdepth if {[catch update]} return ruleoff $stopped } if {[catch update]} return if {[lindex [$canvw yview] 1] >= 1.0} { $canvw yview moveto 0 } } proc textitemat {x y} { global canvw foreach i [$canvw find overlapping $x $y [expr $x+50] $y] { if {[$canvw type $i] == "text"} { return $i } } return {} } proc selcanvline {x y tipe} { debug-info "selcanvline ($x $y $tipe)" global canvw stringx selitem secsel clickitem groups selfile clickmode global filemode doubleclick if {$filemode} return set x [expr $stringx+5] set y [$canvw canvasy $y] set it [textitemat $x $y] #debug-info " it $it" if {$it == {}} return if {$tipe == 0} { # click, no shift clearsecsel set selitem $it $canvw select from $it 0 $canvw select to $it end set clickitem $it set clickmode 1 #debug-info "From selcanvline" selcurfile addsecsel $it #set doubleclick 0 } elseif {$tipe == 1} { # shift-click set clickitem $it if {$it != $selitem} { if {![info exists secsel($it)]} { set clickmode 1 addsecsel $it } else { set clickmode 0 remsecsel $it } } } elseif {$tipe == 2 || $tipe == 3} { # motion with button 1 down if {![info exists clickitem]} return foreach i [eval $canvw find overlapping [$canvw bbox $clickitem $it]] { if {[$canvw type $i] == "text"} { set f [$canvw itemcget $i -text] if {$groups($f) == $groups($selfile)} { if {$clickmode && ![info exists secsel($i)]} { addsecsel $i } elseif {!$clickmode && [info exists secsel($i)]} { remsecsel $i } } } } } } proc addsecsel {it} { #debug-info "addsecsel ($it)" global canvw secsel #debug-info " bbox [$canvw bbox $it]" set t [eval $canvw create rect [$canvw bbox $it] -outline {{}} \ -tags secsel -fill [$canvw cget -selectbackground]] $canvw lower $t set secsel($it) $t #debug-info " secsel($it) $t" } proc remsecsel {it} { global canvw secsel $canvw delete $secsel($it) unset secsel($it) } proc clearsecsel {} { global canvw secsel $canvw delete secsel catch {unset secsel} } proc selnextline {inc} { debug-info "selnextline ($inc)" global canvw selitem linespc stringx canvy filemode if {$filemode} { if {$inc != 0} { return 0 } debug-info "From selnextline" selcurfile return 1 } if {$selitem == ""} { return 0 } set y [expr [lindex [$canvw bbox $selitem] 1] + $linespc * $inc + 5] set x [expr $stringx+5] set i [textitemat $x $y] if {$i == ""} { return 0 } clearsecsel set selitem $i $canvw select from $i 0 $canvw select to $i end set bbox [$canvw bbox $i] set y [expr {([lindex $bbox 1] + [lindex $bbox 3]) / 2.0}] set ytop [expr {($y - $linespc / 2.0) / $canvy}] set ybot [expr {($y + $linespc / 2.0) / $canvy}] set wnow [$canvw yview] if {$ytop < [lindex $wnow 0]} { $canvw yview moveto $ytop } elseif {$ybot > [lindex $wnow 1]} { set wh [expr {[lindex $wnow 1] - [lindex $wnow 0]}] $canvw yview moveto [expr {$ybot - $wh}] } debug-info "From selnextline" selcurfile addsecsel $i return 1 } proc calcgroupelts {f} { #debug-info "calcgroupelts ($f)" if {$f == ""} {return} global groupelts numgroups groups set gr $groups($f) set numgroups [lindex $gr 0] if {$numgroups == "dir"} { set numgroups 1 } set gr [lindex $gr 1] for {set g 0} {$g <= $numgroups} {incr g} { set groupelts($g) {} } set i 0 foreach g $gr { lappend groupelts($g) $i incr i } #debug-info " groupelts [array names groupelts]" } proc selcurfile {} { debug-info "selcurfile" global canvw selitem filelabel selfile groups filemode global groupelts diffbut copybut numgroups #debug-info " filemode $filemode" #debug-info " selitem $selitem" #debug-info " selfile $selfile" if {!$filemode} { if {$selitem == ""} return set name [$canvw itemcget $selitem -text] if {$name != ""} { set selfile $name addsecsel $selitem } } else { set selfile . } #debug-info " selitem $selitem: selfile $selfile" #debug-info " selfile $selfile" calcgroupelts $selfile set x [string trimright $selfile /] if {$x == $selfile} { if {[info exists filelabel]} { $filelabel conf -image paper } confdiffbut 0 confcopybutfile } else { if {[info exists filelabel]} { $filelabel conf -image folder } confdiffbut 1 confcopybutdir } } proc confdiffbut {isdir} { debug-info "confdiffbut ($isdir)" global diffbut numgroups dirs selfile groupelts filemode global groups agecolors bgcolors compound_ok global multiviewer $diffbut delete 0 end destroy [winfo children $diffbut] set ng [lindex $groups($selfile) 0] if {$isdir} { # do nothing } elseif {$numgroups == 1} { set xi [lindex $groupelts(1) 0] if {$xi != ""} { set x [lindex $dirs $xi] $diffbut add command -label "Show $x" \ -command "showfile \"$x\" \"$selfile\"" } } elseif {$numgroups > 1} { for {set gn 1} {$gn <= $numgroups} {incr gn} { set yi [lindex $groupelts($gn) 0] if {$yi == ""} continue set age [lindex [lindex $groups($selfile) 1] $yi] set im [lindex $agecolors($ng) $age] set cl [lindex $bgcolors($ng) $age] set y [lindex $dirs $yi] if {[winfo exists $diffbut.$gn]} {destroy $diffbut.$gn} menu $diffbut.$gn -tearoff 0 set any 0 for {set go 1} {$go <= $numgroups} {incr go} { if {$go == $gn} continue set any 1 set xi [lindex $groupelts($go) 0] set age [lindex [lindex $groups($selfile) 1] $xi] set im2 [lindex $agecolors($ng) $age] set cl2 [lindex $bgcolors($ng) $age] set xi [lindex $groupelts($go) 0] if {$xi == ""} continue set x [lindex $dirs $xi] if {$compound_ok} { $diffbut.$gn add command -label "$x" \ -image $im2 -compound left \ -command "diff2 \"$x\" \"$y\" \"$selfile\"" } else { $diffbut.$gn add command -label "$x" \ -background $cl2 \ -command "diff2 \"$x\" \"$y\" \"$selfile\"" } } if {$any} { if {$compound_ok} { $diffbut add cascade -label "$y vs" \ -image $im -compound left \ -menu $diffbut.$gn } else { $diffbut add cascade -label "$y vs" \ -background $cl \ -menu $diffbut.$gn } } } } if {!$filemode} { $diffbut add separator $diffbut add command -label "Rediff selected file(s)" \ -command "redifffiles" $diffbut add command -label "Multi-Diff Selected file(s)" \ -command "multidifffiles" if {$multiviewer == ""} { # suggest gvimdiff or emacs -f ediff3 debug-info "$diffbut entryconfigure 4 -state disabled" $diffbut entryconfigure "Multi*" -state disabled } } .bar entryconfigure 2 -state normal } proc confcopybutfile {} { global copybut groupelts numgroups selfile dirs global groups agecolors bgcolors compound_ok $copybut delete 0 end destroy [winfo children $copybut] set numdirs [llength $dirs] set srcs {} set rev {} set ng [lindex $groups($selfile) 0] for {set gn 1} {$gn <= $numgroups} {incr gn} { set srcs [concat $srcs $groupelts($gn)] set src [lindex $groupelts($gn) 0] if {$src == ""} continue set age [lindex [lindex $groups($selfile) 1] $src] set im [lindex $agecolors($ng) $age] set cl [lindex $bgcolors($ng) $age] set x [lindex $dirs $src] if {[winfo exists $copybut.new2old$src]} {destroy $copybut.new2old$src} menu $copybut.new2old$src -tearoff 0 set any 0 for {set dst 0} {$dst < $numdirs} {incr dst} { if {[lsearch $srcs $dst] < 0} { set any 1 set age [lindex [lindex $groups($selfile) 1] $dst] set im2 [lindex $agecolors($ng) $age] set cl2 [lindex $bgcolors($ng) $age] if {$im2 == "ex"} {set im2 ""} set y [lindex $dirs $dst] if {$compound_ok} { $copybut.new2old$src add command -label "$y" \ -image $im2 -compound left \ -command "copyselfile \"$src\" \"$dst\" \"$selfile\" 0" } else { $copybut.new2old$src add command -label "$y" \ -background $cl2 \ -command "copyselfile \"$src\" \"$dst\" \"$selfile\" 0" } } } if {$any} { if {$compound_ok} { $copybut add cascade -label "$x ->" \ -image $im -compound left \ -menu $copybut.new2old$src } else { $copybut add cascade -label "$x ->" \ -background $cl \ -menu $copybut.new2old$src } } } set needsep 1 for {set gn $numgroups} {$gn >= 1} {incr gn -1} { set src [lindex $groupelts($gn) 0] if {$src == ""} continue set age [lindex [lindex $groups($selfile) 1] $src] set im [lindex $agecolors($ng) $age] set cl [lindex $bgcolors($ng) $age] set x [lindex $dirs $src] if {[winfo exists $copybut.old2new$src]} {destroy $copybut.old2new$src} menu $copybut.old2new$src -tearoff 0 set any 0 for {set gd 1} {$gd < $gn} {incr gd} { foreach dst $groupelts($gd) { set any 1 set age [lindex [lindex $groups($selfile) 1] $dst] set im2 [lindex $agecolors($ng) $age] set cl2 [lindex $bgcolors($ng) $age] set y [lindex $dirs $dst] if $needsep { $copybut add separator set needsep 0 } if {$compound_ok} { $copybut.old2new$src add command -label "$y" \ -image $im2 -compound left \ -command "copyselfile \"$src\" \"$dst\" \"$selfile\" 1" } else { $copybut.old2new$src add command -label "$y" \ -background $cl2 \ -command "copyselfile \"$src\" \"$dst\" \"$selfile\" 1" } } } if {$any} { if {$compound_ok} { $copybut add cascade -label "$x ->" \ -image $im -compound left \ -menu $copybut.old2new$src } else { $copybut add cascade -label "$x ->" \ -background $cl \ -menu $copybut.old2new$src } } } if {$groupelts(0) != {}} { set needsep 1 for {set gn 1} {$gn <= $numgroups} {incr gn} { foreach dst $groupelts($gn) { set x [lindex $dirs $dst] if $needsep { $copybut add separator set needsep 0 } if {$compound_ok} { $copybut add command -label "Remove from $x" \ -image ex -compound left \ -command "removeselfile \"$dst\" \"$selfile\"" } else { $copybut add command -label "Remove from $x" \ -command "removeselfile \"$dst\" \"$selfile\"" } } } } .bar entryconfigure 3 -state normal } proc confcopybutdir {} { global copybut groupelts selfile dirs compound_ok $copybut delete 0 end set srcs $groupelts(1) set dsts $groupelts(0) if {$srcs != {} && $dsts != {}} { foreach s $srcs { set x [lindex $dirs $s] foreach d $dsts { set y [lindex $dirs $d] $copybut add command -label "$x -> $y" \ -command "copyselfile \"$s\" \"$d\" \"$selfile\" 0" } } $copybut add separator foreach s $srcs { set x [lindex $dirs $s] if {$compound_ok} { $copybut add command -label "Remove from $x" \ -image ex -compound left \ -command "removeselfile \"$s\" \"$selfile\"" } else { $copybut add command -label "Remove from $x" \ -command "removeselfile \"$s\" \"$selfile\"" } } } .bar entryconfigure 3 -state normal } proc resetsel {} { debug-info "resetsel" global selitem selfile filelabel diffbut copybut global canvw #if { [info exists selitem] } { #debug-info " selitem $selitem" #} #if { [info exists selfile] } { #debug-info " selfile $selfile" #} set selitem {} set selfile {} $canvw select clear $canvw delete secsel if {[info exists filelabel]} { $filelabel conf -image paper } .bar entryconfigure 2 -state disabled .bar entryconfigure 3 -state disabled removediffs } proc removediffs {} { global texttop textw diffing difff catch {destroy $texttop} catch {unset texttop} catch {unset textw} catch {close $difff} set diffing 0 } proc showfile {d f} { global showprogram set fn [joinname $d $f] # Show the file in an external viewer if { [llength $showprogram] > 0} { debug-info "$showprogram \"$fn\" &" eval "exec $showprogram \"$fn\" &" return } # Or make our own viewer global textw difflist texttop mergebut if {!([info exists textw] && [winfo exists $textw])} { maketextw } else { raise $texttop } wm title $texttop "Contents of $fn" set difflist {} $mergebut.m delete 0 end $textw conf -state normal -tabs {} $textw delete 0.0 end set nl {} set f [open $fn r] set n [gets $f line] while {$n >= 0} { $textw insert end "$nl$line" set nl "\n" set n [gets $f line] } close $f $textw conf -state disabled } proc redifffiles {} { debug-info "redifffiles ()" global groups showsame selfile selitem rediffed groups set files [secondarysel $selfile] foreach f $files { updatefileinfo $f set d [string trimright $f /] if {[lindex $groups($f) 0] != "dir"} { set groups($f) [diffages $f 1 0] } else { set groups($f) [subdirgroups $d] } calcgroupelts $f refreshcline $f } set rediffed $selfile } proc multidifffiles {} { debug-info "multidifffiles ()" global selfile dirs global multiviewer debug-info " dirs $dirs" set files "" foreach d $dirs { set f [file join $d $selfile] lappend files $f } if { [llength $multiviewer] > 0} { debug-info "$multiviewer $files &" eval "exec $multiviewer $files &" return } } proc diff2 {d1 d2 f} { global diffprogram nullfile global textw groups dirs numgroups bgcolors selfile difflist texttop global difff lno diffdirs diffiflag diffwflag diffbflag diffBflag diffdflag global ctxlines diffoldcolor diffnewcolor difffile charwidth mergebut global diffing filemode rediffed diffnewfirst set group [lindex $groups($selfile) 1] set i1 [lindex $group [lsearch $dirs $d1]] set i2 [lindex $group [lsearch $dirs $d2]] if {($i1 > $i2) == $diffnewfirst} { set x $d1 set d1 $d2 set d2 $x set x $i1 set i1 $i2 set i2 $x } set ds [list $d1 $d2] if {$diffing} { if {$ds == $diffdirs && $f == $difffile} return catch {close $difff} } set diffdirs $ds set difffile $f if {[info exists rediffed] && $rediffed == $f} { unset rediffed } set path1 [joinname $d1 $f] set path2 [joinname $d2 $f] set diffopts "-U $ctxlines $diffiflag $diffwflag $diffbflag $diffBflag $diffdflag" if { [llength $diffprogram] > 0} { debug-info "$diffprogram \"$path1\" \"$path2\" &" eval "exec $diffprogram \"$path1\" \"$path2\" &" return } # If we used an external diff program, its options are used. If we didn't, # we use our diffopts, and we may be in trouble. debug-info "diff $diffopts $nullfile $nullfile" set caught [catch "exec diff $diffopts $nullfile $nullfile" err] if {$caught != 0} { set msg "diff $diffopts\n$err\n" append msg "Suggestion: Use an external diff viewer such as tkdiff or gvimdiff" error_popup "$msg" return } # Build a window if {![info exists textw] || ![winfo exists $textw]} { maketextw } if {$filemode} { wm title $texttop "Differences: $d1 vs $d2" } else { wm title $texttop "Differences: $f" } set difflist {} $mergebut.m delete 0 end $textw conf -state normal $textw delete 0.0 end set charwidth [font measure [$textw cget -font] n] $textw conf -tabs "[expr 4*$charwidth]p left [expr 12*$charwidth]p left" set x $bgcolors($numgroups) $textw tag delete [$textw tag names] set diffoldcolor [lindex $x $i1] set diffnewcolor [lindex $x $i2] $textw tag conf d1 -back $diffoldcolor $textw tag conf d2 -back $diffnewcolor $textw tag conf sep -back blue $textw tag conf ul -underline 1 $textw tag lower sep # Start a diff set difff [open "|diff $diffopts $path1 $path2" r] set diffing 1 set lno 1 catch {unset oldin} catch {unset newin} global linegroups linegroupnum linegrouplast lineinfo catch {unset linegroups} set linegroupnum 0 set linegrouplast 0 catch {unset lineinfo} global file1lnum file2lnum incline lineinfo set file1lnum 0 set file2lnum 0 catch {unset incline} catch {unset lineinfo} fconfigure $difff -blocking 0 fileevent $difff readable "readdiff $difff" } proc readdiff {f} { global difff lno textw difflist global incline lineinfo underlinetabs global linegroups linegroupnum linegrouplast global file1lnum file2lnum diffing textfont if {$f != $difff} { catch {close $f} return } set n [gets $difff line] if {$n < 0} { if {![eof $difff]} return catch {close $difff} set diffing 0 if {$lno > 1} { $textw delete "end - 1c" end set t [$textw tag names "end - 1l"] if {$t != ""} { $textw tag add $t "end - 1l" end } } $textw conf -state disabled if {$lno > 3} { global mergebut diffdirs difffile global groups dirs diffmtime set j 0 set group [lindex $groups($difffile) 1] foreach i {0 1} { set g [lindex $group [lsearch $dirs [lindex $diffdirs $i]]] set k 0 foreach gx $group { if {$gx == $g} { set f [lindex $dirs $k] $mergebut.m add command -label "update $f" \ -command "diffmerge $i $j \"$f\"" set path [joinname $f $difffile] set diffmtime($path) [file mtime $path] incr j } incr k } } } return } set x [string index $line 0] if {$x == "@" && [regexp { -([0-9,]+) .*\+([0-9,]+) } $line z r1 r2]} { set line "\t $r1 $r2 " set file1lnum [string range $r1 0 [expr [string wordend $r1 0]-1]] set file2lnum [string range $r2 0 [expr [string wordend $r2 0]-1]] lappend difflist $lno set line [string range $line 1 end] $textw insert end "$line\n" set lend [$textw index "$lno.0 + 1l"] set i1 [$textw index "$lno.16 + [string length $r1] c"] set i2 [$textw index "$i1 + 8c + [string length $r2] c"] $textw tag add sep $lno.0 $lno.8 $textw tag add d1 $lno.8 $i1 $textw tag add d2 $i1 $i2 $textw tag add sep $i2 $lend set linegrouplast 0 incr lno return } set ix 1 if {($x == "-" || $x == "+") && $lno > 3} { set incline($lno) 0 if {!$linegrouplast} { incr linegroupnum set linegroups($linegroupnum) {} set linegrouplast 1 } lappend linegroups($linegroupnum) $lno checkbutton $textw.inc$lno -variable incline($lno) \ -font {Courier -10} -cursor top_left_arrow \ -highlightthickness 0 -padx 2 -pady 0 $textw window create end -window $textw.inc$lno -stretch true bind $textw.inc$lno "" \ "$textw.inc$lno toggle; togglegroup $linegroupnum $lno; break" bind $textw.inc$lno "" \ "$textw.inc$lno toggle; togglegroup $linegroupnum $lno" set ix 2 set line [string range $line 1 end] set lineinfo($lno) [list $file1lnum $file2lnum [expr {$x=="+"}] $line] } elseif {$x == "-" || $x == "+"} { set line [string trimleft $line $x] } elseif {$x == " "} { set line [string range $line 1 end] } $textw insert end "\t" if {$underlinetabs} { set col 0 set trailb [string length [string trimright $line]] while {[set tpos [string first "\t" $line]] >= 0} { if {$tpos > 0} { $textw insert end [string range $line 0 [expr $tpos-1]] if {$trailb < $tpos} { $textw tag add ul $lno.[expr $ix+$trailb] \ $lno.[expr $ix+$tpos] set trailb 0 } else { set trailb [expr $trailb-$tpos] } incr ix $tpos incr col $tpos } set nsp [expr {8 - ($col & 7)}] $textw insert end [string range " " 1 $nsp] ul set line [string range $line [expr $tpos+1] end] incr ix $nsp incr col $nsp if {$trailb > 0} {incr trailb -1} } $textw insert end "$line\n" set tpos [string length $line] if {$trailb < $tpos} { $textw tag add ul $lno.[expr $ix+$trailb] $lno.[expr $ix+$tpos] } } else { $textw insert end "$line\n" } set lend [$textw index "$lno.0 + 1l"] if {$x == "-"} { $textw tag add d1 $lno.0 $lend } elseif {$x == "+"} { $textw tag add d2 $lno.0 $lend } else { set linegrouplast 0 } if {$linegrouplast} { $textw tag add bindtag$lno $lno.0 $lend $textw tag bind bindtag$lno "" \ "$textw.inc$lno toggle; break" $textw tag bind bindtag$lno "" \ "$textw.inc$lno toggle; togglegroup $linegroupnum $lno; break" $textw tag bind bindtag$lno "" \ "$textw.inc$lno toggle; togglegroup $linegroupnum $lno; break" } if {$x != "+"} {incr file1lnum} if {$x != "-"} {incr file2lnum} incr lno } proc togglegroup {group lno} { global incline linegroups textw if $incline($lno) { set state select } else { set state deselect } foreach l $linegroups($group) { $textw.inc$l $state } } proc invertbuttons {} { global incline textw foreach l [array names incline] { set incline($l) [expr {1 - $incline($l)}] } } proc mergelist {} { global incline lineinfo lno set mlist {} for {set i 1} {$i <= $lno} {incr i} { if {[info exists incline($i)]} { lappend mlist [list $incline($i) $lineinfo($i)] } } return $mlist } proc diffmerge {ix j dir} { global diffdirs difffile diffmtime fserial global dirs diffoldcolor diffnewcolor textfont set infile [joinname $dir $difffile] if {$diffmtime($infile) != [file mtime $infile]} { error_popup "File $infile has changed since the diff was performed." return } set mlist [mergelist] set di [lsearch -exact $dirs $dir] set fi $fserial($difffile) set w ".merge:$di:$fi" catch {destroy $w} toplevel $w wm title $w "Dirdiff: merged $infile" frame $w.bar -relief raised -border 2 pack $w.bar -side top -fill x menubutton $w.bar.file -text File -menu $w.bar.file.m -padx 10 -pady 1 menu $w.bar.file.m -tearoff 0 $w.bar.file.m add command -label Save -command "savemerge $w" $w.bar.file.m add command -label Close -command "destroy $w" pack $w.bar.file -side left menubutton $w.bar.edit -text Edit -menu $w.bar.edit.m -padx 10 -pady 1 menu $w.bar.edit.m -tearoff 0 $w.bar.edit.m add command -label Cut -command "tk_textCut $w.t" $w.bar.edit.m add command -label Copy -command "tk_textCopy $w.t" $w.bar.edit.m add command -label Paste -command "tk_textPaste $w.t" $w.bar.edit.m add command -label Find \ -command "difffind :merge:$di:$fi $w.t" pack $w.bar.edit -side left frame $w.f -relief sunk -border 2 entry $w.f.filename $w.f.filename insert 0 $infile pack $w.f.filename -side left -fill x -expand 1 pack $w.f -side top -fill x text $w.t -yscrollcommand "$w.sb set" -font $textfont scrollbar $w.sb -command "$w.t yview" pack $w.sb -side right -fill y pack $w.t -side left -fill both -expand 1 bind $w "$w.t yview scroll -1 p" bind $w "$w.t yview scroll 1 p" set cols [list $diffoldcolor $diffnewcolor] $w.t tag conf insl -back [lindex $cols [expr {1 - $ix}]] $w.t tag conf ndel -back [lindex $cols $ix] set inf [open $infile r] set l 1 foreach mm $mlist { set inc [lindex $mm 0] set m [lindex $mm 1] set tl [lindex $m $ix] if {!$inc} { if {[lindex $m 2] == $ix} { while {$l < $tl} { if {[gets $inf line] < 0} return $w.t insert end $line $w.t insert end \n incr l } if {[gets $inf line] < 0} return $w.t insert end $line ndel $w.t insert end \n ndel incr l } continue } while {$l < $tl} { if {[gets $inf line] < 0} return $w.t insert end $line $w.t insert end \n incr l } if {[lindex $m 2] != $ix} { # insert this line $w.t insert end [lindex $m 3] insl $w.t insert end \n insl } else { # delete this line if {[gets $inf line] < 0} return incr l } } while {[gets $inf line] >= 0} { $w.t insert end $line $w.t insert end \n } # delete last newline catch {$w.t delete "end - 1c" end} close $inf } proc savemerge {w} { set infile [$w.f.filename get] if {$infile == {}} {return} set tmpfile "$infile.tmp" set tf [open $tmpfile w] puts -nonewline $tf [$w.t get 0.0 end] close $tf bkedit $infile catch {file attr $tmpfile -perm [file attr $infile -perm]} file rename -force $infile $infile.orig file rename $tmpfile $infile destroy $w redifffiles } proc nextdiff {} { global textw difflist set ltop [expr int([$textw index @0,0])] foreach l $difflist { if {$l > $ltop} { $textw yview $l.0 break } } } proc prevdiff {} { global textw difflist set ltop [expr int([$textw index @0,0])] set lprev {} foreach l $difflist { if {$l >= $ltop} { if {$lprev != {}} { $textw yview $lprev.0 } break } set lprev $l } } proc diffnextfile {inc} { debug-info "diffnextfile ($inc)" global diffdirs selfile numgroups groups dirs textw if {!([info exists textw] && [winfo exists $textw])} return if {![selnextline $inc] || $numgroups <= 1 || ![info exists diffdirs]} { return } set d1 [lindex $diffdirs 0] set d2 [lindex $diffdirs 1] set group [lindex $groups($selfile) 1] set i1 [lindex $group [lsearch $dirs $d1]] set i2 [lindex $group [lsearch $dirs $d2]] if {$i1 == 0 || $i2 == 0 || $i1 == $i2} return diff2 $d1 $d2 $selfile } proc showsomediff {} { global diffdirs difffile selfile numgroups groups dirs global ycoord canvw groupelts dirinterest debug-info "showsomediff" debug-info "$groups($selfile)" if {[lindex $groups($selfile) 0] == "dir"} { debug-info "-> $selfile $dirs" set subdirs [lindex $groups($selfile) 1] set i 0 foreach d $dirs { #debug-info "$i $d [lindex $subdirs $i]" if {[lindex $subdirs $i] > 0} { debug-info " $d" } incr i } #cd $selfile #rediff return } if {$numgroups <= 1} { set xi [lindex $groupelts(1) 0] if {$xi != ""} { showfile [lindex $dirs $xi] $selfile } return } set group [lindex $groups($selfile) 1] set g1 1 set g2 1 if {[info exists diffdirs] && [info exists difffile]} { set d1 [lindex $diffdirs 0] set d2 [lindex $diffdirs 1] set i1 [lsearch $dirs $d1] set i2 [lsearch $dirs $d2] set g1 [lindex $group $i1] set g2 [lindex $group $i2] if {$difffile != $selfile || \ ([info exists rediffed] && $rediffed == $difffile)} { # looking at a different file from last time, # try to do the same diff if {$g1 > 0 && $g2 > 0 && $g1 != $g2 \ && $dirinterest($i1) && $dirinterest($i2)} { diff2 $d1 $d2 $selfile return } set g1 1 set g2 1 } else { # looking at the same file as last time, # do the next diff if {$g2 < $g1} { set x $g1 set g1 $g2 set g2 $x } } } # work out which groups are interesting (have interesting dirs) for {set g 0} {$g <= $numgroups} {incr g} { set groupinterest($g) {} foreach i $groupelts($g) { if $dirinterest($i) { set groupinterest($g) $i break } } } set ncomb [expr {$numgroups * ($numgroups - 1) / 2}] for {} {$ncomb > 0} {incr ncomb -1} { if {[incr g2] > $numgroups} { if {[incr g1] >= $numgroups} { set g1 1 } set g2 [expr {$g1 + 1}] } if {$groupelts($g1) != {} && $groupelts($g2) != {} \ && $groupinterest($g1) != {} && $groupinterest($g2) != {}} { set d1 [lindex $dirs $groupinterest($g1)] set d2 [lindex $dirs $groupinterest($g2)] diff2 $d1 $d2 $selfile return } } } proc copydifffile {} { global diffdirs selfile groups dirs if {![info exists diffdirs]} return set d1 [lindex $diffdirs 0] set d2 [lindex $diffdirs 1] if {[lindex $groups($selfile) 0] == "dir"} return set group [lindex $groups($selfile) 1] set n1 [lsearch $dirs $d1] set n2 [lsearch $dirs $d2] set i1 [lindex $group $n1] set i2 [lindex $group $n2] if {$i1 == 0 || $i2 == 0 || $i1 == $i2} return copyfile $n2 $n1 $selfile 0 } proc maketextw {} { global textw texttop mergebut filemode textfont toplevel .diffs wm title .diffs "Differences" frame .diffs.bar -relief sunken -border 2 pack .diffs.bar -side top -fill x button .diffs.bar.rediff -text Rediff -command "diffnextfile 0" pack .diffs.bar.rediff -side left button .diffs.bar.options -text Options -command diffoptions pack .diffs.bar.options -side left button .diffs.bar.find -text Find -command "difffind :diffs .diffs.t" pack .diffs.bar.find -side left menubutton .diffs.bar.merge -text Merge -menu .diffs.bar.merge.m -padx 10 menu .diffs.bar.merge.m -tearoff 0 pack .diffs.bar.merge -side left if {!$filemode} { button .diffs.bar.next -text "Next file" -command "diffnextfile 1" pack .diffs.bar.next -side left button .diffs.bar.prev -text "Previous file" -command "diffnextfile -1" pack .diffs.bar.prev -side left } button .diffs.bar.invert -text "Invert" -command "invertbuttons" pack .diffs.bar.invert -side left set texttop .diffs set textw .diffs.t set mergebut .diffs.bar.merge text $textw -width 84 -height 32 -yscrollcommand ".diffs.sb set" \ -font $textfont scrollbar .diffs.sb -command "$textw yview" pack .diffs.sb -side right -fill y pack $textw -side left -fill both -expand 1 bind .diffs "$textw yview scroll -1 p" bind .diffs b "$textw yview scroll -1 p" bind .diffs B "$textw yview scroll -1 p" bind .diffs "$textw yview scroll -1 p" bind .diffs "$textw yview scroll -1 p" bind .diffs "$textw yview scroll 1 p" bind .diffs "$textw yview scroll 1 p" bind .diffs "$textw yview scroll -1 u" bind .diffs "$textw yview scroll 1 u" bind .diffs d "$textw yview scroll \[expr \"int(\[$textw cget -height\]/2)\"\] u" bind .diffs D "$textw yview scroll \[expr \"int(\[$textw cget -height\]/2)\"\] u" bind .diffs u "$textw yview scroll \[expr \"int(-\[$textw cget -height\]/2)\"\] u" bind .diffs U "$textw yview scroll \[expr \"int(-\[$textw cget -height\]/2)\"\] u" bind .diffs n nextdiff bind .diffs p prevdiff if {!$filemode} { bind .diffs N "diffnextfile 1" bind .diffs P "diffnextfile -1" } bind .diffs q removediffs bind .diffs Q "set stopped 1; destroy ." bind .diffs "$textw yview 1.0" bind .diffs g "$textw yview 1.0" bind .diffs "$textw yview -pickplace \[$textw index end\]" bind .diffs G "$textw yview -pickplace \[$textw index end\]" bind .diffs C copydifffile } proc diffoptions {} { global optionw if {[info exists optionw] && [winfo exists $optionw]} { raise $optionw return } set optionw .options toplevel $optionw wm title .options "Dirdiff options" checkbutton $optionw.diffiflag -text "Ignore case" \ -offvalue "" -onvalue "-i" -anchor w pack $optionw.diffiflag -side top -fill x checkbutton $optionw.diffwflag -text "Ignore all white space" \ -offvalue "" -onvalue "-w" -anchor w pack $optionw.diffwflag -side top -fill x checkbutton $optionw.diffbflag -text "Ignore amount of white space" \ -offvalue "" -onvalue "-b" -anchor w pack $optionw.diffbflag -side top -fill x checkbutton $optionw.diffBflag -text "Ignore blank lines" \ -offvalue "" -onvalue "-B" -anchor w pack $optionw.diffBflag -side top -fill x checkbutton $optionw.diffdflag -text "Minimize diffs" \ -offvalue "" -onvalue "-d" -anchor w pack $optionw.diffdflag -side top -fill x checkbutton $optionw.ultabs -text "Underline tabs" -anchor w \ -variable underlinetabs pack $optionw.ultabs -side top -fill x checkbutton $optionw.newfirst -text "Newer file first" -anchor w \ -variable diffnewfirst pack $optionw.newfirst -side top -fill x frame $optionw.ctx pack $optionw.ctx -side top label $optionw.ctx.l -text "Lines of context: " pack $optionw.ctx.l -side left entry $optionw.ctx.v -width 5 -textvariable ctxlines pack $optionw.ctx.v -side left button $optionw.save -text "Save options" -command saveoptions pack $optionw.save -side top -fill x frame $optionw.space -height 6 pack $optionw.space -side top -fill x button $optionw.dismiss -text "Dismiss" -command "destroy $optionw" pack $optionw.dismiss -side bottom -fill x bind $optionw "destroy $optionw" } proc saveoptions {} { global rcsflag diffiflag diffwflag diffbflag diffBflag diffdflag global ctxlines showsame underlinetabs bitkeeper nukefiles redisp_immed global diffprogram showprogram multiviewer global bkgetmode diffnewfirst textfont filelistfont set f [open "~/.dirdiff" w] puts $f [list set diffprogram $diffprogram] puts $f [list set showprogram $showprogram] puts $f [list set multiviewer $multiviewer] puts $f [list set rcsflag $rcsflag] puts $f [list set diffiflag $diffiflag] puts $f [list set diffwflag $diffwflag] puts $f [list set diffbflag $diffbflag] puts $f [list set diffBflag $diffBflag] puts $f [list set diffdflag $diffdflag] puts $f [list set ctxlines $ctxlines] puts $f [list set showsame $showsame] puts $f [list set underlinetabs $underlinetabs] puts $f [list set bitkeeper $bitkeeper] puts $f [list set bkgetmode $bkgetmode] puts $f [list set redisp_immed $redisp_immed] puts $f [list set diffnewfirst $diffnewfirst] puts $f [list set nukefiles $nukefiles] puts $f [list set filelistfont $filelistfont] puts $f [list set textfont $textfont] close $f } proc difffind {tag txt} { global dfindw$tag igncase$tag diffiflag regexp$tag backwards$tag if {[info exists dfindw$tag] && [winfo exists [set dfindw$tag]]} { raise [set dfindw$tag] return } set w .find$tag set dfindw$tag $w toplevel $w wm title $w "Dirdiff: Find" frame $w.f pack $w.f -side top -fill x -expand 1 button $w.f.b -text "Find:" -command [list dofind $tag $txt $w] bind $w [list dofind $tag $txt $w] pack $w.f.b -side left entry $w.f.e pack $w.f.e -side right if {![info exists igncase$tag]} { set igncase$tag [expr {$diffiflag != {}}] } checkbutton $w.case -variable igncase$tag -text "Ignore case" -anchor w pack $w.case -side top -fill x checkbutton $w.regexp -variable regexp$tag -text "Regular expression" \ -anchor w pack $w.regexp -side top -fill x checkbutton $w.backwards -variable backwards$tag \ -text "Search backwards" -anchor w pack $w.backwards -side top -fill x button $w.close -text "Close" -command "destroy $w" pack $w.close -side top -fill x } proc dofind {tag txt w} { global dfindw$tag igncase$tag regexp$tag backwards$tag if {![winfo exists $txt]} return set w [set dfindw$tag] set str [$w.f.e get] if {$str == {}} return set back [set backwards$tag] # By default, start the search from the insertion point. # If there is a selection, start from the end of the selection for # a forwards search, or from the beginning for a backwards search. set start [$txt index insert] if {[$txt tag ranges sel] != {}} { if {$back} { set start [$txt index sel.first] } else { set start [$txt index sel.last] } } set opts {} if {$back} { lappend opts "-backwards" } if {[set regexp$tag]} { lappend opts "-regexp" } if {[set igncase$tag]} { lappend opts "-nocase" } set pos [eval $txt search $opts -count count -- [list $str] $start] if {$pos == {}} { bell return } set epos "$pos + $count c" $txt mark set insert $epos $txt tag remove sel 0.0 end $txt tag add sel $pos $epos $txt see $epos $txt see $pos } proc makepatch {d1 d2} { global patchnum selfile patchfiles patch_outfile global showprogram set files [secondarysel $selfile] if {$files == {}} { error_popup "No files selected!" return } if {![info exists patchnum]} { set patchnum 0 } set patchfiles($patchnum) $files # Put the diff in a temporary file for external viewer if { [llength $showprogram] > 0} { set patch_outfile "patch${patchnum}.diff" set w [open $patch_outfile w] # Or build our own viewer } else { set w ".patch:$patchnum" catch {destroy $w} toplevel $w wm title $w "Patch: $d1 to $d2" frame $w.bar -relief raised -border 2 pack $w.bar -side top -fill x menubutton $w.bar.file -text File -menu $w.bar.file.m -padx 10 -pady 1 menu $w.bar.file.m -tearoff 0 $w.bar.file.m add command -label Save -command "savepatch $w" $w.bar.file.m add command -label Close -command "destroy $w" pack $w.bar.file -side left menubutton $w.bar.edit -text Edit -menu $w.bar.edit.m -padx 10 -pady 1 menu $w.bar.edit.m -tearoff 0 $w.bar.edit.m add command -label Cut -command "tk_textCut $w.t" $w.bar.edit.m add command -label Copy -command "tk_textCopy $w.t" $w.bar.edit.m add command -label Paste -command "tk_textPaste $w.t" $w.bar.edit.m add command -label Find \ -command "difffind :patch:$patchnum $w.t" pack $w.bar.edit -side left frame $w.f -relief sunk -border 2 label $w.f.l -text "Filename: " entry $w.f.filename $w.f.filename insert 0 "patch$patchnum" pack $w.f.l -side left pack $w.f.filename -side left -fill x -expand 1 pack $w.f -side top -fill x text $w.t -yscrollcommand "$w.sb set" scrollbar $w.sb -command "$w.t yview" pack $w.sb -side right -fill y pack $w.t -side left -fill both -expand 1 bind $w "$w.t yview scroll -1 p" bind $w "$w.t yview scroll 1 p" } patchnext $patchnum $w $d1 $d2 0 incr patchnum } # Output lines to either our external patchfile or the internal vieiwer proc lineout {w line} { if {[string match ".*" $w]} { $w.t insert end "$line\n" } else { puts $w "$line" } } proc patchnext {pnum w d1 d2 i} { global patchfiles have_unidiff showprogram patch_outfile nullfile set contextopt [expr {$have_unidiff ? "-u" : "-c"}] update idletasks for {} {[set f [lindex $patchfiles($pnum) $i]] != {}} {incr i} { set p1 [joinname $d1 $f] set p2 [joinname $d2 $f] if {[file exists $p1] && [file exists $p2]} { set fh [open "|diff $contextopt $p1 $p2" r] } elseif {[file exists $p1] && ! [file exists $p2]} { set fh [open "|diff $contextopt $p1 $nullfile" r] } elseif {! [file exists $p1] && [file exists $p2]} { set fh [open "|diff $contextopt $nullfile $p2" r] } else { continue } fconfigure $fh -blocking 0 fileevent $fh readable "readpatch $fh $pnum $w $d1 $d2 $i \"$f\"" return } if {[string match ".*" $w]} { $w.t delete "end - 1c" end } else { close $w debug-info "$showprogram \"$patch_outfile\" &" eval "exec $showprogram \"$patch_outfile\" &" # Should we remove the tempfile here? We don't have it if we used # the internal viewer } unset patchfiles($pnum) } proc diffl_out {w d1 d2 f} { global have_unidiff set contextopt [expr {$have_unidiff ? "-urN" : "-cr"}] lineout $w "diff $contextopt [joinname $d1 $f] [joinname $d2 $f]" } proc readpatch {difff pnum w d1 d2 i f} { global have_unidiff showprogram set n [gets $difff line] if {$n < 0} { if {![eof $difff]} return catch {close $difff} patchnext $pnum $w $d1 $d2 [expr $i+1] return } if {[string match "Binary*" $line]} return if {$have_unidiff} { if {[string match "---*" $line]} { diffl_out $w $d1 $d2 $f } } else { if {[string match "\*\*\* ${d1}*" $line]} { diffl_out $w $d1 $d2 $f } } lineout $w $line } proc savepatch {w} { set outfile [$w.f.filename get] if {$outfile == {}} {return} set outf [open $outfile w] puts -nonewline $outf [$w.t get 0.0 end] close $outf destroy $w } # invoked from the File->Touch menu item proc touchfiles {d} { global selfile set files [secondarysel $selfile] if {$files == {}} { error_popup "No files selected!" return } set now [clock seconds] set bad {} foreach f $files { set df [file join $d $f] if {[catch {file mtime $df $now} err]} { append bad "$df: $err\n" } } if {$bad != {}} { error_popup "Errors occurred:\n$bad" } redifffiles } # invoked from the File->BK get menu item proc bkgetfiles {d} { global selfile bkgetmode set files [secondarysel $selfile] if {$files == {}} { error_popup "No files selected!" return } set bad {} set flag "-qT" if {$bkgetmode != " "} { append flag $bkgetmode } foreach f $files { set df [file join $d $f] if {[catch {exec bk get $flag $df} err]} { append bad "$df: $err\n" } } if {$bad != {}} { error_popup "Errors occurred:\n$bad" } redifffiles } proc exclfilelist {} { global exclw nukefiles if {[info exists exclw] && [winfo exists $exclw]} { raise $exclw return } toplevel .excl wm title .excl "Dirdiff: excluded files" set exclw .excl frame $exclw.b listbox $exclw.l -height 10 -width 40 -yscrollcommand "$exclw.sb set" \ -selectmode single scrollbar $exclw.sb -command "$exclw.l yview" entry $exclw.e pack $exclw.b -side bottom -fill x pack $exclw.e -side bottom -fill x pack $exclw.sb -side right -fill y pack $exclw.l -side left -fill both -expand 1 button $exclw.b.add -text "Add" -padx 20 -command addexcl button $exclw.b.rem -text "Remove" -command remexcl button $exclw.b.close -text "Close" -command closeexcl pack $exclw.b.add -side left -fill x pack $exclw.b.rem -side left -fill x pack $exclw.b.close -side right -fill x bind $exclw.e "addexcl" foreach i $nukefiles { $exclw.l insert end $i } } proc addexcl {} { global exclw nukefiles if {[info exists exclw] && [winfo exists $exclw]} { set e [$exclw.e get] if {$e != {}} { $exclw.l insert end $e lappend nukefiles $e $exclw.l see end } } } proc remexcl {} { global exclw nukefiles if {[info exists exclw] && [winfo exists $exclw]} { set s [$exclw.l curselection] if {$s != {}} { $exclw.l delete $s set nukefiles [lreplace $nukefiles $s $s] } } } proc extprograms {} { global showprogram diffprogram multiviewer toplevel .ext frame .ext.top label .ext.top.diffl -text "2-way Diff/Merging" entry .ext.top.diffe -textvariable diffprogram label .ext.top.showl -text "File Viewing" entry .ext.top.showe -textvariable showprogram label .ext.top.multl -text "Multidiff Viewing" entry .ext.top.multe -textvariable multiviewer grid .ext.top.showl -row 0 -column 0 -sticky e grid .ext.top.showe -row 0 -column 1 -sticky nsew -pady 4 grid .ext.top.diffl -row 1 -column 0 -sticky e grid .ext.top.diffe -row 1 -column 1 -sticky nsew -pady 4 grid .ext.top.multl -row 2 -column 0 -sticky e grid .ext.top.multe -row 2 -column 1 -sticky nsew -pady 4 grid columnconfigure .ext.top 0 -weight 0 grid columnconfigure .ext.top 1 -weight 1 pack .ext.top -fill x -expand yes frame .ext.bot button .ext.bot.ok -text "OK" \ -command { destroy .ext } pack .ext.bot .ext.bot.ok -fill x -expand yes } proc closeexcl {} { global exclw catch {destroy $exclw} catch {unset exclw} } proc secondarysel {fname} { global secsel canvw set files {} foreach it [array names secsel] { lappend files [$canvw itemcget $it -text] } if {$files == {}} { if {$fname == {}} { return {} } set files [list $fname] } return [lsort $files] } proc copyselfile {src dst fname confirm} { debug-info "copyselfile ($src $dst $fname $confirm)" global dirs selitem set files [secondarysel $fname] set n [llength $files] if {$n == 1} { copyfile $src $dst $fname $confirm } else { if {$confirm} { set sd [lindex $dirs $src] set dd [lindex $dirs $dst] if {![confirm_popup "Copy $n older files from $sd to $dd?"]} { return } } foreach f $files { copyfile $src $dst $f 0 # copyfile does updatecline } } # Have to make sure it's finished update idletasks redifffiles } proc copyfile {src dst fname confirm} { debug-info "copyfile $src $dst $fname $confirm)" global dirs bitkeeper filemode set sd [lindex $dirs $src] set dd [lindex $dirs $dst] set srcf [joinname $sd $fname] set dstf [joinname $dd $fname] if {$filemode} { set msg "$src to $dst" set copydst $dstf } else { set msg "$fname from $sd to $dd" set copydst [file dirname $dstf] } if {$confirm} { if {![confirm_popup "Copy older $msg?"]} { return } } set z [string trimright $fname /] if {$z != $fname} { copydir $src $dst $z return } bkedit $dstf if [catch {file copy -force -- $srcf $copydst} err] { error_popup "Error copying $msg: $err" } else { bknew $dstf updatecline $src $dst $fname if {$bitkeeper} { # make file writable if it isn't already set perm [file attr $dstf -perm] if {($perm & 0200) == 0} { file attr $dstf -perm [expr {$perm | 0200}] } } } } proc copydir {src dst dname} { debug-info "copydir ($src $dst $dname)" global dirs groups alllines set sn [lindex $dirs $src] set dn [lindex $dirs $dst] debug-info "cp -p -r $sn/$dname [file dirname $dn/$dname]" if [catch {exec cp -p -r $sn/$dname [file dirname $dn/$dname]} err] { error_popup "Error copying $dname from $sn to $dn: $err" return } foreach f $alllines { if [string match $dname* $f] { updatecline $src $dst $f } } maybe_redisplay } proc bkedit {name} { global bitkeeper if {!$bitkeeper} return set sfile [file join [file dirname $name] SCCS "s.[file tail $name]"] if {[file isfile $sfile] && ![file writable $name]} { if [catch {exec bk edit -q $name} err] { error_popup "Warning: couldn't check out $name: $err" } } } proc bknew {name} { global bitkeeper bkgetmode if {!$bitkeeper} return set sdir [file join [file dirname $name] SCCS] set sfile [file join $sdir "s.[file tail $name]"] if {[file isdir $sdir] && ![file exists $sfile]} { if [catch {exec bk new -q $name} err] { error_popup "Warning: couldn't create new BK file $name: $err" } else { set flag "-qT" if {$bkgetmode != " "} { append flag $bkgetmode } if [catch {exec bk get $flag $name} err ] { error_popup "Warning: couldn't check out new BK file $name: $err" } } } } proc removeselfile {dst fname} { debug-info "removeselfile ($dst $fname)" global groupelts dirs set files [secondarysel $fname] if {$files == {}} return set nf 0 set nd 0 foreach x $files { if {[string range $x end end] == "/"} { incr nd } else { incr nf } } set dd [lindex $dirs $dst] if {$nd + $nf == 1} { set x [string trimright [joinname $dd $fname] /] if {![confirm_popup "Remove $x?"]} { return } } else { set stuff "Remove " if {$nd > 0} { if {$nd == 1} { append stuff "1 directory " } else { append stuff "$nd directories " } if {$nf > 0} { append stuff "and " } } if {$nf == 1} { append stuff "1 file " } elseif {$nf > 1} { append stuff "$nf files " } append stuff "from $dd?" if {![confirm_popup $stuff]} { return } } foreach f $files { set d [string trimright $f /] set dstf [joinname $dd $d] if {$d == $f} { set bad [catch {file delete $dstf} err] } else { set bad [catch {file delete -force $dstf} err] } if $bad { error_popup "Error deleting $dstf: $err" } else { updatecline [lindex $groupelts(0) 0] $dst $f } } debug-info "From removeselfile" selcurfile maybe_redisplay } proc confirm_popup msg { global confirm_ok set confirm_ok 0 set w .confirm toplevel $w wm transient $w . message $w.m -text $msg -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 button $w.ok -text OK -command "set confirm_ok 1; destroy $w" pack $w.ok -side left -fill x button $w.cancel -text Cancel -command "destroy $w" pack $w.cancel -side right -fill x bind $w "grab $w; focus $w" tkwait window $w return $confirm_ok } proc error_popup msg { set w .error toplevel $w wm transient $w . message $w.m -text $msg -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 button $w.ok -text OK -command "destroy $w" pack $w.ok -side bottom -fill x bind $w "grab $w; focus $w" tkwait window $w } proc notalldirs {dirs} { set type "" foreach d $dirs { if {[catch {file lstat $d stat} err]} { puts stderr $err exit 1 } if {$type == ""} { set type $stat(type) } elseif {$type != $stat(type)} { puts stderr "Error: $d is a $stat(type) but [lindex $dirs 0] is a $type" exit 1 } } return [expr {$type == "file"}] } proc go {} { debug-info "go ()" global diffing filemode dirs nextserial if {[llength $dirs] == 0} {exit 0} set diffing 0 set nextserial 0 set filemode [notalldirs $dirs] icons makewins initcanv resetsel update idletasks canvdiffs } proc rediff {} { debug-info "rediff ()" initcanv resetsel update idletasks canvdiffs } proc repackgroups {gr} { debug-info "repackgroups ($gr)" if {[lindex $gr 0] == "dir"} { return $gr } set glist [lindex $gr 1] set glsort [lsort $glist] set ng(0) 0 set lg 0 set gc 0 foreach e $glsort { if {$e != $lg} { set lg $e incr gc set ng($e) $gc } } if {$gc == [lindex $gr 0]} { return $gr } set newlist {} foreach e $glist { lappend newlist $ng($e) } return [list $gc $newlist] } proc interesting_line {gr} { global dirinterest dirs showsame if {$gr == {}} { return 0 } if {$showsame} { return 1 } set glist [lindex $gr 1] set i 0 foreach e $glist { if $dirinterest($i) { if {[info exists first]} { if {$e != $first} { return 1 } } else { set first $e } } incr i } return 0 } proc redisplay {} { debug-info "redisplay" global canvw canvy canvy0 alllines groups ruletype selitem $canvw delete all set canvy $canvy0 set sav_yview [$canvw yview] $canvw conf -scrollregion "0 0 0 1" debug-info " selitem: $selitem" foreach f $alllines { set gr $groups($f) if {$gr != {}} { set gr [repackgroups $gr] set groups($f) $gr if {[interesting_line $gr]} { displine $gr $f } } } if [info exists ruletype] { ruleoff $ruletype } $canvw yview moveto [lindex $sav_yview 0] } proc maybe_redisplay {} { debug-info "maybe_redisplay" global redisp_immed if $redisp_immed redisplay } proc icons {} { global agecolors image create photo ex \ -format gif -data { R0lGODlhEAANAIAAAAAAAP///yH+Dk1hZGUgd2l0aCBHSU1QACH5BAEAAAEA LAAAAAAQAA0AAAIgjI95ABqcWENSVXMtzE5CR30g5o3PJkYiR05LenauqRQA Ow== } image create photo folder \ -format gif -data { R0lGODlhEAANAMIAAISEhMbGxv/si////wAAAAAAAAAAAAAAACH+Dk1hZGUg d2l0aCBHSU1QACH5BAEAAAQALAAAAAAQAA0AAAMoSATM+nAFQUUAUYFZ6W3g II4kyQxd2p1qy7bpC1fyLNQzDusu6P+ABAA7 } image create photo paper \ -format gif -data { R0lGODlhEAANAKEAAISEhP///8bGxgAAACH+Dk1hZGUgd2l0aCBHSU1QACH5 BAEAAAMALAAAAAAQAA0AAAIp3ICpxhcPAxCgufhAoE1jmXRfVDHeKIloaq6s cY4l7M4XasdfrvSIUQAAOw== } image create photo paper_green \ -format gif -data { R0lGODlhEAANAMIAAP///4SEhP7/vsbGxgDKAP///////////yH5BAEAAAcALAAAAAAQAA0A AAMoeBfcrnCRSUmwUdZ5Mezb821hBJKecpKBiVLt+KbaG6szvZaf4zOKBAA7 } image create photo paper_yellowgreen \ -format gif -data { R0lGODlhEAANAMIAAP///4SEhP7/vsbGxgCAAACAQNLmAP///yH5BAEAAAcALAAAAAAQAA0A AAMoeBfcrnCZSU2wUdZ5Mezb821hBJKecpKBiVLt+KbaG6szvZaf4zOKBAA7 } image create photo paper_yellow \ -format gif -data { R0lGODlhEAANAMIAAP///4SEhPfhAMbGxv///////////////yH5BAEAAAMALAAAAAAQAA0A AAMoOBPcrnCJSUWwUdZ5Mezb821hBJKecpKBiVLt+KbaG6szvZaf4zOKBAA7 } image create photo paper_orange \ -format gif -data { R0lGODlhEAANAMIAAP///4SEhOxzAMbGxv///////////////yH5BAEAAAMALAAAAAAQAA0A AAMoOBPcrnCJSUWwUdZ5Mezb821hBJKecpKBiVLt+KbaG6szvZaf4zOKBAA7 } image create photo paper_red \ -format gif -data { R0lGODlhEAANAKEAAISEhOE+IbchAP///yH5BAEAAAMALAAAAAAQAA0AAAIo3ICpxhcPA5DN xQcEZfPK1HQeFo4QUJqbIY4op66W+bJxPbuhwiNGAQA7 } set agecolors(dir) {ex folder} set agecolors(0) {ex} set agecolors(1) {ex paper} set agecolors(2) {ex paper_green paper_red} set agecolors(3) {ex paper_green paper_yellow paper_red} set agecolors(4) {ex paper_green paper_yellowgreen paper_orange paper_red} set agecolors(5) {ex paper_green paper_yellowgreen paper_yellow paper_orange paper_red} } proc search_canvas {} { global canvw selfile set search $selfile resetsel update idletasks set str_items [$canvw find withtag strings] foreach idx $str_items { set name [$canvw itemcget $idx -text] if {[string match "*$search*" $name]} { set selitem $idx $canvw select from $idx 0 $canvw select to $idx end set clickitem $idx set clickmode 1 debug-info "From search_canvas" selcurfile addsecsel $idx } } } if {![info exists dirs]} { global onlyfiles ctxlines showsame set dirs {} set ok 1 set argc [llength $argv] set moreopts 1 for {set i 0} {$i < $argc} {incr i} { set arg [lindex $argv $i] if {$moreopts && [string range $arg 0 0] == "-"} { switch -regexp -- $arg { "--" { set moreopts 0 } "-a|--all" { set nukefiles {} } "-o|--only" { incr i if {$i < $argc} { lappend onlyfiles [lindex $argv $i] set nukefiles {} } else { puts stderr "no argument given to $arg option" set ok 0 } } "-I|--ignore" { incr i if {$i < $argc} { ignorefile [lindex $argv $i] } else { puts stderr "no argument given to $arg option" set ok 0 } } "-r|--rcs" { if $nofilecmp { puts stderr "can't use $arg: libfilecmp.so.0.0 couldn't be loaded" set ok 0 } set rcsflag "-rcs" } "-t|--bktag" { if $nofilecmp { puts stderr "can't use $arg: libfilecmp.so.0.0 couldn't be loaded" set ok 0 } set rcsflag "-bk" } "-c|--context" { incr i if {$i < $argc} { set ctxlines [lindex $argv $i] } else { puts stderr "no argument given to $arg option" set ok 0 } } "-D|--maxdepth" { incr i if {$i < $argc} { set maxdepth [lindex $argv $i] } else { puts stderr "no argument given to $arg option" set ok 0 } } "-b" { set diffbflag "-b" } "-w" { set diffwflag "-w" } "-B" { set diffBflag "-B" } "-i" { set diffiflag "-i" } "-d" { set diffdflag "-d" } "-S" { set showsame 1 } "-K" { set bitkeeper 1 } "-h|--help" { usage exit 0 } default { puts stderr "unrecognized option $arg" set ok 0 } } } elseif {$arg != {}} { lappend dirs [string trimright $arg /] } } if {$ok && [llength $dirs] == 0} { # Ask for directories if they weren't on the command line wm withdraw . NewDirDialog #set dirs [list $d0 $d1 $d2 $d3 $d4] # Prune out the empty entries set newlist {} for {set i 0} {$i < [llength $dirs]} {incr i} { if {[lindex $dirs $i] != {} } { lappend newlist [lindex $dirs $i] } } set dirs $newlist if {[llength $dirs] < 2 } { # Can't user error_popup here without de-iconifying . first tk_dialog .err "Error" "Need at least 2 directories" error 0 {OK} set ok 0 } wm deiconify . } if {!$ok} {exit 1} set doit 1 } if [info exists doit] {go} tkcvs-8.2.3.orig/contrib/vendorcode.sh0000644000175000017500000000505211664612512016113 0ustar timtim#!/bin/sh # File: vendorcode.sh # By: Eugene Lee, 1995 # Modified: EAL 10/21/03 Changed directory Vendor to 3rdParty # EAL 1/28/04 Code in V1,V2,V3 in directory 3rdParty moved to its # own directories under directory Examples echo "This script will create source code in directories Examples/Local-1.0", echo "Examples/3rdPartyV1, Examples/3rdPartyV2, & Examples/3rdPartyV3 to be" echo "used to demonstrate merging of vendor code into a local version of" echo "the code as described in file vendor5readme.txt" echo "" echo "Continue? (y/n):" read answer case "$answer" in y) ;; Y) ;; *) exit esac if test -d Examples then echo directory Examples exists already else mkdir Examples echo created directory Examples fi cd Examples if test -d Local-1.0 then echo directory Local-1.0 already exists cd Local-1.0 rm -f * else mkdir Local-1.0 echo created directory Local-1.0 cd Local-1.0 fi # Create files for module Local-1.0 cat > main < get < main < get < main < get < sort < main < getsort < $SCRIPTFILE # loop once to generate diff commands for each file (also spit out some useful info) for f in $* do FILEPATH="$f" FILEBASE="`basename ${FILEPATH}`" TMPPATH="${TMP}/${REV}-${FILEBASE}" TMPPATHB="${TMP}/${REVB}-${FILEBASE}" if [ ! -r "${FILEPATH}" ] then echo "$0: Local File '${FILEPATH}' not found." continue fi if [ "$REVB" != "" ] ; then echo -n "${REV} vs. ${REVB} ${FILEBASE}..." else echo -n "${REV} vs. LOCAL ${FILEBASE}..." fi $CVS -r${REV} $FILEPATH > $TMPPATH 2> /dev/null if [ "$REVB" != "" ] ; then $CVS -r${REVB} $FILEPATH > $TMPPATHB 2> /dev/null fi if [ -s "$TMPPATH" ] then if [ "$REVB" != "" ] ; then if [ -s "$TMPPATHB" ]; then echo "$VDIFF $TMPPATH $TMPPATHB >/dev/null &" >> $SCRIPTFILE echo -n " : diffcount =" diff $TMPPATH $TMPPATHB | grep '^[0-9]' | wc -l else echo " :" echo "$0: CVS revision ${REV} for '${FILEPATH}' not found." fi else echo "$VDIFF $TMPPATH $FILEPATH >/dev/null &" >> $SCRIPTFILE echo -n " : diffcount =" diff $TMPPATH $FILEPATH | grep '^[0-9]' | wc -l fi else echo " :" echo "$0: CVS revision ${REV} for '${FILEPATH}' not found." fi done echo "# wait for all diffs to finish, then cleanup temporaries" >> $SCRIPTFILE echo "wait" >> $SCRIPTFILE # loop again for cleanup for f in $* do FILEPATH="$f" FILEBASE="`basename ${FILEPATH}`" TMPPATH="${TMP}/${REV}-${FILEBASE}" TMPPATHB="${TMP}/${REVB}-${FILEBASE}" # loop for these: echo "rm -f '$TMPPATH' '$TMPPATHB'" >> $SCRIPTFILE done # One last step of non-looped cleanup stuff: # Can the script remove itself? YES. echo "rm -f '$SCRIPTFILE'" >> $SCRIPTFILE # Display files . $SCRIPTFILE & tkcvs-8.2.3.orig/contrib/tksvn2bcompare.pl0000755000175000017500000001370411664612512016732 0ustar timtim#! /usr/bin/perl use strict; use warnings; use AtExit; use Getopt::Std; # Temporary working directory use constant TMP_DIR => '/tmp'; # Array of the file name in a file information array use constant FILE_NAME => 0; # Array of the file display title in a file information array use constant FILE_TITLE => 1; # SVN text base prefix use constant SVN_TEXT_BASE_PREFIX => '.svn/text-base'; # SVN text base suffix use constant SVN_TEXT_BASE_SUFFIX => '.svn-base'; # Binary Compare executable use constant BCOMPARE_EXE => 'bcompare'; # Display differences between two files # # Parameters: # - file_info_new: Reference to information array for new file # - file_info_old: Reference to information array for old file # - verbose: Verbose mode flag sub displayDiffs { my ($file_info_new, $file_info_old, $verbose) = @_; # extract all the necessary file information my $file_loc_old = $file_info_old->[FILE_NAME]; my $file_title_old = $file_info_old->[FILE_TITLE]; my $file_loc_new = $file_info_new->[FILE_NAME]; my $file_title_new = $file_info_new->[FILE_TITLE]; # construct the diff command my $bcompare_cmd = BCOMPARE_EXE . " '$file_loc_old' '$file_loc_new' -title1='$file_title_old' -title2='$file_title_new'"; # if we're in verbose mode if($verbose) { print "Executing Binary Compare: $bcompare_cmd\n"; } # display the difference `$bcompare_cmd`; } # Remove a file # # Parameters: # - fname: Name of file to remove # - verbose: Verbose mode flag sub removeFile { my ($fname, $verbose) = @_; # if we're in verbose mode if($verbose) { print "Deleting temporary file: $fname\n"; } # remove the given file unlink $fname; } # Get the file information array for a given file # # Parameters: # - file_rem: Remote file # - verbose: Verbose mode flag # # Returns: Array to file information array sub getFileInfo { my ($file_rem, $verbose) = @_; # file information array my @file_info; # if the file does not contain '://', we assume the file is local if(! ($file_rem =~ /:\/\//)) { # if the local file does not exist if(! -f $file_rem) { print STDERR "Error: Local file does not exist: $file_rem\n"; # error, the local file must exist in order to diff exit 1; } # copy the SVN text base prefix/suffix to local variables so we can use # them in the regex pattern my $svn_text_base_prefix = SVN_TEXT_BASE_PREFIX; my $svn_text_base_suffix = SVN_TEXT_BASE_SUFFIX; # save the filename directly $file_info[FILE_NAME] = $file_rem; # if this is not a file from the SVN text base directory if(! ($file_rem =~ /^$svn_text_base_prefix\/(.+)$svn_text_base_suffix$/)) { # construct the display title $file_info[FILE_TITLE] = "$file_rem : Working Copy"; } else { # construct the display title $file_info[FILE_TITLE] = "$1 : Working Base"; } # return a reference to the file information array return \@file_info; } # extract the file basename from the URL die if(! ($file_rem =~ /^.+\/(.+)$/)); # save the file basename my $file_base = $1; # sanity check the file basename die if(!defined($file_base)); die if(length($file_base) == 0); # compute the local file to save to my $file_loc = TMP_DIR . "/$file_base"; # construct the SVN command to download the file my $svn_cmd = "svn cat $file_rem > $file_loc"; # if we're in verbose mode if($verbose) { print "Executing SVN command to download file: $svn_cmd\n"; } # execute SVN if(0 != system($svn_cmd)) { print STDERR "Error: Unable to save file from SVN: $file_rem\n"; # error, unable to save file from SVN exit 1; } # make sure the temporary file gets removed when the program exits atexit(\&removeFile, $file_loc, $verbose); # save the name of the temporary local file $file_info[FILE_NAME] = $file_loc; # parse the filename and revision from the base of the temporary filename base die if(! ($file_base =~ /^(.+)@(.+)$/)); # save the filename and revision number my ($fname, $rev) = ($1, $2); # make sure the split worked as expected die if(!defined($fname)); die if(!defined($rev)); die if(length($fname) == 0); die if(length($rev) == 0); # construct the display title $file_info[FILE_TITLE] = "$fname Revision $rev"; # return a reference to the file information array return \@file_info; } # Print program usage instructions sub printUsage { print < 2)) { # print program usage instructions printUsage(); # error, user did not provide correct arguments exit 1; } # determine whether or not we're in verbose mode my $verbose = defined($options{v}); # get the first file my $file1_rem = $ARGV[0]; # second file my $file2_rem; # if both files were specified if($num_args == 2) { # get the second file $file2_rem = $ARGV[1]; } else { # otherwise the second file is in the SVN text base directory $file2_rem = SVN_TEXT_BASE_PREFIX . "/$file1_rem" . SVN_TEXT_BASE_SUFFIX; } # if the two filenames are the same if($file1_rem eq $file2_rem) { print STDERR "Error: Cannot compare file with itself!\n"; # error, cannot compare file with itself exit 1; } # get the file information arrays for both files my $file1_info = getFileInfo($file1_rem, $verbose); my $file2_info = getFileInfo($file2_rem, $verbose); # display the differences displayDiffs($file1_info, $file2_info, $verbose); # all done! exit 0; tkcvs-8.2.3.orig/CHANGELOG.txt0000644000175000017500000007257711664612512014037 0ustar timtimRelease 8.2.3 2011 - Works with Subversion 1.7 (no .svn directory at lower levels) - Make the CVS module-level file browser searchable - Choice of sorting files in the working directory by filename or by status is now a persistent preference - Unwork-around some work-arounds for wish8.5, which are fixed now - Fix Bug #2797830 "Bookmark with space can't be deleted" - TkDiff 4.2 (works with Subversion 1.7) Release 8.2.2 May, 2010 - Make the propget svn:mergeinfo branch diagram behave more like our merge tags, i.e. show only the first merge point instead of all revisions containing the merge. This should improve performance relative to version 8.2, too. - Improve performance in large Subversion directories - Fix CDE font problem - Show date of directories, too. Fix sort-by-revision in SVN directory. - Roll back svn update --accept postpone because it doesn't work with older clients - Clean up finished namespaces for some exec viewers Release 8.2.1 January, 2010 - History of commit log messages is kept, so you can copy-and-paste previous messages that you've used during the current session. - When a directory is refreshed in the Working Directory Browser, the scroll position is restored. - Locking and unlocking for SVN, and some additional SVN-specific right-click popups (Matthias Vorwerk) - Icons for symbolic links in a SVN directory (Matthias Vorwerk) - SVN output parsing change (xml-regexp-based) (Matthias Vorwerk) - Subversion Branch diagram can find branchpoint for a file that wasn't revised at that point (Steve Schwarm) - CVS branch browser no longer fooled by a log comment containing a dashed line of exactly the same length as the log's normal delimiter, although you may not see the rest of the comment - tksvn2bcompare.pl added to contrib directory. It enables the use of "bcompare" in place of tkdiff. (Adam McLaurin) - Add a note to the FAQ about running the X11 version of TkCVS on the Mac. - Fix mixed-up tooltips in the File Browser opened from the Module Browser. - Fix Reports->Status recursive/local switch on cvscfg(recurse) - Button in the CVS "Update with Options" dialog to make it easier to update a sticky-tagged file to the current directory tag (Jacques Klein) - Change tkcvs.tcl so it can be made into a starkit more easily Release 8.2 November, 2008 - Merge arrows are drawn in the Branch Browser for merges tracked by Subversion 1.5's mergeinfo property and CVSNT's mergepoint feature. - The branch diagram can be searched to find a version, date, tag, or author - Log browser always produces a verbose log of revisions on the selected branch instead of obeying the Directory Browser's "Log Detail" setting - If your SVN repository has a structure similar to trunk, branches, and tags but with different names, you can tell TkCVS about it by setting variables in tkcvs_def.tcl: set cvscfg(svn_trunkdir) "elephants" set cvscfg(svn_branchdir) "dogs" set cvscfg(svn_tagdir) "ducklings" - Fix a bash-ism in contrib/cvsdiff - Changed the trace levels so that "F" lets you get the CVS/SVN stdout without the whole debug output Release 8.1 November, 2007 - Rework the merge functionality. There's only one dialog for tagging, which you OK when you're ready to commit the merges. - Use panedwindow for the Workdir Browser. It has advantages and disadvantages, but it will have more advantages when we can migrate to tk8.5. Change the highlighting so it goes across all columns, and enable selection from all columns. - Add a menu item to do "svn resolved" - Fix [ 1824733 ] CVS menu in SVN work area for changed file - Fix invocation of tkdiff when one SVN revision is selected in the branch browser, diffing it against the current file like the cvs behavior - Add options to use -l and not use -P in cvs update-with-options. - Improve visibility of searched item in annotation text. - Fix for when an e-mail address appears in svn status in https protocol - Remove white boxes around Aqua pill-style buttons Release 8.0.4 May, 2007 - The Branch Browser detects lack of a trunk directory, warns that it can't do much without that structure, and continues without it. - Fix [ 1483057 ] Empty Branch Diagram for deleted files (TkSVN) If a file had been removed from the trunk and was diagrammed from a branch, some or all of the diagram could be missing. - Fix [ 1673519 ] tcl error with SVN->Browse the Log Diagram - Fix [ 1581111 ] svn url trouble with French localization - Ask for confirmation before reverting files - Display TkCVS version in window title of workdir and module browsers. - Don't fail if CVS gives a date format that tcl can't handle - Added a button for a text history log of the file to the branch browser. - Added a command line option -annotate or -blame to open the annotation browser from the command line - The "New Directory" button is back - cvscfg(svn_branch_filter) and cvscfg(svn_branch_max_count) to filter which branches to draw in the log browser - Delete the exec namespaces after the execs are finished. This should cause better memory usage behavior. - Add a contrib directory containing a wrapper for gvimdiff to replace tkdiff, and a program to compare the contents of directories. Release 8.0.3 March, 2006 - Automatic tagging of merges works for SVN the same as CVS - Working directory browser observes svn_ignore - Clean up some filenames-with spaces issues - TkDiff 4.1.3 Release 8.0.2 January, 2006 - Fix error in Branch Diagram when searching for merge tags - cvscfg(mergetrunkname) option to replace the literal "trunk" in the code with an arbitrary string - Branch Browser in SVN will diff a single selection in the tree with the file in the current directory - Fix strange "SVN Path" in top entry of Branch Browser (only cosmetic) - Lengthen maximum length of error message to trigger an error popup in exec. That lets a cvs log failure due to a permission problem tell us what went wrong. Release 8.0.1 January, 2006 - Fix a couple of undefined variables - Add log button to workdir browser and change the cvs_log function to eliminate post-processing, using syntax highlighting instead Release 8.0 December, 2005 - The Annotation browser optionally shows line numbers. - Multiple branch-browser fixes for Subversion: - Treat branchpoints as real revisions, so they have both a blue box and a black one in the diagram. It's rather inelegant, but it works with the way the branch browser was designed. Solves problem of branches not being drawn if they branch straignt from another branchpoint. - Send URL paths instead of -r arguments to the diff, svn-cat, and annotation commands because Subversion doesn't cross branch boundaries with simple revision arguments, and doesn't tell you that it's not giving you the revision you asked for. - Bugfix: relative URL path in Branch Browser is constructed correctly for path depths > 2 - The Branch Browser counts the tags when making a Subversion diagram and gives you a chance to skip the tag step if there are many, where "many" is defined by cvscfg(toomany_tags). Constructing the branch diagram for Subversion is extremely inefficient, and drawing the tags can take longer than it's worth. - For Subversion directories, the Module Browser shows the number of items within the folder instead of the "svn list -v" info string. That may help you decide whether to open the folder or not. - The Branch Browser positions the diagram so "you are here" is in the visible canvas, fixing a long-time nagging irritation. Release 8.0b1 December, 2005 - TkCVS now supports Subversion. This involved a major re-organization of the program, and many things have changed a little. The program will detect which revision-control system a directory is under and react appropriately. The previously undocumented RCS support is explicit now and has been enhanced somewhat. - Command line "tkcvs " will open the log browser without the -log option. - The annotation browser estimates how many days per color or revs per color to use, so cvscfg(dayspercolor) and cvscfg(revspercolor) are gone. You can still change it per file in the annotation browser. - The directory-level CVS Merge Tool has a pull-down with a list of the tags, to make it easier to "merge since" a tag. - TkDiff 4.1.1, which has a security patch. Release 7.2.4 July, 2005 - Fix problem with confirmation dialog Release 7.2.3 July, 2005 - Close file descriptor for stderr output, which could exhaust the maximum number of open files. - Re-work the pop-up dialogs so they appear in the center of their parent window, not the middle of the screen (or between the two screens.) - The branch browser can now diff two versions even if it was invoked from the Module Browser and the file isn't checked out. - TkDiff 4.1 (Tk8.4 recommended but not required) - The bookmarks stay in alphabetical order. Release 7.2.2 November, 2004 - Handle UTF time format in cvs 1.12.8 log. The author field no longer gets lost during parsing. - Modify the exec module so that it gives back the GUI while the background process is running - If using an external editor for commit messages (use_cvseditor), don't display the dialog but go straight to the editor. - New menu functions to set or unset the -kb (keyword-binary) flag - Added a button to save the contents of a log-viewer window to a file - Choose whether to update the working directory after branching. TkCVS has always updated the working directory to be on the new branch, though cvs itself doesn't do that. Now TkCVS gives you the choice. - Change the cvs log options so the merging tool doesn't have a problem with certain combinations of cvs clients and servers (1.10 client and 1.12 server was one such bad combination) - Fix default cvscfg(editor). The defaults are now set cvscfg(editor) "xterm" set cvscfg(editorargs) "-e vi" - TkDiff 4.0.2 Release 7.2.1 April, 2004 - Vendor Merge is back, rehabilitated by Eugene Lee, its author. - Bug fixes: 892051 apply the tag ignores user input 892050 merge changes to current doesn't do that (No report) Clear entry containing tag instead of appending, so tag doesn't grow if dialog is re-opened. Fixed a few problems with defaults in tkcvs_def.tcl. - The installer no longer hardcodes the library path in tkcvs. The program now figures out where it is at runtime. - You can now configure how many lines to keep in the trace window with $cvscfg(trace_savelines) - Import dialog has better defaults. Version default is the same as you get if you don't supply the -b option on the command line. - Don't show stderr in CVS Commit dialog, since if there are many directories they may make too much output and make you miss what you were interested in. Release 7.2 January 1, 2004 - More merging functionality. Helps you tag the merged-from and merged-to versions, and if you use the tagnames properly, draws curving arrows between them to show where merges occurred. The tagnames are configurable with the cvscfg(mergetoformat) and cvscfg(mergefromformat) variables. - Requires Tk 8.4 for the curved lines. - Fixed bug in annotation browser wherein it didn't change colors when "Days per Color" changed. - No longer pops an error dialog if the background exec fails. Just beeps at you. The command's output should tell you what happened. - TkDiff v4.0: "r" key binding to recompute diffs fix for diff symbols in Change Bars disappearing preferences for showing whitespace differences better tolerance of Windows filenames Release 7.1.4 November 6, 2003 - Bugfix for hangs in 7.1.3 - Bugfix for uninitialized X1 coordinate Release 7.1.3 October 20, 2003 - Compatible with CVS 1.11.8, which lost the global -l flag. - Mainline tkdiff is back. Tkdiff is on Sourceforge now and there's an official beta, which is pretty stable. - Annotation browsing is available from the log branch browser. There's also a button on the main window to make it more likely that people will discover the function, which can be most useful. - Merging will work to the branch as well as to the trunk in the logcanvas browser. - Solved a few problems with the exec functionality. High CPU usage is gone. It now gives back the UI (to one degree or another) and captures stderr (both) instead of doing one or the other. - Made a filter for single-line module-diff (patch) output. Now files that were added, removed, or changed are easier to pick out visually. - Added an Apply button to the module-level checkout, export, and patch dialogs. Since they don't save state, you could have to type the same thing over and over on subsequent operations. Release 7.1.2 December 21, 2002 - Fix exec problems. Exit status is detected properly. There's a new trace level so you can see what CVS says on stderr. - Log browser no longer gives a stack trace if it can't figure out where to put the "you are here" guy. It just draws the diagram without him. - The correct highlight foreground is used in the canvas so the highlighted text is readable with Windows color schemes. - Directory-level merge now picks up new directories (-d flag.) It should be an option, but you get bitten worse without -d than with it. Release 7.1.1 November 13, 2002 - Fix right-mouse button problem that showed up in the contextual popup for the current directory canvas. Fixed an area-select problem while I was at it. - Fix reversed -j arguments in the merge_diff dialog - Required Tk version is 8.3, not 8.1 Release 7.1 November 10, 2002 - New graphical tool to help with merging directories and seeing an overview of the branches. - New, completely re-written, branching diagram. Much more sensible and pleasing to the eye. Contributed by Mike Jagdis - You can invoke the log browser from the command line: tkcvs [-dir directory] [-root cvsroot] [-win workdir|module] [-log file] Saves a lot of time if you're working with a remote repository and you only want to browse one file. Contributed by John Lash. - Option to use an external editor for commit messages so the rcsinfo template feature can be used. Terminal-based editors only for now, unless you don't mind a superfluous shell window popping up in addition to your GUI-based editor. Contributed by Mike Jagdis. - A picklist keeps a temporary history of directories visited. Favorite places can be bookmarked. - Capability to browse RCS files, in case you find yourself in an rcs-controlled directory. You can't do checkins and checkouts, but you can see which files are under RCS control, which ones differ from the checked-in version, and who has them locked. - A heavily patched TkDiff that works in AquaTK, in case you're a MacOS X fan. TkCVS does pretty well in AquaTK as-is, with a few tweaks to tkcvs_def.tcl. - More intuitive module-operation dialogs contributed by Mike Jagdis. - Re-arranged buttons. There's a somewhat overwhelming array, but now almost everything is there without resorting to the menus. I've tried to organize them helpfully. In addition, the ones that do CVS functions are disabled when in a non-CVS directory. - The ".." directory has been removed from the browser, and we now have a "go up" button instead. Saves space in the list and keeps people from doing unfortunate things to ".." - Namespace problems eliminated in log browser. Now you can have as many open as you like. Contributed by Mike Jagdis - Improved viewer for command output. It has multi-command capability. That is used to advantage by the import routine, which used to open "waaaay too many windows." Contributed by Mike Jagdis - Smoother (faster?) scrolling in the directory and module browsers, due to eliminating the windows-within-a-canvas method of drawing icons. Contributed by John Cerney. - Patch for filtering and color coding "cvs update" output, contributed by Laurent Duperval. - Since there are more ways you can start tkcvs, the exiting had to be cleaned up so you don't accidentally exit, or worse, leave a windowless wish running. Contributed by Mike Jagdis. - Enhanced dialog for importing a module. Contributed by Mike Jagdis - Always sort by filename so that even if the files are sorted some other way, they are sub-sorted in alphabetical order. - If the edit file button is clicked with nothing selected, a dialog box pops up to allow input of a (new?) file name rather than erroring. (Mike Jagdis) - There was a call to "cat" in exec.tcl. It's gone now, so Windows users don't have to have cat.exe anymore. - Repaired a bug in which if you did an import and the "Group Aliases in a Folder" option was set, the aliases would be duplicated in the browser. - These days X is usually set up to map mouse wheel motion to button 4/5 events. Patch adds bindings for buttons for and 5 so that the mouse wheel can be used to scroll under X. (Mike Jagdis) Release 7.0.3 January 23, 2002 - Improved the algorithm for building the tree in the module browser, making it less error-prone. - Recursive add respects .cvsignore and $cvscfg(ignore_file_filter) - The Working Directory Browser parses the "Sticky Options" field and uses a different icon if a locally-added or up-to-date file is binary (-kb). - The Log Browser color-codes the selected revisions so you can visually match the log text with the box in the branching diagram. - The dialog for module-level tagging (cvs rtag) is a little more informative (and the code is a little less rococo). - The installer has a new option "-finaldest", to facilitate building debian-style packages. - The man page is installed in man1 instead of mann. - The tooltips no longer persist until the operation started by the button is finished. Release 7.0.2 October 19, 2001 - Fixed duplicate items when using Control-B1 to add items to the selection in the workdir browser. - Several bugfixes to the module browser. You can now have "&" composites at the end of a nested module without blowing it out of the graphical tree structure. Also fixed bugs in finding a module's title and choosing the right icon. - The ability to group alias modules in their own folder is back, but as an option cvscfg(aliasfolder). It defaults to true. - There's now an Options menu in the module browser to turn tracing on and off and temporarily change the display of alias modules. - New "File->Module File" item in the module browser menu displays the CVSROOT/modules file in a text window. - Do a "file join" on the CVSROOT variable to put it in the native path format. That helps with a PC and a Samba-mounted repository and doesn't seem to hurt anything else. Release 7.0.1 September 3, 2001 - By popular demand, made file selection in the main canvas conform more to the Shift-click-adds-range and Ctrl-click-adds-single model. - Made the CDE parameter thing more bullet-proof. It shouldn't fail if something is missing now. - After a module import, it renames the original directory and checks out into a fresh one. Otherwise, the checkout isn't recursive and you get a lot of "independently added by a second party" messages. - Commented out the tkwaits that were causing the commit and merge dialogs to disappear in some window managers. Unfortunately they may have to be un-commented back on some systems, especially Mandrake, which seems to exhibit timing problems sometimes. - If a file's log message had a line containing only "=" characters, the logcanvas browser would drop all the revisions that came after it. It will still do it if there are exactly 77 equal signs, but not otherwise. Release 7.0 June 2, 2001 - Improved main file-browsing window. It now has icons to indicate the status of the files. The right mouse button activates context-sensitive popup menus. As a consequence of using a canvas widget instead of a listbox, the selection mechanism is different. It's click to select, shift-click to add selection, click-on-background to deselect all. The right button does an area select. - The module browser reads whatever information is available in the modules file via "cvs checkout -c" before it looks for the tkcvs-specific extensions. Thus if there is a modules file at all, some information will be available without the additional comments. - Options are specified via the options database instead of with cvscfg variables. If the window manager is CDE, its options are used by default. - The state of the main windows is remembered between sessions. - Bugs in display of the Editors column are fixed. - Finally found a good home for the "Checkout with Options" dialog. Someone pointed out that it belongs in the File menu next to the simple Update item. I'm convinced that that's right. - The module browser window is paned so that you can adjust the relative width of the columns. - CVS version 1.11 is supported better. Release 6.4 October 12, 2000 - An optional column to show who's editing, and buttons to edit and unedit. - The file list can be sorted up or down by each column. - Filenames containing spaces are now permissible. - Some configuration options can be saved. - Most output windows are searchable. - New reports - cvs annotate - cvs log showing only the latest commit - Option to show only a few tags for each revision in the branch browser. - Some bug fixes. - TkCVS 6.4 requires Tcl/Tk8.1 or better! Sorry, 8.0 has problems on too many platforms. Besides, a regular expression parser that doesn't understand [\s\t]+ just isn't good enough. :-( Release 6.3 The bugfixes have finally caught up with the new features, and we are declaring a stable release. This is the first release that runs "native" on Windows - that is, without either the Cygnus emulation layer or a whole lot of fiddling. Release 6.3.b2 (bugfix) - Fix the tag dialog so that it can be invoked more than once. - Insert "tkwait visibility" before "wm withdraw" for the larger dialogs to avert race conditions which occurred on slower framebuffers. Release 6.3.b1 - New graphical Module Browser. I reworked a BWidget implementation that was contributed by Marcel Koelewijn. Then I moved some of the buttons and menus from the workdir to the module browser if they seemed more related to modules than to the working directory. Then I updated the help text to reflect the changes. - The GUI and listbox fonts are now configurable. - TkCVS now looks for a file called "site_def" in the installation directory. That's a good place to define your tagcolours and other site-specific things. It won't be overwritten by installs like tkcvs_def.tcl is. - Some contributions by Andrew Johnson: Added the ability to select an editor based on a pattern match on the filename (eg to launch gimp on .gif files, etc). User configurable with the cvscfg(editors) variable and it defaults to the cvscfg(editor) setting for backwards compatibility. Fixed scrolling in the workdir listboxes so they remain synchronized if you drag one up or down with the middle mouse button. - User-configurable debug output, contributed by Marcel Koelewijn. Tinkerers will like this. - Featurecide: Buttons are always graphical and tooltips are always on. You don't have a choice anymore. Log level "last" was removed because it didn't seem to work. You can't count on the most recent checkin coming first or last, especially on a branch. Support for old versions of CVS was removed. - Replaced the calls to awk with internal tcl parsing. It slows down the workdir listing some, but it solves portability problems and it's cleaner. Also replaced execs of rm, mv, and cp with tcl file commands. - Put scrollbars on the text windows in the Log Browser so you can see how long the log messages are. - Put a fill color in the revision rectangles in the log browser so that you can select them by clicking anywhere instead of just on the border. - Added a conflict-merge proc, translated from a shell script by Bryan Ogawa. - Between Marcel and me, the Vendor Merge functionality is rehabilitated, working with remote repositories, and using the new module browser. At least I think it's working. - And of course I've been messing with the icons. Release 6.2.b3 - Added a button to the filebrowser to list the tags of a file. - Fixed a bug in logbrowsing from the filebrowser. - Scroll to the top of log canvas, so the most recent activity shows first. - Changed the bitmap for iconified workdir and module windows. Release 6.2.b2 - Fixed the invocation of the logbrowser from the module filelist. Release 6.2.b1 - Made tkcvs more remote-client friendly by replacing all calls which change directory to the repository with CVS commands. You can now get a file list from the Module Browser remotely, although it can be slow. - Colorized the icons. - Fixed the module browser so it will display directory trees that are more than two levels deep. - Made the reports behave the way the help file says they do. The 'by name' lists weren't confined to the selected module like the helpfile said, but now they are. - Prevent "runaway tkdiff." If no file is selected, tell the user to select one. - Display "sticky tags" in the logcanvas browser. Fixed another branching bug. The x offsets of the tag labels aren't perfect - we'll have to do a real place-and-route algorithm some day. - Added a checkbox for the -F argument to cvs tag, because everyone argues about whether it should force or not. Let's let them decide. - Folded in some more changes to help it run on Windows. - Fixed the "Go" menu so it remembers where we've been. Don't know when that got broken. Release 6.1.a8: - Quotes in comment strings stopped passing thru when exec_command was implemented. Added a regsub to fix it. Dollar signs are still OK. - Changed "rsh -l user host" to "rsh host -l user" because some implementations of rsh don't understand the former. - Fixed some erratic behavior when a remote module file isn't read successfully. - read the .cvsignore file in the working directory and add it to the ignore patterns which were optionally set in tkcvs_def. - Bugfix: filter "no file xyz" out of getFiles so it doesn't try to commit them. Release 6.1.a7: - Jo Wahle has redone building the workdir columns so it's much faster, and fixed a bug in which merge conflicts would cause it to get confused. - I parsed the branch info from the cvs log to take the guesswork out of the branching tree. - Stephen Kick improved logcanvas sizing so tags on the top revision don't disappear off the top of the canvas. Release 6.1.a6: - Fix some bugs. Put in a dialog to tell you if your rsh to a cvs server failed. Fixed the listing of arguments in the "are you sure?" dialog. Improved the logcanvas' ability to find a parent node in a strange revision sequence. Release 6.1.a5: - Put in some configuration options to run on DOS/Windows. Contributed by Christoph Jaeschke. NOTE: I don't have a Wintel machine to test on (strange but true.) The configuration options are only tested on unix. - Changed the report commands to run under exec_command. I missed some last time. Release 6.1.a4: - Run lengthy cvs operations in the background to avoid locking up the GUI. Contributed by Christoph Jaeschke. - Upgrade tkdiff from v2.03 to 3.0-beta-6, which looks like it's going to be final. Tkdiff has its own editor preference now, so we don't have to kludge one in at install time. - The cvscheck script is integrated into the tcl code. Contributed by Christoph Jaeschke. Release 6.1.a3: - Bugfixes. Module browser looks at the repository that the current CVS directory is in. TkDiff2.03 is patched so that it will work with two un-checked-out files. Release 6.1.a2: - Upgrade from tkdiff v1.0 to v2.03 - Fix the bug in the module browser so that the current module always matches the X selection. - Added command-line options -dir directory -root cvsroot -win (workdir|module) - Logfile browser doesn't try to evaluate dollar signs in comments anymore, so you can use them with impunity. ------------------------------------------------------------------------------- Release 6.1.a1 was put together by Dorothy Robinson. It contains bugfixes and enhancements provided by users. Please don't blame Del for my mistakes. In general, I rolled in patches but didn't do anything with suggestions unless they were very simple. The most noticeable changes are: - Showing the current tag of each file and the directory in the main window, so users know right away whether they are on a branch or not. Providing more information and options in the update dialog box. This group of changes was contributed by Jo Wahle. It reflects use in a production environment where users are sometimes inexperienced. It's informative without getting in the way, I think. - The install script has been updated for Wish8.0, and it should now work on FreeBSD. - ~/.tkcvs is sourced on startup. - The help file no longer talks about marking files, which was obsolete. - The log browser has been reworked to show the tagnames and to draw branches more intelligently, making better use of horizontal space. Blame me for this one. - Import checks out the modules file before trying to change it. This was from M.E. Smith. tkcvs-8.2.3.orig/tkcvs/0000755000175000017500000000000011664612512013117 5ustar timtimtkcvs-8.2.3.orig/tkcvs/branch_diagram.tcl0000644000175000017500000021367011664612512016555 0ustar timtim# # Tcl Library for TkCVS # # This is a major rewrite over the original version. It uses a # top down, recursive, branch-at-a-time, latest-revision-first # algorithm to layout the graph sensibly. # -- Mike Jagdis # namespace eval ::logcanvas { variable instance 0 proc new {filename how scope} { # # Creates a new log canvas. # variable instance set my_idx $instance incr instance global current_tagname global module_dir variable sys variable loc if {[catch "image type Modules"]} { workdir_images } if {[catch "image type Workdir"]} { modbrowse_images } namespace eval $my_idx { set my_idx [uplevel {concat $my_idx}] set how [uplevel {concat $how}] set filename [uplevel {concat $filename}] set scope [uplevel {concat $scope}] #variable cmd_log # Global constants scaled by current scaling factor for this instance variable curr global cvscfg global cvsglb global tcl_platform # User options for info display for this instance variable opt variable revwho variable revdate variable revtime variable revstate variable revbranches variable branchrevs variable revcomment variable revtags variable revbtags variable revpath variable sel_tag set sel_tag(A) {} set sel_tag(B) {} variable sel_rev variable revnum_current set sel_rev(A) {} set sel_rev(B) {} variable search_lastpattern "" variable search_elements [list] variable search_index 0 variable search_lastfill "" variable search_lastcase 0 variable search_nocase variable logcanvas ".logcanvas$my_idx" gen_log:log T "ENTER [namespace current]" set sys_loc [split $how {,}] set sys [lindex $sys_loc 0] set loc [lindex $sys_loc 1] proc ClearSelection {AorB} { variable logcanvas variable sel_tag variable sel_rev #catch {$logcanvas.canvas itemconfigure Sel$AorB -outline black} catch {$logcanvas.canvas itemconfigure Sel$AorB -fill gray90} $logcanvas.canvas dtag Sel$AorB $logcanvas.up.rev${AorB}_rvers configure -text {} $logcanvas.up.log${AorB}_rlogfm.rcomment configure -state normal $logcanvas.up.log${AorB}_rlogfm.rcomment delete 1.0 end $logcanvas.up.log${AorB}_rlogfm.rcomment configure -state disabled $logcanvas.up.rev${AorB}_rwho configure -text {} $logcanvas.up.rev${AorB}_rdate configure -text {} set sel_tag($AorB) {} set sel_rev($AorB) {} return } proc SetSelection {AorB tag rev} { global cvscfg variable logcanvas variable revdate variable revtime variable revwho variable revcomment variable sel_tag variable sel_rev ClearSelection $AorB set other [expr {$AorB == "A" ? {B} : {A}}] if {$rev == $sel_rev($other)} { ClearSelection $other } if {! [info exists revcomment($rev)]} { set revcomment($rev) "*** empty log message ***" } if {$tag != {}} { $logcanvas.up.rev${AorB}_rvers configure -text $tag } else { $logcanvas.up.rev${AorB}_rvers configure -text $rev } if {$rev != {} && [info exists revwho($rev)]} { $logcanvas.up.rev${AorB}_rwho configure -text $revwho($rev) $logcanvas.up.rev${AorB}_rdate configure -text\ "$revdate($rev) $revtime($rev)" $logcanvas.up.log${AorB}_rlogfm.rcomment configure -state normal $logcanvas.up.log${AorB}_rlogfm.rcomment insert end $revcomment($rev) $logcanvas.up.log${AorB}_rlogfm.rcomment configure -state disabled } $logcanvas.canvas addtag Sel$AorB withtag rect$rev $logcanvas.canvas itemconfigure SelA -fill $cvscfg(colourA) $logcanvas.canvas itemconfigure SelB -fill $cvscfg(colourB) set sel_tag($AorB) $tag set sel_rev($AorB) $rev return } proc RevSelect {AorB} { variable logcanvas set t [$logcanvas.canvas gettags current] SetSelection $AorB \ [string range [lindex $t [lsearch -glob $t {T*}]] 1 end] \ [string range [lindex $t [lsearch -glob $t {R*}]] 1 end] return } proc Unselect {AorB} { variable logcanvas set t [$logcanvas.canvas gettags current] if {$t != {} } {return} ClearSelection $AorB } proc ConfigureButtons {fname} { global cvsglb variable logcanvas variable sys variable loc variable revnum_current switch -- $sys { "SVN" { # Find out current rev and if it's a directory, if we can set kind "" set info_cmd [exec::new "svn info \"[file tail $fname]\""] set info_lines [split [$info_cmd\::output] "\n"] foreach infoline $info_lines { if {[string match "Node Kind:*" $infoline]} { gen_log:log D "$infoline" set kind [lindex $infoline end] } elseif {[string match "Last Changed Rev:*" $infoline]} { gen_log:log D "$infoline" set revnum_current [lindex $infoline end] } } if {! [info exists revnum_current]} { gen_log:log E "Warning: couldn't find current revision number!" } $logcanvas.up.bmodbrowse configure -command {modbrowse_run svn} \ -image Modules_svn $logcanvas.up.lfname configure -text "SVN Path" $logcanvas.up.rfname configure -state normal $logcanvas.up.rfname delete 0 end $logcanvas.up.rfname insert end "$fname" $logcanvas.up.rfname configure -state readonly $logcanvas.log configure \ -command [namespace code { set rev [$logcanvas.up.revA_rvers cget -text] if {$rev == ""} { svn_log_rev $filename } else { svn_log_rev $revpath($rev) } }] if {$kind == "directory"} { $logcanvas.diff configure -state disabled $logcanvas.annotate configure -state disabled $logcanvas.view configure \ -command [namespace code { set rev [$logcanvas.up.revA_rvers cget -text] if {$rev ==""} { set rev "r$revnum_current" } svn_fileview $rev $revpath($rev) directory }] } else { $logcanvas.view configure \ -command [namespace code { set rev [$logcanvas.up.revA_rvers cget -text] if {$rev ==""} { set rev "r$revnum_current" } svn_fileview $rev $revpath($rev) file }] $logcanvas.diff configure \ -command [namespace code { set revA [$logcanvas.up.revA_rvers cget -text] set revB [$logcanvas.up.revB_rvers cget -text] set A [string trimleft $revA {r}] set B [string trimleft $revB {r}] # Let's be generous and let either A or B be selected if {$revB == ""} { comparediff_r "$revpath($revA)@$A" "" $logcanvas $filename } elseif {$revA == ""} { comparediff_r "" "$revpath($revB)@$B" $logcanvas $filename } else { comparediff_files $logcanvas "$revpath($revA)@$A" "$revpath($revB)@$B" } }] $logcanvas.annotate configure \ -command [namespace code { set rev [$logcanvas.up.revA_rvers cget -text] if {$rev == ""} { svn_annotate_r "" $filename } else { svn_annotate_r [string trimleft $rev {r}] $revpath($rev) } }] } $logcanvas.delta configure \ -command [namespace code { set currentrevpath "$revpath(r$revnum_current)@$revnum_current" set fromrev [$logcanvas.up.revA_rvers cget -text] if {$fromrev == ""} {cvsfail "Please select a revision!" $logcanvas; return} set fromrevpath "$revpath($fromrev)@[string trimleft $fromrev {r}]" set sincerev [$logcanvas.up.revB_rvers cget -text] set fromtag "" if {[info exists revbtags($sincerev)]} { set fromtag [lindex $revbtags($sincerev) 0] } if {$fromtag == ""} { foreach brev [array names revbtags] { set b $revbtags($brev) foreach r $branchrevs($b) { if {$r == $fromrev} { set fromtag $b } } } } if {$sincerev == ""} { svn_merge $logcanvas $fromrevpath "" $currentrevpath $fromtag $filename } else { set sincerevpath "$revpath($sincerev)@[string trimleft $sincerev {r}]" svn_merge $logcanvas $fromrevpath $sincerev $sincerevpath $fromtag $filename } }] } "CVS" { $logcanvas.up.bmodbrowse configure -command {modbrowse_run cvs} \ -image Modules_cvs $logcanvas.up.lfname configure -text "RCS file" $logcanvas.up.rfname configure -state normal $logcanvas.up.rfname delete 0 end $logcanvas.up.rfname insert end "$fname,v" $logcanvas.up.rfname configure -state readonly if {$loc == "rep"} { # Working on repository files, not checked out $logcanvas.view configure \ -command [namespace code { cvs_fileview_checkout [$logcanvas.up.revA_rvers cget -text] $filename }] $logcanvas.log configure \ -command [namespace code { cvs_filelog $filename $logcanvas 0 }] $logcanvas.annotate configure \ -command [namespace code { cvs_annotate_r [$logcanvas.up.revA_rvers cget\ -text] $filename }] $logcanvas.diff configure \ -command [namespace code { comparediff_sandbox [$logcanvas.up.revA_rvers cget -text] \ [$logcanvas.up.revB_rvers cget -text] $logcanvas \ $filename }] $logcanvas.delta configure -state disabled } else { # We have a checked-out local file $logcanvas.log configure \ -command [namespace code { set rev [$logcanvas.up.revA_rvers cget -text] if {$rev == ""} { cvs_log_rev "" $filename } else { regsub {\.\d+$} $rev {} baserev cvs_log_rev $baserev $filename } }] $logcanvas.view configure \ -command [namespace code { cvs_fileview_update [$logcanvas.up.revA_rvers cget -text] \ $filename }] $logcanvas.annotate configure \ -command [namespace code { cvs_annotate [$logcanvas.up.revA_rvers cget -text] \ $filename }] $logcanvas.delta configure \ -command [namespace code { set fromrev [$logcanvas.up.revA_rvers cget -text] set sincerev [$logcanvas.up.revB_rvers cget -text] set fromtag "" set fromrev_root [join [lrange [split $fromrev {.}] 0 end-1] {.}] if {[info exists revbtags($fromrev_root)]} { set fromtag [lindex $revbtags($fromrev_root) 0] } else { # Just a rev number will do set fromtag $fromrev_root } cvs_merge $logcanvas $fromrev $sincerev $fromtag [list $filename] }] } } "RCS" { $logcanvas.up.lfname configure -text "RCS file" $logcanvas.up.rfname configure -state normal $logcanvas.up.rfname delete 0 end $logcanvas.up.rfname insert end "$fname" $logcanvas.up.rfname configure -state readonly $logcanvas.view configure -state disabled $logcanvas.annotate configure -state disabled $logcanvas.log configure -command [namespace code {rcs_log $filename}] $logcanvas.delta configure -state disabled } } } proc PopupTags { x y } { # # Pop up a transient window with a listbox of the tags for a specific\ # revision # global cvscfg variable logcanvas variable revtags foreach tag [$logcanvas.canvas gettags current] { if {[string index $tag 0] == {R}} { set rev [string range $tag 1 end] break } } set mname "$logcanvas.[join [split $rev {.}] {_}]" if {[winfo exists $mname]} { # Don't let them hit the button twice wm deiconify $mname raise $mname } else { toplevel $mname wm title $mname "Tags: $rev" wm transient $mname $logcanvas.canvas set ntags [llength $revtags($rev)] set h [expr {400 / [font metrics $cvscfg(listboxfont)\ -displayof $mname -linespace]}] if {$h > $ntags} { set h $ntags } listbox $mname.lbx -font $cvscfg(listboxfont) \ -width 0 -height $h \ -listvar [namespace current]::revtags($rev) # Always have a scroll bar because a reload of the log might find # more tags and the list might not fit in the window any longer. scrollbar $mname.scroll -command "$mname.lbx yview" $mname.lbx configure -yscroll "$mname.scroll set" pack $mname.scroll -side right -fill y pack $mname.lbx -ipadx 10 -ipady 10 -expand y -fill both bind $mname.lbx [namespace code " variable revtags set i \[$mname.lbx nearest %y\] SetSelection A \[lindex \$revtags($rev) \$i\] $rev $mname.lbx selection clear 0 end $mname.lbx selection set \$i"] bind $mname.lbx [namespace code " variable revtags set i \[$mname.lbx nearest %y\] SetSelection A \[lindex \$revtags($rev) \$i\] $rev $mname.lbx selection clear 0 end $mname.lbx selection set \$i"] bind $mname.lbx [namespace code " variable revtags set i \[$mname.lbx nearest %y\] SetSelection B \[lindex \$revtags($rev) \$i\] $rev $mname.lbx selection clear 0 end $mname.lbx selection set \$i"] # FIXME: add capability to delete a tag here? # We need it to get laid out before we query its geometry. update } # Centre the pop up on the cursor position then adjust so it doesn't # run off the edge of the screen (if possible!). set w [winfo width $mname] set h [winfo height $mname] set x [expr {$x - $w/2}] set y [expr {$y - $h/2}] set sx [expr {[winfo vrootx $mname] + [winfo vrootwidth $mname]}] if {[expr {$x + $w}] >= $sx} { set x [expr {$sx - $w}] } if {$x < 0} { set x 0 } set sy [expr {[winfo vrooty $mname] + [winfo vrootheight $mname]}] if {[expr {$y + $h}] >= $sy} { set y [expr {$sy - $h}] } if {$y < 0} { set y 0 } wm geometry $mname +$x+$y return } proc CalcCurrent { revision } { variable curr variable font_bold variable font_bold_h variable logcanvas #gen_log:log T "ENTER ($revision)" set box_width \ [expr {[image width Man] \ + $curr(padx) \ + [font measure $font_bold \ -displayof $logcanvas.canvas {You are}] \ + $curr(padx,2)}] set box_height [image height Man] set h [expr {2 * $font_bold_h}] if {$h > $box_height} { set box_height $h } incr box_height $curr(pady,2) #gen_log:log T "LEAVE" return [list $box_width $box_height] } proc DrawCurrent { x y box_width box_height revision } { variable curr variable revstate variable font_bold variable logcanvas variable curr_x variable curr_y #gen_log:log T "ENTER ($x $y $box_width $box_height $revision)" set curr_x $x set curr_y $y # draw the box set tx [expr {$x + $box_width}] set ty [expr {$y - $box_height}] $logcanvas.canvas create rectangle \ $x $y $tx $ty \ -width $curr(width) -fill gray90 -outline red3 if {[info exists revstate(current)]} { if {$revstate(current) == {dead}} { $logcanvas.canvas create line \ $x $y $tx $ty -fill red -width $curr(width) $logcanvas.canvas create line \ $tx $y $x $ty -fill red -width $curr(width) } } set pad \ [expr {($box_width - [image width Man] - \ [font measure $font_bold -displayof $logcanvas.canvas {You are}]) \ / 3}] set ty [expr {$y - [expr {$box_height/2}]}] # add the contents $logcanvas.canvas create image \ [expr {$x + $pad}] $ty \ -image Man -anchor w $logcanvas.canvas create text \ [expr {$x + $box_width - $pad}] $ty \ -text "You are\nhere" -anchor e \ -fill red3 \ -font $font_bold #gen_log:log T "LEAVE" return } proc CalcRoot { root_rev } { global cvscfg variable opt variable curr variable box_height variable font_bold variable font_norm variable font_norm_h variable logcanvas variable root_info variable revtags variable revbtags variable tlist #gen_log:log T "ENTER ($root_rev)" gen_log:log D "CalcRoot ($root_rev)" set height $box_height set tag_width 0 set box_width 0 set tlist($root_rev) {} if {[info exists revtags($root_rev)]} { # We want to show all the coloured tags plus others to take # the total to at least cvscfg(tagdepth) set tag_colour {} set tag_black {} foreach tag $revtags($root_rev) { if {[info exists cvscfg(tagcolour,$tag)]} { lappend tag_colour $tag } else { lappend tag_black $tag } } set tlist($root_rev) [concat $tag_colour $tag_black] if {$opt(show_tags)} { if {[info exists cvscfg(tagdepth)] && $cvscfg(tagdepth) != 0} { set n [expr {$cvscfg(tagdepth) - [llength $tag_colour]}] if {$n < [llength $tag_black]} { set tag_black [concat [lrange $tag_black 0 [expr {$n-1}]] {more...}] } } foreach tag $tlist($root_rev) { if {$tag == {more...}} { set my_font $font_bold } else { set my_font $font_norm } set w [font measure $my_font -displayof $logcanvas.canvas $tag] if {$w > $tag_width} { set tag_width $w } } incr tag_width $curr(tspcb,2) set h [expr {[llength $tlist($root_rev)] * $font_norm_h}] if {$h > $height} { set height $h } } } if {![info exists revbtags($root_rev)]} {set revbtags($root_rev) {}} foreach s [subst $root_info] { set w [font measure $font_norm -displayof $logcanvas.canvas $s] if {$w > $box_width} { set box_width $w } } incr box_width $curr(padx,2) set text_height [expr {$curr(pady,2) + \ [llength [subst $root_info]] * $font_norm_h}] return [list $tag_width $box_width $text_height] } proc DrawRoot { x y box_width box_height cur_rev root_rev } { global cvscfg variable curr variable opt variable font_norm variable font_norm_h variable font_bold variable logcanvas variable root_info variable revbtags variable revbranches variable tlist #gen_log:log T "ENTER ($x $y $box_width $box_height $cur_rev $root_rev )" gen_log:log D "Drawing Root for \"$root_rev\" \"$cur_rev\"" # draw the box $logcanvas.canvas create rectangle \ $x $y \ [expr {$x + $box_width}] [expr {$y - $box_height}] \ -width $curr(width) \ -fill gray90 -outline blue set tx [expr {$x + $box_width/2}] set ty [expr {$y - $curr(pady)}] gen_log:log D "[subst $root_info]" foreach s [subst $root_info] { $logcanvas.canvas create text \ $tx $ty \ -text $s \ -anchor s \ -font $font_norm -fill navy \ -tags [list R$root_rev box active] incr ty -$font_norm_h } #gen_log:log T "LEAVE" return } proc CalcRevision { revision } { global cvscfg variable opt variable curr variable box_height variable rev_info variable revdate variable revtime variable revwho variable font_norm variable font_norm_h variable font_bold variable logcanvas variable revtags variable tlist #gen_log:log T "ENTER ($revision)" set height $box_height set tag_width 0 set box_width 0 set tlist($revision) {} if {[info exists revtags($revision)]} { # We want to show all the coloured tags plus others to take # the total to at least cvscfg(tagdepth) set tag_colour {} set tag_black {} foreach tag $revtags($revision) { if {[info exists cvscfg(tagcolour,$tag)]} { lappend tag_colour $tag } else { lappend tag_black $tag } } if {[info exists cvscfg(tagdepth)] && $cvscfg(tagdepth) != 0} { set n [expr {$cvscfg(tagdepth) - [llength $tag_colour]}] if {$n < [llength $tag_black]} { set tag_black [concat [lrange $tag_black 0 [expr {$n-1}]] {more...}] } } set tlist($revision) [concat $tag_colour $tag_black] if {$opt(show_tags)} { foreach tag $tlist($revision) { if {$tag == {more...}} { set my_font $font_bold } else { set my_font $font_norm } set w [font measure $my_font -displayof $logcanvas.canvas $tag] if {$w > $tag_width} { set tag_width $w } } incr tag_width $curr(tspcb,2) set h [expr {[llength $tlist($revision)] * $font_norm_h}] if {$h > $height} { set height $h } } } if {![info exists revtime($revision)]} {set revtime($revision) {}} if {![info exists revdate($revision)]} {set revdate($revision) {}} if {![info exists revinfo($revision)]} {set revinfo($revision) {}} if {![info exists revwho($revision)]} {set revwho($revision) {}} foreach s [subst $rev_info] { set w [font measure $font_norm -displayof $logcanvas.canvas $s] if {$w > $box_width} { set box_width $w } } incr box_width $curr(padx,2) #gen_log:log T "LEAVE" return [list $tag_width $box_width $height] } proc DrawRevision { x y box_width height revision} { global cvscfg variable opt variable curr variable box_height variable rev_info variable revdate variable revtime variable revwho variable revstate variable revkind variable revtags variable revbtags variable font_norm variable font_norm_h variable font_bold variable logcanvas variable tlist variable match variable fromtags variable totags variable xyw variable boxwidth variable fromprefix variable toprefix variable mrev upvar branch branch #gen_log:log T "ENTER ($x $y $box_width $height $revision)" set xyw($revision) [list $x [expr {$y - ($box_height / 4)}] $box_width ] # Draw the list of tags set tx [expr {$x - $curr(tspcb)}] set ty $y set revbtag $revbtags($branch) foreach tag $tlist($revision) { gen_log:log D "$revision: tag $tag" if {[string match "${fromprefix}_*" $tag]} { set mrev($tag) $revision lappend fromtags $tag regsub {.*_(.*$)} $tag {\1} tagend gen_log:log D " $tag is a FROM TAG" gen_log:log D " will need a TO TAG ${toprefix}_${revbtag}_$tagend" set match($tag) ${toprefix}_${revbtag}_$tagend } if {[string match "${toprefix}_*" $tag]} { set mrev($tag) $revision lappend totags $tag } if {$opt(show_tags)} { set my_font $font_norm set tagcolour black set taglist {} if {$tag == {more...}} { set my_font $font_bold set taglist [list R$revision tag active] } elseif {[info exists cvscfg(tagcolour,$tag)]} { set tagcolour $cvscfg(tagcolour,$tag) } $logcanvas.canvas create text \ $tx $ty \ -text $tag \ -anchor se -fill $tagcolour \ -font $my_font \ -tags $taglist incr ty -$font_norm_h } } # draw the box... set tx [expr {$x + $box_width}] set ty [expr {$y - $box_height}] $logcanvas.canvas create rectangle \ $x $y $tx $ty \ -width $curr(width) -fill gray90 \ -tags [list box R$revision rect$revision active] # ...and add the contents if {[info exists revstate($revision)]} { if {$revstate($revision) == {dead}} { $logcanvas.canvas create line \ $x $y $tx $ty -fill red -width $curr(width) $logcanvas.canvas create line \ $tx $y $x $ty -fill red -width $curr(width) } } set tx [expr {$x + $box_width/2}] set ty [expr {$y - $curr(pady)}] foreach s [subst $rev_info] { $logcanvas.canvas create text \ $tx $ty \ -text $s \ -anchor s \ -font $font_norm \ -tags [list R$revision box active] incr ty -$font_norm_h } #gen_log:log T "LEAVE" return } proc DrawBranch { x y root_rev branch } { variable logcanvas variable opt variable curr variable box_height variable revkind variable branchrevs variable revbranches #gen_log:log T "ENTER ($x $y $root_rev $branch)" gen_log:log D "Drawing branch \"$branch\" rooted at \"$root_rev\" at ($x $y)" # What revisions to show on this branch? if {![info exists branchrevs($branch)]} {set branchrevs($branch) {}} if {$branchrevs($branch) == {}} { set revlist {} } else { # Always have the head revision set revlist [lindex $branchrevs($branch) 0] foreach r [lrange $branchrevs($branch) 1 end-1] { if {![info exists revbranches($r)]} {set revbranches($r) {}} if {$opt(show_inter_revs) || $opt(show_empty_branches) \ && $revbranches($r) != {}} { lappend revlist $r } else { # Only if there are non-empty branches off this revision foreach b $revbranches($r) { if {![info exists branchrevs($b)]} {set branchrevs($b) {}} if {$branchrevs($b) != {}} { lappend revlist $r break } } } } if {[llength $branchrevs($branch)] > 1} { # Always have the first revision on a branch lappend revlist [lindex $branchrevs($branch) end] } } # Work out width and height of this limb, saving sizes of revisions set tag_width 0 set rdata {} if {$branch == {current}} { set rtw 0 foreach {box_width root_height} [CalcCurrent $branch] { break } } else { foreach {rtw box_width root_height} [CalcRoot $branch] { break } } if {$rtw > $tag_width} { set tag_width $rtw } set height [expr {$root_height + $curr(spcy)}] foreach revision $revlist { if {$revision == {current}} { set rtw 0 foreach {rbw rh} [CalcCurrent $revision] { break } } else { foreach {rtw rbw rh} [CalcRevision $revision] { break } } lappend rdata $rtw $rh if {$rtw > $tag_width} { set tag_width $rtw } if {$rbw > $box_width} { set box_width $rbw } incr height $curr(spcy) incr height $rh } # Position branch. # Look for overlap horizontally while {1} { $logcanvas.canvas addtag ol_x overlapping \ [expr {$x - $curr(spcx)}] [expr {$y - $height + $curr(yfudge)}] \ [expr {$x + $tag_width + $box_width}] $y set bbox [$logcanvas.canvas bbox ol_x] $logcanvas.canvas dtag ol_x if {$bbox == {}} { break } gen_log:log D "horizontal overlap with $bbox" # Move branch to rightmost point of overlapped objects plus some space # N.B. +1 because exactly equal counts as an overlap set x [expr {[lindex $bbox 2] + $curr(spcx) + 1}] } # Look for overlap vertically $logcanvas.canvas addtag ol_y overlapping \ $x [expr {$y - $height}] \ [expr {$x + $tag_width + $box_width}] [expr {$y - $height +\ $curr(yfudge)}] set bbox [$logcanvas.canvas bbox ol_y] $logcanvas.canvas dtag ol_y if {$bbox != {}} { # Move down to make space gen_log:log D "vertical overlap with $bbox" incr y [expr {[lindex $bbox 3] - ($y - $height)}] } # Position to top of branch incr x $tag_width incr y -$height # Draw the branch set midx [expr {$x + $box_width/2}] set last_y {} foreach revision $revlist {rtag_width rheight} $rdata { incr y $curr(spcy) incr y $rheight # For each branch off this revision, draw it to the right of this # revision box and a little above the centre line of this box. set x2 [expr {$x +$box_width + $curr(spcx)}] set y2 [expr {$y - $box_height/2 - $curr(boff)}] set brevs {} set bxys {} if {[info exists revbranches($revision)]} { foreach r2 $revbranches($revision) { # Do we display the branch if it is empty? # If it's the you-are-here, we do anyway if {![info exists branchrevs($r2)] } { set branchrevs($r2) {} } if {$branchrevs($r2) == {} && $r2 != {current} && !\ $opt(show_empty_branches)} { continue } lappend brevs $r2 foreach {lx y2 lbw rh lly} [DrawBranch $x2 $y2 $revision $r2] { lappend bxys $lx $lbw $rh $lly break } set x2 [expr {$lx + $lbw + $curr(spcx)}] } } # y2 may have changed to accomodate a long branch. If so we need # to figure out what our y should be set y [expr {$y2 + $box_height/2 + $curr(boff)}] set rx [expr {$x + $box_width}] set ry [expr {$y - $box_height/2}] set by [expr {$ry - $curr(boff)}] # If it has brevs, it's the root of a branch foreach b $brevs {bx bw rh ly} $bxys { set mx [expr {$bx + $bw/2}] if {$ly != {}} { $logcanvas.canvas create line \ $mx $ly $mx [expr {$by - $rh}] \ -arrow first -arrowshape $curr(arrowshape) -width $curr(width) } if {$b == {current}} { DrawCurrent $bx $by $bw $rh $revision } else { set last_rev [lindex $branchrevs($b) 0] if {$last_rev == {current}} { set last_rev [lindex $branchrevs($b) 1] } DrawRoot $bx $by $bw $rh $revision $b } $logcanvas.canvas lower [ \ $logcanvas.canvas create line \ $rx $ry $mx $ry $mx $by \ -arrow last -arrowshape $curr(arrowshape) -width $curr(width) \ -fill blue ] if {$opt(update_drawing) < 1} { UpdateBndBox } } if {$last_y != {}} { $logcanvas.canvas create line \ $midx $last_y $midx [expr {$y - $box_height}] \ -arrow first -arrowshape $curr(arrowshape) -width $curr(width) } if {$revision == {current}} { DrawCurrent $x $y $box_width $rheight $revision } else { DrawRevision $x $y $box_width $rheight $revision } if {$opt(update_drawing) < 1} { UpdateBndBox } set last_y $y set last_rev $revision } if {$opt(update_drawing) < 2} { UpdateBndBox } return [list $x [expr {$y + $root_height + $curr(spcy)}] \ $box_width $root_height $last_y] } proc UpdateBndBox {} { variable logcanvas variable font_bold variable view_xoff variable view_yoff variable curr_x variable curr_y #gen_log:log T "ENTER" foreach {x1 y1 x2 y2} [$logcanvas.canvas bbox all] { break } $logcanvas.canvas configure \ -scrollregion [list \ [expr {$x1 - 5}] [expr {$y1 - 5}] \ [expr {$x2 + 5}] [expr {$y2 + 5}] ] if {[info exists curr_x]} { set canv_width [$logcanvas.canvas cget -width] set canv_height [$logcanvas.canvas cget -height] set bbox [$logcanvas.canvas bbox all] set llx [lindex $bbox 0] set lly [lindex $bbox 1] set urx [lindex $bbox 2] set ury [lindex $bbox 3] set bbox_width [expr {$urx - $llx}] set bbox_height [expr {$ury - $lly}] gen_log:log D "diagram size: $bbox_width x $bbox_height" gen_log:log D "canvas size: $canv_width x $canv_height" set canv_bot [expr {$ury - $canv_height}] set view_y [expr {$canv_bot - $ury}] gen_log:log D "bbox: $bbox" gen_log:log D "canvas view: $llx $canv_bot $canv_width $view_y" gen_log:log D "curr x & y: $curr_x, $curr_y" gen_log:log D "x: (curr_x $curr_x) >? (canv_width $canv_width)" if {$curr_x > $canv_width} { set dist_x [expr {$curr_x - $canv_width/2}] set dist_x [expr {$dist_x - 3 * [font measure $font_bold \ -displayof $logcanvas.canvas {You are}]}] gen_log:log D "positioning x: new x $dist_x" } else { gen_log:log D "not re-positioning x" set dist_x 0 } gen_log:log D "y: (curr_y $curr_y) $xfrom} { set xfrom [expr {$xfrom + $bwfrom}] set yfrom [expr {$yfrom - ($box_height / 2)}] set yto [expr {$yto - ($box_height / 2)}] set xmid [expr {$xfrom + (($xto - $xfrom) / 2)}] set ymid [expr {$yto - $box_height}] } elseif {$xfrom > $xto} { set xto [expr {$xto + $bwto}] set xmid [expr {$xto + (($xfrom - $xto) / 2)}] set ymid [expr {$yto + ($box_height / 2)}] } elseif {$xto == $xfrom} { set xmid [expr {$xto - ($bwfrom / 2)}] set ymid [expr {$yfrom - (($yfrom - $yto) / 2)}] } $logcanvas.canvas create line \ $xfrom $yfrom $xmid $ymid $xto $yto \ -arrow first -smooth 1 } } # Reselect the previously selected revisions variable sel_tag variable sel_rev foreach AorB {A B} { SetSelection $AorB $sel_tag($AorB) $sel_rev($AorB) } busy_done $logcanvas } gen_log:log T "LEAVE" return } proc SaveOptions {} { global logcfg variable opt variable loc # Save the options to the global set set logcfg(update_drawing) $opt(update_drawing) foreach {key value} [array get opt] { gen_log:log D "logcfg($key) $value" set logcfg($key) $value } save_options } # Search functionality for log viewer that searches for strings in the # log windows. It will create a new button and an entry box below the logs. You # can enter a glob-style search pattern in the entry field and click the search # button. With every click (or pressing enter), the log viewer jumps from one # occurrence of the pattern to the next, highlighting it in red. # # The following special characters are used in the search pattern: # # * Matches any sequence of characters in string, including a null string. # # ? Matches any single character in string. # # [chars] Matches any character in the set given by chars. If a sequence of the # form x-y appears in chars, then any character between x and y, inclusive, will # match. # # \x Matches the single character x. This provides a way of avoiding the # special interpretation of the characters *?[]\ in pattern. # # If you only enter "FOO" (without the ") in the entry box, it searches the exact # string "FOO". If you want to search all strings starting with "FOO", you have # to put "FOO*". For all strings containing "FOO", you must put "*FOO*". proc Search {} { variable logcanvas variable font_bold variable search_elements variable search_index variable search_lastfill variable search_lastpattern variable search_lastcase variable search_nocase gen_log:log T "ENTER" # Restore last fill color if {[string length $search_lastfill] != 0} { $logcanvas.canvas itemconfigure [lindex $search_elements $search_index] \ -fill $search_lastfill } # Read search pattern from entry box set pattern [string trim [$logcanvas.down.search.e get]] gen_log:log D "pattern: $pattern" # Check if search pattern or nocase flag have been changed since the # last call if {([string equal $pattern $search_lastpattern] == 0) \ ||($search_lastcase != $search_nocase)} { # Rebuild matching element list set search_lastpattern $pattern set search_lastcase $search_nocase set search_elements [list] # Ignore empty patterns if {[string length $pattern] != 0} { # Loop over all elements in canvas that have text elements foreach element [$logcanvas.canvas find withtag all] { if {[catch {$logcanvas.canvas itemcget $element -text} text] == 0} { # Check if text element matches search pattern gen_log:log D " $text" if {[string is true $search_nocase]} { if {[string match -nocase $pattern $text]} { # Add element to list of matching elements lappend search_elements $element } } else { if {[string match $pattern $text]} { # Add element to list of matching elements lappend search_elements $element gen_log:log D " $pattern MATCHED $text" } } } } } # Reset highlight index set search_index 0 # Pattern has not been changed since last call and there have been # matching elements found in the last call } elseif {[llength $search_elements] != 0} { # Select next matching element (restart if last one has been passed) incr search_index if {$search_index >= [llength $search_elements]} { set search_index 0 puts -nonewline "\a" flush stdout } } # Check if there are matching elements set length [llength $search_elements] if {$length > 0} { $logcanvas.down.search.l configure -text "[expr {$search_index + 1}] / $length" # Scroll to next matching element set element [lindex $search_elements $search_index] set scrollregion [$logcanvas.canvas cget -scrollregion] set coords [$logcanvas.canvas bbox $element] set sx1 [lindex $scrollregion 0] set sy1 [lindex $scrollregion 1] set sx2 [lindex $scrollregion 2] set sy2 [lindex $scrollregion 3] set ix1 [lindex $coords 0] set iy1 [lindex $coords 1] set ix2 [lindex $coords 2] set iy2 [lindex $coords 3] set xview [$logcanvas.canvas xview] set yview [$logcanvas.canvas yview] set vx1 [lindex $xview 0] set vx2 [lindex $xview 1] set vy1 [lindex $yview 0] set vy2 [lindex $yview 1] set x [expr {(double($ix1 - $sx1) / double($sx2 - $sx1)) -(($vx2 - $vx1) / 2)}] set y [expr {(double($iy1 - $sy1) / double($sy2 - $sy1)) -(($vy2 - $vy1) / 2)}] $logcanvas.canvas xview moveto $x $logcanvas.canvas yview moveto $y set search_lastfill [$logcanvas.canvas itemcget $element -fill] $logcanvas.canvas itemconfigure $element -fill red } else { $logcanvas.down.search.l configure -text "Not found" } } # Collect the user options from the global set set opt(update_drawing) $logcfg(update_drawing) set opt(scale) $logcfg(scale) foreach {key value} [array get logcfg show_*] { set opt($key) $value } toplevel $logcanvas wm title $logcanvas "$sys Log $filename" $logcanvas configure -menu $logcanvas.menubar menu $logcanvas.menubar $logcanvas.menubar add cascade -label "File"\ -menu $logcanvas.menubar.file -underline 0 menu $logcanvas.menubar.file -tearoff 0 $logcanvas.menubar.file add command -label "Shell window" -underline 0 \ -command {exec::new $cvscfg(shell)} $logcanvas.menubar.file add separator $logcanvas.menubar.file add command -label "Close" -underline 0 \ -command [namespace code {$logcanvas.close invoke}] $logcanvas.menubar.file add command -label "Exit" -underline 1 \ -command { exit_cleanup 1 } $logcanvas.menubar add cascade -label "View"\ -menu $logcanvas.menubar.view -underline 0 menu $logcanvas.menubar.view -tearoff 0 $logcanvas.menubar.view add cascade -label "Update When Drawing" \ -menu $logcanvas.menubar.view.update menu $logcanvas.menubar.view.update $logcanvas.menubar.view.update add radiobutton -label "Every Revision" \ -variable [namespace current]::opt(update_drawing) -value 0 $logcanvas.menubar.view.update add radiobutton -label "Every Branch" \ -variable [namespace current]::opt(update_drawing) -value 1 $logcanvas.menubar.view.update add radiobutton -label "When Finished" \ -variable [namespace current]::opt(update_drawing) -value 2 $logcanvas.menubar.view add separator $logcanvas.menubar.view add cascade -label "Tree Layout" \ -menu $logcanvas.menubar.view.tree menu $logcanvas.menubar.view.tree $logcanvas.menubar.view.tree add checkbutton -label \ "Show empty branches" \ -variable [namespace current]::opt(show_empty_branches) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view.tree add checkbutton -label \ "Show intermediate revisions" \ -variable [namespace current]::opt(show_inter_revs) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view.tree add checkbutton -label \ "Show merges" \ -variable [namespace current]::opt(show_merges) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view add cascade -label "Branch Layout" \ -menu $logcanvas.menubar.view.branch menu $logcanvas.menubar.view.branch $logcanvas.menubar.view.branch add command -label "Turn all options on" \ -command [namespace code { set opt(show_root_rev) [set opt(show_root_tags) 1] DrawTree }] $logcanvas.menubar.view.branch add command -label "Turn all options off" \ -command [namespace code { set opt(show_root_rev) [set opt(show_root_tags) 0] DrawTree }] $logcanvas.menubar.view.branch add separator $logcanvas.menubar.view.branch add checkbutton -label "Show revision" \ -variable [namespace current]::opt(show_root_rev) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view.branch add checkbutton -label "Show label" \ -variable [namespace current]::opt(show_root_tags) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view add cascade -label "Revision Layout" \ -menu $logcanvas.menubar.view.rev menu $logcanvas.menubar.view.rev $logcanvas.menubar.view.rev add command -label "Turn all options on" \ -command [namespace code { set opt(show_tags) [\ set opt(show_box_rev) [\ set opt(show_box_revwho) [\ set opt(show_box_revdate) [\ set opt(show_box_revtime) 1]]]] DrawTree }] $logcanvas.menubar.view.rev add command -label "Turn all options off" \ -command [namespace code { set opt(show_tags) [\ set opt(show_box_rev) [\ set opt(show_box_revwho) [\ set opt(show_box_revdate) [\ set opt(show_box_revtime) 0]]]] DrawTree }] $logcanvas.menubar.view.rev add separator $logcanvas.menubar.view.rev add checkbutton -label "Show tags" \ -variable [namespace current]::opt(show_tags) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view.rev add checkbutton -label "Show revision" \ -variable [namespace current]::opt(show_box_rev) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view.rev add checkbutton -label "Show author" \ -variable [namespace current]::opt(show_box_revwho) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view.rev add checkbutton -label "Show date" \ -variable [namespace current]::opt(show_box_revdate) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view.rev add checkbutton -label "Show time" \ -variable [namespace current]::opt(show_box_revtime) \ -onvalue 1 -offvalue 0 \ -command [namespace code { DrawTree }] $logcanvas.menubar.view add separator $logcanvas.menubar.view add cascade -label "Size" \ -menu $logcanvas.menubar.view.size menu $logcanvas.menubar.view.size foreach {label factor} $logcfg(scaling_options) { $logcanvas.menubar.view.size add radiobutton -label $label \ -variable [namespace current]::opt(scale) -value $factor \ -command [namespace code { DrawTree }] } $logcanvas.menubar.view add separator $logcanvas.menubar.view add command -label "Save options" \ -command [namespace code { SaveOptions }] menu_std_help $logcanvas.menubar if {$tcl_platform(platform) != "windows"} { wm iconbitmap $logcanvas @$cvscfg(bitmapdir)/branch.xbm } wm protocol $logcanvas WM_DELETE_WINDOW \ [namespace code {$logcanvas.close invoke}] frame $logcanvas.up -relief groove -border 2 set textfont $cvscfg(listboxfont) set disbg [lindex [$logcanvas.up configure -background] 4] label $logcanvas.up.lfname -width 12 -anchor w entry $logcanvas.up.rfname -font $textfont -relief groove \ -bd 1 -relief sunk -state readonly button $logcanvas.up.bmodbrowse -image Modules \ -command modbrowse_run button $logcanvas.up.bworkdir -image Workdir \ -command { workdir_setup } pack $logcanvas.up -side top -fill x foreach fm {A B} { label $logcanvas.up.rev${fm}_lvers -text "Revision $fm" label $logcanvas.up.rev${fm}_rvers -text {} \ -anchor w -font $textfont label $logcanvas.up.rev${fm}_ldate -text "Committed" label $logcanvas.up.rev${fm}_rdate -text {} \ -anchor w -font $textfont label $logcanvas.up.rev${fm}_lwho -text " by " label $logcanvas.up.rev${fm}_rwho -text {} \ -anchor w -font $textfont label $logcanvas.up.log${fm}_lcomment -text "Log $fm" frame $logcanvas.up.log${fm}_rlogfm -bd 3 -bg $cvscfg(colour$fm) text $logcanvas.up.log${fm}_rlogfm.rcomment -height 5 \ -fg $cvsglb(textfg) -bg $cvsglb(textbg) -state disabled \ -yscrollcommand [namespace code\ "$logcanvas.up.log${fm}_rlogfm.yscroll set"] scrollbar $logcanvas.up.log${fm}_rlogfm.yscroll \ -command [namespace code\ "$logcanvas.up.log${fm}_rlogfm.rcomment yview"] } grid columnconf $logcanvas.up 5 -weight 1 grid $logcanvas.up.lfname -column 0 -row 0 -sticky nw grid $logcanvas.up.rfname -column 1 -row 0 -columnspan 5 -sticky ew grid $logcanvas.up.bworkdir -column 6 -row 0 -rowspan 2 -sticky e\ -padx 2 -pady 1 grid $logcanvas.up.bmodbrowse -column 7 -row 0 -rowspan 2 -sticky e\ -padx 2 -pady 1 grid $logcanvas.up.revA_lvers -column 0 -row 1 -sticky w grid $logcanvas.up.revA_rvers -column 1 -row 1 -sticky w grid $logcanvas.up.revA_ldate -column 2 -row 1 -sticky w grid $logcanvas.up.revA_rdate -column 3 -row 1 -sticky w grid $logcanvas.up.revA_lwho -column 4 -row 1 -sticky w grid $logcanvas.up.revA_rwho -column 5 -row 1 -sticky ew grid $logcanvas.up.logA_lcomment -column 0 -row 2 -sticky nw grid $logcanvas.up.logA_rlogfm -column 1 -row 2 -columnspan 7 -sticky ew pack $logcanvas.up.logA_rlogfm.yscroll -side right -fill y pack $logcanvas.up.logA_rlogfm.rcomment -side left -fill x -expand y grid $logcanvas.up.revB_lvers -column 0 -row 3 -sticky w grid $logcanvas.up.revB_rvers -column 1 -row 3 -sticky w grid $logcanvas.up.revB_ldate -column 2 -row 3 -sticky w grid $logcanvas.up.revB_rdate -column 3 -row 3 -sticky w grid $logcanvas.up.revB_lwho -column 4 -row 3 -sticky w grid $logcanvas.up.revB_rwho -column 5 -row 3 -sticky ew grid $logcanvas.up.logB_lcomment -column 0 -row 4 -sticky nw grid $logcanvas.up.logB_rlogfm -column 1 -row 4 -columnspan 7 -sticky ew pack $logcanvas.up.logB_rlogfm.yscroll -side right -fill y pack $logcanvas.up.logB_rlogfm.rcomment -side left -fill x -expand y # Pack the bottom before the middle so it doesnt disappear if # the window is resized smaller frame $logcanvas.down -relief groove -border 2 pack $logcanvas.down -side bottom -fill x frame $logcanvas.down.search -relief sunk -bd 2 button $logcanvas.down.search.b -text "Find a Revision" -command [namespace code {Search}] entry $logcanvas.down.search.e bind $logcanvas.down.search.e [namespace code {Search}] label $logcanvas.down.search.l -anchor e -width 10 -text "" checkbutton $logcanvas.down.search.c -anchor e -text "Ignore case" \ -variable [namespace current]::search_nocase pack $logcanvas.down.search -side top -fill x pack $logcanvas.down.search.b -side left pack $logcanvas.down.search.e -side left pack $logcanvas.down.search.c -side left pack $logcanvas.down.search.l -side left # The canvas for the big picture canvas $logcanvas.canvas -relief sunken -border 2 \ -height 300 \ -yscrollcommand [namespace code "$logcanvas.yscroll set"] \ -xscrollcommand [namespace code "$logcanvas.xscroll set"] scrollbar $logcanvas.xscroll -relief sunken -orient horizontal \ -command [namespace code "$logcanvas.canvas xview"] scrollbar $logcanvas.yscroll -relief sunken \ -command [namespace code "$logcanvas.canvas yview"] # # Create buttons # frame $logcanvas.down.btnfm frame $logcanvas.down.closefm -relief groove -bd 2 button $logcanvas.refresh -image Refresh \ -command [namespace code { $scope\::reloadLog }] button $logcanvas.view -image Fileview button $logcanvas.log -image Log button $logcanvas.annotate -image Annotate button $logcanvas.diff -image Diff \ -command [namespace code { comparediff_r [$logcanvas.up.revA_rvers cget -text] \ [$logcanvas.up.revB_rvers cget -text] $logcanvas $filename }] button $logcanvas.delta -image Mergediff button $logcanvas.viewtags -image Tags \ -command [namespace code { variable revtags variable revbtags set taglist {} foreach r [lsort -command sortrevs \ [concat [array names revtags] \ [array names revbtags]]] { if [info exists revtags($r)] { append taglist "$r: $revtags($r)\n" } elseif [info exists revbtags($r)] { append taglist "$r: $revbtags($r)\n" } } view_output::new Tags $taglist }] button $logcanvas.close -text "Close" \ -command [namespace code { global cvscfg variable logcanvas variable my_idx set cvscfg(loggeom) [wm geometry $logcanvas] destroy $logcanvas catch {namespace delete ::cvs_branchlog::$my_idx} catch {namespace delete ::svn_branchlog::$my_idx} namespace delete [namespace current] exit_cleanup 0 }] button $logcanvas.stop -text "Stop" -bg red4 -fg white \ -activebackground red4 -activeforeground white \ -state [expr {$cvscfg(allow_abort) ? {normal} : {disabled}}] \ -command "$scope\::abortLog" pack $logcanvas.refresh \ -in $logcanvas.down -side left \ -ipadx 4 -ipady 4 pack $logcanvas.down.btnfm -side left -fill y -expand 1 pack $logcanvas.view \ $logcanvas.log \ $logcanvas.annotate \ $logcanvas.diff \ $logcanvas.delta \ $logcanvas.viewtags \ -in $logcanvas.down.btnfm -side left \ -ipadx 4 -ipady 4 pack $logcanvas.down.closefm -side right pack $logcanvas.close \ -in $logcanvas.down.closefm -side right \ -fill both -expand 1 set_tooltips $logcanvas.refresh \ {"Re-read the log information"} set_tooltips $logcanvas.up.bworkdir \ {"Open the Working Directory Browser"} set_tooltips $logcanvas.up.bmodbrowse \ {"Open the Repository Browser"} set_tooltips $logcanvas.view \ {"View a version of the file"} set_tooltips $logcanvas.log \ {"Revision Log of the file"} set_tooltips $logcanvas.annotate \ {"View revision where each line was modified"} set_tooltips $logcanvas.diff \ {"Compare two versions of the file"} set_tooltips $logcanvas.delta \ {"Merge to current"} set_tooltips $logcanvas.viewtags \ {"List all the file\'s tags"} # # Put the canvas on to the display. # pack $logcanvas.xscroll -side bottom -fill x -padx 1 -pady 1 pack $logcanvas.yscroll -side right -fill y -padx 1 -pady 1 pack $logcanvas.canvas -fill both -expand 1 scrollbindings $logcanvas.canvas # # Window manager stuff. # wm minsize $logcanvas 1 1 if {[info exists cvscfg(loggeom)]} { wm geometry $logcanvas $cvscfg(loggeom) } $logcanvas.canvas bind active \ "$logcanvas.canvas config -cursor hand2" $logcanvas.canvas bind active \ "$logcanvas.canvas config -cursor {}" $logcanvas.canvas bind tag \ [namespace code "PopupTags %X %Y"] $logcanvas.canvas bind box \ [namespace code "RevSelect A"] # Tcl/TK for Windows doesn't do Button 3, so we duplicate it on Button 2 $logcanvas.canvas bind box \ [namespace code "RevSelect B"] $logcanvas.canvas bind box \ [namespace code "RevSelect B"] # Clicking in a blank part of the canvas unselects boxes bind $logcanvas.canvas \ [namespace code "Unselect A"] bind $logcanvas.canvas \ [namespace code "Unselect B"] bind $logcanvas.canvas \ [namespace code "Unselect B"] focus $logcanvas.canvas # FIXME: Why isn't there a bbox when we get here? # Then the yview moveto doesn't work, although it does in tkinter $logcanvas.canvas xview moveto 0 $logcanvas.canvas yview moveto 0 return [list [namespace current] $logcanvas] } } } tkcvs-8.2.3.orig/tkcvs/tkcvs.10000644000175000017500000011756411664612512014351 0ustar timtim.TH TkCVS 1 Release 8.2.3 .SH NAME TkCVS - a Tk/Tcl Graphical Interface to CVS and Subversion .SH SYNOPSIS .B tkcvs [\-dir directory] [\-root cvsroot] [\-win workdir|module|merge] [\-log file] .SH DESCRIPTION .LP TkCVS is a Tcl/Tk-based graphical interface to the CVS and Subversion configuration management systems. It displays the status of the files in the current working directory, and provides buttons and menus to execute configuration-management commands on the selected files. Limited RCS functionality is also present. TkDiff is bundled in for browsing and merging your changes. .LP TkCVS also aids in browsing the repository. For Subversion, the repository tree is browsed like an ordinary file tree. For CVS, the CVSROOT/modules file is read. TkCVS extends CVS with a method to produce a browsable, "user friendly" listing of modules. This requires special comments in the CVSROOT/modules file. See "CVS Modules File" for more guidance. .SP .SH OPTIONS .LP TkCVS accepts the following options. .TP .BI -dir " directory" Start TkCVS in the specified directory. .TP .B -help Print a usage message. .TP .BI -log " file" Invoke a log browser for the specified file. -log and -win are mutually exclusive. .TP .BI -root " cvsroot" Set $CVSROOT to the specified repository. .TP .BI -win " workdir|module|merge" Start by displaying the directory browser (the default), the module browser, or the directory-merge tool. -win and -log are mutually exclusive. .SH Examples .RS % tkcvs -win module -root /jaz/repository .RE Browse the modules located in CVSROOT /jaz/repository .RS % tkcvs -log tstheap.c .RE View the log of the file tstheap.c .SP .SH Working Directory Browser .LP The working directory browser shows the files in your local working copy, or "sandbox." It shows the status of the files at a glance and provides tools to help with most of the common CVS, SVN, and RCS operations you might do. .LP At the top of the browser you will find: .LP * The name of the current directory. You can change directories by typing in this field. Recently visited directories are saved in the picklist. .LP * The relative path of the current directory in the repository. If it is not contained in the repository you may import it using the menu or toolbar button. .LP * A Directory Tag name, if the directory is contained in the repository and it has been checked out against a particular branch or tag. In Subversion, the branch or tag is inferred from the URL based on the conventional trunk-branches-tags repository organization. .LP * The CVSROOT of the current directory if it's under CVS control, or the URL of the Subversion repository if it's under Subversion control. If neither is true, it may default to the value of the $CVSROOT environment variable. .LP The main part of the working directory browser is a list of the files in the current directory with an icon next to each showing its status. You select a file by clicking on its name or icon once with the left mouse button. Holding the Control key while clicking will add the file to the group of those already selected. You can select a contiguous group of files by holding the Shift key while clicking. You can also select a group of files by dragging the mouse with the middle or right button pressed to select an area. Selecting an item that's already selected de-selects that item. To unselect all files, click the left mouse button in an empty area of the file list. .LP * The Date column (can be hidden) shows the modification time of the file is shown. The format of the date column may be specified with cvscfg(dateformat). The default format was chosen because it sorts the same way alphabetically as chronologically. .LP If the directory belongs to a revision system, other columns are present. .LP * The revision column shows which revision of the file is checked out, and whether it's on the trunk or on a branch. .LP * The status column (can be hidden) shows the revision of the file spelled out in text. This information is mostly redundant to the icon in the file column. .LP * The Editor/Author/Locker column (can be hidden) varies according to revision system. In Subversion, the author of the most recent checkin is shown. In CVS, it shows a list of people editing the files if your site uses "cvs watch" and/or "cvs edit". Otherwise, it will be empty. In RCS, it shows who, if anyone, has the file locked. .LP The optional columns can be displayed or hidden using the Options menu. .LP You can move into a directory by double-clicking on it. .LP Double clicking on a file will load the file into a suitable editor so you can change it. A different editor can be used for different file types (see Configuration Files). .SS File Status .LP When you are in a directory that is under CVS or Subversion control, the file status will be shown by an icon next to each file. Checking the "Status Column" option causes the status to be displayed in text in its own column. Some possible statuses are: .TP .B Up-to-date The file is up to date with respect to the repository. .TP .B Locally Modified The file has been modified in the current directory since being checked out of the repository. .TP .B Locally Added The file has been added to the repository. This file will become permanent in the repository once a commit is made. .TP .B Locally Removed You have removed the file with remove, and not yet committed your changes. .TP .B Needs Checkout Someone else has committed a newer revision to the repository. The name is slightly misleading; you will ordinarily use update rather than checkout to get that newer revision. .TP .B Needs Patch Like Needs Checkout, but the CVS server will send a patch rather than the entire file. Sending a patch or sending an entire file accomplishes the same thing. .TP .B Needs Merge Someone else has committed a newer revision to the repository, and you have also made modifications to the file. .TP .B Unresolved Conflict This is like Locally Modified, except that a previous update command gave a conflict. You need to resolve the conflict before checking in. .TP .B ? The file is not contained in the repository. You may need to add the file to the repository by pressing the "Add" button. .TP .B [directory:CVS] A directory which has been checked out from a CVS repository. .TP .B [directory:SVN] The file is a directory which has been checked out from a Subversion repository. In Subversion, directories are themselves versioned objects. .TP .B [directory:RCS] A directory which contains an RCS sub-directory or some files with the ,v suffix, presumably containing some files that are under RCS revision control. .TP .B [directory] The file is a directory. .SS File Filters .LP You can specify file matching patterns to instruct TkCVS which files you wish to see. You can also specify patterns telling it which files to remove when you press the "Clean" button or select the \fBFile->Cleanup\fR menu item. .LP "Hide" works exactly the way a .cvsignore file works. That is, it causes non-CVS files with the pattern to be ignored. It's meant for hiding .o files and such. Any file under CVS control will be listed anyway. .LP "Show" is the inverse. It hides non-CVS files except for those with the pattern. .SS Buttons .TP .I Module Browser: The big button at the upper right opens the module browser. Opens a module browser window which will enable you to explore items in the repository even if they're not checked out. In CVS, this requires that there be entries in the CVSROOT/modules file. Browsing can be improved by using TkCVS-specific comments in CVSROOT/modules. .TP .I Go Up: The button to the left of the entry that shows the current directory. Press it and you go up one level. .LP There are a number of buttons at the bottom of the window. Pressing on one of these causes the following actions: .TP .I Delete: Press this button to delete the selected files. The files will not be removed from the repository. To remove the files from the repository as well as delete them, press the "Remove" button instead. .TP .I Edit: Press this button to load the selected files in to an appropriate editor. .TP .I View: Press this button to view the selected files in a Tk text window. This can be a lot faster then Edit, in case your preferred editor is xemacs or something of that magnitude. .TP .I Refresh: Press this button to re-read the current directory, in case the status of some files may have changed. .TP .I Status Check: Shows, in a searchable text window, the status of all the files. By default, it is recursive and lists unknown (?) files. These can be changed in the Options menu. .TP .I Directory Branch Browse: For merging the entire directory. In Subversion, it opens the Branch Browser for "." In CVS, it chooses a "representative" file in the current directory and opens a graphical tool for directory merges. .TP .I Log (Branch) Browse: This button will bring up the log browser window for each of the selected files in the window. See the Log Browser section. .TP .I Annotate: This displays a window in which the selected file is shown with the lines highlighted according to when and by whom they were last revised. In Subversion, it's also called "blame." .TP .I Diff: This compares the selected files with the equivalent files in the repository. A separate program called "TkDiff" (also supplied with TkCVS) is used to do this. For more information on TkDiff, see TkDiff's help menu. .TP .I Merge Conflict: If a file's status says "Needs Merge", "Conflict", or is marked with a "C" in CVS Check, there was a difference which CVS needs help to reconcile. This button invokes TkDiff with the -conflict option, opening a merge window to help you merge the differences. .TP .I Check In: This button commits your changes to the repository. This includes adding new files and removing deleted files. When you press this button, a dialog will appear asking you for the version number of the files you want to commit, and a comment. You need only enter a version number if you want to bring the files in the repository up to the next major version number. For example, if a file is version 1.10, and you do not enter a version number, it will be checked in as version 1.11. If you enter the version number 3, then it will be checked in as version 3.0 instead. It is usually better to use symbolic tags for that purpose. If you use rcsinfo to supply a template for the comment, you must use an external editor. Set cvscfg(use_cvseditor) to do this. For checking in to RCS, an externel editor is always used. .TP .I Update: This updates your sandbox directory with any changes committed to the repository by other developers. .TP .I Update with Options: Allows you to update from a different branch, with a tag, with empty directories, and so on. .TP .I Add Files: Press this button when you want to add new files to the repository. You must create the file before adding it to the repository. To add some files, select them and press the Add Files button. The files that you have added to the repository will be committed next time you press the Check In button. It is not recursive. Use the menu CVS -> Add Recursively for that. .TP .I Remove Files: This button will remove files. To remove files, select them and press the Remove button. The files will disappear from the directory, and will be removed from the repository next time you press the Check In button. It is not recursive. Use the menu CVS -> Remove Recursively for that. .TP .I Tag: This button will tag the selected files. In CVS, the -F (force) option will move the tag if it already exists on the file. .TP .I Branch Tag: This button will tag the selected files, creating a branch. In CVS, the -F (force) option will move the tag if it already exists on the file. .TP .I Lock (CVS and RCS): Lock an RCS file for editing. If cvscfg(cvslock) is set, lock a CVS file. Use of locking is philosophically discouraged in CVS since it's against the "concurrent" part of Concurrent Versioning System, but locking policy is nevertheless used at some sites. One size doesn't fit all. .TP .I Unlock (CVS and RCS): Unlock an RCS file. If cvscfg(cvslock) is set, unlock a CVS file. .TP .I Set Edit Flag (CVS): This button sets the edit flag on the selected files, enabling other developers to see that you are currently editing those files (See "cvs edit" in the CVS documentation). .TP .I Reset Edit Flag (CVS): This button resets the edit flag on the selected files, enabling other developers to see that you are no longer editing those files (See "cvs edit" in the CVS documentation). As the current version of cvs waits on a prompt for "cvs unedit" if changes have been made to the file in question (to ask if you want to revert the changes to the current revision), the current action of tkcvs is to abort the unedit (by piping in nothing to stdin). Therefore, to lose the changes and revert to the current revision, it is necessary to delete the file and do an update (this will also clear the edit flag). To keep the changes, make a copy of the file, delete the original, update, and then move the saved copy back to the original filename. .TP .I Close: Press this button to close the Working Directory Browser. If no other windows are open, TkCVS exits. .SP .SH Log (Branch) Browser .LP The TkCVS Log Browser window enables you to view a graphical display of the revision log of a file, including all previous versions and any branched versions. .LP You can get to the log browser window in three ways, either by invoking it directly with "tkcvs [-log] ", by selecting a file within the main window of TkCVS and pressing the Log Browse button, or by selecting a file in a list invoked from the module browser and pressing the Log Browse button. .LP If the Log Browser is examining a checked-out file, the buttons for performing merge operations are enabled. .SS Log Browser Window .LP The log browser window has three components. These are the file name and version information section at the top, the log display in the middle, and a row of buttons along the bottom. .SS Log Display .LP The main log display is fairly self explanatory. It shows a group of boxes connected by lines indicating the main trunk of the file development (on the left hand side) and any branches that the file has (which spread out to the right of the main trunk). .LP Each box contains the version number, author of the version, and other information determined by the menu View -> Revision Layout. .LP Constructing the branch diagram from Subversion is inefficient, so the Log Browser counts the tags when doing a Subversion diagram and pops up a dialog giving you a chance to skip the tag step if there are too many tags (where "many" arbitrarily equals 10.) .SS Version Numbers .LP Once a file is loaded into the log browser, one or two version numbers may be selected. The primary version (Selection A) is selected by clicking the left mouse button on a version box in the main log display. .LP The secondary version (Selection B) is selected by clicking the right mouse button on a version box in the main log display. .LP Operations such as "View" and "Annotate" operate only on the primary version selected. .LP Operations such as "Diff" and "Merge Changes to Current" require two versions to be selected. .SS Searching the Diagram .LP You can search the canvas for tags, revisions, authors, and dates. .LP The following special characters are used in the search pattern: .LP * Matches any sequence of characters in string, including a null string. .LP ? Matches any single character in string. .LP [chars] Matches any character in the set given by chars. If a sequence of the form x-y appears in chars, then any character between x and y, inclusive, will match. .LP \x Matches the single character x. This provides a way of avoiding the special interpretation of the characters *?[]\ in pattern. .LP If you only enter "FOO" (without the \") in the entry box, it searches the exact string "foo". If you want to search all strings starting with "foo", you have to put "foo*". For all strings containing "foo", you must put "*foo*". .SS Log Browser Buttons .LP The log browser contains the following buttons: .TP .I Refresh: Re-reads the revision history of the file. .TP .I View: Pressing this button displays a Tk text window containing the version of the file at Selection A. .TP .I Annotate: This displays a window in which the file is shown with its lines highlighted according to when and by whom they were last revised. In Subversion, it's also called "blame." .TP .I Diff: Pressing this button runs the "tkdiff" program to display the differences between version A and version B. .TP .I Merge: To use this button, select a branch version of the file, other than the branch you are currently on, as the primary version (Selection A). The changes made along the branch up to that version will be merged into the current version, and stored in the current directory. Optionally, select another version (Selection B) and the changes will be from that point rather than from the base of the branch. The version of the file in the current directory will be merged, but no commit will occur. Then you inspect the merged files, correct any conflicts which may occur, and commit when you are satisfied. Optionally, TkCVS will tag the version that the merge is from. It suggests a tag of the form "mergefrom__date." If you use this auto-tagging function, another dialog containing a suggested tag for the merged-to version will appear. It's suggested to leave the dialog up until you are finished, then copy-and-paste the suggested tag into the "Tag" dialog. It is always a good practice to tag when doing merges, and if you use tags of the suggested form, the Branch Browser can diagram them. (Auto-tagging is not implemented in Subversion because, despite the fact that tags are "cheap," it's somewhat impractical to auto-tag single files. You can do the tagging manually, however.) .TP .I View Tags: This button lists all the tags applied to the file in a searchable text window. .TP .I Close: This button closes the Log Browser. If no other windows are open, TkCVS exits. .SS The View Options Menu The View Menu allows you to control what you see in the branch diagram. You can choose how much information to show in the boxes, whether to show empty revisions, and whether to show tags. You can even control the size of the boxes. If you are using Subversion, you may wish to turn the display of tags off. If they aren't asked for they won't be read from the repository, which can save a lot of time. .SP .SH Merge Tool for CVS .LP The Merge Tool chooses a "representative" file in the current directory and diagrams the branch tags. It tries to pick the "bushiest" file, or failing that, the most-revised file. If you disagree with its choice, you can type the name of another file in the top entry and press Return to diagram that file instead. .LP The main purpose of this tool is to do merges (cvs update -j rev [-j rev]) on the whole directory. For merging one file at a time, you should use the Log Browser. You can only merge to the line (trunk or branch) that you are currently on. Select a branch to merge from by clicking on it. Then press either the "Merge" or "Merge Changes" button. The version of the file in the current directory will be over-written, but it will not be committed to the repository. You do that after you've reconciled conflicts and decided if it's what you really want. .TP .I Merge Branch to Current: The changes made on the branch since its beginning will be merged into the current version. .TP .I Merge Changes to Current: Instead of merging from the base of the branch, this button merges the changes that were made since a particular version on the branch. It pops up a dialog in which you fill in the version. It should usually be the version that was last merged. .SP .SH Module Browser .LP Operations that are performed on the repository instead of in a checked-out working directory are done with the Module Browser. The most common of these operations is checking out or exporting from the repository. The Module Browser can be started from the command line (tkcvs -win module) or started from the main window by pressing the big button. .LP Subversion repositories can be browsed like a file tree, and that is what you will see in the Module Browser. CVS repositories aren't directly browsable, but if the CVSROOT/modules file is maintained appropriately, TkCVS can display the modules and infer tree structures if they are present. See the "CVS Modules File" section. .LP Using the module browser window, you can select a module to check out. When you check out a module, a new directory is created in the current working directory with the same name as the module. .SS Tagging and Branching (cvs rtag) .LP You can tag particular versions of a module or file in the repository, with plain or branch tags, without having the module checked out. .SS Exporting .LP Once a software release has been tagged, you can use a special type of checkout called an export. This allows you to cleanly check out files from the repository, without all of the administrivia that CVS needs to have while working on the files. It is useful for delivery of a software release to a customer. .SS Importing .LP TkCVS contains a special dialog to allow users to import new files into the repository. In CVS, new modules can be assigned places within the repository, as well as descriptive names (so that other people know what they are for). .LP When the Module Browser displays a CVS repository, the first column is a tree showing the module codes and directory names of all of the items in the repository. The icon shows whether the item is a directory (which may contain other directories or modules), or whether it is a module (which may be checked out from TkCVS). It is possible for an item to be both a module and a directory. If it has a red ball on it, you can check it out. If it shows a plain folder icon, you have to open the folder to get to the items that you can check out. .LP To select a module, click on it with the left mouse button. The right mouse button will perform a secondary selection, which is used only for Subversion diff and patch. To clear the selection, click on the item again or click in an empty area of the module column. There can only be one primary and one secondary selection. .SS Repository Browser Buttons .LP The module browser contains the following buttons: .TP .I Who: Shows which modules are checked out by whom. .TP .I Import: This item will import the contents of the current directory (the one shown in the Working Directory Display) into the repository as a module. See the section titled Importing for more information. .TP .I File Browse: Displays a list of the selected module's files. From the file list, you can view the file, browse its revision history, or see a list of its tags. .TP .I Check Out: Checks out the current version of a module. A dialog allows you to specify a tag, change the destination, and so on. .TP .I Export: Exports the current version of a module. A dialog allows you to specify a tag, change the destination, and so on. Export is similar to check-out, except exported directories do not contain the CVS or administrative directories, and are therefore cleaner (but cannot be used for checking files back in to the repository). You must supply a tag name when you are exporting a module to make sure you can reproduce the exported files at a later date. .TP .I Tag: This button tags an entire module. .TP .I Branch Tag: This creates a branch of a module by giving it a branch tag. .TP .I Patch Summary: This item displays a short summary of the differences between two versions of a module. .TP .I Create Patch File: This item creates a Larry Wall format patch(1) file of the module selected. .TP .I Close: This button closes the Repository Browser. If no other windows are open, TkCVS exits. .SP .SH Importing New Modules .LP Before importing a new module, first check to make sure that you have write permission to the repository. Also you'll have to make sure the module name is not already in use. .LP To import a module you first need a directory where the module is located. Make sure that there is nothing in this directory except the files that you want to import. .LP Press the big "Repository Browser" button in the top part of the tkcvs UI, or use CVS -> Import WD into Repository from the menu bar. .LP In the module browser, press the Import button on the bottom, the one that shows a folder and an up arrow. .LP In the dialog that pops up, fill in a descriptive title for the module. This will be what you see in the right side of the module browser. .LP OK the dialog. Several things happen now. The directory is imported, the CVSROOT/module file is updated, your original directory is saved as directory.orig, and the newly created module is checked out. .LP When it finishes, you should find the original Working Directory Browser showing the files in the newly created, checked out module. .LP Here is a more detailed description of the fields in the Import Dialog. .TP .I Module Name: A name for the module. This name must not already exist in the repository. Your organization could settle on a single unambiguous code for modules. One possibility is something like: .LP .RS [project code]-[subsystem code]-[module code] .RE .TP .I Module Path: The location in the repository tree where your new module will go. .TP .I Descriptive Title: A one-line descriptive title for your module. This will be displayed in the right-hand column of the browser. .TP .I Version Number: The current version number of the module. This should be a number of the form X.Y.Z where .Y and .Z are optional. You can leave this blank, in which case 1 will be used as the first version number. .LP Importing a directory into Subversion is similar but not so complicated. You use the SVN -> Import CWD into Repository menu. You need supply only the path in the repository where you want the directory to go. The repository must be prepared and the path must exist, however. .SP .SH Importing to an Existing Module (CVS) .LP Before importing to an existing module, first check to make sure that you have write permission to the repository. .LP To import to an existing module you first need a directory where the code is located. Make sure that there is nothing in this directory (including no CVS directory) except the files that you want to import. .LP Open up the Repository Browser by selecting File/Browse Modules from the menu bar. .LP In the Repository Browser, select File/Import To An Existing Module from the menu bar. .LP In the dialog that pops up, press the Browse button and select the name of an existing module. Press the OK to close this dialog box. Enter the version number of the code to be imported. .LP OK the dialog. Several things happen now. The directory is imported, your original directory is saved as directory.orig, and the newly created module is checked out. .LP When it finishes, you will find the original Working Directory Browser showing the original code. If you press the "Re-read the current directory" button you will see the results of the checked out code. .LP Here is a more detailed description of the fields in the Import Dialog. .TP .I Module Name: A name for the existing module. Filled in by the use of the the Browse button .TP .I Module Path: The location in the repository tree where the existing module is. Filled in by the use of the Browse button. .TP .I Version Number: The current version number of the module to be imported. This should be a number of the form X.Y.Z where .Y and .Z are optional. You can leave this blank, in which case 1 will be used as the first version number. .SP .SH Vendor Merge (CVS) .LP Software development is sometimes based on source distribution from a vendor or third-party distributor. After building a local version of this distribution, merging or tracking the vendor's future release into the local version of the distribution can be done with the vendor merge command. .LP The vendor merge command assumes that a separate module has already been defined for the vendor or third-party distribution with the use of the "Import To A New Module" and "Import To An Existing Module" commands. It also assumes that a separate module has already been defined for the local code for which the vendor merge operation is to be applied to. .LP Start from an empty directory and invoke tkcvs. Open up the Repository Browser by selecting File/Browse Modules from the menu bar. .LP Checkout the module of the local code to be merged with changes from the vendor module. (Use the red icon with the down arrow) .LP In the Repository Browser, after verifying that the Module entry box still has the name the module of the local code to which the vendor code is to be merged into, select File/Vendor Merge from the menu bar. .LP In the Module Level Merge With Vendor Code window, press the Browse button to select the module to be used as the vendor module. .LP OK the dialog. All revisions from the vendor module will be shown in the two scroll lists. Fill in the From and To entry boxes by clicking in the appropriate scroll lists. Ok the dialog. Several things happens now. Several screens will appear showing the output from cvs commands for (1)checking out temp files, (2)cvs merge, and (3)cvs rdiff. Information in these screens will tell you what routines will have merge conflicts and what files are new or deleted. After perusing the files, close each screen. .TP .I (In the preceeding dialog box, there was an option to save outputs from the merge and rdiff operations to files CVSmerge.out and CVSrdiff.out.) .LP The checked out local code will now contain changes from a merge between two revisions of the vendor modules. This code will not be checked into the repository. You can do that after you've reconciled conflicts and decide if that is what you really want. .LP A detailed example on how to use the vendor merge operation is provided in the PDF file vendor5readme.pdf. .SP .SH Configuration Files .LP There are two configuration files for TkCVS. The first is stored in the directory in which the *.tcl files for TkCVS are installed. This is called tkcvs_def.tcl. You can put a file called site_def in that directory, too. That's a good place for site-specific things like tagcolours. Unlike tkcvs_def.tcl, it will not be overwritten when you install a newer version of TkCVS. .LP Values in the site configuration files can be over-ridden at the user level by placing a .tkcvs file in your home directory. Commands in either of these files should use Tcl syntax. In other words, to set a variable name, you should have the following command in your .tkcvs file: .LP .RS set variablename value .RE .LP for example: .LP .RS set cvscfg(editor) "gvim" .RE .LP The following variables are supported by TkCVS: .SS Startup .TP .B cvscfg(startwindow) Which window you want to see on startup. (workdir or module) .SS CVS .TP .B cvscfg(cvsroot) If set, it overrides the CVSROOT environment variable. .SS Subversion If your SVN repository has a structure similar to trunk, branches, and tags but with different names, you can tell TkCVS about it by setting variables in tkcvs_def.tcl: set cvscfg(svn_trunkdir) "elephants" set cvscfg(svn_branchdir) "dogs" set cvscfg(svn_tagdir) "ducklings" The branch browser depends on the convention of having a trunk, branches, and tags structure to draw the diagram. These variables may give you a little more flexibility. .SS GUI .LP Most colors and fonts can be customized by using the options database. For example, you can add lines like these to your .tkcvs file: .LP .RS option add *Canvas.background #c3c3c3 .RE .RS option add *Menu.background #c3c3c3 .RE .RS option add *selectColor #ffec8b .RE .RS option add *Text.background gray92 .RE .RS option add *Entry.background gray92 .RE .RS option add *Listbox.background gray92 .RE .RS option add *ToolTip.background LightGoldenrod1 .RE .RS option add *ToolTip.foreground black .RE .TP .B cvscfg(picklist_items) Maximum number of visited directories and repositories to save in the picklist history .SS Log browser .TP .B cvscfg(colourA) cvscfg(colourB) Hilight colours for revision-log boxes .TP .B cvscfg(tagdepth) Number of tags you want to see for each revision on the branching diagram before it says "more..." and offers a pop-up to show the rest .TP .B cvscfg(toomany_tags) Number of tags in a Subversion repository that's "too many", ie. will take longer to proecess for the branch diagram than you're willing to wait. (Building a branch diagram for Subversion is very inefficient.) If there are more than that number and cvscfg(confirm_prompt) is true, a dialog will appear asking whether to process the tags or to draw the diagram without them. .TP .B cvscfg(tagcolour,tagstring) Colors for marking tags. For example: .RS set cvscfg(tagcolour,tkcvs_r6) Purple .RE .SS Module browser .TP .B cvscfg(aliasfolder) In the CVS module browser, if true this will cause the alias modules to be grouped in one folder. Cleans up clutter if there are a lot of aliases. .SS User preferences .TP .B cvscfg(allfiles) Set this to false to see normal files only in the directory browser. Set it to true to see all files including hidden files. .TP .B cvscfg(auto_status) Set the default for automatic status-refresh of a CVS controlled directory. Automatic updates are done when a directory is entered and after some operations. .TP .B cvscfg(auto_tag) Whether to tag the merged-from revision when using TkCVS to merge different revisions of files by default. A dialog still lets you change your mind, regardless of the default. .TP .B cvscfg(confirm_prompt) Ask for confirmation before performing an operation(true or false) .TP .B cvscfg(dateformat) Format for the date string shown in the "Date" column, for example "%Y/%m/%d %H:%M" .TP .B cvscfg(cvslock) Set to true to turn on the ability to use cvs-admin locking from the GUI. .TP .B cvscfg(econtrol) Set this to true to turn on the ability to use CVS Edit and Unedit, if your site is configured to allow the feature. .TP .B cvscfg(editor) Preferred default editor .TP .B cvscfg(editors) String pairs giving the editor-command and string-match-pattern, for deciding which editor to use .TP .B cvscfg(editorargs) Command-line arguments to send to the default editing program. .TP .B cvscfg(ldetail) Detail level for status reports (latest, summary, verbose) .TP .B cvscfg(mergetoformat) .TP .B cvscfg(mergefromformat) Format for mergeto- and mergefrom- tags. The _BRANCH_ part must be left as-is, but you can change the prefix and the date format, for example "mergeto_BRANCH_%d%b%y". The date format must be the same for both. CVS rule: a tag must not contain the characters `$,.:;@' .TP .B cvscfg(rdetail) Detail for repository and workdir reports (terse, summary, verbose) .TP .B cvscfg(recurse) Whether reports are recursive (true or false) .TP .B cvscfg(savelines) How many lines to keep in the trace window .TP .B cvscfg(status_filter) Filter out unknown files (status "?") from CVS Check and CVS Update reports. .TP .B cvscfg(use_cvseditor) Let CVS invoke an editor for commit log messages rather than having tkcvs use its own input box. By doing this, your site's commit template (rcsinfo) can be used. .SS File filters .TP .B cvscfg(file_filter) Pattern for which files to list. Empty string is equivalent to the entire directory (minus hidden files) .TP .B cvscfg(ignore_file_filter) Pattern used in the workdir filter for files to be ignored .TP .B cvscfg(clean_these) Pattern to be used for cleaning a directory (removing unwanted files) .SS System .TP .B cvscfg(print_cmd) System command used for printing. lpr, enscript -Ghr, etc) .TP .B cvscfg(shell) What you want to happen when you ask for a shell .TP .B cvscfg(terminal) Command prefix to use to run something in a terminal window .SS Portability .TP .B cvscfg(aster) File mask for all files (* for Unix, *.* for windows) .TP .B cvscfg(null) The null device. /dev/null for Unix, nul for windows .TP .B cvscfg(tkdiff) How to start tkdiff. Example sh /usr/local/bin/tkdiff .TP .B cvscfg(tmpdir) Directory in which to do behind-the-scenes checkouts. Usually /tmp or /var/tmp) .SS Debugging .TP .B cvscfg(log_classes) For debugging: C=CVS commands, E=CVS stderr output, F=File creation/deletion, T=Function entry/exit tracing, D=Debugging .TP .B cvscfg(logging) Logging (debugging) on or off .SP .SH Environment Variables .LP You should have the CVSROOT environment variable pointing to the location of your CVS repository before you run TkCVS. It will still allow you to work with different repositories within the same session. .LP If you wish TkCVS to point to a Subversion repository by default, you can set the environment variable SVNROOT. This has no meaning to Subversion itself, but it will clue TkCVS if it's started in an un-versioned directory. .SP .SH User Configurable Menu Extensions .LP It is possible to extend the TkCVS menu by inserting additional commands into the .tkcvs or tkcvs_def.tcl files. These extensions appear on an extra menu to the right of the TkCVS Options menu. .LP To create new menu entries on the user-defined menu, set the following variables: .SS cvsmenu(command) .LP Setting a variable with this name to a value like "commandname" causes the CVS command "cvs commandname" to be run when this menu option is selected. For example, the following line: .LP .RS set cvsmenu(update_A) "update -A" .RE .LP Causes a new menu option titled "update_A" to be added to the user defined menu that will run the command "cvs update -A" on the selected files when it is activated. .LP (This example command, for versions of CVS later than 1.3, will force an update to the head version of a file, ignoring any sticky tags or versions attached to the file). .SS usermenu(command) .LP Setting a variable with this name to a value like "commandname" causes the command "commandname" to be run when this menu option is selected. For example, the following line: .LP .RS set usermenu(view) "cat" .RE .LP Causes a new menu option titled "view" to be added to the User defined menu that will run the command "cat" on the selected files when it is activated. .LP Any user-defined commands will be passed a list of file names corresponding to the files selected on the directory listing on the main menu as arguments. .LP The output of the user defined commands will be displayed in a window when the command is finished. .SP .SH CVS Modules File .LP If you haven't put anything in your CVSROOT/modules file, please do so. See the "Administrative Files" section of the CVS manual. Then, you can add comments which TkCVS can use to title the modules and to display them in a tree structure. .LP The simplest use of TkCVS's "#D" directive is to display a meaningful title for the module: .LP .RS #D softproj Software Development Projects .RE .RS softproj softproj .RE .LP A fancier use is to organize the modules into a tree which will mimic their directory nesting in the repository when they appear in the module browser. For example, suppose we have a directory called "chocolate" which is organized like this: .LP .RS chocolate/ .RE .RS truffle/ .RE .RS cocoa3/ .RE .RS biter/ .RE .RS sniffer/ .RE .RS snuffler/ .RE .LP To display its hierarchy, as well as make the deepest directories more accessible by giving them module names, we could put this in the modules file: .LP .RS #D chocolate Top Chocolate .RE .RS #D chocolate/truffle Cocoa Level 2 .RE .RS #D chocolate/truffle/cocoa3 Cocoa Level 3 .RE .RS #D sniffer Chocolate Sniffer .RE .RS sniffer chocolate/truffle/cocoa3/sniffer .RE .RS #D snuff Chocolate Snuffler .RE .RS snuff chocolate/truffle/cocoa3/snuffler .RE .RS #D biter Chocolate Biter .RE .RS biter chocolate/truffle/cocoa3/biter .RE .LP When you are installing TkCVS, you may like to add these additional lines to the modules file (remember to check out the modules module from the repository, and then commit it again when you have finished the edits). .LP These extension lines commence with a "#" character, so CVS interprets them as comments. They can be safely left in the file whether you are using TkCVS or not. .LP "#M" is equivalent to "#D". The two had different functions in previous versions of TkCVS, but now both are parsed the same way. .SP .SH SEE ALSO cvs(1), svn(1) .SH AUTHOR Del (del@babel.babel.com.au): Maintenance and Subversion support: Dorothy Robinson tkcvs-8.2.3.orig/tkcvs/workdir.tcl0000644000175000017500000016632611664612512015322 0ustar timtim# # Tcl Library for TkCVS # # # Current working directory display. Handles all of the functions # concerned with navigating about the current directory on the main # window. # proc workdir_setup {} { global cwd global module_dir global cvscfg global cvsglb global current_tagname global logclass global tcl_platform gen_log:log T "ENTER" set cwd [pwd] set pid [pid] if {[winfo exists .workdir]} { wm deiconify .workdir raise .workdir return } # Make a new toplevel and unmap . so that the working directory browser # the module browser are equal toplevel .workdir wm title .workdir "TkCVS $cvscfg(version) Working Directory" wm iconname .workdir "TkCVS" if {$tcl_platform(platform) eq "unix"} { wm iconbitmap .workdir @$cvscfg(bitmapdir)/tkcvs48.xbm } wm minsize .workdir 430 300 wm protocol .workdir WM_DELETE_WINDOW { .workdir.close invoke } wm withdraw . if {[catch "image type Conflict"]} { workdir_images } if {[info exists cvscfg(workgeom)]} { wm geometry .workdir $cvscfg(workgeom) } workdir_menus # # Top section - where we are, where the module is # frame .workdir.top -relief groove -border 2 pack .workdir.top -side top -fill x ::picklist::entry .workdir.top.tcwd cwd directory ::picklist::bind .workdir.top.tcwd \ {if {[pwd] != $cwd} {change_dir "$cwd"}} button .workdir.top.updir_btn -image updir \ -command {change_dir ..} label .workdir.top.lmodule -text "Path" label .workdir.top.tmodule -textvariable module_dir -anchor w -relief groove label .workdir.top.ltagname -text "Tag" label .workdir.top.ttagname -textvariable current_tagname \ -anchor w -relief groove # Make the Repository Browser button prominent button .workdir.top.bmodbrowse -image Modules \ -command modbrowse_run label .workdir.top.lcvsroot -text "CVSROOT" entry .workdir.top.tcvsroot -textvariable cvscfg(cvsroot) \ -bd 1 -relief sunk -state readonly grid columnconf .workdir.top 1 -weight 1 grid rowconf .workdir.top 3 -weight 1 grid .workdir.top.updir_btn -column 0 -row 0 -sticky s grid .workdir.top.tcwd -column 1 -row 0 -columnspan 2 \ -sticky sew -padx 4 -pady 1 grid .workdir.top.lmodule -column 0 -row 1 -sticky nw grid .workdir.top.tmodule -column 1 -row 1 -columnspan 2\ -padx 4 -pady 1 -sticky new grid .workdir.top.bmodbrowse -column 2 -row 2 -rowspan 2 -sticky w grid .workdir.top.ltagname -column 0 -row 2 -sticky nw grid .workdir.top.ttagname -column 1 -row 2 -padx 4 -pady 1 -sticky new grid .workdir.top.lcvsroot -column 0 -row 3 -sticky nw grid .workdir.top.tcvsroot -column 1 -row 3 -padx 3 -sticky new # Pack the bottom before the middle so it doesnt disappear if # the window is resized smaller #frame .workdir.bottom -relief groove -border 2 -height 128 frame .workdir.bottom frame .workdir.bottom.filters -relief raised pack .workdir.bottom -side bottom -fill x pack .workdir.bottom.filters -side top -fill x label .workdir.bottom.filters.showlbl -text "Show:" -anchor w entry .workdir.bottom.filters.showentry -textvariable cvscfg(file_filter) -width 12 label .workdir.bottom.filters.hidelbl -text " Hide:" -anchor w entry .workdir.bottom.filters.hideentry -width 12 \ -textvariable cvsglb(default_ignore_filter) label .workdir.bottom.filters.space -text " " button .workdir.bottom.filters.cleanbutton -text "Clean:" \ -pady 0 -highlightthickness 0 \ -command workdir_cleanup entry .workdir.bottom.filters.cleanentry -width 12 \ -textvariable cvscfg(clean_these) bind .workdir.bottom.filters.showentry {setup_dir} bind .workdir.bottom.filters.hideentry { set cvsglb(default_ignore_filter) [.workdir.bottom.filters.hideentry get] setup_dir} bind .workdir.bottom.filters.cleanentry {workdir_cleanup} pack .workdir.bottom.filters.showlbl -side left pack .workdir.bottom.filters.showentry -side left pack .workdir.bottom.filters.hidelbl -side left pack .workdir.bottom.filters.hideentry -side left pack .workdir.bottom.filters.space -side left pack .workdir.bottom.filters.cleanbutton -side left -ipadx 2 -ipady 0 pack .workdir.bottom.filters.cleanentry -side left frame .workdir.bottom.buttons -relief groove -bd 2 frame .workdir.bottom.buttons.funcs -relief groove -bd 2 frame .workdir.bottom.buttons.dirfuncs -relief groove -bd 2 frame .workdir.bottom.buttons.cvsfuncs -relief groove -bd 2 frame .workdir.bottom.buttons.oddfuncs -relief groove -bd 2 frame .workdir.bottom.buttons.close -relief groove -bd 2 pack .workdir.bottom.buttons -side top -fill x -expand yes pack .workdir.bottom.buttons.close -side right -padx 10 pack .workdir.bottom.buttons.funcs -side left -expand yes -anchor w pack .workdir.bottom.buttons.dirfuncs -side left -expand yes -anchor w pack .workdir.bottom.buttons.cvsfuncs -side left -expand yes -anchor w pack .workdir.bottom.buttons.oddfuncs -side left -expand yes -anchor w # # Action buttons along the bottom of the screen. # button .workdir.bottom.buttons.funcs.bedit_files -image Fileedit \ -command { workdir_edit_file [workdir_list_files] } button .workdir.bottom.buttons.funcs.bview_files -image Fileview \ -command { workdir_view_file [workdir_list_files] } button .workdir.bottom.buttons.funcs.bdelete_file -image Delete \ -command { workdir_delete_file [workdir_list_files] } button .workdir.bottom.buttons.funcs.bmkdir -image Dir_new \ -command { file_input_and_do "New Directory" workdir_newdir} button .workdir.bottom.buttons.dirfuncs.brefresh -image Refresh \ -command { setup_dir } button .workdir.bottom.buttons.dirfuncs.bcheckdir -image Check button .workdir.bottom.buttons.cvsfuncs.blogfile -image Branches \ -command { cvs_branches [workdir_list_files] } button .workdir.bottom.buttons.cvsfuncs.bannotate -image Annotate \ -command { cvs_annotate $current_tagname [workdir_list_files] } button .workdir.bottom.buttons.cvsfuncs.bfilelog -image Log \ -command { cvs_log [workdir_list_files] } button .workdir.bottom.buttons.cvsfuncs.bdiff -image Diff \ -command { comparediff [workdir_list_files] } button .workdir.bottom.buttons.cvsfuncs.bconflict -image Conflict \ -command { cvs_merge_conflict [workdir_list_files] } button .workdir.bottom.buttons.cvsfuncs.btag -image Tag \ -command { file_tag_dialog "tag" } button .workdir.bottom.buttons.cvsfuncs.bbranchtag -image Branchtag \ -command { file_tag_dialog "branch" } button .workdir.bottom.buttons.cvsfuncs.badd_files -image Add \ -command { add_dialog [workdir_list_files] } button .workdir.bottom.buttons.cvsfuncs.bremove -image Remove \ -command { subtract_dialog [workdir_list_files] } button .workdir.bottom.buttons.cvsfuncs.bcheckin -image Checkin \ -command cvs_commit_dialog button .workdir.bottom.buttons.cvsfuncs.bupdate -image Checkout button .workdir.bottom.buttons.cvsfuncs.bupdateopts -image CheckoutOpts \ -command { cvs_update_options } button .workdir.bottom.buttons.cvsfuncs.brevert -image Revert \ -command { cvs_revert [workdir_list_files] } button .workdir.bottom.buttons.cvsfuncs.bjoin -image DirBranches \ -command cvs_joincanvas button .workdir.bottom.buttons.oddfuncs.bcvsedit_files -image Edit \ -command { cvs_edit [workdir_list_files] } button .workdir.bottom.buttons.oddfuncs.bunedit_files -image Unedit \ -command { cvs_unedit [workdir_list_files] } button .workdir.bottom.buttons.oddfuncs.block -image Lock button .workdir.bottom.buttons.oddfuncs.bunlock -image UnLock button .workdir.close -text "Close" \ -command { global cvscfg set cvscfg(workgeom) [wm geometry .workdir] destroy .workdir exit_cleanup 0 } # These buttons work in any directory grid .workdir.bottom.buttons.funcs.bdelete_file -column 0 -row 0 \ -ipadx 4 grid .workdir.bottom.buttons.funcs.bedit_files -column 1 -row 0 \ -ipadx 4 grid .workdir.bottom.buttons.funcs.bmkdir -column 0 -row 1 \ -ipadx 4 grid .workdir.bottom.buttons.funcs.bview_files -column 1 -row 1 \ -ipadx 4 # Directory functions grid rowconf .workdir.bottom.buttons.dirfuncs 0 -weight 1 grid .workdir.bottom.buttons.dirfuncs.brefresh -column 0 -row 0 \ -ipadx 4 -ipady 4 grid .workdir.bottom.buttons.dirfuncs.bcheckdir -column 1 -row 0 \ -ipadx 4 -ipady 4 # Revcontrol functions grid .workdir.bottom.buttons.cvsfuncs.blogfile -column 0 -row 0 \ -ipadx 4 grid .workdir.bottom.buttons.cvsfuncs.bjoin -column 0 -row 1 \ -ipadx 4 grid .workdir.bottom.buttons.cvsfuncs.bdiff -column 1 -row 0 \ -ipadx 2 grid .workdir.bottom.buttons.cvsfuncs.bconflict -column 1 -row 1 \ -ipadx 2 grid .workdir.bottom.buttons.cvsfuncs.bfilelog -column 2 -row 0 grid .workdir.bottom.buttons.cvsfuncs.bannotate -column 2 -row 1 grid .workdir.bottom.buttons.cvsfuncs.bupdate -column 3 -row 0 \ -ipadx 4 grid .workdir.bottom.buttons.cvsfuncs.bcheckin -column 3 -row 1 \ -ipadx 4 grid .workdir.bottom.buttons.cvsfuncs.bupdateopts -column 4 -row 0 \ -ipadx 4 grid .workdir.bottom.buttons.cvsfuncs.brevert -column 4 -row 1 \ -ipadx 4 grid .workdir.bottom.buttons.cvsfuncs.badd_files -column 5 -row 0 grid .workdir.bottom.buttons.cvsfuncs.bremove -column 5 -row 1 grid .workdir.bottom.buttons.cvsfuncs.btag -column 6 -row 0 \ -ipadx 4 grid .workdir.bottom.buttons.cvsfuncs.bbranchtag -column 6 -row 1 \ -ipadx 4 # These are specialized an not always available grid .workdir.bottom.buttons.oddfuncs.block -column 0 -row 0 grid .workdir.bottom.buttons.oddfuncs.bunlock -column 0 -row 1 grid .workdir.bottom.buttons.oddfuncs.bcvsedit_files -column 1 -row 0 grid .workdir.bottom.buttons.oddfuncs.bunedit_files -column 1 -row 1 pack .workdir.close -in .workdir.bottom.buttons.close \ -side right -fill both -expand yes set_tooltips .workdir.top.updir_btn \ {"Go up (..)"} set_tooltips .workdir.bottom.buttons.funcs.bedit_files \ {"Edit the selected files"} set_tooltips .workdir.bottom.buttons.funcs.bview_files \ {"View the selected files"} set_tooltips .workdir.bottom.buttons.funcs.bdelete_file \ {"Delete the selected files from the current directory"} set_tooltips .workdir.bottom.buttons.funcs.bmkdir \ {"Make a new directory"} set_tooltips .workdir.bottom.buttons.dirfuncs.brefresh \ {"Re-read the current directory"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bjoin \ {"Directory Branch Diagram and Merge Tool"} set_tooltips .workdir.bottom.buttons.dirfuncs.bcheckdir \ {"Check the status of the directory"} set_tooltips .workdir.bottom.buttons.cvsfuncs.blogfile \ {"Graphical Branch Diagram of the selected files"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bfilelog \ {"Revision Log of the selected files"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bannotate \ {"Revision where each line was modified (annotate/blame)"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bdiff \ {"Compare the selected files with the repository version"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bconflict \ {"Merge Conflicts using TkDiff"} set_tooltips .workdir.bottom.buttons.cvsfuncs.badd_files \ {"Add the selected files to the repository"} set_tooltips .workdir.bottom.buttons.cvsfuncs.btag \ {"Tag the selected files"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bbranchtag \ {"Branch the selected files"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bremove \ {"Remove the selected files from the repository"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bcheckin \ {"Check in (commit) the selected files to the repository"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bupdate \ {"Update (checkout, patch) the selected files from the repository"} set_tooltips .workdir.bottom.buttons.cvsfuncs.brevert \ {"Revert the selected files, discarding local edits"} set_tooltips .workdir.bottom.buttons.cvsfuncs.bupdateopts \ {"Update with options (-A, -r, -f, -d, -kb)"} set_tooltips .workdir.bottom.buttons.oddfuncs.block \ {"Lock the selected files"} set_tooltips .workdir.bottom.buttons.oddfuncs.bunlock \ {"Unlock the selected files"} set_tooltips .workdir.bottom.buttons.oddfuncs.bcvsedit_files \ {"Set the Edit flag on the selected files"} set_tooltips .workdir.bottom.buttons.oddfuncs.bunedit_files \ {"Reset the Edit flag on the selected files"} set_tooltips .workdir.top.bmodbrowse \ {"Open the Repository Browser"} set_tooltips .workdir.close \ {"Close the Working Directory Browser"} frame .workdir.main pack .workdir.main -side bottom -fill both -expand 1 -fill both update idletasks if {! [winfo ismapped .workdir]} { wm deiconify .workdir } #change_dir "[pwd]" setup_dir gen_log:log T "LEAVE" } proc workdir_images {} { global cvscfg image create photo arr_up \ -format gif -file [file join $cvscfg(bitmapdir) arrow_up.gif] image create photo arr_dn \ -format gif -file [file join $cvscfg(bitmapdir) arrow_dn.gif] image create photo arh_up \ -format gif -file [file join $cvscfg(bitmapdir) arrow_hl_up.gif] image create photo arh_dn \ -format gif -file [file join $cvscfg(bitmapdir) arrow_hl_dn.gif] image create photo updir \ -format gif -file [file join $cvscfg(bitmapdir) updir.gif] image create photo Folder \ -format gif -file [file join $cvscfg(bitmapdir) dir.gif] image create photo Check \ -format gif -file [file join $cvscfg(bitmapdir) check.gif] image create photo Fileview \ -format gif -file [file join $cvscfg(bitmapdir) fileview.gif] image create photo Fileedit \ -format gif -file [file join $cvscfg(bitmapdir) fileedit.gif] image create photo Annotate \ -format gif -file [file join $cvscfg(bitmapdir) annotate.gif] image create photo Delete \ -format gif -file [file join $cvscfg(bitmapdir) delete.gif] image create photo Dir_new \ -format gif -file [file join $cvscfg(bitmapdir) dir_new.gif] image create photo Refresh \ -format gif -file [file join $cvscfg(bitmapdir) loop-glasses.gif] image create photo Branches \ -format gif -file [file join $cvscfg(bitmapdir) branch.gif] image create photo DirBranches \ -format gif -file [file join $cvscfg(bitmapdir) dirbranch.gif] image create photo Add \ -format gif -file [file join $cvscfg(bitmapdir) add.gif] image create photo Remove \ -format gif -file [file join $cvscfg(bitmapdir) remove.gif] image create photo Diff \ -format gif -file [file join $cvscfg(bitmapdir) diff.gif] image create photo Checkin \ -format gif -file [file join $cvscfg(bitmapdir) checkin.gif] image create photo Revert \ -format gif -file [file join $cvscfg(bitmapdir) loop-ball.gif] image create photo Edit \ -format gif -file [file join $cvscfg(bitmapdir) edit.gif] image create photo Unedit \ -format gif -file [file join $cvscfg(bitmapdir) unedit.gif] image create photo Modules \ -format gif -file [file join $cvscfg(bitmapdir) modbrowse.gif] image create photo Modules_cvs \ -format gif -file [file join $cvscfg(bitmapdir) modbrowse_cvs.gif] image create photo Modules_svn \ -format gif -file [file join $cvscfg(bitmapdir) modbrowse_svn.gif] image create photo Lock \ -format gif -file [file join $cvscfg(bitmapdir) locked.gif] image create photo UnLock \ -format gif -file [file join $cvscfg(bitmapdir) unlocked.gif] image create photo Tags \ -format gif -file [file join $cvscfg(bitmapdir) tags.gif] image create photo Mergebranch \ -format gif -file [file join $cvscfg(bitmapdir) newmerge_simple.gif] image create photo Mergediff \ -format gif -file [file join $cvscfg(bitmapdir) newmerge.gif] image create photo Conflict \ -format gif -file [file join $cvscfg(bitmapdir) conflict.gif] image create photo Man \ -format gif -file [file join $cvscfg(bitmapdir) man.gif] } proc workdir_menus {} { global cvscfg global cvsglb global cvsmenu global usermenu global execmenu global bookmarks gen_log:log T "ENTER" set startdir "[pwd]" .workdir configure -menu .workdir.menubar menu .workdir.menubar # # Create the Menu bar # .workdir.menubar add cascade -label "File" -menu .workdir.menubar.file -underline 0 menu .workdir.menubar.file -tearoff 0 .workdir.menubar add cascade -label "CVS" -menu .workdir.menubar.cvs -underline 0 menu .workdir.menubar.cvs -tearoff 0 .workdir.menubar add cascade -label "SVN" -menu .workdir.menubar.svn -underline 0 menu .workdir.menubar.svn -tearoff 0 .workdir.menubar add cascade -label "RCS" -menu .workdir.menubar.rcs -underline 0 menu .workdir.menubar.rcs -tearoff 0 .workdir.menubar add cascade -label "Reports" -menu .workdir.menubar.reports -underline 2 menu .workdir.menubar.reports -tearoff 0 .workdir.menubar add cascade -label "Options" -menu .workdir.menubar.options -underline 0 menu .workdir.menubar.options -tearoff 0 if { [info exists cvsmenu] || \ [info exists usermenu] || \ [info exists execmenu]} { .workdir.menubar add cascade -label "User Defined" -menu .workdir.menubar.user -underline 0 menu .workdir.menubar.user -tearoff 0 gen_log:log T "Adding user defined menu" } .workdir.menubar add cascade -label "Go" -menu .workdir.menubar.goto -underline 0 menu .workdir.menubar.goto -tearoff 0 menu_std_help .workdir.menubar # # Create the Menus # # File .workdir.menubar.file add command -label "Open Selection" -underline 0 \ -command { workdir_edit_file [workdir_list_files] } .workdir.menubar.file add command -label "Print Selected File" -underline 0 \ -command { workdir_print_file [workdir_list_files ] } .workdir.menubar.file add command -label "Make New Directory" -underline 0 \ -command { file_input_and_do "New Directory" workdir_newdir} .workdir.menubar.file add separator .workdir.menubar.file add command -label "Browse Modules" -underline 0 \ -command modbrowse_run .workdir.menubar.file add command -label "Cleanup Directory" -underline 4 \ -command workdir_cleanup .workdir.menubar.file add separator .workdir.menubar.file add command -label "Shell window" -underline 0 \ -command { exec::new $cvscfg(shell) } .workdir.menubar.file add separator .workdir.menubar.file add command -label Close -underline 1 \ -command {.workdir.close invoke} .workdir.menubar.file add command -label Exit -underline 1 \ -command { exit_cleanup 1 } # CVS .workdir.menubar.cvs add command -label "Update" -underline 0 \ -command { \ cvs_update {BASE} {Normal} {Remove} {recurse} {prune} {No} { } [workdir_list_files] } .workdir.menubar.cvs add command -label "Update with Options" -underline 7 \ -command cvs_update_options .workdir.menubar.cvs add command -label "Commit/Checkin" -underline 0 \ -command cvs_commit_dialog .workdir.menubar.cvs add command -label "Add Files" -underline 0 \ -command { add_dialog [workdir_list_files] } .workdir.menubar.cvs add command -label "Add Recursively" \ -command { addir_dialog [workdir_list_files] } .workdir.menubar.cvs add command -label "Remove Files" -underline 0 \ -command { subtract_dialog [workdir_list_files] } .workdir.menubar.cvs add command -label "Remove Recursively" \ -command { subtractdir_dialog [workdir_list_files] } .workdir.menubar.cvs add command -label "Set Binary Flag" \ -command { cvs_binary [workdir_list_files] } .workdir.menubar.cvs add command -label "Unset Binary Flag" \ -command { cvs_ascii [workdir_list_files] } .workdir.menubar.cvs add command -label "Set Edit Flag (Edit)" -underline 15 \ -command { cvs_edit [workdir_list_files] } .workdir.menubar.cvs add command -label "Unset Edit Flag (Unedit)" -underline 11 \ -command { cvs_unedit [workdir_list_files] } .workdir.menubar.cvs add command -label "Tag Files" -underline 0 \ -command { file_tag_dialog "tag" } .workdir.menubar.cvs add command -label "Browse the Log Diagram" \ -command { cvs_branches [workdir_list_files] } .workdir.menubar.cvs add command -label "Resolve Conflicts" \ -command { cvs_merge_conflict [workdir_list_files] } .workdir.menubar.cvs add separator .workdir.menubar.cvs add command -label "Release" \ -command { release_dialog [workdir_list_files] } .workdir.menubar.cvs add command -label "Join (Merge) Directory" \ -underline 0 -command { cvs_directory_merge } .workdir.menubar.cvs add command -label "Import CWD into Repository" \ -underline 0 -command import_run # SVN .workdir.menubar.svn add command -label "Update" -underline 0 \ -command {svn_update [workdir_list_files]} .workdir.menubar.svn add command -label "Resolve (Un-mark Conflict)" -underline 0 \ -command {svn_resolve [workdir_list_files]} .workdir.menubar.svn add command -label "Commit/Checkin" -underline 0 \ -command svn_commit_dialog .workdir.menubar.svn add command -label "Add Files" -underline 0 \ -command { add_dialog [workdir_list_files] } .workdir.menubar.svn add command -label "Remove Files" -underline 0 \ -command { subtract_dialog [workdir_list_files] } .workdir.menubar.svn add command -label "Browse the Log Diagram" \ -command { svn_branches [workdir_list_files] } .workdir.menubar.svn add separator .workdir.menubar.svn add command -label "Import CWD into Repository" \ -underline 0 -command svn_import_run # RCS .workdir.menubar.rcs add command -label "Checkout" -underline 0 \ -command { rcs_checkout [workdir_list_files] } .workdir.menubar.rcs add command -label "Checkin" -underline 0 \ -command { rcs_checkin [workdir_list_files] } .workdir.menubar.rcs add command -label "Browse the Log Diagram" \ -command { rcs_branches [workdir_list_files] } # These commands will vary according to revision system. Does it still make sense to # keep them in their own menu? .workdir.menubar.reports add command -label "Check Directory" -underline 0 .workdir.menubar.reports add command -label "Status" -underline 0 .workdir.menubar.reports add command -label "Log" -underline 0 .workdir.menubar.reports add command -label "Annotate/Blame" -underline 0 .workdir.menubar.reports add command -label "Info" -underline 0 .workdir.menubar.options add checkbutton -label "Show hidden files" \ -variable cvscfg(allfiles) -onvalue true -offvalue false \ -command setup_dir .workdir.menubar.options add checkbutton -label "Automatic directory status" \ -variable cvscfg(auto_status) -onvalue true -offvalue false .workdir.menubar.options add checkbutton -label "Confirmation Dialogs" \ -variable cvscfg(confirm_prompt) -onvalue true -offvalue false .workdir.menubar.options add separator .workdir.menubar.options add checkbutton -label "Editor/Author/Locker Column" \ -variable cvscfg(showeditcol) -onvalue true -offvalue false \ -command { if {($incvs || $insvn || $inrcs) && $cvscfg(showeditcol)} { DirCanvas:map_column .workdir.main editcol } else { DirCanvas:unmap_column .workdir.main editcol } } .workdir.menubar.options add checkbutton -label "Status Column" \ -variable cvscfg(showstatcol) -onvalue true -offvalue false \ -command { if {($incvs || $insvn || $inrcs) && $cvscfg(showstatcol)} { DirCanvas:map_column .workdir.main statcol } else { DirCanvas:unmap_column .workdir.main statcol } } .workdir.menubar.options add checkbutton -label "Date Column" \ -variable cvscfg(showdatecol) -onvalue true -offvalue false \ -command { if {$cvscfg(showdatecol)} { DirCanvas:map_column .workdir.main datecol } else { DirCanvas:unmap_column .workdir.main datecol } } .workdir.menubar.options add radiobutton -label "Sort by Name" \ -variable cvscfg(sortcol) -value filecol \ -command "DirCanvas:sort_by_col .workdir.main filecol -decreasing" .workdir.menubar.options add radiobutton -label "Sort by Status" \ -variable cvscfg(sortcol) -value statcol \ -command "DirCanvas:sort_by_col .workdir.main statcol -decreasing" .workdir.menubar.options add separator .workdir.menubar.options add checkbutton -label "Report->Check Shows Unknown Files" \ -variable cvscfg(status_filter) -onvalue false -offvalue true .workdir.menubar.options add checkbutton -label "Report->Check/Status are Recursive" \ -variable cvscfg(recurse) -onvalue true -offvalue false .workdir.menubar.options add cascade -label "Status Detail" \ -menu .workdir.menubar.options.report_detail .workdir.menubar.options add cascade -label "Log Detail" \ -menu .workdir.menubar.options.logfile_detail .workdir.menubar.options add separator .workdir.menubar.options add checkbutton -label "Tracing On/Off" \ -variable cvscfg(logging) -onvalue true -offvalue false \ -command log_toggle .workdir.menubar.options add cascade -label "Trace Level" \ -menu .workdir.menubar.options.loglevel .workdir.menubar.options add separator .workdir.menubar.options add command -label "Save Options" -underline 0 \ -command save_options menu .workdir.menubar.options.loglevel .workdir.menubar.options.loglevel add checkbutton -label "commands (C)" \ -variable logclass(C) -onvalue "C" -offvalue "" \ -command gen_log:changeclass .workdir.menubar.options.loglevel add checkbutton -label "stderr (E)" \ -variable logclass(E) -onvalue "E" -offvalue "" \ -command gen_log:changeclass .workdir.menubar.options.loglevel add checkbutton -label "stdout and file creation/deletion (F)"\ -variable logclass(F) -onvalue "F" -offvalue "" \ -command gen_log:changeclass .workdir.menubar.options.loglevel add checkbutton -label "Function entry/exit (T)" \ -variable logclass(T) -onvalue "T" -offvalue "" \ -command gen_log:changeclass .workdir.menubar.options.loglevel add checkbutton -label "Debugging (D)" \ -variable logclass(D) -onvalue "D" -offvalue "" \ -command gen_log:changeclass menu .workdir.menubar.options.report_detail .workdir.menubar.options.report_detail add radiobutton -label "Verbose" \ -variable cvscfg(rdetail) -value "verbose" .workdir.menubar.options.report_detail add radiobutton -label "Summary" \ -variable cvscfg(rdetail) -value "summary" .workdir.menubar.options.report_detail add radiobutton -label "Terse" \ -variable cvscfg(rdetail) -value "terse" menu .workdir.menubar.options.logfile_detail .workdir.menubar.options.logfile_detail add radiobutton -label "Summary" \ -variable cvscfg(ldetail) -value "summary" .workdir.menubar.options.logfile_detail add radiobutton -label "Latest" \ -variable cvscfg(ldetail) -value "latest" .workdir.menubar.options.logfile_detail add radiobutton -label "Verbose" \ -variable cvscfg(ldetail) -value "verbose" .workdir.menubar.goto add command -label "Go Home" \ -command {change_dir $cvscfg(home)} .workdir.menubar.goto add command -label "Add Bookmark" \ -command add_bookmark .workdir.menubar.goto add command -label "Delete Bookmark" \ -command delete_bookmark_dialog .workdir.menubar.goto add separator foreach mark [lsort [array names bookmarks]] { # Backward compatibility. Value used to be a placeholder, is now a revsystem type if {$bookmarks($mark) == "t"} {set bookmarks($mark) ""} .workdir.menubar.goto add command -label "$mark $bookmarks($mark)" \ -command "change_dir \"$mark\"" } # # Add user commands to the menu. # if {[info exists cvsmenu]} { foreach item [array names cvsmenu] { .workdir.menubar.user add command -label $item \ -command "eval cvs_usercmd $cvsmenu($item) \[workdir_list_files\]" } } if {[info exists usermenu]} { .workdir.menubar.user add separator foreach item [array names usermenu] { .workdir.menubar.user add command -label $item \ -command "eval cvs_catchcmd $usermenu($item) \[workdir_list_files\]" } } if {[info exists execmenu]} { .workdir.menubar.user add separator foreach item [array names execmenu] { .workdir.menubar.user add command -label $item \ -command "eval cvs_execcmd $execmenu($item) \[workdir_list_files\]" } } gen_log:log T "LEAVE" } proc workdir_list_files {} { global cvscfg global cvsglb gen_log:log T "ENTER (cvsglb(current_selection) = $cvsglb(current_selection))" for {set i 0} {$i < [llength $cvsglb(current_selection)]} {incr i} { set item [lindex $cvsglb(current_selection) $i] regsub {^no file } $item "" item # regsub here causes file isfile to return 0. You have to do it in each # proc, just before the cvs command, after file tests have been done. #regsub -all {\$} $item {\$} item set cvsglb(current_selection) [lreplace $cvsglb(current_selection) $i $i $item] } gen_log:log T "LEAVE -- ($cvsglb(current_selection))" return $cvsglb(current_selection) } proc workdir_edit_command {file} { global cvscfg gen_log:log T "ENTER ($file)" if {[info exists cvscfg(editors)]} { foreach {editor pattern} $cvscfg(editors) { if {[string match $pattern $file]} { return "$editor \"$file\"" } } } return "$cvscfg(editor) \"$file\"" } proc workdir_newdir {file} { global cvscfg gen_log:log T "ENTER ($file)" file mkdir $file if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc workdir_edit_file {args} { global cvscfg global cwd gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { file_input_and_do "Edit File" workdir_edit_file return } gen_log:log D "$filelist" foreach file $filelist { if {[file isdirectory $file]} { change_dir "$file" } else { if {![file exists $file] || [file isfile $file]} { # If the file doesn't exist it's tempting to touch the file and # trigger a reread, but is an empty file of this type valid? regsub -all {\$} $file {\$} file set commandline [workdir_edit_command $file] set editcmd [exec::new $commandline] } else { cvsfail "$file is not a plain file" .workdir } } } gen_log:log T "LEAVE" } proc workdir_view_file {args} { global cvscfg global cwd gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { cvsfail "Please select some files to view first!" .workdir return } gen_log:log D "$filelist" foreach file $filelist { set filelog "" if {[file isfile $file]} { #regsub -all {\$} $file {\$} file gen_log:log F "OPEN $file" set f [open $file] while { [eof $f] == 0 } { append filelog [gets $f] append filelog "\n" } view_output::new "$file" $filelog } else { cvsfail "$file is not a plain file" .workdir } } gen_log:log T "LEAVE" } # Let the user mark directories they visit often proc add_bookmark { } { global incvs inrcs insvn global bookmarks gen_log:log T "ENTER" set dir [pwd] regsub -all {\$} $dir {\$} dir gen_log:log D "directory $dir" foreach mark [array names bookmarks] { gen_log:log D " $mark \"$bookmarks($mark)\"" } if {[info exists bookmarks($dir)]} { .workdir.menubar.goto delete "$dir $bookmarks($dir)" } set rtype "" if {$inrcs} { set rtype "(RCS)" } elseif {$incvs} { set rtype "(CVS)" } elseif {$insvn} { set rtype "(SVN)" } set bookmarks($dir) $rtype .workdir.menubar.goto add command -label "$dir $rtype" \ -command "change_dir \"$dir\"" gen_log:log T "LEAVE" } # A listbox to choose a bookmark to delete proc delete_bookmark_dialog { } { global cvscfg global cvsglb global bookmarks gen_log:log T "ENTER" set maxlbl 0 foreach mark [array names bookmarks] { gen_log:log D " $mark $bookmarks($mark)" set len [string length "$mark $bookmarks($mark)"] if {$len > $maxlbl} { set maxlbl $len } } set wname .workdir.bookmarkedit toplevel $wname grab set $wname wm title $wname "Delete Bookmarks" listbox $wname.lbx -selectmode multiple \ -font $cvscfg(listboxfont) -width $maxlbl pack $wname.lbx -ipadx 10 -ipady 10 -expand y -fill both foreach mark [lsort [array names bookmarks]] { $wname.lbx insert end "$mark $bookmarks($mark)" } frame $wname.buttons pack $wname.buttons -side top -fill x button $wname.delete -text "Delete" \ -command "delete_bookmark $wname" button $wname.close -text "Done" \ -command " grab release $wname destroy $wname exit_cleanup 0" pack $wname.delete $wname.close -in $wname.buttons \ -side right -ipadx 2 -ipady 2 -padx 4 -pady 4 \ -expand y gen_log:log T "LEAVE" } # Do the actual deletion of the bookmark proc delete_bookmark {w} { global bookmarks gen_log:log T "ENTER ($w)" set items [$w.lbx curselection] foreach item $items { set itemstring [$w.lbx get $item] #set dir [join [lrange $itemstring 0 end-1]] regsub {\s+$} $itemstring {} dir regsub {\s+\([A-Z][A-Z][A-Z]\)$} $dir {} dir gen_log:log D "$item \"$itemstring\"" gen_log:log D " directory \"$dir\"" unset bookmarks($dir) $w.lbx delete $item .workdir.menubar.goto delete $itemstring } gen_log:log T "LEAVE" } proc change_dir {new_dir} { global cwd gen_log:log T "ENTER ($new_dir)" if {![file exists $new_dir]} { set cwd [pwd] cvsfail "Directory $new_dir doesn\'t exist!" .workdir return } set cwd $new_dir # Deleting the tree discards the saved scroll position # so we start with yview 0 in a new directory DirCanvas:deltree .workdir.main setup_dir gen_log:log T "LEAVE" } proc auto_setup_dir {command} { global cvscfg if {$cvscfg(auto_status)} { $command\::wait setup_dir } else { after 0 "$command\::wait; $command\::destroy" } } proc setup_dir { } { # # Call this when entering a directory. It puts all of the file names # in the listbox, and reads the directory. # global cwd global module_dir global incvs global insvn global inrcs global cvscfg global current_tagname global cvsglb gen_log:log T "ENTER" set savyview 0 if { ! [winfo exists .workdir.main] } { workdir_setup return } else { if {[winfo exists .workdir.main.filecol.list]} { set savyview [lindex [.workdir.main.filecol.list yview] 0] } DirCanvas:deltree .workdir.main } gen_log:log D "YVIEW $savyview" if {![file isdirectory $cwd]} { gen_log:log D "$cwd is not a directory" gen_log:log T "LEAVE -- $cwd is not a directory" return } cd $cwd gen_log:log F "CD [pwd]" set module_dir "" set current_tagname "" ::picklist::used directory [pwd] foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } gen_log:log D "incvs $incvs inrcs $inrcs insvn $insvn" .workdir.top.bmodbrowse configure -image Modules .workdir.top.lmodule configure -text "Path" .workdir.top.ltagname configure -text "Tag" # Start with the revision-control menus disabled .workdir.menubar entryconfigure "CVS" -state normal .workdir.menubar entryconfigure "SVN" -state normal .workdir.menubar entryconfigure "RCS" -state normal .workdir.menubar.reports entryconfigure 0 -state disabled .workdir.menubar.reports entryconfigure 1 -state disabled .workdir.menubar.reports entryconfigure 2 -state disabled .workdir.menubar.reports entryconfigure 3 -state disabled .workdir.menubar.reports entryconfigure 4 -state disabled # Start with the revision-control buttons disabled and the .workdir.bottom.buttons.dirfuncs.bcheckdir configure -state disabled foreach widget [grid slaves .workdir.bottom.buttons.cvsfuncs ] { $widget configure -state disabled } foreach widget [grid slaves .workdir.bottom.buttons.cvsfuncs ] { $widget configure -state disabled } foreach widget [grid slaves .workdir.bottom.buttons.oddfuncs ] { $widget configure -state disabled } # Now enable them depending on where we are if {$inrcs} { # Top .workdir.top.lcvsroot configure -text "RCS *,v" .workdir.top.tcvsroot configure -textvariable cvscfg(rcsdir) # Buttons .workdir.bottom.buttons.dirfuncs.bcheckdir configure -state normal \ -command { rcs_check } .workdir.bottom.buttons.cvsfuncs.bdiff configure -state normal .workdir.bottom.buttons.cvsfuncs.blogfile configure -state normal \ -command { rcs_branches [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bfilelog configure -state normal \ -command { rcs_log [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bupdate configure -state normal \ -command { rcs_checkout [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bcheckin configure -state normal \ -command { rcs_checkin [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.brevert configure -state normal \ -command { rcs_revert [workdir_list_files] } .workdir.bottom.buttons.oddfuncs.block configure -state normal \ -command { rcs_lock lock [workdir_list_files] } .workdir.bottom.buttons.oddfuncs.bunlock configure -state normal \ -command { rcs_lock unlock [workdir_list_files] } # Menus .workdir.menubar entryconfigure "CVS" -state disabled .workdir.menubar entryconfigure "SVN" -state disabled .workdir.menubar entryconfigure "RCS" -state normal # Reports Menu # Check Directory (log & rdiff) .workdir.menubar.reports entryconfigure 0 -state normal \ -command { rcs_check } .workdir.menubar.reports entryconfigure 1 -state disabled # Log (rlog) .workdir.menubar.reports entryconfigure 2 -state normal \ -command { rcs_log [workdir_list_files] } .workdir.menubar.reports entryconfigure 3 -state disabled .workdir.menubar.reports entryconfigure 4 -state disabled } elseif {$insvn} { # Top .workdir.top.bmodbrowse configure -image Modules_svn \ -command {modbrowse_run svn} .workdir.top.lmodule configure -text "Path" .workdir.top.ltagname configure -text "Tag" .workdir.top.lcvsroot configure -text "SVN URL" .workdir.top.tcvsroot configure -textvariable cvscfg(url) # Buttons .workdir.bottom.buttons.dirfuncs.bcheckdir configure -state normal \ -command { svn_check [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bjoin configure -state normal \ -command { svn_branches . } .workdir.bottom.buttons.cvsfuncs.bdiff configure -state normal .workdir.bottom.buttons.cvsfuncs.blogfile configure -state normal \ -command { svn_branches [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bfilelog configure -state normal \ -command { svn_log [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bannotate configure -state normal \ -command { svn_annotate BASE [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bconflict configure -state normal \ -command { foreach f [workdir_list_files] {svn_merge_conflict \"$f\"} } .workdir.bottom.buttons.cvsfuncs.badd_files configure -state normal .workdir.bottom.buttons.cvsfuncs.bremove configure -state normal .workdir.bottom.buttons.cvsfuncs.bupdate configure -state normal \ -command { svn_update [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bupdateopts configure -state normal \ -command { svn_update_options } .workdir.bottom.buttons.cvsfuncs.bcheckin configure -state normal \ -command svn_commit_dialog .workdir.bottom.buttons.cvsfuncs.brevert configure -state normal \ -command { svn_revert [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.btag configure -state normal .workdir.bottom.buttons.cvsfuncs.bbranchtag configure -state normal .workdir.bottom.buttons.oddfuncs.block configure -state normal \ -command { svn_lock lock [workdir_list_files] } .workdir.bottom.buttons.oddfuncs.bunlock configure -state normal \ -command { svn_lock unlock [workdir_list_files] } # Menus .workdir.menubar entryconfigure "CVS" -state disabled .workdir.menubar entryconfigure "SVN" -state normal .workdir.menubar entryconfigure "RCS" -state disabled # Reports Menu # Check Directory (svn status) .workdir.menubar.reports entryconfigure 0 -state normal \ -command { svn_check {} } # Status (svn status ) .workdir.menubar.reports entryconfigure 1 -state normal \ -command { svn_check [workdir_list_files] } # Log (svn log) .workdir.menubar.reports entryconfigure 2 -state normal \ -command { svn_log [workdir_list_files] } # Annotate/Blame (svn blame) .workdir.menubar.reports entryconfigure 3 -state normal \ -command { svn_annotate BASE [workdir_list_files] } # Info (svn info) .workdir.menubar.reports entryconfigure 4 -state normal \ -command { svn_info [workdir_list_files] } } elseif {$incvs} { # Top .workdir.top.bmodbrowse configure -image Modules_cvs \ -command {modbrowse_run cvs} .workdir.top.lmodule configure -text "Module" .workdir.top.ltagname configure -text "Tag" .workdir.top.lcvsroot configure -text "CVSROOT" .workdir.top.tcvsroot configure -textvariable cvscfg(cvsroot) # Buttons .workdir.bottom.buttons.dirfuncs.bcheckdir configure -state normal \ -command { cvs_check [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bjoin configure -state normal \ -command cvs_joincanvas .workdir.bottom.buttons.cvsfuncs.bdiff configure -state normal .workdir.bottom.buttons.cvsfuncs.bconflict configure -state normal \ -command { cvs_merge_conflict [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bfilelog configure -state normal \ -command { cvs_log [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bannotate configure -state normal \ -command { cvs_annotate $current_tagname [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.badd_files configure -state normal .workdir.bottom.buttons.cvsfuncs.bremove configure -state normal .workdir.bottom.buttons.cvsfuncs.bupdate configure -state normal \ -command { \ cvs_update {BASE} {Normal} {Remove} {recurse} {prune} {No} { } [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bupdateopts configure -state normal \ -command { cvs_update_options } .workdir.bottom.buttons.cvsfuncs.bcheckin configure -state normal \ -command cvs_commit_dialog .workdir.bottom.buttons.cvsfuncs.brevert configure -state normal \ -command {cvs_revert [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.btag configure -state normal .workdir.bottom.buttons.cvsfuncs.bbranchtag configure -state normal .workdir.bottom.buttons.cvsfuncs.blogfile configure -state normal \ -command { cvs_branches [workdir_list_files] } .workdir.bottom.buttons.cvsfuncs.bfilelog configure -state normal \ -command { cvs_log [workdir_list_files] } if {$cvscfg(econtrol)} { .workdir.bottom.buttons.oddfuncs.bcvsedit_files configure -state normal .workdir.bottom.buttons.oddfuncs.bunedit_files configure -state normal } if {$cvscfg(cvslock)} { .workdir.bottom.buttons.oddfuncs.block configure -state normal \ -command { cvs_lock lock [workdir_list_files] } .workdir.bottom.buttons.oddfuncs.bunlock configure -state normal \ -command { cvs_lock unlock [workdir_list_files] } } # Menus .workdir.menubar entryconfigure "CVS" -state normal .workdir.menubar entryconfigure "SVN" -state disabled .workdir.menubar entryconfigure "RCS" -state disabled # Reports Menu # Check Directory (cvs -n -q update) .workdir.menubar.reports entryconfigure 0 -state normal \ -command { cvs_check {} } # Status (cvs -Q status) .workdir.menubar.reports entryconfigure 1 -state normal \ -command { cvs_status [workdir_list_files] } # Log (cvs log) .workdir.menubar.reports entryconfigure 2 -state normal \ -command { cvs_log [workdir_list_files] } # Annotate/Blame (cvs annotate) .workdir.menubar.reports entryconfigure 3 -state normal \ -command { cvs_annotate $current_tagname [workdir_list_files] } .workdir.menubar.reports entryconfigure 4 -state disabled } DirCanvas:create .workdir.main pack .workdir.main.pw -side bottom -fill both -expand yes set cvsglb(current_selection) {} set cvscfg(ignore_file_filter) $cvsglb(default_ignore_filter) if { [ file exists ".cvsignore" ] } { set fileId [ open ".cvsignore" "r" ] while { [ eof $fileId ] == 0 } { gets $fileId line append cvscfg(ignore_file_filter) " $line" } close $fileId } else { if {$insvn} { # Have to do eval exec because we need the error output set command "svn propget svn:ignore" gen_log:log C "$command" set ret [catch {eval "exec $command"} output] if {$ret} { cvsfail $output return } foreach infoline [split $output "\n"] { append cvscfg(ignore_file_filter) " $infoline" } } } set filelist [ getFiles ] directory_list $filelist # Update, otherwise it won't be mapped before we restore the scroll position update DirCanvas:yview_windows .workdir.main $savyview gen_log:log T "LEAVE" } proc directory_list { filenames } { global module_dir global incvs global insvn global inrcs global cvs global cwd global cvscfg global cvsglb global cmd global Filelist gen_log:log T "ENTER ($filenames)" if {[info exists Filelist]} { unset Filelist } busy_start .workdir.main #gen_log:log F "processing files in the local directory" set cwd [pwd] set my_cwd $cwd # If we have commands running they were for a different directory # and won't be needed now. (i.e. this is a recursive invocation # triggered by a button click) if {[info exists cmd(cvs_status)]} { catch {$cmd(cvs_status)\::abort} catch {unset cmd(cvs_status)} } if {[info exists cmd(cvs_editors)]} { catch {$cmd(cvs_editors)\::abort} catch {unset cmd(cvs_editors)} } # Select from those files only the ones we want (e.g., no CVS dirs) foreach i $filenames { if { $i == "." || $i == ".."} { gen_log:log D "SKIPPING $i" continue } if {[file isdirectory $i]} { if {[isCmDirectory $i]} { # Read the bookkeeping files but don't list the directory if {$i == "CVS" || $i == ".svn" || $i == "RCS"} { continue } } if {[file exists [file join $i "CVS"]]} { set Filelist($i:status) "" } elseif {[file exists [file join $i ".svn"]]} { set Filelist($i:status) "" } elseif {[file exists [file join $i "RCS"]]} { set Filelist($i:status) "" } else { set Filelist($i:status) "" } } else { if {$incvs} { set Filelist($i:status) "Not managed by CVS" } else { set Filelist($i:status) "" } } set Filelist($i:wrev) "" set Filelist($i:stickytag) "" set Filelist($i:option) "" # Prepending ./ to the filename prevents tilde expansion catch {set Filelist($i:date) \ [clock format [file mtime ./$i] -format $cvscfg(dateformat)]} } gen_log:log D "incvs=$incvs insvn=$insvn inrcs=$inrcs" if {$incvs} { DirCanvas:headtext .workdir.main "editors" cvs_workdir_status } if {$inrcs} { DirCanvas:headtext .workdir.main "locked by" rcs_workdir_status } if {$insvn} { DirCanvas:headtext .workdir.main "author" svn_workdir_status } gen_log:log D "Sending all files to the canvas" foreach i [array names Filelist *:status] { regsub {:status$} $i "" j # If it's locally removed or missing, it may not have # gotten a date especially on a remote client. if {! [info exists Filelist($j:date)]} { set Filelist($j:date) "" } DirCanvas:newitem .workdir.main "$j" } busy_done .workdir.main gen_log:log T "LEAVE" } proc workdir_cleanup {} { global cvscfg gen_log:log T "ENTER" set rmitem "" set list [ split $cvscfg(clean_these) " " ] foreach pattern $list { gen_log:log D "pattern $pattern" if { $pattern != "" } { set items [lsort [glob -nocomplain $pattern]] gen_log:log D "$items" if {[llength $items] != 0} { append rmitem " [concat $items]" } } } if {$rmitem != ""} { if { [ are_you_sure "You are about to delete:\n" $rmitem] == 1 } { gen_log:log F "DELETE $rmitem" eval file delete -force -- $rmitem } } else { gen_log:log F "No files to delete" cvsok "Nothing matched $cvscfg(clean_these)" .workdir return } setup_dir gen_log:log T "LEAVE" } proc workdir_delete_file {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { cvsfail "Please select some files to delete first!" .workdir return } if { [ are_you_sure "This will delete these files from your local, working directory:\n" $filelist ] == 1 } { gen_log:log F "DELETE $filelist" eval file delete -force -- $filelist setup_dir } gen_log:log T "LEAVE" } proc are_you_sure {mess args} { # # General posting message # global cvscfg gen_log:log T "ENTER ($mess $args)" set filelist [join $args] if {$cvscfg(confirm_prompt)} { append mess "\n" set indent " " foreach item $filelist { if { $item != {} } { append mess " $indent" append mess " $item\n" } } append mess "\nAre you sure?" if {[cvsconfirm $mess .workdir] != "ok"} { gen_log:log T "LEAVE 0" return 0 } } gen_log:log T "LEAVE 1" return 1 } proc workdir_print_file {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { cvsfail "Please select some files to print first!" .workdir return } set mess "This will print these files:\n\n" foreach file $filelist { append mess " $file\n" } append mess "\nUsing $cvscfg(print_cmd)\n" append mess "\nAre you sure?" if {[cvsconfirm $mess .workdir] == "ok"} { set final_result "" foreach file $filelist { set commandline [concat $cvscfg(print_cmd) \"$file\"] exec::new $commandline } } gen_log:log T "LEAVE" } proc cvsroot_check { dir } { global cvscfg global cvsglb gen_log:log T "ENTER ($dir)" foreach {incvs insvn inrcs} {0 0 0} {break} if {[file isfile [file join $dir CVS Root]]} { set incvs [ read_cvs_dir [file join $dir CVS] ] } elseif {! [catch {eval "exec svn info"}]} { set insvn [ read_svn_dir $dir ] } else { set rcsdir [file join $dir RCS] if {[file exists $rcsdir]} { set cvscfg(rcsdir) $rcsdir set inrcs 1 } elseif {[llength [glob -nocomplain -dir $dir *,v]] > 0} { set inrcs 1 set cvscfg(rcsdir) $dir } else { set cvscfg(rcsdir) "" } } if {$inrcs} { # Make sure we have rcs, and bag this (silently) if we don't set command "rcs -V" gen_log:log C "$command" set ret [catch {eval "exec $command"} raw_rcs_log] if {$ret} { gen_log:log D "$raw_rcs_log" if [string match {*Unknown option:*} $raw_rcs_log] { # An old version of RCS, but it's here set inrcs 1 } else { set inrcs 0 } } } gen_log:log T "LEAVE ($incvs $insvn $inrcs)" return [list $incvs $insvn $inrcs] } proc isCmDirectory { file } { #gen_log:log T "ENTER ($file)" switch -- $file { "CVS" - "RCS" - ".svn" - "SCCS" { set value 1 } default { set value 0 } } #gen_log:log T "LEAVE ($value)" return $value } # Get the files in the current working directory. Use the file_filter # values Add hidden files if desired by the user. Sort them to match # the ordering that will be returned by cvs commands (this matches the # default ls ordering.). proc getFiles { } { global cvscfg global cvsglb gen_log:log T "ENTER" set filelist "" # make sure the file filter is at least set to "*". if { $cvscfg(file_filter) == "" } { set cvscfg(file_filter) "* .svn" } # get the initial file list, including hidden if requested if {$cvscfg(allfiles)} { # get hidden as well foreach item $cvscfg(file_filter) { set filelist [ concat [ glob -nocomplain .$item $item ] $filelist ] } } else { foreach item $cvscfg(file_filter) { set filelist [ concat [ glob -nocomplain $item ] $filelist ] } } #gen_log:log D "filelist ($filelist)" # ignore files if requested if { $cvscfg(ignore_file_filter) != "" } { foreach item $cvscfg(ignore_file_filter) { # for each pattern if { $item != "*" } { # if not "*" while { [set idx [lsearch $filelist $item]] != -1 } { # for each occurence, delete catch { set filelist [ lreplace $filelist $idx $idx ] } } } } } # make sure "." is always in the list for 'cd' purposes if { ( [ lsearch -exact $filelist "." ] == -1 ) } { set filelist [ concat "." $filelist ] } # make sure ".." is always in the list for 'cd' purposes if { ( [ lsearch -exact $filelist ".." ] == -1 ) } { set filelist [ concat ".." $filelist ] } # sort it set filelist [ lsort $filelist ] # if this directory is under CVS and CVS is not in the list, add it. Its # presence is needed for later processing if { ( [ file exists "CVS" ] ) && ( [ lsearch -exact $filelist "CVS" ] == -1 ) } { #puts "********* added CVS" catch { set filelist [ concat "CVS" $filelist ] } } set cvscfg(ignore_file_filter) $cvsglb(default_ignore_filter) gen_log:log T "return ($filelist)" return $filelist } proc log_toggle { } { global cvscfg if {$cvscfg(logging)} { gen_log:init } else { gen_log:quit } } proc exit_cleanup { force } { global cvscfg # Count the number of toplevels that are currently interacting # with the user (i.e. exist and are not withdrawn) set wlist {} foreach w [winfo children .] { if {[wm state $w] != {withdrawn}} { lappend wlist $w } } if {$force == 0 && [llength $wlist] != 0 \ && $wlist != {.trace} && $wlist != {.bgerrorTrace}} { return } # If toplevel windows exist ask them to close gracefully if possible foreach w $wlist { # Except .trace! if {$w != {.trace}} { catch {$w.close invoke} } else { # Invoking trace's close turns off logging. We don't want that, # but we do want to save its geometry. if {[winfo exists .trace]} { set cvscfg(tracgeom) [wm geometry .trace] } } } save_options set pid [pid] gen_log:log F "DELETE $cvscfg(tmpdir)/cvstmpdir.$pid" catch {file delete -force [file join $cvscfg(tmpdir) cvstmpdir.$pid]} exit } proc save_options { } { # # Save the options which are configurable from the GUI # global cvscfg global logcfg global bookmarks gen_log:log T "ENTER" # There are two kinds of options we can set set BOOLopts { allfiles auto_status confirm_prompt \ showstatcol showdatecol showeditcol auto_tag \ status_filter recurse logging blame_linenums} set STRGopts { file_filter ignore_file_filter clean_these \ printer rdetail ldetail log_classes lastdir sortcol \ workgeom modgeom loggeom tracgeom editor editorargs} # Plus the logcanvas options set LOGopts [concat [array names logcfg show_*] scale] # set this to current directory, so we'll add it to the menu next time if ([catch pwd]) { return } set cvscfg(lastdir) [pwd] if {[info exists cvscfg(editorargs)] } { # editorargs is no longer necessary if {$cvscfg(editorargs) != ""} { set cvscfg(editor) [concat $cvscfg(editor) $cvscfg(editorargs)] } unset cvscfg(editorargs) } # Save the list so we can keep track of what we've done set BOOLset $BOOLopts set STRGset $STRGopts set LOGset $LOGopts set optfile [file join $cvscfg(home) .tkcvs] set bakfile [file join $cvscfg(home) .tkcvs.bak] # Save the old .tkcvs file gen_log:log F "MOVE $optfile $bakfile" catch {file rename -force $optfile $bakfile} gen_log:log F "OPEN $optfile" if {[catch {set fo [open $optfile w]}]} { cvsfail "Cannot open $optfile for writing" .workdir return } gen_log:log F "OPEN $bakfile" if {! [catch {set fi [open $bakfile r]}]} { while { [eof $fi] == 0 } { gets $fi line set match 0 if {[regexp {^#} $line]} { # Don't try to scan comments. #gen_log:log D "PASSING \"$line\"" puts $fo "$line" continue } elseif {[string match "*set *bookmarks*" $line]} { # Discard old bookmarks continue } else { foreach opt $BOOLopts { if {! [info exists cvscfg($opt)]} { continue } if {[string match "*set *cvscfg($opt)*" $line]} { # Print it and remove it from the list gen_log:log D "REPLACING $line w/ set cvscfg($opt) $cvscfg($opt)" puts $fo "set cvscfg($opt) $cvscfg($opt)" set idx [lsearch $BOOLset $opt] set BOOLset [lreplace $BOOLset $idx $idx] set match 1 break } } if {[string match "*set *cvscfg(checkrecursive)*" $line]} { # This helps us recover from a problem left behind by tkcvs 7.2 continue } foreach opt $STRGopts { if {! [info exists cvscfg($opt)]} { continue } if {[string match "*set *cvscfg($opt)*" $line]} { # Print it and remove it from the list gen_log:log D "REPLACING $line w/ set cvscfg($opt) $cvscfg($opt)" puts $fo "set cvscfg($opt) \"$cvscfg($opt)\"" set idx [lsearch $STRGset $opt] set STRGset [lreplace $STRGset $idx $idx] set match 1 break } } if {[string match "*set *cvscfg(editorargs)*" $line]} { # editorargs is no longer necessary continue } foreach opt $LOGopts { if {! [info exists logcfg($opt)]} { continue } if {[string match "*set *logcfg($opt)*" $line]} { # Print it and remove it from the list gen_log:log D "REPLACING \"$line\" w/ set logcfg($opt) \"$logcfg($opt)\"" puts $fo "set logcfg($opt) \"$logcfg($opt)\"" set idx [lsearch $LOGset $opt] set LOGset [lreplace $LOGset $idx $idx] set match 1 break } } if {$match == 0} { # We didn't do a replacement gen_log:log D "PASSING \"$line\"" # If we don't check this, we get an extra blank line every time # we save the file. Messy. if {[eof $fi] == 1} { break } puts $fo "$line" } } } foreach mark [lsort [array names bookmarks]] { gen_log:log D "Adding bookmark \"$mark\"" puts $fo "set \"bookmarks($mark)\" \"$bookmarks($mark)\"" } close $fi } # Print what's left over foreach opt $BOOLset { if {! [info exists cvscfg($opt)]} { continue } gen_log:log D "ADDING cvscfg($opt) $cvscfg($opt)" puts $fo "set cvscfg($opt) $cvscfg($opt)" } foreach opt $STRGset { if {! [info exists cvscfg($opt)]} { continue } gen_log:log D "ADDING cvscfg($opt) \"$cvscfg($opt)\"" puts $fo "set cvscfg($opt) \"$cvscfg($opt)\"" } foreach opt $LOGset { if {! [info exists logcfg($opt)]} { continue } gen_log:log D "ADDING logcfg($opt) \"$logcfg($opt)\"" puts $fo "set logcfg($opt) \"$logcfg($opt)\"" } close $fo ::picklist::save gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/tkcvs/dircanvas.tcl0000644000175000017500000013255411664612512015607 0ustar timtim# # Columns for listing CVS files and their status # proc DirCanvas:create {w} { global cvscfg global cvsglb global arr global incvs global insvn global inrcs gen_log:log T "ENTER ($w)" if {[catch "image type folder"]} { DirCanvas:loadimages } if [winfo exists $w.pw] { catch {DirCanvas:destroy $w.pw} catch {destroy $w.pw} } set winwid [winfo width $w] set beginwid [expr {$winwid / 5}] panedwindow $w.pw -relief sunk -bd 2 $w.pw configure -handlepad 35 -sashwidth 4 -sashpad 0 -handlesize 10 foreach column {filecol statcol datecol wrevcol editcol} { frame $w.$column canvas $w.$column.list -highlightthickness 0 -width $beginwid $w.$column configure -bg $cvsglb(canvbg) $w.$column.list configure -bg $cvsglb(canvbg) gen_log:log D "$column start width $beginwid" } scrollbar $w.yscroll -orient vertical -command "DirCanvas:scroll_windows $w" \ -highlightthickness 0 pack $w.yscroll -side right -fill y DirCanvas:column $w filecol "file" DirCanvas:column $w statcol "status" DirCanvas:column $w datecol "date" gen_log:log D "incvs=$incvs insvn=$insvn inrcs=$inrcs" if {$incvs || $insvn || $inrcs} { gen_log:log D "**** going to make wrevcol ****" DirCanvas:column $w wrevcol "revision" DirCanvas:column $w editcol "editors" } # Put an extra arrow on the file column for sorting by status set statusbutton $w.filecol.head.statbut button $statusbutton -image arr_dn \ -relief raised -bd 1 -highlightthickness 0 set arr(filestatcol) $statusbutton pack $statusbutton -side left bind $statusbutton \ "DirCanvas:toggle_col $w statcol" bind $statusbutton \ "DirCanvas:sort_by_col $w statcol -decreasing" bind $statusbutton \ "DirCanvas:sort_by_col $w statcol -increasing" gen_log:log D "sort_pref: $cvsglb(sort_pref)" set col [lindex $cvsglb(sort_pref) 0] set sense [lindex $cvsglb(sort_pref) 1] gen_log:log D "$incvs $col" if { (! ($incvs || $inrcs || $insvn)) && ( $col == "editcol" || $col == "wrevcol") } { gen_log:log T "setting sort to column \"filecol!\"" set col "filecol" set sense "-decreasing" } if {[string match "-inc*" $sense]} { gen_log:log D "sort column $col -increasing" $arr($col) configure -image arh_up if {$col == "statcol"} {$arr(filestatcol) configure -image arh_up} } else { gen_log:log D "sort column $col -decreasing" if {[info exists arr($col)] && [winfo exists $arr($col)]} { $arr($col) configure -image arh_dn gen_log:log D "arr(col) = arr($col); arr($col) = $arr($col)" } if {$col == "statcol"} { $arr(filestatcol) configure -image arh_dn } } focus $w.filecol.list if {! [winfo exists $w.paper_pop]} { DirCanvas:makepopup $w } gen_log:log T "LEAVE" } proc DirCanvas:headtext {w lbltext} { $w.editcol.head.lbl configure -text "$lbltext" } proc DirCanvas:column {w column headtext} { global cvscfg global cvsglb global incvs global insvn global inrcs global arr gen_log:log T "ENTER ($w $column headtext)" gen_log:log T "showstatcol $cvscfg(showstatcol) showdatecol $cvscfg(showdatecol) showeditcol $cvscfg(showeditcol)" $w.$column.list configure -yscrollcommand "$w.yscroll set" bind $w.$column.list "DirCanvas:scroll_windows $w scroll 1 pages" bind $w.$column.list "DirCanvas:scroll_windows $w scroll -1 pages" bind $w.$column.list "DirCanvas:scroll_windows $w scroll 1 units" bind $w.$column.list "DirCanvas:scroll_windows $w scroll -1 units" bind $w.$column.list "DirCanvas:drag_windows $w %W %y" bind $w.$column.list \ "DirCanvas:scroll_windows $w scroll \[expr {-(%D/120)*4}\] units" bind $w.$column.list \ "DirCanvas:scroll_windows $w scroll -1 units" bind $w.$column.list \ "DirCanvas:scroll_windows $w scroll 1 units" frame $w.$column.head -relief raised -bd 2 label $w.$column.head.lbl -text "$headtext" button $w.$column.head.sbut -image arr_dn -relief flat \ -highlightthickness 0 set arr($column) $w.$column.head.sbut gen_log:log D "$w.$column.head.sbut" bind $w.$column.head.sbut "DirCanvas:toggle_col $w $column" bind $w.$column.head.sbut "DirCanvas:sort_by_col $w $column -decreasing" bind $w.$column.head.sbut "DirCanvas:sort_by_col $w $column -increasing" if {$column == "datecol"} { if {$cvscfg(showdatecol)} { DirCanvas:map_column $w datecol } else { gen_log:log T "LEAVE (skipping datecol)" } return } if {$column == "statcol"} { if {($incvs || $insvn || $inrcs) && $cvscfg(showstatcol)} { DirCanvas:map_column $w statcol } else { gen_log:log T "LEAVE (skipping statcol)" } return } if {$column == "editcol"} { if {($incvs || $insvn || $inrcs) && $cvscfg(showeditcol)} { DirCanvas:map_column $w editcol } else { gen_log:log T "LEAVE (skipping editcol)" } return } DirCanvas:map_column $w $column } proc DirCanvas:map_column {w column} { gen_log:log T "ENTER ($w $column)" set mapped_columns [$w.pw panes] #gen_log:log D "mapped columns: $mapped_columns" if {[lsearch -exact $mapped_columns "$w.statcol"] > -1} { set leftcol "$w.statcol" } else { set leftcol "$w.filecol" } if {$column == "datecol"} { $w.pw add $w.$column -after $leftcol -minsize 50 gen_log:log D "ADD $w.$column" } elseif {$column == "statcol"} { $w.pw add $w.$column -after $w.filecol -minsize 50 gen_log:log D "ADD $w.$column" } elseif {$column == "editcol"} { $w.pw add $w.$column -after $w.wrevcol -minsize 50 gen_log:log D "ADD $w.$column" } else { $w.pw add $w.$column -minsize 50 gen_log:log D "ADD $w.$column" } pack $w.$column.head -side top -fill x -expand no pack $w.$column.head.sbut -side right pack $w.$column.head.lbl -side right -fill x -expand yes pack $w.$column.list -side top -fill both -ipadx 2 -expand yes set winwid [winfo width $w] gen_log:log D "WIDTH $winwid" set mapped_columns [$w.pw panes] set num_columns [llength $mapped_columns] gen_log:log D "mapped_columns: $mapped_columns" set newwid [expr {$winwid / $num_columns}] update idletasks for {set i 0} { $i < [expr {$num_columns - 1}] } {incr i} { set coords [$w.pw sash coord $i] $w.pw sash place $i [expr {($i+1) * $newwid}] [lindex $coords 1] #gen_log:log D "$column: moving sash $i from $coords to [expr {($i+1) * $newwid}] [lindex $coords 1]" } update idletasks gen_log:log T "LEAVE" } proc DirCanvas:unmap_column {w column} { gen_log:log T "ENTER ($w $column)" $w.pw forget $w.$column set winwid [winfo width $w] #gen_log:log D "WIDTH $winwid" set mapped_columns [$w.pw panes] set num_columns [llength $mapped_columns] gen_log:log D "mapped_columns: $mapped_columns" set newwid [expr {$winwid /$num_columns}] for {set i 0} { $i < [expr {$num_columns - 1}] } {incr i} { set coords [$w.pw sash coord $i] $w.pw sash place $i [expr {($i+1) * $newwid}] [lindex $coords 1] gen_log:log D "$column: moving sash $i from $coords to [expr {($i+1) * $newwid}] [lindex $coords 1]" } gen_log:log T "LEAVE" } # # Insert a new element $v into the list $w. # proc DirCanvas:newitem {w f} { global DirList global Filelist global cvsglb #gen_log:log T "ENTER ($w $f)" set DirList($w:$f:name) $f #gen_log:log D "Newitem $f status $Filelist($f:status)" set DirList($w:$f:status) $Filelist($f:status) set DirList($w:$f:date) $Filelist($f:date) set DirList($w:$f:sticky) $Filelist($f:stickytag) set DirList($w:$f:option) $Filelist($f:option) #gen_log:log D "Newitem $f option $Filelist($f:option)" # Why did I do this? #set DirList($w:$f:option) "" if { [info exists Filelist($f:editors)]} { set DirList($w:$f:editors) $Filelist($f:editors) } else { set DirList($w:$f:editors) "" } set DirList($w:$f:selected) 0 DirCanvas:buildwhenidle $w #gen_log:log T "LEAVE" } proc DirCanvas:loadimages { } { global cvscfg image create photo paper \ -format gif -file [file join $cvscfg(bitmapdir) paper.gif] image create photo cvsdir \ -format gif -file [file join $cvscfg(bitmapdir) cvsdir.gif] image create photo svndir \ -format gif -file [file join $cvscfg(bitmapdir) svndir.gif] image create photo rcsdir \ -format gif -file [file join $cvscfg(bitmapdir) rcsdir.gif] image create photo folder \ -format gif -file [file join $cvscfg(bitmapdir) folder.gif] image create photo dir_ok \ -format gif -file [file join $cvscfg(bitmapdir) dir_ok.gif] image create photo dir_ood \ -format gif -file [file join $cvscfg(bitmapdir) dir_ood.gif] image create photo dir_ques \ -format gif -file [file join $cvscfg(bitmapdir) dir_ques.gif] image create photo dir_plus \ -format gif -file [file join $cvscfg(bitmapdir) dir_plus.gif] image create photo dir_minus \ -format gif -file [file join $cvscfg(bitmapdir) dir_minus.gif] image create photo link \ -format gif -file [file join $cvscfg(bitmapdir) link.gif] image create photo link_ok \ -format gif -file [file join $cvscfg(bitmapdir) link_ok.gif] image create photo link_okml \ -format gif -file [file join $cvscfg(bitmapdir) link_okml.gif] image create photo link_okol \ -format gif -file [file join $cvscfg(bitmapdir) link_okol.gif] image create photo link_mod \ -format gif -file [file join $cvscfg(bitmapdir) link_mod.gif] image create photo link_modml \ -format gif -file [file join $cvscfg(bitmapdir) link_modml.gif] image create photo link_modol \ -format gif -file [file join $cvscfg(bitmapdir) link_modol.gif] image create photo link_ques \ -format gif -file [file join $cvscfg(bitmapdir) link_ques.gif] image create photo link_plus \ -format gif -file [file join $cvscfg(bitmapdir) link_plus.gif] image create photo stat_ques \ -format gif -file [file join $cvscfg(bitmapdir) stat_ques.gif] image create photo stat_ex \ -format gif -file [file join $cvscfg(bitmapdir) stat_ex.gif] image create photo stat_kb \ -format gif -file [file join $cvscfg(bitmapdir) stat_kb.gif] image create photo stat_plus_kb \ -format gif -file [file join $cvscfg(bitmapdir) stat_plus_kb.gif] image create photo stat_ok \ -format gif -file [file join $cvscfg(bitmapdir) stat_ok.gif] image create photo stat_ood \ -format gif -file [file join $cvscfg(bitmapdir) stat_ood.gif] image create photo stat_okml \ -format gif -file [file join $cvscfg(bitmapdir) stat_okml.gif] image create photo stat_okol \ -format gif -file [file join $cvscfg(bitmapdir) stat_okol.gif] image create photo stat_merge \ -format gif -file [file join $cvscfg(bitmapdir) stat_merge.gif] image create photo stat_mod \ -format gif -file [file join $cvscfg(bitmapdir) stat_mod.gif] image create photo stat_modml \ -format gif -file [file join $cvscfg(bitmapdir) stat_modml.gif] image create photo stat_modol \ -format gif -file [file join $cvscfg(bitmapdir) stat_modol.gif] image create photo stat_plus \ -format gif -file [file join $cvscfg(bitmapdir) stat_plus.gif] image create photo stat_minus \ -format gif -file [file join $cvscfg(bitmapdir) stat_minus.gif] image create photo stat_conf \ -format gif -file [file join $cvscfg(bitmapdir) stat_conf.gif] } # # Delete element $v from the list $w. # deleted. # proc DirCanvas:delitem {w v} { gen_log:log T "ENTER ($w $v)" DirCanvas:buildwhenidle $w gen_log:log T "LEAVE" } proc DirCanvas:deltree {w} { global DirList foreach column {filecol statcol datecol wrevcol editcol} { catch {destroy $w.$column} } catch {destroy $w.yscroll} foreach t [array names DirList $w:*] { unset DirList($t) } } proc DirCanvas:flash {w y} { global cvscfg $w.filecol.list itemconfigure $w.filecol.list.tx$y -font $cvscfg(flashfont) foreach column [lrange [$w.pw panes] 1 end] { set i [$column.list find withtag $w.filecol.list.tx$y] $column.list itemconfigure $i -font $cvscfg(flashfont) } } proc DirCanvas:unflash {w y} { global cvscfg $w.filecol.list itemconfigure $w.filecol.list.tx$y -font $cvscfg(listboxfont) foreach column [lrange [$w.pw panes] 1 end] { set i [$column.list find withtag $w.filecol.list.tx$y] $column.list itemconfigure $i -font $cvscfg(listboxfont) } } # # Change the selection to the indicated item # proc DirCanvas:setselection {w y f} { global DirList global cvsglb gen_log:log T "ENTER ($w $y $f)" DirCanvas:unselectall $w 1 gen_log:log D "adding \"$f\"" set DirList($w:$f:selected) 1 set DirList($w:selection) [list "$f"] set cvsglb(current_selection) $DirList($w:selection) DirCanvas:setTextHBox $w $w.filecol.list.tx$y gen_log:log T "LEAVE" } proc DirCanvas:addselection {w y f} { global DirList global cvsglb gen_log:log T "ENTER ($w $y $f)" regsub -all {\%\%} $f {%} fn regsub -all {\\\$} $fn {$} fn # If it's already selected, unselect it if { $DirList($w:$fn:selected) } { gen_log:log D "\"$fn\" was selected - unselecting" set DirList($w:$fn:selected) 0 set idx [lsearch -exact $DirList($w:selection) "$fn"] if {$idx > -1} { gen_log:log D "found \"$fn\" - removing from selection list" set DirList($w:selection) [lreplace $DirList($w:selection) $idx $idx] gen_log:log D "$DirList($w:selection)" DirCanvas:clearTextHBox $w $w.filecol.list.tx$y } } else { gen_log:log D "adding \"$fn\"" DirCanvas:setTextHBox $w $w.filecol.list.tx$y set DirList($w:$fn:selected) 1 lappend DirList($w:selection) "$fn" } set cvsglb(current_selection) $DirList($w:selection) gen_log:log D "selection is \"$cvsglb(current_selection)\"" gen_log:log T "LEAVE" } # clear any text highlight box (used by set/clearselection) proc DirCanvas:clearTextHBox {w id} { global cvsglb # clear the tag corresponding to the text label foreach column [$w.pw panes] { catch {$column.list delete HBox$id} $column.list itemconfigure $id -fill $cvsglb(fg) } } # set a text highligh box (used by set/clearselection) proc DirCanvas:setTextHBox {w id} { global cvsglb # get the bounding box for the text id set bbox [$w.filecol.list bbox $id] if {[llength $bbox] != 4} { return } set lx [lindex $bbox 0] #set uy [lindex $bbox 1] set ly [lindex $bbox 3] set ly [expr {$ly +1}] set uy [expr {$ly -16}] set i [eval $w.filecol.list create rectangle \ $lx $ly [winfo width $w.filecol] $uy \ -fill $cvsglb(hlbg) -tag HBox$id -outline \"\"] $w.filecol.list itemconfigure $id -fill $cvsglb(hlfg) $w.filecol.list lower $i foreach column [lrange [$w.pw panes] 1 end] { # create rectangle with fill, tagged with the same ID as the text, # so we can delete it later set i [eval $column.list create rectangle \ 0 $ly [winfo width $column] $uy \ -fill $cvsglb(hlbg) -tag HBox$id -outline \"\"] $column.list itemconfigure $id -fill $cvsglb(hlfg) $column.list lower $i } } proc DirCanvas:addrange {w y f} { global DirList global cvsglb gen_log:log T "ENTER ($w $y $f)" if {! [info exists DirList($w:selection)] || [llength $DirList($w:selection)] < 1} { DirCanvas:clearTextHBox $w $w.filecol.list.tx$y set DirList($w:$f:selected) 1 lappend DirList($w:selection) "$f" set cvsglb(current_selection) $DirList($w:selection) return } set sel1 [lindex $DirList($w:selection) 0] set iy $DirList($w:$sel1:y) gen_log:log D "Selection 1 : $sel1 y=$iy" gen_log:log D "New Selection: $f y=$y\n" if { $y > $iy } { foreach item [array names DirList $w:*:name] { set j $DirList($item) set jy $DirList($w:$j:y) if { $jy > $iy && $y > $jy} { gen_log:log D "$j y=$jy" DirCanvas:setTextHBox $w $w.filecol.list.tx$jy set DirList($w:$j:selected) 1 if {[lsearch -exact $DirList($w:selection) $j] == -1} { lappend DirList($w:selection) "$j" } } } } elseif {$y < $iy } { foreach item [array names DirList $w:*:name] { set j $DirList($item) set jy $DirList($w:$j:y) if { $jy < $iy && $y < $jy} { gen_log:log D "$j y=$jy" DirCanvas:setTextHBox $w $w.filecol.list.tx$jy set DirList($w:$j:selected) 1 if {[lsearch -exact $DirList($w:selection) $j] == -1} { lappend DirList($w:selection) "$j" } } } } DirCanvas:setTextHBox $w $w.filecol.list.tx$y set DirList($w:$f:selected) 1 if {[lsearch -exact $DirList($w:selection) $f] == -1} { lappend DirList($w:selection) "$f" } set cvsglb(current_selection) $DirList($w:selection) gen_log:log D "selection is \"$cvsglb(current_selection)\"" gen_log:log T "LEAVE" } proc DirCanvas:unselectall {w force} { global DirList global cvsglb gen_log:log T "ENTER ($w)" # Don't clear unless we aren't over anything if { $force || [ $w.filecol.list gettags current ] == "" } { foreach s [array names DirList $w:*:name] { set f $DirList($s) set y $DirList($w:$f:y) set DirList($w:$f:selected) 0 DirCanvas:clearTextHBox $w $w.filecol.list.tx$y } set DirList($w:selection) {} set cvsglb(current_selection) {} } gen_log:log T "LEAVE" } # Internal use only. # Draw the files on the canvas proc DirCanvas:build {w} { global DirList global Filelist global cvscfg global cvsglb global incvs global insvn global inrcs gen_log:log T "ENTER ($w)" foreach b [winfo children $w.filecol.list] { destroy $b } foreach column [$w.pw panes] { $column.list delete all } catch {unset DirList($w:buildpending)} set x 3 set lblx 21 set y 20 set imy [expr {[image height paper] + 2}] set fy [font metrics $cvscfg(listboxfont) -displayof $w.filecol.list -linespace] set fy [expr {$fy + 2}] if {$imy > $fy} { set yincr $imy gen_log:log D "Y spacing: $y set from icon" } else { set yincr $fy gen_log:log D "Y spacing: $y set from font" } set maxlbl 0; set longlbl "" set maxstat 0; set longstat "" set maxdate 0; set longdate "" set maxtag 0; set longtag "" set maxed 0; set longed "" set sortcol [lindex $cvsglb(sort_pref) 0] set sortsense [lindex $cvsglb(sort_pref) 1] if { (!($incvs || $inrcs || $insvn)) && ( $sortcol == "editcol" || $sortcol == "wrevcol") } { gen_log:log T "setting sort to column \"filecol!\"" set sortcol "filecol" set sortsense "-decreasing" } set rtype "" if {$inrcs} { set rtype "RCS" } elseif {$incvs} { set rtype "CVS" } elseif {$insvn} { set rtype "SVN" } gen_log:log D "Directory Type: $rtype" gen_log:log D "sortcol=$sortcol sortsense=$sortsense" set AllColumns {} foreach k [array names DirList $w:*:name] { set key $DirList($k) set DirList($w:$k:allcolumns) [list $key $DirList($w:$key:status) \ $DirList($w:$key:date) $DirList($w:$key:sticky) $DirList($w:$key:editors)] lappend AllColumns $DirList($w:$k:allcolumns) } set itemlist {} # Always sort by name first set sortedlist [lsort $sortsense -index 0 $AllColumns] switch -- $sortcol { "filecol" { # Only by name } "statcol" { set sortedlist [lsort $sortsense -index 1 $sortedlist] } "datecol" { set sortedlist [lsort $sortsense -index 2 $sortedlist] } "wrevcol" { set sortedlist [lsort -dictionary $sortsense -index 3 $sortedlist] } "editcol" { set sortedlist [lsort $sortsense -index 4 $sortedlist] } } # Create items. foreach item $sortedlist { set f [lindex $item 0] set flen [string length $f] if {$flen > $maxlbl} { set maxlbl $flen set longlbl $f } incr y -$yincr set lblfg $cvsglb(fg) # Up-to-date # The file is identical with the latest revision in the repository for the # branch in use # Locally Modified # You have edited the file, and not yet committed your changes. # Locally Added # You have added the file with add, and not yet committed your changes. # Locally Removed # You have removed the file with remove, and not yet committed your changes # Needs Checkout # Someone else has committed a newer revision to the repository. The name # is slightly misleading; you will ordinarily use update rather than # checkout to get that newer revision. # Needs Patch # Like Needs Checkout, but the CVS server will send a patch rather than the # entire file. Sending a patch or sending an entire file accomplishes # the same thing. # Needs Merge # Someone else has committed a newer revision to the repository, and you # have also made modifications to the file. # Unresolved Conflict # This is like Locally Modified, except that a previous update command gave # a conflict. You need to resolve the conflict as described in section # Conflicts example. # Unknown # CVS doesn't know anything about this file. For example, you have created # a new file and have not run add. switch -glob -- $DirList($w:$f:status) { "" { set DirList($w:$f:icon) paper set DirList($w:$f:popup) paper_pop } "

" { set DirList($w:$f:icon) folder set DirList($w:$f:popup) svnfolder_pop } " Up-to-date" { set DirList($w:$f:icon) dir_ok set DirList($w:$f:popup) svnfolder_pop } " Not managed*" { set DirList($w:$f:icon) dir_ques set DirList($w:$f:popup) svnfolder_pop } " Locally Added" { set DirList($w:$f:icon) dir_plus set DirList($w:$f:popup) svnfolder_pop } " Locally Removed" { set DirList($w:$f:icon) dir_minus set DirList($w:$f:popup) svnfolder_pop } " " { set DirList($w:$f:icon) link set DirList($w:$f:popup) paper_pop } " Not managed by SVN" { set DirList($w:$f:icon) link_ques set DirList($w:$f:popup) paper_pop } " Up-to-date" { set DirList($w:$f:icon) link_ok set DirList($w:$f:popup) stat_svnok_pop } " Up-to-date/Locked" { set DirList($w:$f:icon) link_okol set DirList($w:$f:popup) stat_svnok_pop } " Up-to-date/HaveLock" { set DirList($w:$f:icon) link_okml set DirList($w:$f:popup) stat_svnok_pop } " Locally Modified" { set DirList($w:$f:icon) link_mod set DirList($w:$f:popup) stat_svnok_pop } " Locally Modified/Locked" { set DirList($w:$f:icon) link_modol set DirList($w:$f:popup) stat_svnok_pop } " Locally Modified/HaveLock" { set DirList($w:$f:icon) link_modml set DirList($w:$f:popup) stat_svnok_pop } " Locally Added" { set DirList($w:$f:icon) link_plus set DirList($w:$f:popup) stat_svnok_pop } "" { set DirList($w:$f:icon) folder switch -- $rtype { "CVS" { set DirList($w:$f:popup) incvs_folder_pop } default { set DirList($w:$f:popup) folder_pop } } } "" { set DirList($w:$f:icon) cvsdir switch -- $rtype { "CVS" { set DirList($w:$f:popup) cvscvs_pop } default { set DirList($w:$f:popup) cvsdir_pop } } } "" { set DirList($w:$f:icon) svndir set DirList($w:$f:popup) svndir_pop } "" { set DirList($w:$f:icon) rcsdir set DirList($w:$f:popup) folder_pop } "Up-to-date" { set DirList($w:$f:icon) stat_ok switch -- $rtype { "CVS" { set DirList($w:$f:popup) stat_cvsok_pop if {[string match "*-kb*" $DirList($w:$f:option)]} { set DirList($w:$f:icon) stat_kb } } "SVN" { set DirList($w:$f:popup) stat_svnok_pop } default { set DirList($w:$f:popup) paper_pop } } } "Up-to-date/HaveLock" { set DirList($w:$f:icon) stat_okml set DirList($w:$f:popup) stat_svnok_pop } "Up-to-date/Locked" { set DirList($w:$f:icon) stat_okol set DirList($w:$f:popup) stat_svnok_pop } "Missing*" { set DirList($w:$f:icon) stat_ex switch -- $rtype { "CVS" { set DirList($w:$f:popup) needsupdate_pop } "SVN" { set DirList($w:$f:popup) stat_svnood_pop } } } "Needs Checkout" { # Prepending ./ to the filename prevents tilde expansion if {[file exists ./$f]} { set DirList($w:$f:icon) stat_ood } else { set DirList($w:$f:icon) stat_ex } set DirList($w:$f:popup) needsupdate_pop } "Needs Patch" { set DirList($w:$f:icon) stat_ood set DirList($w:$f:popup) needsupdate_pop } " Out-of-date" { set DirList($w:$f:icon) dir_ood switch -- $rtype { "CVS" { set DirList($w:$f:popup) needsupdate_pop } "SVN" { set DirList($w:$f:popup) stat_svnood_pop } } } "Out-of-date" { set DirList($w:$f:icon) stat_ood switch -- $rtype { "CVS" { set DirList($w:$f:popup) needsupdate_pop } "SVN" { set DirList($w:$f:popup) stat_svnood_pop } } } "Needs Merge" { set DirList($w:$f:icon) stat_merge set DirList($w:$f:popup) stat_merge_pop } "Locally Modified" { set DirList($w:$f:icon) stat_mod switch -- $rtype { "CVS" { set DirList($w:$f:popup) stat_mod_pop } "SVN" { set DirList($w:$f:popup) stat_svnmod_pop } } } "Locally Modified/HaveLock" { set DirList($w:$f:icon) stat_modml set DirList($w:$f:popup) stat_mod_pop } "Locally Modified/Locked" { set DirList($w:$f:icon) stat_modol set DirList($w:$f:popup) stat_mod_pop } "Locally Added" { set DirList($w:$f:icon) stat_plus switch -- $rtype { "CVS" { set DirList($w:$f:popup) stat_plus_pop if {[string match "*-kb*" $DirList($w:$f:option)]} { set DirList($w:$f:icon) stat_plus_kb } } "SVN" { set DirList($w:$f:popup) stat_svnplus_pop } } } "Locally Removed" { set DirList($w:$f:icon) stat_minus switch -- $rtype { "CVS" { set DirList($w:$f:popup) stat_plus_pop } "SVN" { set DirList($w:$f:popup) stat_svnplus_pop } } } "*onflict*" { set DirList($w:$f:icon) stat_conf set DirList($w:$f:popup) stat_conf_pop } "Not managed*" { set DirList($w:$f:icon) stat_ques set DirList($w:$f:popup) paper_pop } "RCS Up-to-date" { set DirList($w:$f:icon) stat_ok set DirList($w:$f:popup) rcs_pop } "RCS Modified" { set DirList($w:$f:icon) stat_mod set DirList($w:$f:popup) rcs_pop } "RCS Needs Checkout" { set DirList($w:$f:icon) stat_ex set DirList($w:$f:popup) rcs_pop } default { set DirList($w:$f:icon) paper set DirList($w:$f:popup) paper_pop } } # Easy way to unselect everything by clicking in a blank area bind $w.filecol.list <1> "DirCanvas:unselectall $w 0" bind $w.filecol.list " " # For columns except filecol, this breaks selecting the text item. Why?? #bind $w.datecol.list <1> "DirCanvas:unselectall $w 0" # In the bindings, filenames need any single percents replaced with # double to avoid interpretation as an event field regsub -all {\%} $f {%%} fn regsub -all {\$} $fn {\$} fn # Draw the icon set k [$w.filecol.list create image $x $y -image $DirList($w:$f:icon) \ -anchor w -tags [list $y] ] $w.filecol.list bind $k <1> "DirCanvas:setselection $w $y \"$fn\"" $w.filecol.list bind $k "DirCanvas:addrange $w $y \"$fn\"" $w.filecol.list bind $k "DirCanvas:addselection $w $y \"$fn\"" $w.filecol.list bind $k {workdir_edit_file [workdir_list_files]} # Draw the label $w.filecol.list create text $lblx $y -text $f -font $cvscfg(listboxfont) \ -anchor w -tags [list $w.filecol.list.tx$y $y $fn] -fill $lblfg $w.filecol.list bind $w.filecol.list.tx$y <1> "DirCanvas:setselection $w $y \"$fn\"" $w.filecol.list bind $w.filecol.list.tx$y "DirCanvas:addrange $w $y \"$fn\"" $w.filecol.list bind $w.filecol.list.tx$y "DirCanvas:flash $w $y" $w.filecol.list bind $w.filecol.list.tx$y "DirCanvas:unflash $w $y" $w.filecol.list bind $w.filecol.list.tx$y "DirCanvas:addselection $w $y \"$fn\"" $w.filecol.list bind $w.filecol.list.tx$y {workdir_edit_file [workdir_list_files]} $w.filecol.list bind $w.filecol.list.tx$y <2> " DirCanvas:popup $w.filecol.list $y %X %Y \"$fn\"" $w.filecol.list bind $w.filecol.list.tx$y <3> " DirCanvas:popup $w.filecol.list $y %X %Y \"$fn\"" set DirList($w:$f:y) $y set DirList($w.filecol.list:$y) $f set status $DirList($w:$f:status) set k [$w.statcol.list create text 8 $y -text $status \ -font $cvscfg(listboxfont) -fill $cvsglb(fg) -anchor w \ -tags [list $w.filecol.list.tx$y $y $fn]] $w.statcol.list bind $k <1> "DirCanvas:setselection $w $y \"$fn\"" $w.statcol.list bind $k "DirCanvas:addrange $w $y \"$fn\"" $w.statcol.list bind $k "DirCanvas:flash $w $y" $w.statcol.list bind $k "DirCanvas:unflash $w $y" $w.statcol.list bind $k "DirCanvas:addselection $w $y \"$fn\"" set slen [string length $status] if {$slen > $maxstat} { set maxstat $slen set longstat $status } set date $DirList($w:$f:date) set k [$w.datecol.list create text 4 $y -text $date \ -font $cvscfg(listboxfont) -fill $cvsglb(fg) -anchor w \ -tags [list $w.filecol.list.tx$y $y $fn]] $w.datecol.list bind $k <1> "DirCanvas:setselection $w $y \"$fn\"" $w.datecol.list bind $k "DirCanvas:addrange $w $y \"$fn\"" $w.datecol.list bind $k "DirCanvas:flash $w $y" $w.datecol.list bind $k "DirCanvas:unflash $w $y" $w.datecol.list bind $k "DirCanvas:addselection $w $y \"$fn\"" set dlen [string length $date] if {$dlen > $maxdate} { set maxdate $dlen set longdate $date } if {[info exists DirList($w:$f:sticky)]} { set tag $DirList($w:$f:sticky) set k [$w.wrevcol.list create text 4 $y -text $tag \ -font $cvscfg(listboxfont) -fill $cvsglb(fg) -anchor w \ -tags [list $w.filecol.list.tx$y $y $fn]] $w.wrevcol.list bind $k <1> "DirCanvas:setselection $w $y \"$fn\"" $w.wrevcol.list bind $k "DirCanvas:addrange $w $y \"$fn\"" $w.wrevcol.list bind $k "DirCanvas:flash $w $y" $w.wrevcol.list bind $k "DirCanvas:unflash $w $y" $w.wrevcol.list bind $k "DirCanvas:addselection $w $y \"$fn\"" set tlen [string length $tag] if {$tlen > $maxtag} { set maxtag $tlen set longtag $tag } } set editors $DirList($w:$f:editors) set k [$w.editcol.list create text 4 $y -text $editors \ -font $cvscfg(listboxfont) -fill $cvsglb(fg) -anchor w \ -tags [list $w.filecol.list.tx$y $y $fn]] $w.editcol.list bind $k <1> "DirCanvas:setselection $w $y \"$fn\"" $w.editcol.list bind $k "DirCanvas:addrange $w $y \"$fn\"" $w.editcol.list bind $k "DirCanvas:flash $w $y" $w.editcol.list bind $k "DirCanvas:unflash $w $y" $w.editcol.list bind $k "DirCanvas:addselection $w $y \"$fn\"" set edlen [string length $editors] if {$edlen > $maxed} { set maxed $edlen set longed $editors } } # See which optional columns we need to draw if {$incvs || $insvn || $inrcs} { if {$cvscfg(showstatcol)} { DirCanvas:map_column $w statcol } if {$cvscfg(showeditcol)} { DirCanvas:map_column $w editcol } } if {$cvscfg(showdatecol)} { DirCanvas:map_column $w datecol } # Set a minimum width for the labels. Otherwise ".." can be hard to select. set minlabel 6 foreach labl [$w.filecol.list find withtag lbl] { set itags [$w.filecol.list gettags $labl] set iy [lindex $itags 1] if {[string length $DirList($w.filecol.list:$iy)] < $minlabel} { $w.filecol.list.tx$iy configure -width $minlabel } } # Scroll to the top of the lists set fbbox [$w.filecol.list bbox all] gen_log:log D "fbbox \"$fbbox\"" if {[llength $fbbox] == 4} { set ylen [expr {[lindex $fbbox 3] - [lindex $fbbox 1]}] set wview [winfo height $w.filecol.list] $w.yscroll set 0 [expr ($wview * 1.0) / ($ylen * 1.0)] update idletasks $w.filecol.list config -scrollregion $fbbox $w.filecol.list yview moveto 0 if {$cvscfg(showdatecol)} { $w.datecol.list config -scrollregion [$w.datecol.list bbox all] $w.datecol.list yview moveto 0 } if {$incvs || $insvn || $inrcs} { $w.wrevcol.list config -scrollregion [$w.wrevcol.list bbox all] $w.wrevcol.list yview moveto 0 if {$cvscfg(showstatcol)} { $w.statcol.list config -scrollregion [$w.statcol.list bbox all] $w.statcol.list yview moveto 0 } if {$cvscfg(showeditcol)} { $w.editcol.list config -scrollregion [$w.editcol.list bbox all] $w.editcol.list yview moveto 0 } } } #gen_log:log D "[array names DirList $w:*:selected]" gen_log:log T "LEAVE" } # Internal use only # Call DirCanvas:build the next time we're idle proc DirCanvas:buildwhenidle {w} { global DirList if {![info exists DirList($w:buildpending)]} { set DirList($w:buildpending) 1 after idle "DirCanvas:build $w" } } # For restoring the scroll positions after re-scanning the directory proc DirCanvas:yview_windows {w yview} { global cvscfg gen_log:log T "ENTER YVIEW $yview" eval $w.filecol.list yview moveto $yview if {[winfo exists $w.datecol.list]} { eval $w.datecol.list yview moveto $yview } if {[winfo exists $w.revcol.list]} { eval $w.wrevcol.list yview moveto $yview } if {[winfo exists $w.statcol.list]} { eval $w.statcol.list yview moveto $yview } if {[winfo exists $w.editcol.list]} { eval $w.editcol.list yview moveto $yview } } proc DirCanvas:scroll_windows {w args} { global cvscfg global cvsglb global incvs global insvn global inrcs #gen_log:log T "ENTER ($w $args)" set way [lindex $args 1] set units [lindex $args 2] set yget [$w.yscroll get] set first [lindex $yget 0] set last [lindex $yget 1] eval $w.filecol.list yview $args if {$cvscfg(showdatecol)} { eval $w.datecol.list yview $args } if {$incvs || $insvn || $inrcs} { eval $w.wrevcol.list yview $args if {$cvscfg(showstatcol)} { eval $w.statcol.list yview $args } if {$cvscfg(showeditcol)} { eval $w.editcol.list yview $args } } } proc DirCanvas:drag_windows {w W y} { #Scrolling caused by dragging global incvs global insvn global inrcs global cvscfg global cvsglb set height [$W cget -height] #gen_log:log D "$w %y $height" if {$y < 0} {set y 0} if {$y > $height} {set y $height} set yfrac [expr {double($y) / $height}] eval $w.filecol.list yview moveto $yfrac if {$cvscfg(showdatecol)} { eval $w.datecol.list yview moveto $yfrac } if {$incvs || $insvn || $inrcs} { eval $w.wrevcol.list yview moveto $yfrac if {$cvscfg(showstatcol)} { eval $w.statcol.list yview moveto $yfrac } if {$cvscfg(showeditcol)} { eval $w.editcol.list yview moveto $yfrac } } } proc DirCanvas:sort_by_col {w col sense} { global DirList global cvsglb global arr gen_log:log T "ENTER ($w $col $sense)" foreach a [array names arr] { catch "$arr($a) configure -image arr_dn" } set cvsglb(sort_pref) [list $col $sense] if {[string match "-inc*" $sense]} { gen_log:log D "sort column $col -increasing" $arr($col) configure -image arh_up if {$col == "statcol"} {$arr(filestatcol) configure -image arh_up} } else { gen_log:log D "sort column $col -decreasing" $arr($col) configure -image arh_dn if {$col == "statcol"} {$arr(filestatcol) configure -image arh_dn} } if {$col != "statcol"} { $arr(filestatcol) configure -image arr_dn } gen_log:log D " $cvsglb(sort_pref)" DirCanvas:build $w gen_log:log T "LEAVE" } proc DirCanvas:toggle_col {w col} { global cvsglb global cvscfg gen_log:log T "ENTER ($col)" set cur_col [lindex $cvsglb(sort_pref) 0] set cur_sense [lindex $cvsglb(sort_pref) 1] if {$col == $cur_col} { # if it's the currently sorted column, reverse the direction. if {[string match "-incr*" $cur_sense]} { set sense "-decreasing" } else { set sense "-increasing" } } else { # Otherwise, default to decreasing (down) set sense "-decreasing" } gen_log:log D "sort column $col $sense" DirCanvas:sort_by_col $w $col $sense gen_log:log T "LEAVE" } proc DirCanvas:makepopup {w} { # # Context-sensitive popups for list items # We build them all at once here, then bind canvas items to them as appropriate # gen_log:log T "ENTER ($w)" # For plain files in an un-versioned directory menu $w.paper_pop -tearoff 0 $w.paper_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.paper_pop add command -label "Delete Locally" \ -command { workdir_delete_file [workdir_list_files] } # For plain directories in an un-versioned directory menu $w.folder_pop -tearoff 0 $w.folder_pop add command -label "Descend" \ -command { workdir_edit_file [workdir_list_files] } $w.folder_pop add command -label "Delete Locally" \ -command { workdir_delete_file [workdir_list_files] } # For plain directories in CVS menu $w.incvs_folder_pop -tearoff 0 $w.incvs_folder_pop add command -label "Descend" \ -command { workdir_edit_file [workdir_list_files] } $w.incvs_folder_pop add command -label "CVS Add Recursively" \ -command { addir_dialog [workdir_list_files] } $w.incvs_folder_pop add command -label "Delete Locally" \ -command { workdir_delete_file [workdir_list_files] } # For CVS directories when cwd is in CVS menu $w.cvscvs_pop -tearoff 0 $w.cvscvs_pop add command -label "Descend" \ -command { workdir_edit_file [workdir_list_files] } $w.cvscvs_pop add command -label "CVS Remove Recursively" \ -command { subtractdir_dialog [workdir_list_files] } # For CVS directories when cwd isn't in CVS menu $w.cvsdir_pop -tearoff 0 $w.cvsdir_pop add command -label "Descend" \ -command { workdir_edit_file [workdir_list_files] } $w.cvsdir_pop add command -label "CVS Release" \ -command { release_dialog [workdir_list_files] } # For CVS files menu $w.stat_cvsok_pop -tearoff 0 $w.stat_cvsok_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_cvsok_pop add command -label "Browse the Log Diagram" \ -command { cvs_branches [workdir_list_files] } $w.stat_cvsok_pop add command -label "CVS Annotate/Blame" \ -command { cvs_annotate $current_tagname [workdir_list_files] } $w.stat_cvsok_pop add command -label "CVS Remove" \ -command { subtract_dialog [workdir_list_files] } $w.stat_cvsok_pop add command -label "Set Edit Flag" \ -command { cvs_edit [workdir_list_files] } $w.stat_cvsok_pop add command -label "Unset Edit Flag" \ -command { cvs_unedit [workdir_list_files] } $w.stat_cvsok_pop add command -label "Set Binary Flag" \ -command { cvs_binary [workdir_list_files] } $w.stat_cvsok_pop add command -label "Unset Binary Flag" \ -command { cvs_ascii [workdir_list_files] } # For CVS files that are not up-to-date menu $w.needsupdate_pop -tearoff 0 $w.needsupdate_pop add command -label "Update" \ -command { \ cvs_update {BASE} {Normal} {Remove} {recurse} {prune} {No} { } [workdir_list_files] } $w.needsupdate_pop add command -label "Update with Options" \ -command cvs_update_options # For CVS files that need merging menu $w.stat_merge_pop -tearoff 0 $w.stat_merge_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_merge_pop add command -label "Diff" \ -command { comparediff [workdir_list_files] } $w.stat_merge_pop add command -label "CVS Annotate/Blame" \ -command { cvs_annotate $current_tagname [workdir_list_files] } $w.stat_merge_pop add command -label "Browse the Log Diagram" \ -command { cvs_branches [workdir_list_files] } # For CVS files that are modified menu $w.stat_mod_pop -tearoff 0 $w.stat_mod_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_mod_pop add command -label "Diff" \ -command { comparediff [workdir_list_files] } $w.stat_mod_pop add command -label "CVS Commit" \ -command { cvs_commit_dialog } $w.stat_mod_pop add command -label "CVS Revert" \ -command { cvs_revert [workdir_list_files] } # For CVS files that have been added or removed but not commited menu $w.stat_plus_pop -tearoff 0 $w.stat_plus_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_plus_pop add command -label "CVS Commit" \ -command { cvs_commit_dialog } # For CVS files with conflicts menu $w.stat_conf_pop -tearoff 0 $w.stat_conf_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_conf_pop add command -label "Merge Conflict" \ -command { cvs_merge_conflict [workdir_list_files] } $w.stat_conf_pop add command -label "CVS Annotate/Blame" \ -command { cvs_annotate $current_tagname [workdir_list_files] } $w.stat_conf_pop add command -label "Browse the Log Diagram" \ -command { cvs_branches [workdir_list_files] } # For RCS files menu $w.rcs_pop -tearoff 0 $w.rcs_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.rcs_pop add command -label "Browse the Log Diagram" \ -command { rcs_branches [workdir_list_files] } $w.rcs_pop add command -label "RCS Lock" \ -command { rcs_lock lock [workdir_list_files] } $w.rcs_pop add command -label "RCS Unlock" \ -command { rcs_lock unlock [workdir_list_files] } $w.rcs_pop add command -label "Delete Locally" \ -command { workdir_delete_file [workdir_list_files] } $w.rcs_pop add command -label "RCS Revert" \ -command { rcs_revert [workdir_list_files] } # For SVN files menu $w.stat_svnok_pop -tearoff 0 $w.stat_svnok_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_svnok_pop add command -label "SVN Log" \ -command { svn_log [workdir_list_files] } $w.stat_svnok_pop add command -label "SVN Info" \ -command { svn_info [workdir_list_files] } $w.stat_svnok_pop add command -label "Browse the Log Diagram" \ -command { svn_branches [workdir_list_files] } $w.stat_svnok_pop add command -label "SVN Annotate/Blame" \ -command { svn_annotate "" [workdir_list_files] } $w.stat_svnok_pop add command -label "SVN Remove" \ -command { subtract_dialog [workdir_list_files] } # For SVN files that are out of date menu $w.stat_svnood_pop -tearoff 0 $w.stat_svnood_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_svnood_pop add command -label "SVN Update" \ -command { svn_update [workdir_list_files] } # For SVN file added/deleted menu $w.stat_svnplus_pop -tearoff 0 $w.stat_svnplus_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_svnplus_pop add command -label "SVN Commit" \ -command { svn_commit_dialog } $w.stat_svnplus_pop add command -label "SVN Revert" \ -command { svn_revert [workdir_list_files] } # For SVN files that are modified menu $w.stat_svnmod_pop -tearoff 0 $w.stat_svnmod_pop add command -label "Edit" \ -command { workdir_edit_file [workdir_list_files] } $w.stat_svnmod_pop add command -label "Diff" \ -command { comparediff [workdir_list_files] } $w.stat_svnmod_pop add command -label "SVN Commit" \ -command { svn_commit_dialog } $w.stat_svnmod_pop add command -label "SVN Revert" \ -command { svn_revert [workdir_list_files] } # For SVN directories menu $w.svnfolder_pop -tearoff 0 $w.svnfolder_pop add command -label "Descend" \ -command { workdir_edit_file [workdir_list_files] } $w.svnfolder_pop add command -label "SVN Log" \ -command { svn_log [workdir_list_files] } $w.svnfolder_pop add command -label "SVN Info" \ -command { svn_info [workdir_list_files] } $w.svnfolder_pop add command -label "Browse the Log Diagram" \ -command { svn_branches [workdir_list_files] } $w.svnfolder_pop add command -label "SVN Remove" \ -command { subtract_dialog [workdir_list_files] } # For SVN directories menu $w.svndir_pop -tearoff 0 $w.svndir_pop add command -label "Descend" \ -command { workdir_edit_file [workdir_list_files] } $w.svndir_pop add command -label "SVN Log" \ -command { svn_log [workdir_list_files] } $w.svndir_pop add command -label "SVN Info" \ -command { svn_info [workdir_list_files] } gen_log:log T "LEAVE" } proc DirCanvas:popup {w y X Y f} { global DirList gen_log:log T "ENTER ($w $y $X $Y $f)" set parent [winfo parent [winfo parent $w]] DirCanvas:setselection $parent $y $f tk_popup $parent.$DirList($parent:$f:popup) $X $Y gen_log:log T "LEAVE" } proc DirCanvas:destroy {w} { foreach u [winfo children $w] { catch {destroy $u} } } tkcvs-8.2.3.orig/tkcvs/rcs.tcl0000644000175000017500000001573011664612512014420 0ustar timtim # Get the revision log of an RCS file and send it to the # branch-diagram browser. # Disable merge buttons. proc rcs_branches {files} { global cvscfg global cwd gen_log:log T "ENTER ($files)" if {$files == {}} { cvsfail "Please select one or more files!" .workdir return } foreach filename $files { ::cvs_branchlog::new RCS "$filename" } gen_log:log T "LEAVE" } # check out (co) a file. Called from the "update" button proc rcs_checkout {files} { global cvscfg gen_log:log T "ENTER ($files)" if {$files == {}} { cvsfail "Please select one or more files!" .workdir return } set commandline "co -l $files" set v [::viewer::new "RCS Checkout"] $v\::do "$commandline" 1 if {$cvscfg(auto_status)} { $v\::wait setup_dir } gen_log:log T "LEAVE" } proc rcs_lock {do files} { global cvscfg if {$files == {}} { cvsfail "Please select one or more files!" .workdir return } switch -- $do { lock { set commandline "rcs -l $files"} unlock { set commandline "rcs -u $files"} } set rcscmd [::exec::new "$commandline"] if {$cvscfg(auto_status)} { $rcscmd\::wait setup_dir } } # RCS checkin. Have to use terminal, because ci -m won't take # a message with a newline proc rcs_checkin {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [lindex $args 0] update idletasks set commandline "$cvscfg(terminal) ci -u $filelist" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] if {$ret} { cvsfail $view_this .workdir gen_log:log T "LEAVE ERROR ($view_this)" return } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc rcs_commit_dialog {} { # RCS checkin. Have to use terminal, because ci -m won't take # a message with a newline # But some day, investigate this: # % set ms "this has a \ # CR" # % puts $ms # this has a # CR # # % regsub -all {\n} $ms {\n} msg # % puts $msg # this has a\nCR # # puts "this has a\nCR" # this has a # CR } # Get an rcs status for files in working directory, for the dircanvas proc rcs_workdir_status {} { global cvscfg global Filelist gen_log:log T "ENTER" set rcsfiles [glob -nocomplain -- RCS/* RCS/.??* *,v .??*,v] set command "rlog -h $rcsfiles" gen_log:log C "$command" set ret [catch {eval "exec $command"} raw_rcs_log] #gen_log:log D "$raw_rcs_log" # The older version (pre-5.x or something) of RCS is a lot different from # the newer versions, explaining some of the ugliness here set rlog_lines [split $raw_rcs_log "\n"] set lockers "" foreach rlogline $rlog_lines { gen_log:log D "$rlogline" # Found one! if {[string match "*Working file:*" $rlogline]} { regsub {^.*Working file:\s+} $rlogline "" filename regsub {\s*$} $filename "" filename gen_log:log D "RCS file $filename" set Filelist($filename:wrev) "" set Filelist($filename:stickytag) "" set Filelist($filename:option) "" if {[file exists $filename]} { set Filelist($filename:status) "RCS Up-to-date" # Do rcsdiff to see if it's changed #set command "rcsdiff -q \"$filename\" > $cvscfg(null)" set command "rcsdiff \"$filename\"" gen_log:log C "$command" set ret [catch {eval "exec $command"} output] gen_log:log D "$output" set splitline [split $output "\n"] if [string match {====*} [lindex $splitline 0]] { set splitline [lrange $splitline 1 end] } if {[llength $splitline] > 3} { set Filelist($filename:status) "RCS Modified" gen_log:log D "$filename MODIFIED" } } else { set Filelist($filename:status) "RCS Needs Checkout" } set who "" set lockers "" continue } if {[string match "head:*" $rlogline]} { regsub {head:\s+} $rlogline "" revnum set Filelist($filename:wrev) "$revnum" set Filelist($filename:stickytag) "$revnum on trunk" #gen_log:log D " Rev \"$revnum\"" continue } if {[string match "branch:*" $rlogline]} { regsub {branch: *} $rlogline "" revnum if {[string length $revnum] > 0} { set Filelist($filename:wrev) "$revnum" set Filelist($filename:stickytag) "$revnum on branch" #gen_log:log D " Branch rev \"$revnum\"" } continue } if { [string index $rlogline 0] == "\t" } { set splitline [split $rlogline] #gen_log:log D "\"[lindex $splitline 1]\"" #gen_log:log D "\"[lindex $splitline 2]\"" set who [lindex $splitline 1] set who [string trimright $who ":"] #gen_log:log D " who $who" append lockers ",$who" #gen_log:log D " lockers $lockers" } else { if {[string match "access list:*" $rlogline]} { set lockers [string trimleft $lockers ","] set Filelist($filename:editors) $lockers # No more tags after this point continue } } } gen_log:log T "LEAVE" } # for Directory Status Check proc rcs_check {} { global cvscfg gen_log:log T "ENTER" set v [::viewer::new "Directory Status Check"] set rcsfiles [glob -nocomplain -- RCS/* RCS/.??* *,v .??*,v] set command "rlog -h $rcsfiles" gen_log:log C "$command" set ret [catch {eval "exec $command"} raw_rcs_log] #gen_log:log D "$raw_rcs_log" set rlog_lines [split $raw_rcs_log "\n"] foreach rlogline $rlog_lines { if {[string match "Working file:*" $rlogline]} { regsub {Working file: } $rlogline "" filename regsub {\s*$} $filename "" filename gen_log:log D "RCS file $filename" if {[file exists $filename]} { # Do rcsdiff to see if it's changed set command "rcsdiff -q \"$filename\" > $cvscfg(null)" gen_log:log C "$command" set ret [catch {eval "exec $command"}] if {$ret == 1} { $v\::log "\nM $filename" } } else { $v\::log "\nU $filename" } } } gen_log:log T "LEAVE" } # for Log in Reports Menu proc rcs_log {args} { global cvscfg gen_log:log T "ENTER" set filelist [join $args] if {$filelist == ""} { set filelist [glob -nocomplain -dir RCS *,v] } gen_log:log D "detail $cvscfg(ldetail)" gen_log:log D "$filelist" set commandline "rlog " switch -- $cvscfg(ldetail) { latest { append commandline "-r " } summary { append commandline "-t " } } append commandline "$filelist" set logcmd [viewer::new "RCS log ($cvscfg(ldetail))"] $logcmd\::do "$commandline" 0 hilight_rcslog busy_done .workdir.main gen_log:log T "LEAVE" } # Revert a file to checked-in version by removing the local # copy and updating it proc rcs_revert {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] gen_log:log D "Reverting $filelist" gen_log:log F "DELETE $filelist" file delete $filelist set rcscmd [exec::new "co $filelist"] if {$cvscfg(auto_status)} { $rcscmd\::wait setup_dir } gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/tkcvs/filebrowse.tcl0000644000175000017500000001313511664612512015767 0ustar timtim# # Tcl library for TkCVS # # # Sets up a dialog to browse the contents of a module. # proc browse_files {module} { global filenames global modval global checkout_version global cvscfg global cvsglb gen_log:log T "ENTER ($module)" static {browser 0} if {$module == ""} { cvsfail "Please select a module!" .modbrowse return } #gen_log:log D "[array names modval]" if {[lsearch -exact [array names modval] $module] < 0} { cvsfail "$module is not a CVS module" .modbrowse return } # Find the list of file names. find_filenames $module if {! [info exists filenames($module)]} { cvsfail "There are no files in this module!" .modbrowse return } # # Create the browser window. # incr browser set filebrowse ".filebrowse$browser" toplevel $filebrowse frame $filebrowse.top ;#-relief raised -border 2 frame $filebrowse.buttons ;#-relief raised -border 2 frame $filebrowse.srch ;#-relief raised -border 2 pack $filebrowse.top -side top -fill x pack $filebrowse.buttons -side bottom -fill x pack $filebrowse.srch -side bottom -fill x label $filebrowse.top.verlbl -text "Version / Tag " -anchor w entry $filebrowse.top.verent -relief sunken -textvariable checkout_version button $filebrowse.srch.srchbtn -text Search \ -command "search_listbox $filebrowse.list" entry $filebrowse.srch.srchent -width 20 -textvariable cvsglb(searchstr) bind $filebrowse.srch.srchent "search_listbox $filebrowse.list" pack $filebrowse.top.verlbl -side left pack $filebrowse.top.verent -side right -fill x -expand y pack $filebrowse.srch.srchbtn -side left pack $filebrowse.srch.srchent -side right -fill x -expand y # # Create buttons # if {[catch "image type Fileview"]} { workdir_images } button $filebrowse.view -image Fileview \ -command "module_fileview $filebrowse $module" button $filebrowse.log -image Log \ -command "module_filelog $filebrowse $module 0" button $filebrowse.branches -image Branches \ -command "module_filelog $filebrowse $module 1" button $filebrowse.tag -image Tags \ -command "module_tagview $filebrowse $module" button $filebrowse.quit -text "Close" \ -padx 0 -pady 0 \ -command "destroy $filebrowse; exit_cleanup 0" pack $filebrowse.view \ $filebrowse.log \ $filebrowse.branches \ $filebrowse.tag \ -in $filebrowse.buttons -side left -ipadx 1 -ipady 1 -fill x -expand 1 pack $filebrowse.quit \ -in $filebrowse.buttons -side left -ipadx 0 -ipady 0 -fill both -expand 1 set_tooltips $filebrowse.view \ {"View the selected file"} set_tooltips $filebrowse.log \ {"See the revision log of the selected file"} set_tooltips $filebrowse.branches \ {"See the branch diagram of the selected file"} set_tooltips $filebrowse.tag \ {"List the tags of the selected file"} # # Create a scrollbar and a list box. # scrollbar $filebrowse.scroll -relief sunken \ -command "$filebrowse.list yview" listbox $filebrowse.list \ -yscroll "$filebrowse.scroll set" -relief sunken \ -font $cvscfg(listboxfont) \ -width 40 -height 25 -setgrid yes pack $filebrowse.scroll -side right -fill y pack $filebrowse.list -side left -fill both -expand 1 # # Window manager stuff. # wm title $filebrowse "Files in $module" wm minsize $filebrowse 5 5 # # Fill the list. # foreach file $filenames($module) { if {[info exists modval($module)]} { set module $modval($module) } regsub "^$module/" $file "" file $filebrowse.list insert end $file } search_listbox_init gen_log:log T "LEAVE" } proc filepath {module filename} { # Prepend a path to the filename if needed global modval global module_dir global cvscfg global cvs gen_log:log T "ENTER ($filename $module)" regsub -all {\$} $filename {\$} file # set global module variable - logcanvas may need it set commandline \ "$cvs -d $cvscfg(cvsroot) rdiff -s -D 01/01/1971 \"$file\"" gen_log:log C $commandline set ret [catch {eval "exec $commandline"} view_this] gen_log:log D "\"$view_this\"" if {! $ret} { gen_log:log T "LEAVE (fine the way we are) ($file)" return $file } if {[info exists modval($module)]} { gen_log:log D "modval $module \"$modval($module)\"" set module_dir $modval($module) #set file "$module_dir/[file tail $file]" set file "$module_dir/$file" gen_log:log T "LEAVE (prepend modval) ($file)" return $file } set file "$module/$file" gen_log:log T "LEAVE (default) ($file)" return $file } proc module_filelog {toplevelname module {graphic {0}} } { # Open the logbrowser from the file list gen_log:log T "ENTER ($toplevelname $module $graphic)" set listname $toplevelname.list foreach item [$listname curselection] { set v [$listname get $item] set f [filepath $module $v] cvs_filelog "$f" $toplevelname $graphic } gen_log:log T "LEAVE" } proc module_fileview {toplevelname module} { # View a file from the file list gen_log:log T "ENTER ($toplevelname $module)" set listname $toplevelname.list foreach item [$listname curselection] { set v [$listname get $item] set f [filepath $module $v] cvs_fileview_checkout [$toplevelname.top.verent get] "$f" } gen_log:log T "LEAVE" } proc module_tagview {toplevelname module} { # List the tags of a file from the filelist gen_log:log T "ENTER ($toplevelname $module)" set listname $toplevelname.list foreach item [$listname curselection] { set v [$listname get $item] set f [filepath $module $v] view_output::new "$f Tags" [cvs_gettaglist "$f" $toplevelname] } gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/tkcvs/tkcvs_def.tcl0000644000175000017500000002763711664612512015612 0ustar timtim# TkCVS defaults file. # # This file is read by TkCVS on startup. It will be installed # automatically by the "configure" script. # # Defaults in the .tkcvs file in the user's home directory will # over-ride this file. # # Working Directory Browser options # If you want to use "cvs edit" set cvscfg(econtrol) false # If you want to use cvs in locking mode set cvscfg(cvslock) false # If you want to see the status column set cvscfg(showstatcol) true # If you want to see the date column set cvscfg(showdatecol) true # If you want to see the editors/author/lockers column set cvscfg(showeditcol) true # Sort by filename or status (filecol or statcol) set cvscfg(sortcol) filecol # Branch Diagram options # Number of tags in a Subversion repository that's "too many", ie # will take longer to proecess than you're willing to wait. set cvscfg(toomany_tags) 25 # Number of tags you want to see for each revision on the branching # diagram before it says "more..." set cvscfg(tagdepth) 6 # Hilight colours for revision-log boxes set cvscfg(colourA) darkgreen set cvscfg(colourB) brown # Maximum number of places to save in the picklist history set cvscfg(picklist_items) 10 # If you want the module browser to come up on startup instead of the # working-directory browser, uncomment this. #set cvscfg(startwindow) "module" # Colours. "Colors" that is if you are a yanqui who can't spell. # Added support for monochrome machines. -sj if { [winfo depth .] == 1 } { option add *ToolTip.background "white" option add *ToolTip.foreground "black" } # # You can either un-comment these lines or # you can use the Xdefaults method of colouring the windows. # The conditional at the beginning prevents over-writing CDE's # options in case you sometimes use CDE and sometimes not. # #if {![string length [option get . background background]]} { ## These are subtle shades that work well in vanilla X # option add *Canvas.background #c3c3c3 # option add *Menu.background #c3c3c3 # option add *selectColor #ffec8b # option add *Text.background gray92 # option add *Entry.background gray92 # option add *Listbox.background gray92 #} # # To use the Xdefaults method, put lines like the following into # your .Xdefaults or .Xresources file: # # tkcvs*background: SkyBlue2 # tkcvs*activeBackground: green # tkcvs*Button.background: LightSteelBlue # tkcvs*Button.activeBackground: green # tkcvs*Scrollbar.background: LightSteelBlue # tkcvs*Scrollbar.activeBackground: green # # Format of date display in workdir dialog # The default: # # %Y/%m/%d %H:%M:%S - 2000/03/25 14:41:33 # # is useful because it sorts properly. Other possibilities # are: # # %d/%m/%y %I:%M:%S %p - 03/25/00 02:41:33 PM # %d-%b-%y %H:%M:%S - 03-Mar-00 14:41:33 # # Look up "date" in the tcl reference manual for a complete # description of date formats. # #set cvscfg(dateformat) "%Y/%m/%d %H:%M:%S" set cvscfg(dateformat) "%Y/%m/%d %H:%M" # Format for mergeto- and mergefrom- tags. The _BRANCH_ part must be # left as-is, but you can change the prefix and the date format, for # example "mergeto_BRANCH_%d%b%y". The date format must be the same # for both. # CVS rule: a tag must not contain the characters `$,.:;@' #set cvscfg(mergetoformat) "t_BRANCH_%d%b%y_%H-%M" #set cvscfg(mergefromformat) "f_BRANCH_%d%b%y_%H-%M" set cvscfg(mergetoformat) "mergeto_BRANCH_%d%b%y" set cvscfg(mergefromformat) "mergefrom_BRANCH_%d%b%y" set cvscfg(mergetrunkname) "trunk" # The branch browser depends on the convention of having a trunk, branches, and # tags structure to draw the diagram. These variables may give you a little # more flexibility. set cvscfg(svn_trunkdir) "trunk" set cvscfg(svn_branchdir) "branches" set cvscfg(svn_tagdir) "tags" # -------------------- # Revision tree log display configuration. # Font size for tag lists and box contents (+ve = points, -ve = pixels) set logcfg(font_size) -12 # Gaps between revisions in units of the chosen font's line spacing # spcx = x spacing between revisions # spcy = y spacing between revisions # yfudge = max extra y space used to fit branch in rather than moving right # boff = vertical offset for branch placement set logcfg(spcx) 3 set logcfg(spcy) 1 set logcfg(yfudge) 12 set logcfg(boff) 1 # Padding between box outline and box contents in pixels set logcfg(padx) 4 set logcfg(pady) 2 # Space between tag list and box in pixels set logcfg(tspcb) 2 # Line and box outline width in pixels set logcfg(width) 3 # Arrow shape for connecting lines set logcfg(arrowshape) { 6 6.7 3 } # Delay between a user option being changed and the redraw of the # tree taking place. This is to allow the user chance to change # several options at once without the tree being redrawn unecessarily. # It's in milliseconds and something in the 1.5-3 second range is # generally reasonable. set logcfg(draw_delay) 2000 # Scaling options to offer user set logcfg(scaling_options) {50% 0.5 80% 0.8 90% 0.9 100% 1.0 120% 1.2 150% 1.5} # User options for info display set logcfg(update_drawing) 2 set logcfg(scale) 1.0 set logcfg(show_tags) 1 set logcfg(show_merges) 1 set logcfg(show_empty_branches) 1 set logcfg(show_inter_revs) 1 set logcfg(show_root_tags) 1 set logcfg(show_root_rev) 0 set logcfg(show_box_rev) 1 set logcfg(show_box_revwho) 1 set logcfg(show_box_revdate) 1 set logcfg(show_box_revtime) 0 # -------------------- # Platform specific configuration. # # Decide wether you are unlucky and have to run tkcvs on DOS/WIN # some things will be setup in the following # # Please note that you may have to setup a bit more. # if {$tcl_platform(platform) == "windows"} { # file mask for all files set cvscfg(aster) "*.*" # null-device set cvscfg(null) "nul" # Terminal program set cvscfg(terminal) "command /c" # Please don't ask me why you have to set -T on DOS, # experiments say you have! - CJ #set cvs "cvs -T $cvscfg(tmpdir)" set cvs "cvs" set cvscfg(editor) "notepad" # set temp directory set cvscfg(tmpdir) "c:/temp" #set cvscfg(tkdiff) "$TclExe [file join \"[file dirname $ScriptBin] tkdiff.tcl\"]" set cvscfg(tkdiff) "[file join \"[file dirname "$ScriptBin"] tkdiff\"]" set cvscfg(print_cmd) "pr" set cvscfg(shell) "" set cvscfg(allow_abort) "no" } else { set cvscfg(tmpdir) "/tmp" set cvscfg(aster) "*" set cvscfg(null) "/dev/null" # Terminal program set cvscfg(terminal) "xterm -e" # # Other defaults # # Full path to the CVS program if you want to give it, # otherwise the PATH environment variable will be searched. set cvs "cvs" # To override the default editor (setup when tkcvs is configured and # installed) a user can set the cvscfg(editor) variable to the editor # of choice in their .tkcvs file (if they have one). #set cvscfg(editor) "dtpad" set cvscfg(editor) {xterm -e vi} # The file editor to be used may also be identified by pattern-matching the # filename by setting the cvscfg(editors) variable. This contains a series # of string pairs giving the editor-command and string-match-pattern. The # first pattern (see rules for [string match]) which matches the filename # going down the list determines which editor is run. If no patterns match # or the option is not set, the cvscfg(editor) value will be used instead. # - anj@aps.anl.gov #set cvscfg(editors) { # nedit *.html # nedit *.c # bitmap *.xbm # gimp *.xpm # gimp *.gif # {calibredrv -m} *.gds #} set cvscfg(tkdiff) "tkdiff" #set cvscfg(print_cmd) {enscript -Ghr -fCourier8} set cvscfg(print_cmd) "lpr" set cvscfg(allow_abort) "yes" # What do you want to happen when you ask for a shell? set cvscfg(shell) {xterm -name tkcvsxterm -n {TkCVS xterm}} # Some special stuff for MacOSX "native" Tk if {! [catch {set windowingsystem [tk windowingsystem]}] && $windowingsystem == "aqua"} { set cvscfg(editor) /Applications/TextEdit.app/Contents/MacOS/TextEdit # If you invoke vim this way, -psn_ tells it to run in its own window #set cvscfg(editor) {/Applications/Vim.app/Contents/MacOS/Vim -psn} set cvscfg(shell) /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal set cvscfg(tkdiff) "\"/Applications/TkDiff.app/Contents/MacOS/tkdiff\"" } } # # -------------------- # User Menus # # Set any of these strings to a cvs command to add to the User Menu set cvsmenu(Show_My_Checkouts) "history" set cvsmenu(Show_All_Checkouts) "history -a" # Set these to a shell command whose output you want to catch # set usermenu(show_makevars) "gmake -pn | grep '='" # Set these to standalone programs # set execmenu(tkman_cvs) "tkman cvs" # # -------------------- # Other defaults # These can be set and saved from the GUI. # # Set this to 1 to see all files displayed in the directory # browser (including hidden files) by default. set cvscfg(allfiles) false # set the default log file detail to be reported; one of # "latest" latest log message on the current branch # "summary" version number and comment string for all check-ins # "verbose" all logfile detail possible, including symbolic tags set cvscfg(ldetail) "summary" # set the default detail for repository and workdir reports; one of # "terse" report "status" only and only on those files which # are not "up-to-date" # "summary" report the "status" and include "up-to-date" # "verbose" provide the report as it would appear unmodified set cvscfg(rdetail) "summary" # set the default pattern to be used by the filter. Use any valid # pattern that can be used for a pattern for 'ls'. An empty string # is equivalent to the entire directory (minus hidden files); # i.e., ls * set cvscfg(file_filter) "" set cvscfg(ignore_file_filter) "*.a *.o *~" set cvscfg(clean_these) "*.bak *~ .#* *tmp #* *%" # set the default for automatic statusing of a CVS controlled # directory. Automatic updates are done when a directory is # entered and after some operations. set cvscfg(auto_status) true # set the default value for confirmation prompting before performing an # operation over selected files. set cvscfg(confirm_prompt) true # some of the reporting operations could usefully be recursive. Set # the default value here. set cvscfg(recurse) false # Filter out "?" unknown files from CVS Check and CVS Update reports set cvscfg(status_filter) false # Kinds of messages for debugging: # C CVS commands # E stderr from commands # F File creation/deletion # T Function entry/exit tracing # D Debugging" set cvscfg(log_classes) "CEF" # On (1) or off (0) set cvscfg(logging) false # How many trace lines to save. The debugging output can get very large. set cvscfg(trace_savelines) 5000 # In the Repository Browser, if true this will cause the alias modules # to be grouped in one folder. Cleans up clutter if there are a lot of # aliases. If it's false, they will be listed separately at the top # level. set cvscfg(aliasfolder) true # Set colours for tagging cvs output set cvscfg(outputColor,patched) blue3 set cvscfg(outputColor,modified) purple set cvscfg(outputColor,conflict) red set cvscfg(outputColor,updated) darkgoldenrod set cvscfg(outputColor,added) darkgreen set cvscfg(outputColor,removed) maroon set cvscfg(outputColor,warning) orange set cvscfg(outputColor,unknown) gray30 set cvscfg(outputColor,stderr) red4 # Print setup. Removed in v7.1 #set cvscfg(papersize) "A4" #set cvscfg(pointsize) 10 #set cvscfg(headingsize) 13 #set cvscfg(subheadingsize) 11 #set cvscfg(printer) "ps" # # -------------------- # At the very end, look for a file called "site_def" in the installation # directory. That's a good place to define your tagcolours and other # site-specific things. It won't be overwritten by installs like this file is. set tkcvs_path [lrange $auto_path 0 0] if {[file exists [file join $tkcvs_path site_def]]} { source [file join $tkcvs_path site_def] } tkcvs-8.2.3.orig/tkcvs/tooltips.tcl0000644000175000017500000000533711664612512015510 0ustar timtim# # tooltips version 0.1 # Paul Boyer # Science Applications International Corp. # # THINGS I'D LIKE TO DO: # 1. make a widget called "tooltip_button" which does it all # and takes name and helptext as arguments in addition to all # button args # 2. Keep visibility of tooltip always on top # 3. Must be a better way to maintain button presses than rebinding? # Because I don't want to explicitly handle all possible bindings # such as etc # 4. Allow for capability for status window at bottom of a frame # that gets the status of the selected icon ############################## # set_tooltips gets a button's name and the tooltip string as # arguments and creates the proper bindings for entering # and leaving the button proc set_tooltips { widget name } { global cvsglb bind $widget " catch { after 500 { internal_tooltips_PopUp %W $name } } \ cvsglb(tooltip_id) " bind $widget "internal_tooltips_PopDown" bind $widget "internal_tooltips_PopDown" } ############################## # internal_tooltips_PopUp is used to activate the tooltip window proc internal_tooltips_PopUp { wid name } { global cvscfg cvsglb # get rid of other existing tooltips catch { destroy .tooltips_wind } toplevel .tooltips_wind -class ToolTip set size_changed 0 set bg [option get .tooltips_wind background background] set fg [option get .tooltips_wind foreground foreground] # get the cursor position set X [winfo pointerx $wid] set Y [winfo pointery $wid] # add a slight offset to make tooltips fall below cursor set Y [expr { $Y + 20 }] # Now pop up the new widgetLabel wm overrideredirect .tooltips_wind 1 wm geometry .tooltips_wind +$X+$Y label .tooltips_wind.l \ -text $name \ -border 2 \ -relief raised \ -font $cvscfg(listboxfont) \ -background $bg \ -foreground $fg pack .tooltips_wind.l # make invisible wm withdraw .tooltips_wind update idletasks # adjust for bottom of screen if { ($Y + [winfo reqheight .tooltips_wind]) > [winfo screenheight .] } { set Y [expr { $Y - [winfo reqheight .tooltips_wind] - 25 }] set size_changed 1 } # adjust for right border of screen if { ($X + [winfo reqwidth .tooltips_wind]) > [winfo screenwidth .] } { set X [expr { [winfo screenwidth .] - [winfo reqwidth .tooltips_wind] }] set size_changed 1 } # reset position if { $size_changed == 1 } { wm geometry .tooltips_wind +$X+$Y } # make visible wm deiconify .tooltips_wind # make tooltip dissappear after 5 sec set cvsglb(tooltip_id) [after 5000 { internal_tooltips_PopDown }] } proc internal_tooltips_PopDown { } { global cvsglb after cancel $cvsglb(tooltip_id) catch { destroy .tooltips_wind } } tkcvs-8.2.3.orig/tkcvs/joincanvas.tcl0000644000175000017500000006432111664612512015764 0ustar timtim# # Tcl Library for TkCVS # namespace eval joincanvas { variable instance 0 proc new {localfile filelog {current_tagname {}}} { variable instance set my_idx $instance incr instance if {[catch "image type Modules"]} { workdir_images } if {[catch "image type Workdir"]} { modbrowse_images } # # Creates a new log canvas. filelog must be the output of a cvs # log or rlog command. # namespace eval $my_idx { set my_idx [uplevel {concat $my_idx}] set filelog [uplevel {concat $filelog}] variable localfile [uplevel {concat $localfile}] variable current_tagname [uplevel {concat $current_tagname}] global cvscfg global cvsglb global cvs global tcl_platform # Height and width to draw boxes variable cvscanv set cvscanv(boxx) 60 set cvscanv(boxy) 20 set cvscanv(midx) [expr {$cvscanv(boxx) / 2}] set cvscanv(midy) [expr {$cvscanv(boxy) / 2}] set cvscanv(boxmin) 64 # Gaps between boxes set cvscanv(space) [expr {$cvscanv(boxy) + 16}] # Indent at top left of canvas set cvscanv(indx) 5 set cvscanv(indy) 5 # Static type variables used while drawing on the canvas. set cvscanv(xhigh) 0 set cvscanv(yhigh) 0 set cvscanv(xlow) 0 set cvscanv(ylow) 0 variable revlist variable revbranches variable tags variable headrev variable joincanvas set joincanvas ".joincanvas$my_idx" proc parse_cvslog_tags {filelog} { variable joincanvas variable tags variable headrev gen_log:log T "ENTER ($joincanvas ...)" set loglist [split $filelog "\n"] set logstate "rcsfile" foreach logline $loglist { #puts "$logline" switch -exact -- $logstate { "rcsfile" { # Look for the first text line which should give the file name. set fileline [split $logline] if {[lindex $fileline 0] == "RCS"} { set logstate "head" continue } } "head" { set fileline [split $logline] if {[lindex $fileline 0] == "head:"} { set headrev [lindex $fileline 1] set logstate "tags" set taglist "" continue } } "tags" { # Any line with a tab leader is a tag if { [string index $logline 0] == "\t" } { set taglist "$taglist$logline\n" set tagitems [split $logline ":"] set tagrevision [string trim [lindex $tagitems 1]] set tagname [string trim [lindex $tagitems 0]] # Add all the tags to a picklist for our "since" tag ::picklist::used alltags $tagname set parts [split $tagrevision {.}] if {[expr {[llength $parts] & 1}] == 1} { set parts [linsert $parts end-1 {0}] set tagrevision [join $parts {.}] } # But we only want to know the branch tags if { [regexp {\.0\.\d+$} $tagrevision] } { set tagstring [string trim [lindex $tagitems 0]] lappend tags($tagrevision) $tagstring } } else { if {$logline == "description:"} { # No more tags after this point set logstate "searching" continue } if {$logline == "----------------------------"} { # Oops, missed something. set logstate "revision" continue } } } "terminated" { # ignore any further lines continue } } } ::picklist::used alltags "" } proc node {joincanvas rev x y} { global cvscfg variable cvscanv variable tags upvar treelist treelist upvar ylevel ylevel upvar ind ind gen_log:log T "ENTER ($rev $x $y)" $joincanvas.canvas create line \ $x [expr {$y + $cvscanv(boxy)}] \ $x [expr {$y + $cvscanv(space)}] gen_log:log T "LEAVE" } proc rectangle {joincanvas rev x y} { # # Breaks out some of the code from the joincanvas_draw_box procedure. # Work out the width of the text to go in the box first, then draw a # box wide enough. # global cvscfg variable cvscanv variable tags variable current_tagname upvar x xpos gen_log:log T "ENTER ($rev $x $y)" set parts [split $rev "."] set tagtext $tags($rev) gen_log:log D "$tagtext\t$rev" $joincanvas.canvas create text \ [expr {$x + 4}] [expr {$y + 2}] \ -text "$tagtext" \ -anchor nw -fill blue \ -font {Helvetica -12 bold} \ -tags b$rev set tagwidth [font measure {Helvetica -12 bold} \ -displayof $joincanvas.canvas $tagtext] if {$tagwidth < $cvscanv(boxmin)} { set tagwidth $cvscanv(boxmin) } # draw the box set boxid [$joincanvas.canvas create rectangle \ $x $y \ [expr {$x + $tagwidth + 5}] [expr {$y + $cvscanv(boxy)}] \ -width 3 \ -fill gray90 \ -tags [list b$rev rect$rev] \ ] # Drop the fill color below the text so the text isn't hidden $joincanvas.canvas lower $boxid # Bind button-presses to the rectangles. if {$tags($rev) != ""} { $joincanvas.canvas bind b$rev \ [namespace code "select_rectangle $rev $tags($rev)"] } if {"$current_tagname" == "$tagtext"} { you_are_here $rev $tagwidth $x $y } gen_log:log T "LEAVE" } proc unselect_all {} { variable joincanvas set t [$joincanvas.canvas gettags current] if {$t != {} } {return} unselect_rectangle } proc unselect_rectangle {} { variable joincanvas catch {$joincanvas.canvas itemconfigure SelA -fill gray90} $joincanvas.up.rversFrom delete 0 end $joincanvas.canvas dtag SelA } proc select_rectangle {rev tags} { global cvscfg variable joincanvas gen_log:log T "ENTER ($rev $tags)" unselect_rectangle $joincanvas.up.rversFrom delete 0 end $joincanvas.up.rversFrom insert end $tags $joincanvas.canvas addtag SelA withtag rect$rev $joincanvas.canvas itemconfigure SelA -fill $cvscfg(colourA) } proc fillcanvas {filename filelog} { global cvscfg variable joincanvas variable cvscanv variable headrev variable tags variable current_tagname gen_log:log T "ENTER ($filename )" catch {unset tags} # Collect the history from the RCS log $joincanvas.canvas delete all parse_cvslog_tags $filelog # Sort the branch revisions set tagrevlist [lsort -command sortrevs [array names tags]] # Get rid of duplicates set revlist "" foreach t $tagrevlist { if {[lsearch -exact $revlist $t] < 0} { lappend revlist $t } } # Find everybody's parents. Add parent nodes to a new nodelist. # Keep track of everybody's children set treelist "" foreach rev $revlist { gen_log:log D "$rev" # Find its parent set alist [split $rev "."] set alength [llength $alist] set isodd [expr {$alength % 2}] set parent($rev) [join [lrange $alist 0 [expr {$alength - 3}]] "."] #gen_log:log D " parent $parent($rev)" set parentbranch [join [lrange $alist 0 [expr {$alength - 5}]] "."] #gen_log:log D " parentbrancch $parentbranch" set branchnum [lindex $alist [expr {$alength - 4}]] set branchparent [join [list $parentbranch 0 $branchnum] "."] #gen_log:log D " branchparent $branchparent" if {$isodd > 0} { set parent($rev) [join [lrange $alist 0 [expr {$alength - 2}]] "."] #gen_log:log D " parent $parent($rev)" } if {[string length $parentbranch] > 0} { gen_log:log D "set parent parent($rev)" set parent($rev) $branchparent lappend children($branchparent) $rev } else { lappend children($parent($rev)) $rev } # Add to new list of nodes if {[lsearch -exact $revlist $parent($rev)] < 0 && \ [lsearch -exact $treelist $parent($rev)] < 0 } { lappend treelist $parent($rev) gen_log:log D " add parent $parent($rev) of $rev" } } # Do it all over again for the new ones we added foreach rev $treelist { gen_log:log D "new $rev" # Find its parent set alist [split $rev "."] set alength [llength $alist] set isodd [expr {$alength % 2}] set parent($rev) [join [lrange $alist 0 [expr {$alength - 3}]] "."] #gen_log:log D " parent $parent($rev)" set parentbranch [join [lrange $alist 0 [expr {$alength - 5}]] "."] #gen_log:log D " parentbrancch $parentbranch" set branchnum [lindex $alist [expr {$alength - 4}]] set branchparent [join [list $parentbranch 0 $branchnum] "."] #gen_log:log D " branchparent $branchparent" if {$isodd > 0} { set parent($rev) [join [lrange $alist 0 [expr {$alength - 2}]] "."] #gen_log:log D " parent $parent($rev)" } if {[string length $parentbranch] > 0} { gen_log:log D "set parent parent($rev)" set parent($rev) $branchparent lappend children($branchparent) $rev } else { lappend children($parent($rev)) $rev } } set treelist [concat $revlist $treelist] set treelist [lsort -command sortrevs $treelist] # Now prepare to draw the revision tree # Root first set y $cvscanv(space) set px(0) 10 set x [font measure {Helvetica -12 bold} \ -displayof $joincanvas.canvas $cvscfg(mergetrunkname)] set px(1) [expr {$px(0) + $x / 2}] set py(1) [expr {$cvscanv(boxy) - 4}] $joincanvas.canvas create text \ $px(1) $y \ -text "ROOT" \ -anchor n -fill black \ -font {Helvetica -12 bold} # Then the rest foreach rev $treelist { gen_log:log D "$rev" if {[info exists children($rev)]} { foreach r $children($rev) { gen_log:log D "\tparent of $r" } set nchildren($rev) [llength $children($rev)] set kids [array names children $rev.*] foreach kid $kids { set descendents $children($kid) set ndescendents [llength $descendents] gen_log:log D "\tgranchildren: $descendents" incr nchildren($rev) $ndescendents } } else { set nchildren($rev) 0 } gen_log:log D "\t$nchildren($rev) descendents" if {[info exists parent($rev)]} { gen_log:log D "\tchild of $parent($rev)" } set alist [split $rev "."] set alength [llength $alist] # Round up instead of down set ind [expr {($alength +1)/ 2}] set pind [expr {$ind - 1}] if {! [info exists py($ind)]} { gen_log:log D " starting new column $ind" set py($ind) $cvscanv(space) set px($ind) [expr {$px($pind) + $cvscanv(midx) + $cvscanv(space)}] } if {[info exists parent($rev)] && $parent($rev) != ""} { gen_log:log D " this one has a parent in col >=1" if {[info exists ylevel($parent($rev))] && $py($ind) > $ylevel($parent($rev))} { gen_log:log D " jumping to level of parent" set py($ind) $ylevel($parent($rev)) if {$ind > 2} { # Give it a node if its parent isn't in column1 incr ylevel($parent($rev)) -$cvscanv(space) set px($ind) [expr {$px($pind) + $cvscanv(boxx) + $cvscanv(space)}] set py($ind) $ylevel($parent($rev)) node $joincanvas $rev \ [expr {$px($pind) + $cvscanv(midx)}] \ [expr {$py($ind) - 1}] } } else { gen_log:log D " parent not higher" set py($ind) [expr {$py($ind) - $cvscanv(space)}] } set xlevel($rev) [expr {$px($ind) + $cvscanv(midx)}] } else { set py($ind) [expr {$py($ind) - $cvscanv(space)}] gen_log:log D " just stacking it above the last one" set xlevel($rev) $px($ind) } set ylevel($rev) $py($ind) # For column 1, just draw a nondescript node if {$ind == 1} { #node $joincanvas $rev $px($ind) $py($ind) set py($ind) [expr {$py($ind) - ($nchildren($rev) - 1) * $cvscanv(space)}] } else { if {! [info exists tags($rev)]} { set tags($rev) "" } gen_log:log D " tag: $tags($rev)" rectangle $joincanvas $rev $px($ind) $py($ind) # Line linking it to parent if {$ind > 2} { set ly [expr {$ylevel($parent($rev)) + $cvscanv(midy)}] } else { set ly [expr {$py($ind) + $cvscanv(midy)}] } if {![info exists xlevel($parent($rev))]} {set xlevel($parent($rev)) $px([expr $ind-1])} $joincanvas.canvas create line \ $xlevel($parent($rev)) [expr {$ly + 10}] \ [expr {$xlevel($parent($rev)) + 10}] $ly \ $px($ind) [expr {$py($ind) + $cvscanv(midy)}] set py($ind) [expr {$py($ind) - $nchildren($rev) * $cvscanv(space)}] } } set py(1) [expr {$cvscanv(boxy) - 4}] set maxyind 0 foreach i [array names py] { if {$py($i) < $maxyind} { set maxyind $py($i) } } set tags($headrev) $cvscfg(mergetrunkname) gen_log:log D "HEAD $headrev" gen_log:log D "tagtext \"$tags($headrev)\"" # Make a box for top of trunk set ylevel(trunk) [expr {$maxyind - $cvscanv(boxy)}] set tagwidth [font measure {Helvetica -12 bold} \ -displayof $joincanvas.canvas $cvscfg(mergetrunkname)] if {$tagwidth < $cvscanv(boxmin)} { set tagwidth $cvscanv(boxmin) } set boxid [$joincanvas.canvas create rectangle \ [expr {$px(1) - $tagwidth / 2}] $ylevel(trunk) \ [expr {$px(1) + 5 + $tagwidth / 2}] \ [expr {$ylevel(trunk) - $cvscanv(boxy)}] \ -width 3 \ -fill gray90 \ -tags b$headrev] $joincanvas.canvas lower $boxid $joincanvas.canvas create text \ [expr {$px(1) + 2}] [expr {$ylevel(trunk) - 2}] \ -text "$cvscfg(mergetrunkname)" \ -anchor s -justify center -fill blue \ -font {Helvetica -12 bold} \ -tags b$headrev # Bottom then top $joincanvas.canvas create line \ $px(1) [expr {$cvscanv(space) - 4}] \ $px(1) $ylevel(trunk) # Bind button-press $joincanvas.canvas bind b$headrev \ [namespace code "select_rectangle $headrev $cvscfg(mergetrunkname)"] # Clicking in a blank part of the canvas unselects boxes bind $joincanvas.canvas \ [namespace code unselect_all] # You are Here if {$current_tagname == "trunk"} { you_are_here $headrev $tagwidth \ [expr {$px(1) - $tagwidth / 2 }] \ [expr {$ylevel(trunk) - $cvscanv(boxy)}] } # now calculate the bounding box using the canvas bbox function set bbox [$joincanvas.canvas bbox all] set boty [lindex $bbox 1] set topy [lindex $bbox 3] set bheight [expr {$topy - $boty}] set origheight [lindex [$joincanvas.canvas config -height] 4] set screenHeight [winfo vrootheight .] if {$bheight > $screenHeight} { set bheight $screenHeight } if {$bheight > $origheight} { $joincanvas.canvas config -height $bheight } $joincanvas.canvas config -scrollregion $bbox $joincanvas.canvas yview moveto 0 set here [$joincanvas.up.rversTo get] if {$here == ""} { cvsfail "I can't find where I am. Perhaps the working directory isn't at the head of a branch?" $joincanvas } gen_log:log T "LEAVE" } proc you_are_here {rev offset hx hy} { variable cvscanv variable joincanvas variable tags gen_log:log T "ENTER ($rev $offset $hx $hy)" gen_log:log D "tags($rev) $tags($rev)" $joincanvas.canvas create image \ [expr {$hx + $offset + 16}] [expr {$hy + $cvscanv(boxy)}] \ -image Man -anchor s \ -tag you_are_here_icon $joincanvas.canvas create text \ [expr {$hx + $offset + 26}] [expr {$hy + $cvscanv(boxy)}] \ -text "You are\nhere" -anchor sw \ -fill red3 \ -font {Helvetica -10 bold} \ -tag you_are_here_icon # Put the name in the "To" entry and disable it. You can only # merge to where you are. $joincanvas.up.rversTo configure -state normal $joincanvas.up.rversTo delete 0 end $joincanvas.up.rversTo insert end $tags($rev) $joincanvas.up.rversTo configure -state readonly $joincanvas.canvas bind b$rev {} } toplevel $joincanvas wm title $joincanvas "CVS Directory Merge" if {$tcl_platform(platform) != "windows"} { wm iconbitmap $joincanvas @$cvscfg(bitmapdir)/dirbranch.xbm } wm protocol $joincanvas WM_DELETE_WINDOW \ [namespace code {$joincanvas.close invoke}] $joincanvas configure -menu $joincanvas.menubar menu $joincanvas.menubar $joincanvas.menubar add cascade -label "File" \ -menu $joincanvas.menubar.file -underline 0 menu $joincanvas.menubar.file -tearoff 0 $joincanvas.menubar.file add command -label "Close" -underline 0 \ -command [namespace code {$joincanvas.close invoke}] $joincanvas.menubar.file add command -label "Exit" -underline 1 \ -command { exit_cleanup 1 } $joincanvas.menubar add cascade -label "Help" \ -menu $joincanvas.menubar.help -underline 0 menu $joincanvas.menubar.help -tearoff 0 $joincanvas.menubar.help add command -label "Merge Tool" -underline 0 \ -command directory_branch_viewer frame $joincanvas.up -relief groove -border 2 pack $joincanvas.up -side top -fill x button $joincanvas.up.bworkdir -image Workdir \ -command { workdir_setup } button $joincanvas.up.bmodbrowse -image Modules_cvs \ -command { modbrowse_run cvs } label $joincanvas.up.lfname -text "Representative File" -anchor w entry $joincanvas.up.rfname -textvariable [namespace current]::repfile bind $joincanvas.up.rfname \ [namespace code {join_getlog $repfile [namespace current]}] label $joincanvas.up.lversFrom -text "Merge From" -anchor w frame $joincanvas.up.eFrom -bg $cvscfg(colourA) entry $joincanvas.up.rversFrom label $joincanvas.up.lversSince -text " Since" -anchor w frame $joincanvas.up.eSince -bg $cvscfg(colourB) ::picklist::clear alltags ::picklist::entry $joincanvas.up.rversSince "" alltags label $joincanvas.up.lversTo -text "Merge To" -anchor w entry $joincanvas.up.rversTo -relief groove \ -bd 1 -relief sunk -state readonly -readonlybackground $cvsglb(bg) grid columnconf $joincanvas.up 1 -weight 1 grid rowconf $joincanvas.up 3 -weight 1 grid $joincanvas.up.lfname -column 0 -row 0 -sticky w grid $joincanvas.up.rfname -column 1 -row 0 -padx 3 -sticky ew grid $joincanvas.up.bworkdir -column 2 -row 0 -rowspan 2 \ -sticky e -padx 2 -pady 1 grid $joincanvas.up.lversFrom -column 0 -row 1 -sticky w grid $joincanvas.up.eFrom -column 1 -row 1 -sticky ew -padx 4 grid $joincanvas.up.bmodbrowse -column 2 -row 2 -rowspan 2 \ -sticky e -padx 2 -pady 1 grid $joincanvas.up.lversSince -column 0 -row 2 -sticky w grid $joincanvas.up.eSince -column 1 -row 2 -sticky ew -padx 4 grid $joincanvas.up.lversTo -column 0 -row 3 -sticky w grid $joincanvas.up.rversTo -column 1 -row 3 -padx 3 -sticky ew pack $joincanvas.up.rversFrom -in $joincanvas.up.eFrom \ -padx 2 -pady 2 -fill x pack $joincanvas.up.rversSince -in $joincanvas.up.eSince \ -padx 2 -pady 2 -fill x set textfont [$joincanvas.up.rfname cget -font] # Pack the bottom before the middle so it doesnt disappear if # the window is resized smaller frame $joincanvas.down -relief groove -border 2 pack $joincanvas.down -side bottom -fill x set repfile $localfile # The canvas for the big picture canvas $joincanvas.canvas -relief sunken -border 2 \ -yscrollcommand "$joincanvas.yscroll set" \ -xscrollcommand "$joincanvas.xscroll set" scrollbar $joincanvas.xscroll -relief sunken -orient horizontal \ -command "$joincanvas.canvas xview" scrollbar $joincanvas.yscroll -relief sunken \ -command "$joincanvas.canvas yview" # # Create buttons # button $joincanvas.delta -image Mergediff \ -command [namespace code { set fromrev [$joincanvas.up.rversFrom get] if {$fromrev == ""} { cvsfail "Please select a branch!" $joincanvas; return } set sincerev [$joincanvas.up.rversSince.e get] cvs_merge $joincanvas $fromrev $sincerev $fromrev . }] button $joincanvas.down.blogfile -image Branches \ -command "cvs_branches $repfile" frame $joincanvas.down.btnfm frame $joincanvas.down.closefm -relief groove -bd 2 button $joincanvas.close -text "Close" \ -command [namespace code " destroy $joincanvas namespace delete [namespace current] exit_cleanup 0 "] pack $joincanvas.down.blogfile -side left \ -ipadx 4 -ipady 4 pack $joincanvas.down.btnfm -side left -fill y -expand 1 pack $joincanvas.delta \ -in $joincanvas.down.btnfm -side left \ -ipadx 4 -ipady 4 pack $joincanvas.down.closefm -side right pack $joincanvas.close \ -in $joincanvas.down.closefm -side right \ -fill both -expand 1 set_tooltips $joincanvas.down.blogfile \ {"Revision Log and Branch Diagram of the current file"} set_tooltips $joincanvas.delta \ {"Merge to current"} set_tooltips $joincanvas.up.bworkdir \ {"Open the Working Directory Browser"} set_tooltips $joincanvas.up.bmodbrowse \ {"Open the Repository Browser"} # # Put the canvas on to the display. # pack $joincanvas.xscroll -side bottom -fill x -padx 1 -pady 1 pack $joincanvas.yscroll -side right -fill y -padx 1 -pady 1 pack $joincanvas.canvas -fill both -expand 1 $joincanvas.canvas delete all # # Window manager stuff. # wm minsize $joincanvas 1 1 scrollbindings Canvas focus $joincanvas.canvas fillcanvas $localfile $filelog return [namespace current] } } } proc cvs_joincanvas { } { # Find the bushiest file in the directory and diagram it global cvs global incvs global cvscfg global current_tagname gen_log:log T "ENTER" if {! $incvs} { cvs_notincvs return 1 } set files [glob -nocomplain -types f -- .??* *] regsub -all {\$} $files {\$} files set commandline "$cvs -d $cvscfg(cvsroot) log $files" gen_log:log C "$commandline" catch {eval "exec $commandline"} raw_log set log_lines [split $raw_log "\n"] gen_log:log D "Directory tag: $current_tagname" foreach logline $log_lines { if {[string match "Working file:*" $logline]} { set filename [lrange [split $logline] 2 end] set nbranches($filename) 0 continue } if {[string match "total revisions:*" $logline]} { set nrevs($filename) [lindex [split $logline] end] continue } if { [regexp {^\t[-\w]+: .*\.0\.\d+$} $logline] } { incr nbranches($filename) } } set bushiestfile "" set mostrevisedfile "" set nbrmax 0 foreach br [array names nbranches] { if {$nbranches($br) > $nbrmax} { set bushiestfile $br set nbrmax $nbranches($br) } } set nrevmax 0 foreach br [array names nrevs] { if {$nrevs($br) > $nrevmax} { set mostrevisedfile $br set nrevmax $nrevs($br) } } gen_log:log F "Bushiest file \"$bushiestfile\" has $nbrmax branches" gen_log:log F "Most Revised file \"$mostrevisedfile\" has $nrevmax revisions" # Sometimes we don't find a file with any branches at all, so bushiest # is empty. Fall back to mostrevised. All files have at least one rev. if {[string length $bushiestfile] > 0} { join_getlog $bushiestfile } else { join_getlog $mostrevisedfile } gen_log:log T "LEAVE" } # Get the file log. Make a new canvas or re-draw an existing one. proc join_getlog {filename {name_idx {}}} { global cvscfg global cvs global current_tagname gen_log:log T "ENTER ($filename $name_idx)" set commandline "$cvs -d $cvscfg(cvsroot) log \"$filename\"" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] # If you bail, sometimes you discard a perfectly good log #if {$ret} { #cvsfail $view_this #gen_log:log T "LEAVE ERROR ($view_this)" #return #} if {$name_idx == ""} { joincanvas::new $filename $view_this $current_tagname } else { $name_idx\::fillcanvas $filename $view_this } gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/tkcvs/import2.tcl0000644000175000017500000002260311664612512015222 0ustar timtim# # Tcl Library for TkCVS # # # import2.tcl is similar to import1.tcl except that it is used for # importing to an existing module. # By: Eugene Lee, Aerospace Corporation, 10/16/03 # proc import2_run {} { # Called from "Import To An Existing Module" global cwd global incvs global cvsglb gen_log:log T "ENTER" cvsroot_check [pwd] if {[winfo exists .import2]} { wm deiconify .import2 raise .import2 grab set .import2 gen_log:log T "LEAVE" return } toplevel .import2 grab set .import2 frame .import2.top message .import2.top.explain -justify left -width 500 -relief groove \ -text "This will import the current directory and its sub-directories\ into an Existing CVS module." label .import2.top.lnewcode -text "Module Name" -anchor w label .import2.top.lnewdir -text "Module path relative to \$CVSROOT" -anchor w label .import2.top.lnewdesc -text "Descriptive Title" -anchor w label .import2.top.lnewvers -text "Version Number" -anchor w # Give it a default set cvsglb(existmodule) "" set cvsglb(newdir) "" set cvsglb(newdesc) "" set cvsglb(newvers) "" # label .import2.top.tnewcode -textvariable cvsglb(existmodule) -relief sunken -width 40 -anchor w # label .import2.top.tnewdir -textvariable cvsglb(newdir) -relief sunken -width 40 -anchor w label .import2.top.tnewcode -textvariable cvsglb(existmodule) -relief sunken -width 40 -anchor w label .import2.top.tnewdir -textvariable cvsglb(newdir) -relief sunken -width 40 -anchor w # entry .import2.top.tnewdesc -textvariable cvsglb(newdesc) -width 40 entry .import2.top.tnewvers -textvariable cvsglb(newvers) -width 40 button .import2.top.bnewcode -text "Browse ..." \ -command "moduleDialog" grid .import2.top.explain -column 0 -row 0 -columnspan 3 -sticky ew grid .import2.top.lnewcode -column 0 -row 1 -sticky w grid .import2.top.tnewcode -column 1 -row 1 -sticky w grid .import2.top.bnewcode -column 2 -row 1 -sticky e grid .import2.top.lnewdir -column 0 -row 2 -sticky w grid .import2.top.tnewdir -column 1 -row 2 -sticky w # grid .import2.top.lnewdesc -column 0 -row 3 -sticky w # grid .import2.top.tnewdesc -column 1 -row 3 -sticky ew grid .import2.top.lnewvers -column 0 -row 3 -sticky w grid .import2.top.tnewvers -column 1 -row 3 -sticky ew frame .import2.down -relief groove -border 2 button .import2.down.ok -text "OK" \ -command { grab release .import2 # wm withdraw .import2 catch do_import2 results } button .import2.down.quit -text "Cancel" \ -command { grab release .import2 wm withdraw .import2 } pack .import2.down -side bottom -expand yes -fill x pack .import2.top -side top -expand yes -fill x pack .import2.down.ok -side left -expand yes pack .import2.down.quit -side left -expand yes # Needed for slower framebuffers #tkwait visibility .import2 wm title .import2 "Import To An Existing Module" wm minsize .import2 1 1 gen_log:log T "LEAVE" } proc do_import2 {} { global cvs global cvsglb global cvscfg global cwd global modlist_sorted global modval gen_log:log T "ENTER" set imdir [pwd] # Error checks if { $cvsglb(existmodule) == "" } { cvsok "You must select an existing module from the repository." .import2 raise .import2 grab set .import2 return 1 } if { $cvsglb(newdir) == "" } { cvsok "You must select an existing module from the repository." .import2 raise .import2 grab set .import2 return 1 } if { $cvsglb(newvers) == "" } { cvsok "You must type in a version number." .import2 raise .import2 grab set .import2 return 1 return } wm withdraw .import2; # After no more errors # We may have gotten here before opening the module browser if {! [info exists modlist_sorted]} { modbrowse_run cvs } # See if all apropriate Directories in newdirname exist. CVS import will # create them, but we'll want to make a #D entry. set cvsglb(newdir) [string trimleft $cvsglb(newdir) "/"] set pathname [file dirname $cvsglb(newdir)] set need_Dir 0 if {$pathname != "."} { foreach idx $modlist_sorted { lappend knowndirs [lindex $idx 0] } gen_log:log D "looking for $pathname in known directories ($knowndirs)" if {[lsearch -exact $knowndirs $pathname] == -1} { set need_Dir 1 } } # Make a baseline tag set versions [split $cvsglb(newvers) ".,/ -"] set baseline "baseline-[join $versions {_}]" set v [viewer::new "Import Module"] set commandline "$cvs -d $cvscfg(cvsroot) import -m \"Imported using TkCVS\" \ $cvsglb(newdir) VENDOR $baseline" $v\::log "\nCVS Import\n" $v\::do "$commandline" $v\::wait update # No need to update the modules file. cd ../ gen_log:log F "CD [pwd]" set commandline "$cvs -d $cvscfg(cvsroot) -Q release -d CVSROOT" $v\::do "$commandline" $v\::wait cd $cwd gen_log:log F "CD [pwd]" #modbrowse_run cvs # Now check out the new module cd .. gen_log:log F "CD [pwd]" set ckmoddir $cwd; # save later for use in checking out # We have to move the original stuff entirely out of the way. # Otherwise checkout won't do the whole tree. gen_log:log F "MOVE $imdir $imdir.orig" file rename $imdir $imdir.orig set $cwd $cwd.orig set commandline \ "$cvs -d $cvscfg(cvsroot) checkout -r$baseline \"$cvsglb(existmodule)\"" $v\::log "\nCVS Checkout\n" $v\::do "$commandline" $v\::wait set cwd $imdir.orig # cd to the checked out module. $cwd is the correct directory to cd to # only if the name of the existing module is the same as the directory name # where the source code is in. If the existing module name is different modify # ckmoddir if { $cvsglb(existmodule) != [file tail $ckmoddir] } { set ckmoddir [file join [file dirname $ckmoddir] $cvsglb(existmodule)] } change_dir $ckmoddir #gen_log:log F "CD [pwd]" if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc import_wait { } { # For importing to an existing module # By: Eugene Lee, Aerospace Corporation, 7/10/01 global modbrowse_module global dparent global cvsglb global dcontents global modlist # gen_log:log T "ENTER" # raise .modbrowse # tkwait variable modbrowse_module set modbrowse_module Vendor set importselect $modbrowse_module # Check to see if importselect is an existing module. First, # 1. See if is a module in the root, then # 2. See if is a module that is in a directory instead. # # 1. importselect in root? set dirlist {}; # List of directories (not modules) foreach {dir contents} [array get dcontents] { lappend dirlist $dir } set module_in_root 0 foreach tmp $modlist { set f [split $tmp "\t"] set module [lindex [split $tmp "\t"] 0] if {$importselect == $module} { # Make sure that $importselect is not a directory if {[lsearch -exact $dirlist $importselect] == -1} { incr module_in_root } } } set cvsglb(existmodule) $importselect set cvsglb(newdir) $importselect # 2. importselect in a directory? set module_in_dir 0 foreach {key value} [array get dparent] { # dparent will be of the form: Examples/Vendor Examples ... # key = Examples/Vendor, value = Examples set filetail [file tail $key] if {$filetail == $importselect} { puts "found $filetail" incr module_in_dir set cvsglb(newdir) [file join $value $importselect] break; #eal 10/13/03 } } if { $module_in_root == 0 && $module_in_dir == 0 } { cvsok "$importselect is not an existing module" .import2 set cvsglb(existmodule) "" set cvsglb(newdir) "" raise .import2 return 1 } if { $module_in_root > 0 && $module_in_dir > 0 } { cvsok "Error: $importselect found in more that one module." .import2 set cvsglb(existmodule) "" set cvsglb(newdir) "" raise .import2 return 1 } raise .import2 } proc getExistModDialog { } { global modval global ExModList ExModDirList set ExModList {} set ExModDirList {} foreach {key value} [array get modval] { if { $key != "" } { lappend ExModList $key lappend ExModDirList $value } } } proc moduleDialog { } { global ExModList ExModDirList set w .modDialog grab release .import2 catch {destroy $w} toplevel $w wm title $w "Select An Existing Module" wm minsize $w 28 3 grab set $w frame $w.buttons pack $w.buttons -side bottom -fill x -pady 2m button $w.buttons.ok -text Ok -command { destroy .modDialog raise .import2 grab set .import2 } button $w.buttons.cancel -text Cancel \ -command { grab release .modDialog wm withdraw .modDialog } pack $w.buttons.ok -side left -expand 1 pack $w.buttons.cancel -side left -expand 1 frame $w.frame -borderwidth .5c pack $w.frame -side top -expand yes -fill y scrollbar $w.frame.scroll -command "$w.frame.list yview" listbox $w.frame.list -yscroll "$w.frame.scroll set" -setgrid 1 -height 5 pack $w.frame.scroll -side right -fill y pack $w.frame.list -side left -expand 1 -fill both getExistModDialog set nModule [llength $ExModList] for {set i 0} {$i < $nModule} {incr i} { $w.frame.list insert end [lindex $ExModList $i] } bind $w.frame.list { set cvsglb(existmodule) [%W get [%W nearest %y] ] set tmp [%W get [%W nearest %y] ] set cvsglb(newdir) $tmp set index [lsearch -exact $ExModList $tmp] set cvsglb(newdir) [lindex $ExModDirList $index] } } tkcvs-8.2.3.orig/tkcvs/vendor_merge.tcl0000644000175000017500000004024011664612512016277 0ustar timtim # Tcl Library for TkCVS # # Modifications by Eugene Lee 10/16/03 # 1. .merge window made more robust so user cannot key in incorrect data. # 2. Name of Vendor Module is selectable by user (no longer hardcoded to Vendor). # proc vendor_wait no longer used # 3. Added proc vendorDialog proc merge_run {mcode} { # By: Eugene Lee, Aerospace Corporation, 11/12/95 # Modified by E. Lee 10/16/03 global cvs global cvsglb global modbrowse_module global from_to global sel_to global cwd global module_dir global merge gen_log:log T "ENTER ($mcode)" if {$mcode == ""} { cvsfail "Please select a module!" .modbrowse gen_log:log T "LEAVE" return } if {[winfo exists .merge]} { .merge.right.tcwd configure -textvariable cwd .merge.right.tmodule configure -textvariable modbrowse_module wm deiconify .merge raise .merge #grab set .merge gen_log:log T "LEAVE" return } toplevel .merge #grab set .merge frame .merge.left frame .merge.right frame .merge.vendor -relief groove -border 2 frame .merge.down -relief groove -border 2 pack .merge.down -side bottom -fill x -expand yes pack .merge.vendor -side bottom -fill x -expand yes pack .merge.left -side left pack .merge.right -side right -fill x -expand yes label .merge.left.lcwd -text "Current Directory" -width 16 -anchor w label .merge.left.lmodule -text "Module" -width 16 -anchor w label .merge.right.tcwd -textvariable cwd -relief sunken -width 40 -anchor w label .merge.right.tmodule -textvariable modbrowse_module -relief sunken -width 40 -anchor w pack .merge.left.lcwd -side top -fill x -pady 3 pack .merge.left.lmodule -side top -fill x pack .merge.right.tcwd -side top -fill x -pady 3 pack .merge.right.tmodule -side top -fill x frame .merge.vendor.name pack .merge.vendor.name -side top -fill x -expand yes label .merge.vendor.name.l -text "Vendor Module" -width 16 -anchor w label .merge.vendor.name.e -relief sunken -textvariable venselect_mcode -anchor w button .merge.vendor.name.b -text "Browse ..." \ -command "vendorDialog" pack .merge.vendor.name.l -side left -fill x -pady 3 pack .merge.vendor.name.b -side right -anchor w -fill x pack .merge.vendor.name.e -side right -anchor w -fill x -pady 3 -expand yes #.merge.vendor.name.e config -state disabled bind .merge.vendor.name.e { put_rev_tags $venselect_mcode } frame .merge.vendor.l frame .merge.vendor.r pack .merge.vendor.l .merge.vendor.r -side left foreach i {l r} { if { $i == "l" } { set x "From" } else { set x "To" } label .merge.vendor.$i.rev -text "$x Revision Tags" pack .merge.vendor.$i.rev -side top frame .merge.vendor.$i.scroll eval {listbox .merge.vendor.$i.scroll.list \ -yscrollcommand [list .merge.vendor.$i.scroll.sy set] \ -xscrollcommand [list .merge.vendor.$i.scroll.sx set]} \ -relief sunken -width 40 -height 8 scrollbar .merge.vendor.$i.scroll.sx -orient horizontal \ -command [list .merge.vendor.$i.scroll.list xview] \ -relief sunken scrollbar .merge.vendor.$i.scroll.sy -orient vertical \ -command [list .merge.vendor.$i.scroll.list yview] \ -relief sunken pack .merge.vendor.$i.scroll.sx -side bottom -fill x pack .merge.vendor.$i.scroll.sy -side right -fill y pack .merge.vendor.$i.scroll.list -side left -fill both -expand true pack .merge.vendor.$i.scroll -side top frame .merge.vendor.$i.f pack .merge.vendor.$i.f -side bottom label .merge.vendor.$i.f.l -text $x if { $i == "l" } { label .merge.vendor.$i.f.s \ -textvariable merge(from) -relief sunken -width 15 } else { label .merge.vendor.$i.f.s \ -textvariable merge(to) -relief sunken -width 15 } pack .merge.vendor.$i.f.l -side left -padx 3 -pady 3 pack .merge.vendor.$i.f.s -side left -pady 3 } button .merge.ok -text "OK" \ -command { if { $venselect_mcode == "" } { cvsfail "Please select a Vendor" .merge return } catch do_merge results if { $results == "err" } { return } grab release .merge wm withdraw .merge } button .merge.quit -text "Cancel" \ -command { grab release .merge wm withdraw .merge } pack .merge.ok .merge.quit -in .merge.down -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both bind .merge.vendor.l.scroll.list <> { get_j .merge.vendor.l.scroll.list left } bind .merge.vendor.r.scroll.list <> { get_j .merge.vendor.r.scroll.list right } # Needed for slower framebuffers #tkwait visibility .merge wm title .merge "Module Level Merge With Vendor Code" wm minsize .merge 30 10 gen_log:log T "LEAVE" } proc get_j { list side} { # Written by Eugene A. Lee, Aerospace Corp., 12/20/94 global merge gen_log:log T "ENTER ($list $side)" gen_log:log D "[$list curselection]" if {[string compare [$list curselection] ""] == 0} return set Sel [$list get [$list curselection]] if {$side == "left"} { set merge(from) [lindex [split $Sel] 0] } else { set merge(to) [lindex [split $Sel] 0] } gen_log:log T "LEAVE" } proc put_rev_tags {code} { # Written by Eugene A. Lee, Aerospace Corp., 11/12/95 # Called by button .venselect.ok in venget.tcl # Made usable for remote repositories by MK # # Go to the tmpdir aka cvs.tcl # Retrieve the whole friggin stuff into the directory (update from head) # Get the tags for all the files by calling put_rv_tags # Parse that result # global cvscfg global merge global venselect_mcode global cwd global cvs global filenames set tmpwdir [pwd] gen_log:log T "ENTER" .merge.vendor.l.scroll.list delete 0 end .merge.vendor.r.scroll.list delete 0 end set ret [cvs_sandbox_runcmd \ "$cvs -d $cvscfg(cvsroot) checkout $venselect_mcode" cmd_output] if {$ret == $cwd} { cd $cwd gen_log:log T "leave -- failed cvs checkout statement" return } cd $venselect_mcode gen_log:log F "CD [pwd]" set view_lines [split $cmd_output "\n"] foreach line $view_lines { gen_log:log D "Evaluating line $line" if {[string match "U *" $line]} { set dname [lindex [split $line] 1] regsub "$venselect_mcode/" $dname "" fname if {[info exists filenames($venselect_mcode)]} { lappend filenames($venselect_mcode) $fname } else { set filenames($venselect_mcode) $fname } } } gen_log:log F "filenames($venselect_mcode) existence:[info exists filenames($venselect_mcode)]" # get the module into the source if {[info exists filenames($venselect_mcode)]} { get_rv_tags $venselect_mcode r_tag_list v_tag_list } cd $tmpwdir if { [info exists r_tag_list] == 0 } { foreach i {l r} { .merge.vendor.$i.scroll.list insert end "No revision tags found" } } else { for {set i 0} {$i < [llength $r_tag_list]} {incr i} { set tmp [lindex $r_tag_list $i] .merge.vendor.l.scroll.list insert end $tmp .merge.vendor.r.scroll.list insert end $tmp } } cd $cwd gen_log:log T "LEAVE" } proc do_merge {} { global merge global cvscfg global cvs global venselect_mcode global modbrowse_module gen_log:log T "ENTER" set merge(3rd_party) $venselect_mcode if { $merge(3rd_party) == "" } { cvsfail "Vendor Module not specified" .merge return err } if { $merge(from) == "" || $merge(to) == "" } { cvsfail "not all entries filled" .merge return err } # In order to merge difference between tags of 3rd_party into $modbrowse_module # the directory where the merge is to be done later must be in the checkout # directory of $modbrowse_module. If the user just checked out the # $modbrowse_module and invoked the merge command, the current directory at this # point in the script is most likely one above directory $modbrowse_module. # Check for this and save the directory where the merge operation is to be done # later. if { [file tail [pwd]] == $modbrowse_module } { set dir4merge [pwd] } else { set tmpdir [glob -nocomplain $modbrowse_module] if { $tmpdir == "" } { cvsfail "You must invoke the merge command from the checked out directory of $modbrowse_module or one above it" .merge return } set dir4merge [file join [pwd] $tmpdir] } set mess "This will merge differences between $merge(from) and" append mess " $merge(to) of $merge(3rd_party) into $modbrowse_module" append mess "\n\n Are you sure?" if {[cvsconfirm $mess .merge] == 1} { return } # The CVS directory in the checked out $modbrowse_module are associated with # $modbrowse_module. Since we are going to merge in differences between # $merge(from) & $merge(to) of $merge(3rd_party), the CVS directory to be used # later in directory $dir4merge needs to from directory $merge(3rd_party). # Do that next and save it into a temp directory mktemp_dir set mktemp "$cvscfg(tmpdir)/merge[pid]" set mktemp_dir $mktemp.dir set v [viewer::new "Vendor Merge"] $v\::log "CVS Checkout of temp sandbox for $merge(3rd_party)\n" set co_cmd "$cvs checkout -d $mktemp_dir -r$merge(from) $merge(3rd_party)" $v\::do "$co_cmd" $v\::wait update # CVS directory in $mktemp_dir will be copied later cd $dir4merge gen_log:log F "CD [pwd]" # Save CVS directory of $modbrowse_module which is to be restored after # the merge command has been completed. Save it to $mktemp_dir set sav_dir [file join $mktemp_dir CVS_save] gen_log:log F "COPY CVS $sav_dir" file copy CVS $sav_dir gen_log:log F "DELETE CVS" file delete -force CVS; # Need -force for a directory # Then copy the CVS file from $mktemp_dir set sav_cvs [file join $mktemp_dir CVS] gen_log:log F "COPY $sav_cvs CVS" file copy -force $sav_cvs CVS $v\::log "\nCVS Merge of $merge(3rd_party) into $modbrowse_module\n" set co_cmd "$cvs checkout -d [pwd] -j$merge(from) -j$merge(to) $merge(3rd_party)" $v\::do "$co_cmd" $v\::wait $v\::log "\nCVS rdiff from $merge(from) to $merge(to) for $merge(3rd_party)\n" set co_cmd "$cvs rdiff -s -r$merge(from) -r$merge(to) $merge(3rd_party)" $v\::do "$co_cmd" $v\::wait update # Restore CVS directory associated with #modbrowse_module gen_log:log F "DELETE CVS" file delete -force CVS file copy $sav_dir CVS gen_log:log F "COPY $sav_dir CVS" # Remove the temp directory gen_log:log F "DELETE $mktemp_dir" file delete -force $mktemp_dir gen_log:log T "LEAVE" } proc unpack_tag_word { tag_word type tag_message} { upvar $type typ $tag_message tag_m # # Unpacks vendor and release tag information obtained from an RCS ,v file. # In an RCS ,v file, between the keywords "symbols" and "locks" keywords, # there are packed words with the following format: # # tag_info:tag_ident # # where: tag_info is either the vendortag or releasetag which was entered # when a cvs checkin or import command was invoked. # tag_ident is of the form: # x.y.z for a vendor tag (3 subfields or 2 dots) # x.y, x.y.z.w, or x.y.z.w.u.v for a release tag # # Called by: # # input: tag_word - word from a RCS ,v file between the "symbols" and "locks" # keywords # output: type - 0 if tag_word contains packed info on a release tag # 1 if tag_word contains packed info on a vendor tag # output: tag_message - a vendortag or releasetag as entered when a cvs # checkin or import command was invoked # # By: Eugene A. Lee, Aerospace Corporation # Date: Sept 15, 1995 # gen_log:log T "ENTER ($tag_word $type $tag_message)" set fields [split $tag_word :] set tag_m [string trimleft [lindex $fields 0]] set tag_num [string trimleft [lindex $fields 1]] # strip off any trailing ; character regsub {;$} $tag_num "" tag_num if { [llength [split $tag_num . ]] == 3 } { set typ 1; # release tag } else { set typ 0; # vendor tag } gen_log:log T "LEAVE" } proc get_rv_tags { mcode r_tag_list v_tag_list } { global filenames upvar $v_tag_list vtag_list upvar $r_tag_list rtag_list # # From the original code of E.A. Lee # Rewrite by M.R. Koelewijn, trying to make this work with a remote repository # Assumption: # The caller has created the sandbox in a local tmpdir, containing the # relevant files for this 'mcode' # The global 'filenames' has been set up to contain the names of the files # So, with merge_taglist the filenames are passed to CVS, with the request # to cough up some info. This info contains the tags (thanks, whoever did the # logcanvas): one big list of tags. Than we sort out the uniqe ones. # # Packed releasetag word has the format: # releasetag:branch_id # where: releasetag was specified when the cvs import command was invoked. # branch_id is of the forms: x.y, x.y.z.w, x.y.z.w.u.v, etc, # (odd number or subfields) # # Packed vendortag word has the format: # vendortag:branch_id # where: vendortag was specified when the cvs import command was invoked. # branch_id is of the forms: x.y.z (3 subfields or 2 dots) # # Output: r_tag_list - sorted releasetag list for the CVS module # Output: v_tag_list - sorted vendortag list for the CVS module # # Note: v_tag_list has no planned use for tkcvs yet. They are returned just # because this information was available. # gen_log:log T "ENTER ($mcode $r_tag_list $v_tag_list)" set rlist "" ;# easies way to allow lsearch to work without having to set vlist "" ;# use info exists statements foreach tag [cvs_sandbox_filetags $mcode $filenames($mcode)] { gen_log:log D "Next tag: $tag" unpack_tag_word $tag type tag_message gen_log:log D "$tag is type $type message $tag_message" if {$type == 0 } { if {[lsearch -exact $rlist $tag_message] < 0} { gen_log:log D "New Release tag found: $tag_message" lappend rlist $tag_message } } if {$type == 1 } { if {[lsearch -exact $rlist $tag_message] < 0} { gen_log:log D "New Vendor tag found: $tag_message" lappend vlist $tag_message } } } # Unsorted master releasetag and versiontag lists have been found. if { [info exists rlist] == 1 } { set rtag_list [lsort $rlist] } else { gen_log:log D "no mrlist created" } if { [info exists vlist] == 1 } { set vtag_list [lsort $vlist] } else { gen_log:log D "no mvlist created" } gen_log:log T "LEAVE" } proc merge_taglist {files} { global cvscfg global cvs gen_log:log T "ENTER ($files)" set commandline "$cvs -d $cvscfg(cvsroot) log $files" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] gen_log:log "C" "$view_this" if {$ret} { cvsfail $view_this .merge gen_log:log T "LEAVE ERROR" return $keepers } set view_lines [split $view_this "\n"] foreach line $view_lines { if {[string index $line 0] == "\t" } { regsub -all {[\t ]*} $line "" tag append keepers "$tag " } } gen_log:log T "LEAVE" return $keepers } proc vendorDialog {} { global ExModList ExModDirList global venselect_mcode global cvsglb set w .venDialog grab release .merge catch {destroy $w} toplevel $w wm title $w "Select A Vendor" grab set $w frame $w.buttons pack $w.buttons -side bottom -fill x -pady 2m button $w.buttons.ok -text Ok \ -command { if {$venselect_mcode == ""} { return } put_rev_tags $venselect_mcode destroy .venDialog raise .merge #grab set .merge } button $w.buttons.cancel -text Cancel \ -command { grab release .venDialog wm withdraw .venDialog } pack $w.buttons.ok -side left -expand 1 pack $w.buttons.cancel -side left -expand 1 frame $w.frame -borderwidth .5c pack $w.frame -side top -expand yes -fill y scrollbar $w.frame.scroll -command "$w.frame.list yview" listbox $w.frame.list -yscroll "$w.frame.scroll set" -setgrid 1 -height 5 pack $w.frame.scroll -side right -fill y pack $w.frame.list -side left -expand 1 -fill both getExistModDialog set nModule [llength $ExModList] for {set i 0} {$i < $nModule} {incr i} { $w.frame.list insert end [lindex $ExModList $i] } bind $w.frame.list { set venselect_mcode [%W get [%W nearest %y] ] } } tkcvs-8.2.3.orig/tkcvs/annotate.tcl0000644000175000017500000002556511664612512015451 0ustar timtimnamespace eval ::annotate { variable instance 0 proc new {revision file local} { # # show information on the last modification for each line of a file. # variable instance set my_idx $instance incr instance gen_log:log T "ENTER ($revision $file local)" namespace eval $my_idx { set my_idx [uplevel {concat $my_idx}] variable revision [uplevel {concat $revision}] variable file [uplevel {concat $file}] variable local [uplevel {concat $local}] variable w .annotate$my_idx variable ll global cvs global tcl_platform proc redo {w} { global cvscfg variable log_lines variable revcolors variable blameproc variable now variable nrevs variable revlist variable lc gen_log:log T "ENTER ($w)" catch {unset revcolors} $w.text configure -state normal $w.text delete 1.0 end busy_start $w set lc 0 foreach logline [lrange $log_lines 0 end-1] { incr lc $blameproc $w.text $now $logline $lc } $w.text configure -state disabled # Focus in the text widget to activate the text bindings focus $w.text busy_done $w update idletasks gen_log:log T "LEAVE" } proc cvs_annotate_color {w now logline ln} { global cvscfg variable revcolors variable agecolors variable revlist variable nrevs variable revspercolor variable maxrevlen variable ll set line [split $logline] set revnum [lindex $line 0] set line [string range $logline [string length $revnum] end] set line [string trimleft $line] # Beginning of a revision if {! [info exists revcolors($revnum)]} { # determine the number of revisions # between this commit and the now, then set color accordingly set revticks [lsearch -exact $revlist $revnum] set revticks [expr {$nrevs - $revticks}] set revindex [expr {$revticks / $revspercolor}] set ncolors [expr {[array size agecolors] - 1}] if {$revindex > $ncolors} {set revindex $ncolors} if {$revindex < 0} {set revindex 0} set revcolors($revnum) $agecolors($revindex) $w tag configure $revnum \ -background $revcolors($revnum) -foreground black } if {$cvscfg(blame_linenums)} { $w insert end [format "%${ll}d " $ln] } $w insert end [format "%-${maxrevlen}s " $revnum] $revnum $w insert end "$line\n" $revnum } proc svn_annotate_color {w now logline ln} { global cvscfg global cvsglb variable revcolors variable agecolors variable revlist variable nrevs variable revspercolor variable maxrevlen variable ll set logline [string trimleft $logline] set line [split $logline] set revnum [lindex $line 0] set line [string range $logline [string length $revnum] end] set line [string trimleft $line] set revnum [string trimleft $revnum] if {$revnum == "Skipping"} { cvsfail "Skipping binary file" $w return } # Beginning of a revision if {! [info exists revcolors($revnum)]} { # determine the number of revisions # between this commit and the now, then set color accordingly set revticks [lsearch -exact $revlist $revnum] set revticks [expr {$nrevs - $revticks}] set revindex [expr {$revticks / $revspercolor}] set ncolors [expr {[array size agecolors] - 1}] if {$revindex > $ncolors} {set revindex $ncolors} if {$revindex < 0} {set revindex 0} set revcolors($revnum) $agecolors($revindex) $w tag configure $revnum \ -background $revcolors($revnum) -foreground black } if {$cvscfg(blame_linenums)} { $w insert end [format "%${ll}d " $ln] } # we're sticking an "r" on - one more character set lr [expr {$maxrevlen+1}] $w insert end [format "r%-${lr}s " $revnum] $revnum $w insert end "$line\n" $revnum } regsub {^-} $revision {} revlabel regsub -all {\$} $file {\$} file if {$local == "svn"} { set info_cmd [exec::new "svn info \"$file\""] set info_lines [split [$info_cmd\::output] "\n"] foreach infoline $info_lines { if {[string match "Revision:*" $infoline]} { gen_log:log D "$infoline" set now [lrange $infoline 1 end] } } set blameproc svn_annotate_color set commandline "svn blame $revision \"$file\"" } elseif {$local == "svn_r"} { set blameproc svn_annotate_color set now $revision set commandline "svn blame $revision \"$file\"" } elseif {$local == "cvs"} { set info_cmd [exec::new "$cvs status \"$file\""] set info_lines [split [$info_cmd\::output] "\n"] foreach infoline $info_lines { if {[string match "*Working revision:*" $infoline]} { gen_log:log D "$infoline" set now [lindex $infoline 2] } } set blameproc cvs_annotate_color set commandline "$cvs annotate $revision \"$file\"" } elseif {$local == "cvs_r"} { # First see if we can do this # rannotate appeared in 1.11.1 set versionsplit [split $cvsglb(cvs_version) {.}] set major [lindex $versionsplit 1] set minor [lindex $versionsplit 2] set too_old 0 if {$major < 11} { set too_old 1 } elseif {($major == 11) && ($minor < 1)} { set too_old 1 } if {$too_old} { cvsfail "You need CVS >= 1.11.1 to do this" $w namespace delete [namespace current] return } set blameproc cvs_annotate_color set commandline "$cvs -d $cvscfg(cvsroot) rannotate $revision \"$file\"" set now $revlabel } else { cvsfail "I don't understand flag \"$local\"" return } # Initialize searching search_textwidget_init # Make the window toplevel $w text $w.text -setgrid yes -exportselection 1 \ -relief sunken -border 2 -height 40 -width 122 \ -yscroll "$w.scroll set" scrollbar $w.scroll -relief sunken -command "$w.text yview" frame $w.bottom button $w.bottom.close -text "Close" \ -command "destroy $w; exit_cleanup 0" label $w.bottom.days -text "Revs per Color" -width 20 -anchor e checkbutton $w.bottom.linum -text "Show Line Numbers" \ -variable cvscfg(blame_linenums) \ -onvalue 1 -offvalue 0 entry $w.bottom.dayentry -width 3 \ -textvariable [namespace current]::revspercolor button $w.bottom.redo -text "Redo Colors" button $w.bottom.srchbtn -text Search \ -command "search_textwidget $w.text" entry $w.bottom.entry -width 20 -textvariable cvsglb(searchstr) bind $w.bottom.entry "search_textwidget $w.text" pack $w.bottom -side bottom -fill x pack $w.bottom.srchbtn -side left pack $w.bottom.entry -side left pack $w.bottom.linum -side left -ipadx 15 pack $w.bottom.days -side left pack $w.bottom.dayentry -side left pack $w.bottom.redo -side left pack $w.bottom.close -side right -ipadx 15 pack $w.scroll -side right -fill y pack $w.text -fill both -expand 1 wm title $w "$file" if {$revision != ""} { wm title $w "$file Revision $revlabel" } if {$tcl_platform(platform) != "windows"} { wm iconbitmap $w @$cvscfg(bitmapdir)/annotate.xbm } # Define the colors array set agecolors { 0 #FFFF4B4B4B4B 1 #FFFF6C6C4B4B 2 #FFFF82824B4B 3 #FFFF97974B4B 4 #FFFFA8A84B4B 5 #FFFFB4B44B4B 6 #FFFFC5C54B4B 7 #FFFFDBDB4B4B 8 #FFFFFCFC4B4B 9 #DBDBFFFF4B4B 10 #ACACFFFF4B4B 11 #7575FFFF4B4B 12 #4F4FFFFF4B4B 13 #4B4BFFFFB4B4 14 #4B4BFFFFDFDF 15 #4B4BF4F4FFFF 16 #4B4BDFDFFFFF 17 #4B4BD2D2FFFF 18 #4B4BB0B0FFFF 19 #4B4B8686FFFF 20 #4B4B7979FFFF 21 #4B4B6464FFFF 22 #4B4B5757FFFF 23 #4B4B4B4BFFFF } #gen_log:log C "$commandline" busy_start $w set exec_cmd [exec::new "$commandline"] set log [$exec_cmd\::output] # Read the log lines. Assign a color to each unique revision. catch {unset revcolors} set log_lines [split [set log] "\n"] # We have 24 colors. How many revs do we have? set revlist {} # Might as well use the minimum space needed for revision numbers while # we're at it. The cvs annotate output wastes space set maxrevlen 0 foreach logline $log_lines { set line [split [string trimleft $logline]] set revnum [lindex $line 0] if {$revnum == ""} {continue} if {[lsearch -exact $revlist $revnum] == -1} { lappend revlist $revnum set l [string length $revnum] if {$l > $maxrevlen} { set maxrevlen $l } } } # Sort the revisions, using the "sortrevs" proc we wrote for # cvs/rcs revision numbers (and which is unneeded but harmless # for svn numbers set revlist [lsort -command sortrevs $revlist] set nrevs [llength $revlist] gen_log:log D "$revlist" set ncolors [expr {[array size agecolors] - 1}] if {$nrevs < $ncolors} { set revspercolor 1 } else { set rpc [expr {1 + ($nrevs / $ncolors)}] set revspercolor $rpc } gen_log:log D "nrevs $nrevs" gen_log:log D "revs per color $revspercolor" # Since there's an entry for changing revspercolor, make sure it's # something you can divide by or it will produce an error. if {[string length $revspercolor] == 0 || $revspercolor == 0} { gen_log:log D "revspercolor was \"$revspercolor\": setting to 1" set revspercolor 1 } # linecount set lc 0 set ll [string length [llength $log_lines]] foreach logline [lrange $log_lines 0 end-1] { incr lc $blameproc $w.text $now $logline $lc } $w.text yview moveto 0 update idletasks $w.text configure -state disabled bind $w.bottom.dayentry [namespace code {redo $w}] $w.bottom.redo configure -command [namespace code {redo $w}] $w.bottom.redo configure -command [namespace code {redo $w}] $w.bottom.linum configure -command [namespace code {redo $w}] # Focus in the text widget to activate the text bindings focus $w.text #bind_show $w.text -verbose busy_done $w return [namespace current] } } } tkcvs-8.2.3.orig/tkcvs/import.tcl0000644000175000017500000001512311664612512015137 0ustar timtim# # Tcl Library for TkCVS # # # Adds a new document to the repository. # proc import_run {} { global cwd global incvs global insvn global cvsglb gen_log:log T "ENTER" cvsroot_check [pwd] if {$incvs} { cvsok "This directory is already in CVS.\nCan\'t import here!" .import gen_log:log T "LEAVE" return } elseif {$insvn} { cvsok "There are Subversion directories here.\nPlease remove them first." .import gen_log:log T "LEAVE" return } # This is just a default. The user can change it. set cvsglb(newcode) [file tail $cwd] if {[winfo exists .import]} { wm deiconify .import raise .import grab set .import gen_log:log T "LEAVE" return } # Give it a default. This is what you get without the -b option. set cvsglb(newvers) 1.1.1 set cvsglb(newdir) $cvsglb(newcode) toplevel .import grab set .import frame .import.top message .import.top.explain -justify left -width 500 -relief groove \ -text "This will import the current directory and its sub-directories\ into CVS, creating a new module. If you haven't created a CVS repository,\ you must do that first with \"cvs init.\"" label .import.top.lnewcode -text "New Module Name" -anchor w label .import.top.lnewdir -text "New Module path relative to \$CVSROOT" -anchor w label .import.top.lnewdesc -text "Descriptive Title" -anchor w label .import.top.lnewvers -text "Version Number" -anchor w entry .import.top.tnewcode -textvariable cvsglb(newcode) -width 40 entry .import.top.tnewdir -textvariable cvsglb(newdir) -width 40 entry .import.top.tnewdesc -textvariable cvsglb(newdesc) -width 40 entry .import.top.tnewvers -textvariable cvsglb(newvers) -width 40 grid .import.top.explain -column 0 -row 0 -columnspan 3 -sticky ew grid .import.top.lnewcode -column 0 -row 1 -sticky w grid .import.top.tnewcode -column 1 -row 1 -sticky ew grid .import.top.lnewdir -column 0 -row 2 -sticky w grid .import.top.tnewdir -column 1 -row 2 -sticky ew grid .import.top.lnewdesc -column 0 -row 3 -sticky w grid .import.top.tnewdesc -column 1 -row 3 -sticky ew grid .import.top.lnewvers -column 0 -row 4 -sticky w grid .import.top.tnewvers -column 1 -row 4 -sticky ew frame .import.down -relief groove -border 2 button .import.down.ok -text "OK" \ -command { grab release .import wm withdraw .import do_import } button .import.down.quit -text "Cancel" \ -command { grab release .import wm withdraw .import } pack .import.down -side bottom -expand yes -fill x pack .import.top -side top -expand yes -fill x pack .import.down.ok -side left -expand yes pack .import.down.quit -side left -expand yes # Needed for slower framebuffers #tkwait visibility .import wm title .import "Create a New Module" wm minsize .import 1 1 gen_log:log T "LEAVE" } proc do_import {} { global cvs global cvsglb global cvscfg global cwd global modlist_sorted global modval global modtitle global ExModList ExModDirList gen_log:log T "ENTER" set imdir [pwd] # Error checks if { $cvsglb(newcode) == "" } { cvsok "You must type in a new module name." .import return 1 } if { $cvsglb(newdir) == "" } { cvsok "You must type in a new module path directory." .import return 1 } # We may have gotten here before opening the module browser if {! [info exists modlist_sorted]} { modbrowse_run cvs } # Make sure it isn't a duplicate key foreach {key value} [array get modval] { if { $cvsglb(newcode) == $key } { cvsok "$cvsglb(newcode) is not a new Module" .import return 1 } } # See if all apropriate Directories in newdirname exist. CVS import will # create them, but we'll want to make a #D entry. set cvsglb(newdir) [string trimleft $cvsglb(newdir) "/"] set pathname [file dirname $cvsglb(newdir)] set need_Dir 0 if {$pathname != "."} { foreach idx $modlist_sorted { lappend knowndirs [lindex $idx 0] } gen_log:log D "looking for $pathname in known directories ($knowndirs)" if {[lsearch -exact $knowndirs $pathname] == -1} { set need_Dir 1 } } # Make a baseline tag set versions [split $cvsglb(newvers) ".,/ -"] set baseline "baseline-[join $versions {_}]" set commandline "$cvs -d \"$cvscfg(cvsroot)\" import -m \"Imported using TkCVS\"" # Let it default to 1.1.1 or you will have big problems later from cvs. #if {$cvsglb(newvers) != ""} { #append commandline " -b 1.1.1" #} append commandline " \"$cvsglb(newdir)\" IMPORT $baseline" set v [viewer::new "Import Module"] $v\::log "\nCVS Import\n" $v\::do "$commandline" $v\::wait update # Update the modules file. set commandline "$cvs -d $cvscfg(cvsroot) -w checkout CVSROOT/modules" $v\::log "\nCheckout New Module\n" $v\::do "$commandline" $v\::wait cd CVSROOT gen_log:log F "CD [pwd]" set modfile [open modules a] if {$need_Dir} { puts $modfile "" gen_log:log D "#D $pathname" puts $modfile "#D $pathname" } gen_log:log D "#M\t$cvsglb(newcode)\t$cvsglb(newdesc)" puts $modfile "#M\t$cvsglb(newcode)\t$cvsglb(newdesc)" gen_log:log D "$cvsglb(newcode)\t$cvsglb(newdir)" puts $modfile "$cvsglb(newcode)\t$cvsglb(newdir)" close $modfile set commandline "$cvs -d $cvscfg(cvsroot) ci -m \"added $cvsglb(newcode)\" modules" $v\::log "\nCVS Checkin CVSROOT\n" $v\::do "$commandline" $v\::wait cd ../ gen_log:log F "CD [pwd]" set commandline "$cvs -d $cvscfg(cvsroot) -Q release -d CVSROOT" $v\::do "$commandline" $v\::wait modbrowse_run cvs # Now check out the new module cd .. gen_log:log F "CD [pwd]" # We have to move the original stuff entirely out of the way. # Otherwise checkout won't do the whole tree. gen_log:log F "MOVE $imdir $imdir.orig" if {[file isdirectory $imdir.orig]} { file delete -force -- $imdir.orig } file rename $imdir $imdir.orig set commandline \ "$cvs -d $cvscfg(cvsroot) checkout -R \"$cvsglb(newcode)\"" #gen_log:log C "$commandline" $v\::log "\nCVS Checkout\n" $v\::do "$commandline" $v\::wait # cd to the checked out module. $cwd is the correct directory to cd to # only if the name of the new module is the same as the directory name # where the source code is in. Define ckmoddir to be used instead. set ckmoddir $cwd if { $cvsglb(newcode) != [file tail $cwd] } { set ckmoddir [file join [file dirname $cwd] $cvsglb(newcode)] } if { [catch "cd $ckmoddir" err]} { cvsok "$err" .import } else { gen_log:log F "CD [pwd]" } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/tkcvs/mkmanpage.pl0000644000175000017500000000270311664612512015416 0ustar timtim#!/usr/bin/perl -w open(HELP, "help.tcl") || die("Can't open helpl.tcl"); print ".TH TkCVS 1 Release 8.2.3\n"; print ".SH NAME\n"; print "TkCVS \- a Tk/Tcl Graphical Interface to CVS and Subversion\n"; print ".SH SYNOPSIS\n"; print ".B tkcvs\n"; print "[\\-dir directory] [\\-root cvsroot] [\\-win workdir|module|merge] [\\-log file]\n"; while() { if (/^\s+do_help.*{/) { while() { chomp; if (/\s+}/) { print ".SP\n"; last; } if (/^\s*$/) { next; } # Turn h1 into Section Head s/

(.*)<\/h1>/.SH $1/; # Turn h2 into Section Subhead s/

(.*)<\/h2>/.SS $1/; # Make h3 start a hanging indent s/

(.*)<\/h3>/.TP\n.B $1/; if (//) { if ($` =~ /^.TP/) { s/\.B /.BI /; s/(.*)<\/itl>/" $1"/; } else { s/(.*)<\/itl>/.TP\n.I $1/; } #print STDERR "$_\n"; } #print STDERR "$` + $& + $'\n"; #print STDERR "$_\n"; if ($& =~ /do_help/) { # This decides whether its a free paragraph, in which case # it needs a space above it. print ".LP\n"; } s/(.*)<\/bld>/\\fB$1\\fR/; s/(.*)<\/cmp>/.RS\n$1\n.RE/; print; print "\n"; } } } print ".SH SEE ALSO\n"; print "cvs(1), svn(1)\n"; print ".SH AUTHOR\n"; print "Del (del\@babel.babel.com.au): Maintenance and Subversion support: Dorothy Robinson\n"; close HELP; tkcvs-8.2.3.orig/tkcvs/gen_log.tcl0000644000175000017500000000706611664612512015246 0ustar timtim# # Debugging trace functions adapted from set by Marcel Koelewijn # proc gen_log:init {} { global cvscfg global cvsglb global tcl_platform toplevel .trace wm protocol .trace WM_DELETE_WINDOW { .trace.close invoke } if {[info exists cvscfg(tracgeom)]} { wm geometry .trace $cvscfg(tracgeom) } text .trace.text -setgrid yes -relief sunken -border 2 \ -exportselection 1 -yscroll ".trace.scroll set" scrollbar .trace.scroll -relief sunken \ -command ".trace.text yview" frame .trace.bottom button .trace.bottom.clear -text "Clear" \ -command gen_log:clear button .trace.bottom.save -text "Save to File" \ -command gen_log:save search_textwidget_init button .trace.bottom.srchbtn -text Search \ -command "search_textwidget .trace.text" entry .trace.bottom.entry -width 20 -textvariable cvsglb(searchstr) bind .trace.bottom.entry \ "search_textwidget .trace.text" button .trace.close -text "Stop Tracing" \ -command { gen_log:quit; exit_cleanup 0 } pack .trace.bottom -side bottom -fill x pack .trace.scroll -side right -fill y pack .trace.text -fill both -expand 1 pack .trace.bottom.srchbtn -side left pack .trace.bottom.entry -side left pack .trace.bottom.clear -side left -expand 1 -anchor c pack .trace.bottom.save -side left pack .trace.close -in .trace.bottom -side right # Lets hard-code these colors - otherwise at least one is very # likely to blend into the bakcground .trace.text configure -background gray92 .trace.text tag configure tagC -foreground purple .trace.text tag configure tagE -foreground maroon .trace.text tag configure tagF -foreground darkgreen .trace.text tag configure tagT -foreground black .trace.text tag configure tagD -foreground red # Focus in the text widget to activate the text bindings focus .trace.text #bind_show .trace.text wm title .trace "TkCVS Trace" if {$tcl_platform(platform) != "windows"} { wm iconbitmap .trace @$cvscfg(bitmapdir)/trace.xbm } } proc gen_log:log { class string } { global cvscfg # check class+level first, if no logging required, skip if {$cvscfg(logging) && [string match "*\[$class\]*" $cvscfg(log_classes)]} { set callerlevel [expr {[info level] - 1}] if { $callerlevel == 0 } { # called from the toplevel set callerid "toplevel" } else { set callerid [lindex [info level $callerlevel] 0] } # Uncomment this to see the trace on stdout #puts "$class ($callerid) $string" .trace.text insert end [format "\[%s] %s\n" $callerid "$string"] tag$class set overflow [expr {[.trace.text index end] - $cvscfg(trace_savelines)}] if { $overflow > 10 } { .trace.text delete 0.0 $overflow } .trace.text yview end } } proc gen_log:quit { } { global cvscfg set cvscfg(logging) false if {[winfo exists .trace]} { set cvscfg(tracgeom) [wm geometry .trace] destroy .trace } } proc gen_log:clear { } { .trace.text delete 1.0 end } proc gen_log:save { } { set initialfile "tkcvs_log.txt" set types { {{All Files} *} } set savfile [ \ tk_getSaveFile -title "Save Trace" \ -filetypes $types \ -initialfile $initialfile \ -parent .trace ] if {$savfile == ""} { return } if {[catch {set fo [open $savfile w]}]} { puts "Cannot open $savfile for writing" return } puts $fo [.trace.text get 1.0 end] close $fo } proc gen_log:changeclass { } { global cvscfg global logclass set cvscfg(log_classes) "" foreach c [array names logclass] { append cvscfg(log_classes) $logclass($c) } } tkcvs-8.2.3.orig/tkcvs/modules.tcl0000644000175000017500000000736111664612512015302 0ustar timtim# # Tcl Library for TkCVS # # # Procedures to parse the CVS modules file and store whatever is # read into various associative arrays, sorted, and unsorted lists. # # # Global variables: # # modval # The string that specifies or defines the module. # modtitle # The descriptive title of the module. If not specified, modval is used. # cvscfg # General configuration variables (array) # filenames # For each module, the list of files that it contains. proc gather_mod_index {} { # # Creates a new global list called modlist for the report printouts # global cvscfg global modtitle global dcontents global dparent global modlist global modlist_sorted #gen_log:log T "ENTER ()" set modlist {} set dlist {} if {! [info exists modtitle]} { gen_log:log T "LEAVE (no modtitle array)" return } foreach d [array names dcontents] { #gen_log:log D "dcontents($d) is $dcontents($d)" foreach i $dcontents($d) { lappend dlist $i set path [file join $d $i] set dparent($path) $d #gen_log:log D "dparent($path) is $d" } } foreach mcode [array names modtitle] { # Skip aliases if {[string match "-a *" $modtitle($mcode)]} { continue } # Dont add subdirs to the list set match 0 foreach i $dlist { if {$i == $mcode} { set match 1 } } if {! $match} { lappend modlist "$mcode\t$modtitle($mcode)" } } set modlist_sorted [lsort $modlist] if {$cvscfg(logging) && [regexp -nocase {d} $cvscfg(log_classes)]} { foreach idx $modlist_sorted { #gen_log:log D "$idx" set dname [lindex $idx 0] if {[info exists dparent($dname)]} { #gen_log:log D " PARENT: $dparent($dname)" } if {[info exists dcontents($dname)]} { #gen_log:log D " CHILDREN: $dcontents($dname)" } set desc [find_subdirs $dname 0] if {$desc != ""} { #gen_log:log D " SUBDIRS: $desc" } } } #gen_log:log T "LEAVE" } proc find_filenames {mcode} { # # This does the work of setting up the filenames array for a module, # containing the list of file names within it. # global filenames global cwd global cvs global cvsglb global cvscfg global checkout_version global feedback gen_log:log T "ENTER ($mcode)" if {[info exists filenames($mcode)]} { set filenames($mcode) "" } # Trick of using rdiff to list files without checking them out # derived from "cvsls" by Eugene Kramer # cvs 1.9: # Need to use -f with pserver, or it skips files that havent # changed. With local repository, it reports them as new. # But without pserver, it skips them with -f but not without! # cvs 1.10.8: # Both pserver and local act like 1.9 local, that is, -f makes # it skip new files. set commandline \ "$cvs -d $cvscfg(cvsroot) rdiff -s -D 01/01/1971 $mcode" gen_log:log C $commandline catch {eval "exec $commandline"} view_this set view_lines [split $view_this "\n"] foreach line $view_lines { #gen_log:log D "$line" if {[string match "File *" $line]} { set lst [split $line] set cut [expr {[llength $lst] - 6}] set dname [join [lrange $lst 1 $cut]] #gen_log:log D "$dname" lappend filenames($mcode) $dname } } gen_log:log T "LEAVE" } proc find_subdirs {mname level} { global dcontents global subdirs #gen_log:log T "ENTER ($mname $level)" if {$level == 0} { set subdirs {} } if {[info exists dcontents($mname)]} { #gen_log:log D "$mname contents: {$dcontents($mname)}" foreach d $dcontents($mname) { set path [file join $mname $d] if {[info exists dcontents($path)]} { lappend subdirs $path } find_subdirs $path 1 } } #gen_log:log T "LEAVE ($subdirs)" return $subdirs } tkcvs-8.2.3.orig/tkcvs/dialog.tcl0000644000175000017500000014430011664612512015064 0ustar timtim# # Tcl Library for TkCVS # # # Smallish dialogs - add, tag # if {[catch "image type arr_dn"]} { workdir_images } # Creates the widgets for the dynamic form dialog proc dialog_FormCreate { title form_data } { global cvscfg global cvsglb global dynamic_dialog global dialog_action set font_star $cvscfg(dialogfont) set font_normal $cvscfg(listboxfont) set font_bold $cvscfg(dialogfont) set font_italic [font create -family Helvetica -size -12 -slant italic] if {[winfo exists .dynamic_dialog]} { destroy .dynamic_dialog } set w .dynamic_dialog toplevel $w frame $w.form pack $w.form -side top -fill x set row 0 foreach {field req type labeltext data} $form_data { # If you wanted another default, set it in the calling function if {! [info exists dynamic_dialog($field)]} { set dynamic_dialog($field) {} } if {$type == {l}} { # Section label frame $w.form.rule$field -relief groove -borderwidth 2 -height 4 label $w.form.l$field -font $font_bold -text $labeltext grid $w.form.rule$field -column 0 -row [incr row] -columnspan 3 -sticky ew grid $w.form.l$field -column 0 -row [incr row] -sticky w } else { # It's something else. It has a label and a req though. label $w.form.l$field -anchor w -text " $labeltext" label $w.form.r$field -anchor e -foreground red \ -font $font_star -text [expr {$req ? "*" : " "}] grid $w.form.l$field -column 0 -row [incr row] -sticky w grid $w.form.r$field -column 1 -row $row -sticky w if {$type == {t}} { # It's an entry entry $w.form.e$field -width 65 \ -textvariable dynamic_dialog($field) grid $w.form.e$field -column 2 -row $row -sticky w } elseif {$type == {r}} { # It's a radiobutton frame $w.form.f$field set k 1 foreach {text value} $data { radiobutton $w.form.f$field$k -text $text -value $value \ -variable dynamic_dialog($field) pack $w.form.f$field$k -in $w.form.f$field -side left incr k } grid $w.form.f$field -column 2 -row $row -sticky ew } } } incr row label $w.form.xstar -anchor e -foreground red \ -font $font_italic -text "* = required field" grid $w.form.xstar -column 1 -columnspan 2 -row $row -sticky w frame $w.buttons -relief groove -bd 2 pack $w.buttons -side top -fill x button $w.ok -text "OK" \ -command " if {\[dialog_FormComplete $w [list $form_data]\] } { destroy $w $dialog_action exit_cleanup 0 } " button $w.apply -text "Apply" \ -command " if {\[dialog_FormComplete $w [list $form_data]\] } { $dialog_action } " button $w.close -text "Cancel" \ -command " destroy $w exit_cleanup 0 " pack $w.close $w.apply $w.ok -in $w.buttons -side right \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 wm title $w $title wm minsize $w 1 1 return } proc dialog_FormComplete { w form_data } { global dynamic_dialog gen_log:log T "ENTER ($w ...)" foreach a [array names dynamic_dialog] { gen_log:log D "$a $dynamic_dialog($a)" } set section {} foreach {field req type labeltext data} $form_data { if {$type == {l}} { set section $dynamic_dialog($field) } else { if {$req && [set dynamic_dialog($field)] == {}} { cvsok "$field may not be blank" $w.form return 0 } } } return 1 } # Check out a CVS module from the Repository Browser proc dialog_cvs_checkout { cvsroot module {revtag {} } } { global dynamic_dialog global dialog_action gen_log:log T "ENTER ($cvsroot $module $revtag)" # Remember tags from last time if {$revtag == {} && [info exists dynamic_dialog(revtag)]} { set revtag $dynamic_dialog(revtag) } set dir [pwd] set dynamic_dialog(cvsroot) $cvsroot set dynamic_dialog(module) $module set dynamic_dialog(revtag) $revtag set dynamic_dialog(dir) $dir set dynamic_dialog(prune) {-P} set dynamic_dialog(kflag) {} # field req type labeltext data set dialog_form_checkout { 1 0 l {CVS Repository} 1 cvsroot 1 t {CVSROOT} {} 2 0 l {Module} 1 module 1 t {Name/Path} {} revtag 0 t {Revision/Tag} {} date 0 t {Date} {} 3 0 l {Destination} 1 dir 1 t {Current Directory} {} target 0 t {Working Directory} {} 4 0 l {Merge } 0 mtag1 0 t {Old tag} {} mtag2 0 t {New tag} {} 5 0 l {Advanced} 0 prune 0 r {Empty Directories} {{Create} {} {Don't Create} {-P}} kflag 0 r {Keyword Expansion} {{Default} {} {Keep as-is} {-ko} {Treat files as binary} {-kb} {Keywords only} {-kk}} } # Action function set dialog_action {cvs_checkout $dynamic_dialog(dir) \ $dynamic_dialog(cvsroot) \ $dynamic_dialog(prune) $dynamic_dialog(kflag) \ $dynamic_dialog(revtag) $dynamic_dialog(date) $dynamic_dialog(target) \ $dynamic_dialog(mtag1) $dynamic_dialog(mtag2) $dynamic_dialog(module) } set form [dialog_FormCreate "Checkout Module" $dialog_form_checkout] gen_log:log T "LEAVE" } # Export a CVS module from the Repository Browser proc dialog_cvs_export { cvsroot module {revtag {}} } { global dynamic_dialog global dialog_action gen_log:log T "ENTER ($cvsroot $module $revtag)" # Remember tags from last time if {$revtag == {} && [info exists dynamic_dialog(revtag)]} { set revtag $dynamic_dialog(revtag) } set dir [pwd] set dynamic_dialog(cvsroot) $cvsroot set dynamic_dialog(module) $module set dynamic_dialog(revtag) $revtag set dynamic_dialog(dir) $dir # field req type labeltext data set dialog_form_export { 1 0 l {CVS Repository} 1 cvsroot 1 t {CVSROOT} {} 2 0 l {Module} 1 module 1 t {Name/Path} {} revtag 0 t {Revision/Tag} {} date 0 t {Date} {} 3 0 l {Destination} 1 dir 1 t {Current Directory} {} target 0 t {Target Directory} {} 4 0 l {Advanced} 0 kflag 0 r {Keyword Expansion} {{Default} {} {Keep as-is} {-ko} {Treat files as binary} {-kb} {Keywords only} {-kk}} } # Action function set dialog_action {cvs_export $dynamic_dialog(dir) \ $dynamic_dialog(cvsroot) $dynamic_dialog(kflag) \ $dynamic_dialog(revtag) $dynamic_dialog(date) \ $dynamic_dialog(target) $dynamic_dialog(module) } set form [dialog_FormCreate "Export Module" $dialog_form_export] gen_log:log T "LEAVE" } # Checkout or Export a SVN module from the Repository Browser proc dialog_svn_checkout { svnroot path command } { global dynamic_dialog global dialog_action if {[info exists dynamic_dialog(rev)]} { set rev $dynamic_dialog(rev) } set dir [pwd] set dynamic_dialog(path) $path set dynamic_dialog(svnroot) $svnroot set dynamic_dialog(command) $command set dynamic_dialog(dir) $dir # field req type labeltext data set dialog_form_export { 1 0 l {SVN Repository} 1 svnroot 1 t {SVN URL} {} path 1 t {Path in Repository} {} rev 0 t {Revision/Date} {} 2 0 l {Destination} 1 dir 1 t {Current Directory} {} target 0 t {Target Directory} {} 3 0 l {Working Copy or Unversioned Copy} {} command 0 r {Versioning} {{Versioned (Checkout)} {checkout} {Un-Versioned (Export)} {export}} } # Action function set dialog_action {svn_checkout $dynamic_dialog(dir) \ $dynamic_dialog(svnroot) $dynamic_dialog(path) \ $dynamic_dialog(rev) $dynamic_dialog(target) \ $dynamic_dialog(command) } set form [dialog_FormCreate "Checkout or Export" $dialog_form_export] gen_log:log T "LEAVE" } # Make a branch or tag (svn copy) from the Repository Browser proc dialog_svn_tag { svnroot path b_or_t } { global dynamic_dialog global dialog_action set dynamic_dialog(path) $path set dynamic_dialog(svnroot) $svnroot set dynamic_dialog(b_or_t) $b_or_t set dynamic_dialog(frompath) "$dynamic_dialog(svnroot)/$dynamic_dialog(path)" # field req type labeltext data set dialog_form_tagcopy { 1 0 l {Copy Path to Tag or Branch} 1 frompath 1 t {Copy From} {} b_or_t 0 r {Tag or Branch} {{Branch} {branches} {Tag} {tags}} target 1 t {New Branch/Tag} {} } # Action function set dialog_action {svn_rcopy $dynamic_dialog(svnroot)/$dynamic_dialog(path) \ $dynamic_dialog(b_or_t) $dynamic_dialog(target) } set form [dialog_FormCreate "SVN Branch or Tag Copy" $dialog_form_tagcopy] gen_log:log T "LEAVE" } # Compare two revisions of a module, from the module browser # Can make a patch file or send a summary to the screen proc dialog_cvs_patch { cvsroot module summary {revtagA {}} {revtagB {}} } { global dynamic_dialog global dialog_action gen_log:log T "ENTER ( $cvsroot $module $summary $revtagA $revtagB )" # Remember tags if {$revtagA == {} && [info exists dynamic_dialog(revtagA)]} { set revtagA $dynamic_dialog(revtagA) } if {$revtagB == {} && [info exists dynamic_dialog(revtagB)]} { set revtagB $dynamic_dialog(revtagB) } set dynamic_dialog(cvsroot) $cvsroot set dynamic_dialog(module) $module set dynamic_dialog(revtagA) $revtagA set dynamic_dialog(revtagB) $revtagB set dynamic_dialog(outfile) "$module.patch" if {$summary} { set dynamic_dialog(outmode) 0 set dynamic_dialog(difffmt) {-s} } else { set dynamic_dialog(outmode) 1 set dynamic_dialog(difffmt) {} } # field req type labeltext data set dialog_form_patch { 1 0 l {CVS Repository} 1 cvsroot 1 t {CVSROOT} {} 2 0 l {Module} 1 module 1 t {Name/Path} {} 3 0 l {Destination} 1 outmode 0 r {Output Mode} {{To Screen} 0 {To File} 1} outfile 0 t {Output File} {outfile} 4 0 l {Old Revision} 1 revtagA 0 t {Revision/Tag} {} dateA 0 t {Date} {} 5 0 l {New Revision} 1 revtagB 0 t {Revision/Tag} {} dateB 0 t {Date} {} 6 0 l {Format} 1 difffmt 0 r {Diff Format} {{Default} {} {Context diff} {-c} {Unidiff} {-u} {One liner} {-s}} } # Action function set dialog_action {cvs_patch $dynamic_dialog(cvsroot) \ $dynamic_dialog(module) $dynamic_dialog(difffmt) \ $dynamic_dialog(revtagA) $dynamic_dialog(dateA) \ $dynamic_dialog(revtagB) $dynamic_dialog(dateB) \ $dynamic_dialog(outmode) $dynamic_dialog(outfile) } set form [dialog_FormCreate "Diff/Patch Module" $dialog_form_patch] gen_log:log T "LEAVE" } # Compare two revisions, from the module browser # Can make a patch file or send a summary to the screen proc dialog_svn_patch { cvsroot pathA pathB summary } { global dynamic_dialog global dialog_action gen_log:log T "ENTER ( $cvsroot $pathA $pathB $summary )" set dynamic_dialog(cvsroot) $cvsroot set dynamic_dialog(pathA) $pathA set dynamic_dialog(pathB) $pathB if {$summary} { set dynamic_dialog(outmode) 0 } else { set dynamic_dialog(outmode) 1 } set dynamic_dialog(outfile) "patchfile.patch" set dynamic_dialog(fullA) "$cvsroot$pathA" if {$pathB == ""} { set dynamic_dialog(fullB) "" } else { set dynamic_dialog(fullB) "$cvsroot$pathB" } # field req type labeltext data set dialog_form_patch { 1 0 l {Repository Paths} 1 pathA 1 t {Path A} {} pathB 0 t {Path B} {} 3 0 l {Destination} 1 outmode 0 r {Output Mode} {{To Screen} 0 {To File} 1} outfile 0 t {Output File} {outfile} 4 0 l {Old Revision} 1 revA 0 t {Revision} {} dateA 0 t {Date} {} 5 0 l {New Revision} 1 revB 0 t {Revision} {} dateB 0 t {Date} {} } # Action function set dialog_action { # Make new fullA and fullB from the pathA and pathB entries set dynamic_dialog(fullA) "$dynamic_dialog(cvsroot)/$dynamic_dialog(pathA)" if {$dynamic_dialog(pathB) == ""} { set dynamic_dialog(fullB) "" } else { set dynamic_dialog(fullB) "$dynamic_dialog(cvsroot)/$dynamic_dialog(pathB)" } svn_patch $dynamic_dialog(fullA) \ $dynamic_dialog(fullB) \ $dynamic_dialog(revA) $dynamic_dialog(dateA) \ $dynamic_dialog(revB) $dynamic_dialog(dateB) \ $dynamic_dialog(outmode) $dynamic_dialog(outfile) } set form [dialog_FormCreate "SVN Diff/Patch" $dialog_form_patch] gen_log:log T "LEAVE" } proc add_dialog {args} { global cvs global cvsglb global incvs global insvn gen_log:log T "ENTER ($args)" set binflag "" toplevel .add grab set .add set filelist [join $args] if {$filelist == ""} { set mess "This will add all new files" } else { set mess "This will add these files:\n\n" foreach file $filelist { append mess " $file\n" } } message .add.top -justify left -aspect 300 -relief groove \ -text "Add a file or files to the module. The repository\ will not be changed until you do a commit." pack .add.top -side top -fill x message .add.middle -text $mess -aspect 200 pack .add.middle -side top -fill x frame .add.down button .add.down.add -text "Add" if {$incvs} { .add.down.add configure -command { grab release .add destroy .add cvs_add $binflag [workdir_list_files] } checkbutton .add.binary -text "-kb (binary)" \ -variable binflag -onvalue "-kb" -offvalue "" pack .add.binary -side top } elseif {$insvn} { .add.down.add configure -command { grab release .add destroy .add svn_add [workdir_list_files] } } button .add.down.cancel -text "Cancel" \ -command { grab release .add; destroy .add } pack .add.down -side bottom -fill x -expand 1 pack .add.down.add .add.down.cancel -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 wm title .add "Add Files" wm minsize .add 1 1 gen_log:log T "LEAVE" } proc file_tag_dialog {branch} { global incvs insvn inrcs global cvscfg global cvsglb global branchflag gen_log:log T "ENTER ($branch)" # FIXME: This is too messy and should be split into two dialogs, # one for cvs and one for svn set branchflag $branch toplevel .tag #grab set .tag frame .tag.top pack .tag.top -side top -fill x set msg "" if {$incvs} { set msg "Apply a new tag or branch tag \ to the marked files, recursively.\ Will change the repository.\ If a branch, it can also update local directory if desired." } elseif {$insvn} { set msg "Create a new branch or tag copy \ of the files in this directory" } message .tag.top.msg -justify left -aspect 300 -relief groove \ -text $msg label .tag.top.lbl -text "Tag Name" -anchor w entry .tag.top.entry -relief sunken -textvariable usertagname checkbutton .tag.top.branch -text "Branch tag (-b)" \ -variable branchflag -onvalue "branch" -offvalue "tag" \ -command { if {$branchflag == "tag"} { .tag.mid.upd config -state disabled; set updflag "no" } else { .tag.mid.upd config -state normal } } checkbutton .tag.top.force -text "Move existing (-F)" \ -variable forceflag -onvalue "yes" -offvalue "no" frame .tag.mid -relief groove -bd 2 checkbutton .tag.mid.upd -text "Update current directory to be on the new tag" \ -variable updflag -onvalue "yes" -offvalue "no" grid columnconf .tag.top 1 -weight 1 grid rowconf .tag.top 3 -weight 1 grid .tag.top.msg -column 0 -row 0 -columnspan 2 -pady 2 -sticky ew grid .tag.top.lbl -column 0 -row 1 -sticky nw grid .tag.top.entry -column 1 -row 1 -sticky ew grid .tag.top.branch -column 1 -row 2 -sticky w if {$incvs} { grid .tag.top.force -column 1 -row 3 -sticky w } pack .tag.mid -side top pack .tag.mid.upd frame .tag.down -relief groove -bd 2 pack .tag.down -side bottom -fill x -expand 1 button .tag.down.tag -text "Tag" if {$incvs} { .tag.down.tag configure -command { cvs_tag $usertagname $forceflag $branchflag $updflag \ [workdir_list_files] grab release .tag destroy .tag } } elseif {$insvn} { if {$branchflag == "branch"} {set branchtag "branches"} if {$branchflag == "tag"} {set branchtag "tags"} .tag.down.tag configure -command { svn_tag $usertagname $branchflag $updflag [workdir_list_files] grab release .tag destroy .tag } } button .tag.down.cancel -text "Cancel" \ -command { grab release .tag; destroy .tag } pack .tag.down.tag .tag.down.cancel -in .tag.down -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 if {$branchflag == "tag"} { .tag.mid.upd config -state disabled set updflag "no" } else { .tag.mid.upd config -state normal } wm title .tag "tag" wm minsize .tag 1 1 gen_log:log T "LEAVE" } proc rtag_dialog { cvsroot module b_or_t } { global cvscfg global cvsglb gen_log:log T "ENTER ($cvsroot $module $b_or_t)" toplevel .modtag grab set .modtag frame .modtag.top pack .modtag.top -side top -fill x message .modtag.top.lbl -aspect 300 -relief groove \ -text "Tag the module \"$module\" with the new tag you specify.\ If you fill in \"Existing Tag\", the revisions having that tag will get\ the new tag. Otherwise, the head revision will be tagged." label .modtag.top.olbl -text "Existing Tag" -anchor w entry .modtag.top.oentry -textvariable otag \ -relief sunken label .modtag.top.nlbl -text "New Tag" -anchor w entry .modtag.top.nentry -textvariable ntag \ -relief sunken checkbutton .modtag.top.branch -text "Branch tag (-b)" \ -variable b_or_t -onvalue "branch" -offvalue "tag" checkbutton .modtag.top.force -text "Move existing (-F)" \ -variable force -onvalue "yes" -offvalue "no" grid columnconf .modtag.top 1 -weight 1 grid rowconf .modtag.top 4 -weight 1 grid .modtag.top.lbl -column 0 -row 0 -columnspan 2 -pady 2 -sticky ew grid .modtag.top.olbl -column 0 -row 1 -sticky nw grid .modtag.top.oentry -column 1 -row 1 grid .modtag.top.nlbl -column 0 -row 2 -sticky nw grid .modtag.top.nentry -column 1 -row 2 grid .modtag.top.branch -column 1 -row 3 -sticky w grid .modtag.top.force -column 1 -row 4 -sticky w frame .modtag.down -relief groove -bd 2 pack .modtag.down -side top -fill x button .modtag.down.tag -text "Tag" \ -command " cvs_rtag $cvsroot $module $b_or_t \$force \$otag \$ntag; \ .modtag.down.cancel invoke " button .modtag.down.delete -text "Remove" \ -command " cvs_rtag $cvsroot $module tag remove \$otag \$ntag; \ .modtag.down.cancel invoke " button .modtag.down.cancel -text "Cancel" \ -command { grab release .modtag destroy .modtag } pack .modtag.down.tag .modtag.down.delete .modtag.down.cancel -in .modtag.down -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 bind .modtag.top.nentry \ { .modtag.down.tag invoke } wm title .modtag "Tag Module" wm minsize .modtag 1 1 gen_log:log T "LEAVE" } proc subtract_dialog {args} { global incvs global insvn global cvsglb gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { cvsfail "Please select some files to delete first!" .workdir return } foreach f $filelist { if {$incvs && [file isdirectory $f]} { cvsfail "$f is a directory. Try \"Remove Recursively\" instead" .workdir return } } toplevel .subtract grab set .subtract set mess "This will remove these files:\n\n" foreach file $filelist { append mess " $file\n" } message .subtract.top -justify left -aspect 300 -relief groove \ -text "Remove a file or files from the module. The repository\ will not be changed until you do a commit." pack .subtract.top -side top -fill x message .subtract.middle -text $mess -aspect 200 pack .subtract.middle -side top -fill x frame .subtract.down button .subtract.down.remove -text "Remove" if {$incvs} { .subtract.down.remove configure -command { grab release .subtract destroy .subtract cvs_remove [workdir_list_files] } } elseif {$insvn} { .subtract.down.remove configure -command { grab release .subtract destroy .subtract svn_remove [workdir_list_files] } } button .subtract.down.cancel -text "Cancel" \ -command { grab release .subtract; destroy .subtract } pack .subtract.down -side bottom -fill x -expand 1 pack .subtract.down.remove .subtract.down.cancel -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 wm title .subtract "Remove Files" wm minsize .subtract 1 1 gen_log:log T "LEAVE" } proc edit_dialog {args} { global incvs global cvsglb gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } if {$args == "."} { cvsfail "Please select some files to edit first!" .workdir return } toplevel .editflag grab set .editflag set filelist [join $args] set mess "This will set the edit flag on these files:\n\n" foreach file $filelist { append mess " $file\n" } message .editflag.top -justify left -aspect 300 -relief groove \ -text "Set the edit flag on a file or files from the module" pack .editflag.top -side top -fill x message .editflag.middle -text $mess -aspect 200 pack .editflag.middle -side top -fill x frame .editflag.down button .editflag.down.remove -text "Edit" \ -command { grab release .editflag destroy .editflag cvs_edit [workdir_list_files] } button .editflag.down.cancel -text "Cancel" \ -command { grab release .editflag; destroy .editflag } pack .editflag.down -side bottom -fill x -expand 1 pack .editflag.down.remove .editflag.down.cancel -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 wm title .editflag "Edit Files" wm minsize .editflag 1 1 gen_log:log T "LEAVE" } proc unedit_dialog {args} { global incvs global cvsglb gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } if {$args == "."} { cvsfail "Please select some files to unedit first!" .workdir return } toplevel .uneditflag grab set .uneditflag set filelist [join $args] set mess "This will reset the edit flag on these files:\n\n" foreach file $filelist { append mess " $file\n" } message .uneditflag.top -justify left -aspect 300 -relief groove \ -text "Reset the edit flag on a file or files from the module." pack .uneditflag.top -side top -fill x message .uneditflag.middle -text $mess -aspect 200 pack .uneditflag.middle -side top -fill x frame .uneditflag.down button .uneditflag.down.remove -text "Unedit" \ -command { grab release .uneditflag destroy .uneditflag cvs_unedit [workdir_list_files] } button .uneditflag.down.cancel -text "Cancel" \ -command { grab release .uneditflag; destroy .uneditflag } pack .uneditflag.down -side bottom -fill x -expand 1 pack .uneditflag.down.remove .uneditflag.down.cancel -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 wm title .uneditflag "Unedit Files" wm minsize .uneditflag 1 1 gen_log:log T "LEAVE" } # # Set up a small(?) update dialog. # proc cvs_update_options {} { global cvsglb global cvscfg gen_log:log T "ENTER" if {[winfo exists .update]} { wm deiconify .update raise .update grab set .update gen_log:log T "LEAVE" return } # Set defaults if {! [info exists cvsglb(tagmode_selection)]} { update_set_defaults } toplevel .update grab set .update frame .update.explaintop # Provide an explanation of this dialog box label .update.explaintop.explain -relief raised -bd 1 \ -text "Update files in local directory" frame .update.options frame .update.options.whichrev -relief groove -border 2 frame .update.options.diropts -relief groove -border 2 frame .update.options.normbin -relief groove -border 2 frame .update.down # Always pack OK/Cancel first so they don't disappear pack .update.down -side bottom -fill x pack .update.explaintop -side top -fill x -pady 1 pack .update.explaintop.explain -side top -fill x -pady 1 pack .update.options -side top -fill x -pady 1 pack .update.options.whichrev -side top -fill x pack .update.options.diropts -side top -fill x pack .update.options.normbin -side top -fill x # keep-same-tag update radiobutton .update.options.whichrev.keep \ -text "Keep same branch or trunk" \ -variable cvsglb(tagmode_selection) -value "Keep" -anchor w \ -command {.update.options.whichrev.getrev.lblentry.tname configure -state disabled .update.options.whichrev.getrev.lblentry.dirtag configure -state disabled} # update to the head revision radiobutton .update.options.whichrev.trunk \ -text "Update local files to be on main trunk (-A)" \ -variable cvsglb(tagmode_selection) -value "Trunk" -anchor w \ -command {.update.options.whichrev.getrev.lblentry.tname configure -state disabled .update.options.whichrev.getrev.lblentry.dirtag configure -state disabled} # update to different branch/tag or not radiobutton .update.options.whichrev.tag \ -text "Update (-r) local files to be on tag/branch" \ -variable cvsglb(tagmode_selection) -value "Getrev" -anchor w \ -command {.update.options.whichrev.getrev.lblentry.tname configure -state normal .update.options.whichrev.getrev.lblentry.dirtag configure -state normal} message .update.options.whichrev.explainkeep -font $cvscfg(listboxfont) \ -justify left -width 400 \ -text "If local directory is on main trunk, get latest on main trunk. If local directory is on a branch, get latest on that branch. If local directory/file has \"sticky\" non-branch tag, no update." message .update.options.whichrev.explaintrunk -font $cvscfg(listboxfont) \ -justify left -width 400 \ -text "Advice: If your local directories are currently on a branch, you may want to commit any local changes to that branch first." pack .update.options.whichrev.keep -side top -fill x pack .update.options.whichrev.explainkeep \ -side top -fill x -pady 1 -ipady 0 pack .update.options.whichrev.trunk -side top -fill x pack .update.options.whichrev.explaintrunk \ -side top -fill x -pady 1 -ipady 0 pack .update.options.whichrev.tag -side top -fill x frame .update.options.whichrev.getrev frame .update.options.whichrev.getrev.lblentry label .update.options.whichrev.getrev.lblentry.tlbl -text "Tag Name" -anchor w entry .update.options.whichrev.getrev.lblentry.tname -relief sunken \ -textvariable cvsglb(updatename) button .update.options.whichrev.getrev.lblentry.dirtag -text "Dir Tag" \ -command { set cvsglb(updatename) $current_tagname } message .update.options.whichrev.getrev.explaintag -font $cvscfg(listboxfont) \ -justify left -width 400 \ -text "Advice: Update local files to main trunk (head) first. Note: The tag will be 'sticky' for the directory and for each file." pack .update.options.whichrev.getrev -side top -expand 1 -fill x pack .update.options.whichrev.getrev.lblentry -side top -expand 1 -fill x pack .update.options.whichrev.getrev.lblentry.tlbl -side left pack .update.options.whichrev.getrev.lblentry.tname -side left -fill x -padx 2 -pady 4 pack .update.options.whichrev.getrev.lblentry.dirtag -side left -fill x -padx 2 -pady 4 pack .update.options.whichrev.getrev.explaintag \ -side top -fill x -pady 1 -ipady 0 # Where user chooses the action to take if tag is not on a file label .update.options.whichrev.getrev.asknotfound \ -text "If file doesn't exist on this branch/tag:" -anchor w frame .update.options.whichrev.getrev.notfound radiobutton .update.options.whichrev.getrev.notfound.remove \ -text "Remove file from local directory" \ -variable cvsglb(action_notag) -value "Remove" radiobutton .update.options.whichrev.getrev.notfound.gethead \ -text "Get head revision (-f)" \ -variable cvsglb(action_notag) -value "Get_head" pack .update.options.whichrev.getrev.asknotfound -side top -fill x pack .update.options.whichrev.getrev.notfound -side top -expand 1 -fill x pack .update.options.whichrev.getrev.notfound.remove -side left pack .update.options.whichrev.getrev.notfound.gethead -side left # Recurse or not. frame .update.options.diropts.radio1 radiobutton .update.options.diropts.radio1.recurse -text "Recurse the subdirectories" \ -variable cvsglb(update_recurse) -value "recurse" -anchor w \ -command { .update.options.diropts.getdir configure -state normal .update.options.diropts.prune configure -state normal .update.options.diropts.lblentry.tdir configure -state normal } radiobutton .update.options.diropts.radio1.local -text "This directory only (-l)" \ -variable cvsglb(update_recurse) -value "local" -anchor w \ -command { .update.options.diropts.getdir configure -state disabled .update.options.diropts.prune configure -state disabled .update.options.diropts.lblentry.tdir configure -state disabled } pack .update.options.diropts.radio1 -side top -expand 1 -fill x pack .update.options.diropts.radio1.recurse -side left pack .update.options.diropts.radio1.local -side left label .update.options.diropts.prunelbl \ -text "\nIf directory is here but no longer in repository:" -anchor w checkbutton .update.options.diropts.prune -text "Prune it (-P)" \ -variable cvsglb(update_prune) -onvalue "prune" -offvalue "no-prune" -anchor w # Where user chooses whether to pick up directories not currently in local label .update.options.diropts.getlbl \ -text "If directory is in repository but not in local:" -anchor w checkbutton .update.options.diropts.getdir -text "Get it (-d)" \ -variable cvsglb(get_all_dirs) -onvalue "Yes" -offvalue "No" -anchor w \ -command { if {$cvsglb(get_all_dirs) != "Yes"} { .update.options.diropts.lblentry.tdir configure -state disabled } else { .update.options.diropts.lblentry.tdir configure -state normal } } frame .update.options.diropts.lblentry label .update.options.diropts.lblentry.tlbl -text "Specific directory (optional)" -anchor w entry .update.options.diropts.lblentry.tdir -relief sunken -state disabled \ -textvariable cvsglb(getdirname) # State of top radiobuttons (keep same, main, or tag) if {$cvsglb(tagmode_selection) != "Getrev"} { .update.options.whichrev.getrev.lblentry.tname configure -state disabled .update.options.whichrev.getrev.lblentry.dirtag configure -state disabled } # state of -l radiobuttons if {$cvsglb(update_recurse) != "recurse"} { .update.options.diropts.getdir configure -state disabled .update.options.diropts.prune configure -state disabled .update.options.diropts.lblentry.tdir configure -state disabled } # State of -d checkbutton if {$cvsglb(get_all_dirs) != "Yes"} { .update.options.diropts.lblentry.tdir configure -state disabled } pack .update.options.diropts.prunelbl -side top -expand 1 -fill x pack .update.options.diropts.prune -side top -expand 1 -fill x pack .update.options.diropts.getlbl -side top -expand 1 -fill x pack .update.options.diropts.getdir -side top -expand 1 -fill x pack .update.options.diropts.lblentry -side top -expand 1 -fill x pack .update.options.diropts.lblentry.tlbl -side left pack .update.options.diropts.lblentry.tdir -side left -fill x -padx 2 -pady 4 # normal or binary? label .update.options.normbin.lnormbin -text "Treat files as:" -anchor w frame .update.options.normbin.radio radiobutton .update.options.normbin.radio.normalfile -text "Normal file" \ -variable cvsglb(norm_bin) -value "Normal" -anchor w radiobutton .update.options.normbin.radio.binaryfile -text "Binary file (-kb)" \ -variable cvsglb(norm_bin) -value "Binary" -anchor w pack .update.options.normbin.lnormbin -side top -fill both pack .update.options.normbin.radio -side top -expand 1 -fill x pack .update.options.normbin.radio.normalfile -side left pack .update.options.normbin.radio.binaryfile -side left # The OK/Cancel buttons button .update.ok -text "OK" \ -command { grab release .update; wm withdraw .update; update_with_options } button .update.apply -text "Apply" \ -command update_with_options button .update.reset -text "Reset defaults" \ -command update_set_defaults button .update.quit -text "Close" \ -command { grab release .update; wm withdraw .update } pack .update.ok .update.apply .update.reset .update.quit -in .update.down \ -side left -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 # Window Manager stuff wm title .update "Update a Module" wm minsize .update 1 1 gen_log:log T "LEAVE" } # Set defaults for "Update with Options" dialog proc update_set_defaults {} { global cvsglb set cvsglb(tagmode_selection) "Keep" set cvsglb(updatename) "" set cvsglb(update_recurse) "recurse" set cvsglb(action_notag) "Remove" set cvsglb(update_prune) "prune" set cvsglb(get_all_dirs) "No" set cvsglb(getdirname) "" set cvsglb(norm_bin) "Normal" } # Do what was setup in the "Update with Options" dialog proc update_with_options {} { global cvsglb gen_log:log T "ENTER" if { $cvsglb(updatename) == "" } { set tagname "BASE" } else { set tagname $cvsglb(updatename) } if { $cvsglb(get_all_dirs) == "No" } { set cvsglb(getdirname) "" } if { $cvsglb(getdirname) == "" } { set dirname " " } else { set dirname $cvsglb(getdirname) } #puts "from update_setup, tagname $tagname. norm_bin $cvsglb(norm_bin)" if { $cvsglb(tagmode_selection) == "Keep" } { set tagname "BASE" } elseif { $cvsglb(tagmode_selection) == "Trunk" } { set tagname "HEAD" } eval "cvs_update {$tagname} {$cvsglb(norm_bin)} \ {$cvsglb(action_notag)} \ {$cvsglb(update_recurse)} {$cvsglb(update_prune)} \ {$cvsglb(get_all_dirs)} {$dirname} \ [workdir_list_files]" gen_log:log T "LEAVE" } proc addir_dialog {args} { global cvs global incvs gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } set binflag "" toplevel .add grab set .add set filelist [join $args] if {$filelist == ""} { set mess "This will add all new directories" } else { set mess "This will add these directories:\n\n" foreach file $filelist { append mess " $file\n" } } message .add.top -justify left -aspect 300 -relief groove \ -text "Add (recursively) a directory to the module.\ The repository will not be changed until you do a commit." pack .add.top -side top -fill x message .add.middle -text $mess -aspect 200 pack .add.middle -side top -fill x checkbutton .add.binary -text "-kb (binary)" \ -variable binflag -onvalue "-kb" -offvalue "" pack .add.binary -side top frame .add.down button .add.down.add -text "Add" \ -command { grab release .add destroy .add foreach dir [workdir_list_files] { cvs_add_dir $binflag $dir } } button .add.down.cancel -text "Cancel" \ -command { grab release .add; destroy .add } pack .add.down -side bottom -fill x -expand 1 pack .add.down.add .add.down.cancel -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 wm title .add "Add Directories" wm minsize .add 1 1 gen_log:log T "LEAVE" } proc subtractdir_dialog {args} { global cvs global incvs gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } set filelist [join $args] if {$filelist == ""} { cvsfail "Please select some directories to remove first!" .workdir return } toplevel .subtract grab set .subtract set mess "This will remove these directories:\n\n" foreach file $filelist { append mess " $file\n" } message .subtract.top -justify left -aspect 300 -relief groove \ -text "Remove (recursively) a directory from the module. The repository\ will not be changed until you do a commit." pack .subtract.top -side top -fill x message .subtract.middle -text $mess -aspect 200 pack .subtract.middle -side top -fill x frame .subtract.down button .subtract.down.remove -text "Remove" \ -command { grab release .subtract destroy .subtract cvs_remove_dir [workdir_list_files] } button .subtract.down.cancel -text "Cancel" \ -command { grab release .subtract; destroy .subtract } pack .subtract.down -side bottom -fill x -expand 1 pack .subtract.down.remove .subtract.down.cancel -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 wm title .subtract "Remove Directories" wm minsize .subtract 1 1 gen_log:log T "LEAVE" } proc file_input_and_do {title command} { global filename gen_log:log T "ENTER ($title $command)" toplevel .file_input_and_do grab set .file_input_and_do frame .file_input_and_do.top pack .file_input_and_do.top -side top -fill both -expand 1 -pady 4 -padx 4 label .file_input_and_do.top.lbl -text "File Name" -anchor w entry .file_input_and_do.top.entry -relief sunken -textvariable filename bind .file_input_and_do.top.entry \ { .file_input_and_do.ok invoke } pack .file_input_and_do.top.lbl -side left pack .file_input_and_do.top.entry -side left -fill x -expand 1 frame .file_input_and_do.bottom pack .file_input_and_do.bottom -side bottom -fill x -pady 4 -padx 4 button .file_input_and_do.ok -text "Ok" \ -command " .file_input_and_do.close invoke $command \$filename " button .file_input_and_do.close -text "Cancel" \ -command { grab release .file_input_and_do destroy .file_input_and_do } pack .file_input_and_do.ok .file_input_and_do.close \ -in .file_input_and_do.bottom \ -side left -fill both -expand 1 wm title .file_input_and_do $title wm minsize .file_input_and_do 1 1 focus .file_input_and_do.top.entry gen_log:log T "LEAVE" } proc release_dialog { args } { gen_log:log T "ENTER ($args)" set delflag "" toplevel .release grab set .release set filelist [join $args] message .release.top -justify left -aspect 300 -relief groove \ -text "Tell CVS that the directory is no longer being\ worked on. CVS will stop tracking it in the\ CVS history file. Optionally, delete the directory." pack .release.top -side top -fill x #set mess "This will release these directories:\n\n" #foreach file $filelist { #append mess " $file\n" #} #message .release.middle -text $mess -aspect 200 #pack .release.middle -side top -fill x checkbutton .release.binary -text "delete (-d)" \ -variable delflag -onvalue "-d" -offvalue "" pack .release.binary -side top frame .release.down button .release.down.release -text "Release" \ -command { grab release .release destroy .release cvs_release $delflag [workdir_list_files] } button .release.down.cancel -text "Cancel" \ -command { grab release .release; destroy .release } pack .release.down -side bottom -fill x -expand 1 pack .release.down.release .release.down.cancel -side left \ -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 wm title .release "Release Directories" wm minsize .release 1 1 gen_log:log T "LEAVE" } proc svn_update_options {} { global cvsglb global cvscfg gen_log:log T "ENTER" if {[winfo exists .svn_update]} { wm deiconify .svn_update raise .svn_update grab set .svn_update gen_log:log T "LEAVE" return } if {! [info exists cvsglb(tagmode_selection)]} { set cvsglb(tagmode_selection) "Keep" } toplevel .svn_update grab set .svn_update frame .svn_update.explaintop frame .svn_update.options frame .svn_update.down frame .svn_update.options.keep -relief groove -border 2 frame .svn_update.options.trunk -relief groove -border 2 frame .svn_update.options.branch -relief groove -border 2 frame .svn_update.options.revision -relief groove -border 2 pack .svn_update.down -side bottom -fill x pack .svn_update.explaintop -side top -fill x -pady 1 pack .svn_update.options -side top -fill x -pady 1 # Provide an explanation of this dialog box label .svn_update.explain -relief raised -bd 1 \ -text "Update files in local directory" pack .svn_update.explain \ -in .svn_update.explaintop -side top -fill x pack .svn_update.options.keep -side top -fill x pack .svn_update.options.trunk -side top -fill x pack .svn_update.options.branch -side top -fill x pack .svn_update.options.revision -side top -fill x # If the user wants to simply do a normal update radiobutton .svn_update.options.keep.select \ -text "Update to most recent revision on same branch or trunk." \ -variable cvsglb(tagmode_selection) -value "Keep" -anchor w message .svn_update.options.keep.explain -font $cvscfg(listboxfont) \ -justify left -width 400 \ -text "If local directory is on main trunk, get latest on main trunk. If local directory is on a branch, get latest on that branch." pack .svn_update.options.keep.select -side top -fill x pack .svn_update.options.keep.explain -side top -fill x -pady 1 -ipady 0 # If the user wants to update to the head revision radiobutton .svn_update.options.trunk.select \ -text "Switch local files to be on main trunk" \ -variable cvsglb(tagmode_selection) -value "Trunk" -anchor w message .svn_update.options.trunk.explain -font $cvscfg(listboxfont) \ -justify left -width 400 \ -text "Advice: If your local directories are currently on a branch, \ you may want to commit any local changes to that branch first." pack .svn_update.options.trunk.select -side top -fill x pack .svn_update.options.trunk.explain -side top -fill x -pady 1 -ipady 0 # If the user wants to update to a branch radiobutton .svn_update.options.branch.select \ -text "Switch local files to be on a branch" \ -variable cvsglb(tagmode_selection) -value "Branch" -anchor w frame .svn_update.options.branch.lblentry label .svn_update.lbranch -text "Branch" -anchor w entry .svn_update.tbranch -relief sunken -textvariable cvsglb(branchname) pack .svn_update.options.branch.select -side top -fill x pack .svn_update.options.branch.lblentry -side top -fill x \ -expand y -pady 1 -ipady 0 pack .svn_update.lbranch -in .svn_update.options.branch.lblentry \ -side left -fill x -pady 4 pack .svn_update.tbranch -in .svn_update.options.branch.lblentry \ -side left -fill x -padx 2 -pady 4 # Where user enters a revision number radiobutton .svn_update.options.revision.select \ -text "Update (-r) local files to be a specific revision:" \ -variable cvsglb(tagmode_selection) -value "Revision" -anchor w frame .svn_update.options.revision.lblentry label .svn_update.lrev -text "Revision" -anchor w entry .svn_update.trev -relief sunken -textvariable cvsglb(revnumber) pack .svn_update.options.revision.select -side top -fill x pack .svn_update.options.revision.lblentry -side top -fill x \ -expand y -pady 1 -ipady 0 pack .svn_update.lrev -in .svn_update.options.revision.lblentry \ -side left -fill x -pady 4 pack .svn_update.trev -in .svn_update.options.revision.lblentry \ -side left -fill x -padx 2 -pady 4 # The OK/Cancel buttons button .svn_update.ok -text "OK" \ -command { grab release .svn_update wm withdraw .svn_update svn_opt_update } button .svn_update.apply -text "Apply" \ -command svn_opt_update button .svn_update.quit -text "Close" \ -command { grab release .svn_update wm withdraw .svn_update } pack .svn_update.ok .svn_update.apply .svn_update.quit -in .svn_update.down \ -side left -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 # Window Manager stuff wm title .svn_update "Update from Repository" wm minsize .svn_update 1 1 gen_log:log T "LEAVE" } proc assemble_mergetags {from} { global cvscfg global current_tagname gen_log:log T "ENTER ($from)" # Construct tag names set totagbegin [string first "_BRANCH_" $cvscfg(mergetoformat)] set totagend [expr {$totagbegin + 8}] set toprefix [string range $cvscfg(mergetoformat) 0 [expr {$totagbegin - 1}]] set fromtagbegin [string first "_BRANCH_" $cvscfg(mergefromformat)] set fromprefix [string range $cvscfg(mergefromformat) 0 [expr {$fromtagbegin - 1}]] set datef [string range $cvscfg(mergetoformat) $totagend end] set today [clock format [clock seconds] -format "$datef"] if {[llength $current_tagname] == 1} { set curr_tag $current_tagname } else { set curr_tag "trunk" } set curr $curr_tag gen_log:log D "curr_tag $curr" if {$curr == "trunk"} {set curr $cvscfg(mergetrunkname)} if {$from == "trunk"} {set from $cvscfg(mergetrunkname)} set totag "${toprefix}_${curr}_$today" set fromtag "${fromprefix}_${from}_$today" # I had symbolic tags in mind, but some people are using untagged versions. # Substitute the dots, which are illegal for tagnames. regsub -all {\.} $totag {-} totag regsub -all {\.} $fromtag {-} fromtag gen_log:log T "LEAVE ($curr_tag $fromtag $totag)" return [list $curr_tag $fromtag $totag] } proc dialog_merge_notice {sys from frombranch fromtag totag filelist} { global cvscfg toplevel .reminder wm title .reminder "Tag and Commit" label .reminder.m1 -text \ "Now, you must examine the merged files and resolve any conflicts.\ \nLeave this dialog up, and when you are ready to commit,\ press the Ready button" button .reminder.ready -text "I'm ready" \ -command { foreach w {m2 totag fromtag bottom.ok} { .reminder.$w configure -state normal } foreach w {m1 ready} { .reminder.$w configure -state disabled } } label .reminder.m2 -text \ "If you check the box, TkCVS will apply the \"to\" tag,\ \ncommit your changes, and finally\napply the \"from\" tag.\ \nIf you don't check the box, the changes will be committed\ \nbut no tagging will be done" checkbutton .reminder.autotag -text "Apply these tags" \ -variable cvscfg(auto_tag) entry .reminder.totag -width 32 .reminder.totag insert end $totag entry .reminder.fromtag -width 32 .reminder.fromtag insert end $fromtag frame .reminder.bottom -relief raised -bd 2 button .reminder.bottom.cancel -text "Cancel" \ -command {destroy .reminder} button .reminder.bottom.ok -text "OK" \ -command "${sys}_merge_tag_seq $from $frombranch $totag $fromtag $filelist;\ destroy .reminder" pack .reminder.bottom -side bottom -fill x pack .reminder.bottom.ok -side left -expand yes pack .reminder.bottom.cancel -side right -expand yes pack .reminder.m1 -side top pack .reminder.ready -side top pack .reminder.m2 -side top pack .reminder.autotag -side top pack .reminder.fromtag -side top -padx 2 pack .reminder.totag -side top -padx 2 foreach w {m2 totag fromtag bottom.ok} { .reminder.$w configure -state disabled } } # Keep a log of commit log messages. We want to do this whether the # history has ever been examined by the user or not proc commit_history {comment} { global cvsglb set comment [string trimright $comment] set c 0 foreach ch [array names cvsglb commit_comment,*] { if {$comment eq $cvsglb($ch)} { # We already have this one. We don't have to # do anything else. gen_log:log D "Comment is a duplicate" return } incr c } # We don't have this one yet set cvsglb(commit_comment,$c) $comment gen_log:log D "New comment $c" if [winfo exists .ci_history] { .ci_history.text insert end "$comment" .ci_history.text insert end "\n" .ci_history.text insert end "================================================================================\n" } } # See the previous log messages proc history_browser {} { global cvsglb gen_log:log T "ENTER history_browser" if {! [winfo exists .ci_history]} { toplevel .ci_history wm protocol .ci_history WM_DELETE_WINDOW { wm withdraw .ci_history } wm title .ci_history "Commit Log History for Session" text .ci_history.text -setgrid yes -relief sunken -border 2 \ -exportselection 1 -yscroll ".ci_history.scroll set" scrollbar .ci_history.scroll -relief sunken \ -command ".ci_history.text yview" frame .ci_history.bottom search_textwidget_init button .ci_history.bottom.srchbtn -text Search \ -command "search_textwidget .ci_history.text" entry .ci_history.bottom.entry -width 20 -textvariable cvsglb(searchstr) bind .ci_history.bottom.entry \ "search_textwidget .ci_history.text" button .ci_history.bottom.close -text "Close" \ -command { wm withdraw .ci_history } pack .ci_history.bottom -side bottom -fill x pack .ci_history.scroll -side right -fill y pack .ci_history.text -fill both -expand 1 pack .ci_history.bottom.srchbtn -side left pack .ci_history.bottom.entry -side left pack .ci_history.bottom.close -side right # If this is the first time we've built the window, add the history we have so far foreach ch [array names cvsglb commit_comment,*] { .ci_history.text insert end $cvsglb($ch) .ci_history.text insert end "\n" .ci_history.text insert end "================================================================================\n" } } wm deiconify .ci_history gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/tkcvs/svn.tcl0000644000175000017500000017252711664612512014447 0ustar timtimproc svn_version {} { global cvsglb gen_log:log T "ENTER" # We don't need the version. We just need to know if mergeinfo is # going to work in this server/client combination. But let's not forget # how to get the version. #set cvsglb(svn_version) "" #set commandline "svn --version" #gen_log:log C "$commandline" #set ret [catch {eval "exec $commandline"} output] #if {$ret} { # cvsfail $output # return #} #foreach infoline [split $output "\n"] { # if {[string match "svn,*" $infoline]} { # set lr [split $infoline] # set version [lindex $lr 2] # gen_log:log D "version $version" # } #} #set cvsglb(svn_version) $version set commandline "svn log -g -l 1" set ret [catch {eval "exec $commandline"} output] if {$ret == 0} { set cvsglb(svn_mergeinfo_works) 1 gen_log:log D "svn mergeinfo works" } else { set cvsglb(svn_mergeinfo_works) 0 gen_log:log D "svn mergeinfo doesn't work" } gen_log:log T "LEAVE" } # Find SVN URL proc read_svn_dir {dirname} { global cvscfg global cvsglb global current_tagname global module_dir global cmd gen_log:log T "ENTER ($dirname)" # Whether mergeinfo works depends on the server as well as the local svn program, # so it may work for us in one repository but not another svn_version # svn info gets the URL # Have to do eval exec because we need the error output set command "svn info" gen_log:log C "$command" set ret [catch {eval "exec $command"} output] if {$ret} { cvsfail $output return 0 } foreach infoline [split $output "\n"] { if {[string match "URL*" $infoline]} { set cvscfg(url) [lrange $infoline 1 end] gen_log:log D "$cvscfg(url)" } } if {! [info exists cvscfg(url)]} { set cvscfg(url) "" } if {$cvscfg(url) == ""} { cvsfail "Can't get the SVN URL" return 0 } set root "" foreach s [list $cvscfg(svn_trunkdir) $cvscfg(svn_branchdir) $cvscfg(svn_tagdir)] { if {[regexp "/$s/" $cvscfg(url)] || [regexp "/$s" $cvscfg(url)]} { set spl [split $cvscfg(url) "/"] set root "" set relp "" set current_tagname "" set state P for {set j 0} {$j < [llength $spl]} {incr j} { set word [lindex $spl $j] switch -- $state { P { if {$word eq $cvscfg(svn_trunkdir)} { gen_log:log D "Matched $word for trunk" set type "trunk" set current_tagname $word set state E } elseif { $word eq $cvscfg(svn_branchdir)} { gen_log:log D "Matched $word for branches" set type "branches" set state W } elseif { $word eq $cvscfg(svn_tagdir)} { gen_log:log D "Matched $word for tags" set type "tags" set state W } else { append root "$word/" #gen_log:log D "No match for $word" } } W { set current_tagname $word set state E } E { lappend relp "$word" } default {} } } set cvscfg(svnroot) [string trimright $root "/"] set cvsglb(root) $cvscfg(svnroot) gen_log:log D "SVN URL: $cvscfg(url)" gen_log:log D "svnroot: $cvscfg(svnroot)" set cvsglb(relpath) [join $relp {/}] gen_log:log D "relpath: $cvsglb(relpath)" regsub -all {%20} $cvsglb(relpath) { } module_dir gen_log:log D "tagname: $current_tagname" } } if {$root == ""} { gen_log:log F "Nonconforming repository" puts "No conforming $cvscfg(svn_trunkdir)/$cvscfg(svn_branchdir)/$cvscfg(svn_tagdir) structure detected in the repository" puts " I won't be able to detect any branches or tags." gen_log:log D "SVN URL: $cvscfg(url)" set cvscfg(svnroot) $cvscfg(url) set cvsglb(root) $cvscfg(svnroot) gen_log:log D "svnroot: $cvscfg(svnroot)" set cvsglb(relpath) "" set cvsglb(svnconform) 0 gen_log:log T "LEAVE (-1)" return -1 } set cvsglb(svnconform) 1 gen_log:log T "LEAVE (0)" return 1 } proc svn_lock {do files} { global cvscfg if {$files == {}} { cvsfail "Please select one or more files!" .workdir return } switch -- $do { lock { set commandline "svn lock $files"} unlock { set commandline "svn unlock $files"} } set cmd [::exec::new "$commandline"] if {$cvscfg(auto_status)} { $cmd\::wait setup_dir } } # Get stuff for main workdir browser proc svn_workdir_status {} { global cmd global Filelist gen_log:log T "ENTER" set cmd(svn_status) [exec::new "svn status -uvN --xml"] set xmloutput [$cmd(svn_status)\::output] set entrylist [regexp -all -inline {} $xmloutput] if [info exists cmd(svn_status)] { $cmd(svn_status)\::destroy catch {unset cmd(svn_status)} } # do very simple xml parsing foreach entry $entrylist { set filename "" set cauthor "" set lockstatus "" set wrev "" set crev "" regexp {} $entry tmp filename regexp {} $entry wcstatusent if { [ regexp {} $entry repstatusent ] } { regexp {]*)>} $repstatusent tmp repstatusheader regexp {item=\"(\w+)\"} $repstatusheader tmp repstatus if { [ regexp {.*} $repstatusent replock ] } { set lockstatus "locked" } } else { set repstatus "" } regexp {(.*)} $wcstatusent tmp cauthor regexp {]*)>} $wcstatusent tmp wcstatusheader regexp {item=\"(\w+)\"} $wcstatusheader tmp wcstatus regexp {revision=\"(\w+)\"} $wcstatusheader tmp wrev if { [ regexp {.*} $wcstatusent wclock ] } { set lockstatus "havelock" } # wcstatus="added|normal|deleted|unversioned|modified|none # repstatus="modified|none" set status "" set displaymod "" if { [file exists $filename] && [file type $filename] == "link" } { set displaymod " " } if [file isdirectory $filename] { set displaymod " " } set mayhavelock false switch -exact -- $wcstatus { "normal" { if { $repstatus == "modified"} { append displaymod "Out-of-date" } else { append displaymod "Up-to-date" set mayhavelock true } } "modified" { if { $repstatus == "modified"} { append displaymod "Needs Merge" } else { append displaymod "Locally Modified" set mayhavelock true } } "added" { append displaymod "Locally Added" } "deleted" { append displaymod "Locally Removed" } "unversioned" { append displaymod "Not managed by SVN" } "conflicted" { append displaymod "Conflict" } L { append displaymod "Locked" } S { append displaymod "Switched to Branch" } "none" { append displaymod "Missing/Needs Update" } ~ { append displaymod "Dir/File Mismatch" } } #in some cases there might be locks: check now if { $mayhavelock } { switch -exact -- $lockstatus { "" { } "havelock" { append displaymod "/HaveLock" } "locked" { append displaymod "/Locked" } } } set Filelist($filename:wrev) $wrev set Filelist($filename:status) $displaymod set Filelist($filename:stickytag) "$wrev $crev" if {$wrev != "" && $crev != ""} { #set Filelist($filename:stickytag) "working:$wrev committed:$crev" set Filelist($filename:stickytag) "$wrev (committed:$crev)" } set Filelist($filename:option) "" set Filelist($filename:editors) "$cauthor" #gen_log:log D " \ \"$Filelist($filename:status)\" \ \"$wrev (committed:$crev)\" \ \"$Filelist($filename:editors)\" \ \"$filename\" \ " } gen_log:log T "LEAVE" } # does svn add from workdir browser proc svn_add {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { set mess "This will add all new files" } else { set mess "This will add these files:\n\n" foreach file $filelist { append mess " $file\n" } } if {$filelist == ""} { append filelist [glob -nocomplain $cvscfg(aster) .??*] } set addcmd [exec::new "svn add $filelist"] auto_setup_dir $addcmd gen_log:log T "LEAVE" } # does svn remove from workdir browser proc svn_remove {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] set command [exec::new "svn remove $filelist"] auto_setup_dir $command gen_log:log T "LEAVE" } # called from the workdir browser checkmark button proc svn_check {directory} { global cvscfg gen_log:log T "ENTER ($directory)" busy_start .workdir.main # Always show updates set flags "-u" # Only recurse if flag is set if {! $cvscfg(recurse)} { append flags "N" } # unknown files are removed by the filter but we might as well minimize # the work the filter has to do if {$cvscfg(status_filter)} { append flags "q" } set command "svn status $flags $directory" set check_cmd [viewer::new "SVN Status Check"] $check_cmd\::do "$command" 0 status_colortags busy_done .workdir.main gen_log:log T "LEAVE" } # svn update - called from workdir browser proc svn_update {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { append mess "\nThis will download from" append mess " the repository to your local" append mess " filespace ** ALL ** files which" append mess " have changed in it." } else { append mess "\nThis will download from" append mess " the repository to your local" append mess " filespace these files which" append mess " have changed:\n" } foreach file $filelist { append mess "\n\t$file" } append mess "\n\nAre you sure?" #set command "svn update --accept postpone" set command "svn update" if {[cvsconfirm $mess .workdir] == "ok"} { foreach file $filelist { append command " \"$file\"" } } else { return; } set co_cmd [viewer::new "SVN Update"] $co_cmd\::do "$command" 0 status_colortags auto_setup_dir $co_cmd gen_log:log T "LEAVE" } # Called from "update with options" dialog of workdir browser proc svn_opt_update {} { global cvscfg global cvsglb switch -exact -- $cvsglb(tagmode_selection) { "Keep" { set command "svn update" } "Trunk" { set command "svn switch $cvscfg(svnroot)/$cvscfg(svn_trunkdir)" } "Branch" { set command "svn switch $cvscfg(svnroot)/$cvscfg(svn_branchdir)/$cvsglb(branchname)" } "Revision" { # Let them get away with saying r3 instead of 3 set rev [string trimleft $cvsglb(revnumber) {r}] set command "svn update -r $rev" } } set upd_cmd [viewer::new "SVN Update/Switch"] $upd_cmd\::do "$command" 0 status_colortags auto_setup_dir $upd_cmd } # dialog for svn commit - called from workdir browser proc svn_commit_dialog {} { global cvsglb global cvscfg # If marked files, commit these. If no marked files, then # commit any files selected via listbox selection mechanism. # The cvsglb(commit_list) list remembers the list of files # to be committed. set cvsglb(commit_list) [workdir_list_files] # If we want to use an external editor, just do it if {$cvscfg(use_cvseditor)} { svn_commit "" "" $cvsglb(commit_list) return } if {[winfo exists .commit]} { destroy .commit } toplevel .commit #grab set .commit frame .commit.top -border 8 frame .commit.down -relief groove -border 2 pack .commit.top -side top -fill x pack .commit.down -side bottom -fill x frame .commit.comment pack .commit.comment -side top -fill both -expand 1 label .commit.comment.lcomment -text "Your log message" -anchor w button .commit.comment.history -text "Log History" \ -command history_browser text .commit.comment.tcomment -relief sunken -width 70 -height 10 \ -bg $cvsglb(textbg) -exportselection 1 \ -wrap word -border 2 -setgrid yes # Explain what it means to "commit" files message .commit.message -justify left -aspect 800 \ -text "This will commit changes from your \ local, working directory into the repository, recursively." pack .commit.message -in .commit.top -padx 2 -pady 5 button .commit.ok -text "OK" \ -command { #grab release .commit wm withdraw .commit set cvsglb(commit_comment) [.commit.comment.tcomment get 1.0 end] svn_commit $cvsglb(commit_comment) $cvsglb(commit_list) commit_history $cvsglb(commit_comment) } button .commit.apply -text "Apply" \ -command { set cvsglb(commit_comment) [.commit.comment.tcomment get 1.0 end] svn_commit $cvsglb(commit_comment) $cvsglb(commit_list) commit_history $cvsglb(commit_comment) } button .commit.clear -text "ClearAll" \ -command { set version "" .commit.comment.tcomment delete 1.0 end } button .commit.quit \ -command { #grab release .commit wm withdraw .commit } .commit.ok configure -text "OK" .commit.quit configure -text "Close" grid columnconf .commit.comment 1 -weight 1 grid rowconf .commit.comment 1 -weight 1 grid .commit.comment.lcomment -column 0 -row 0 grid .commit.comment.tcomment -column 1 -row 0 -rowspan 2 -padx 4 -pady 4 -sticky nsew grid .commit.comment.history -column 0 -row 1 pack .commit.ok .commit.apply .commit.clear .commit.quit -in .commit.down \ -side left -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 # Fill in the most recent commit message .commit.comment.tcomment insert end $cvsglb(commit_comment) wm title .commit "Commit Changes" wm minsize .commit 1 1 gen_log:log T "LEAVE" } # svn commit - called from commit dialog proc svn_commit {comment args} { global cvscfg gen_log:log T "ENTER ($comment $args)" set filelist [join $args] set commit_output "" if {$filelist == ""} { set mess "This will commit your changes to ** ALL ** files in" append mess " and under this directory." } else { foreach file $filelist { append commit_output "\n$file" } set mess "This will commit your changes to:$commit_output" } append mess "\n\nAre you sure?" set commit_output "" if {[cvsconfirm $mess .workdir] != "ok"} { return 1 } if {$cvscfg(use_cvseditor)} { # Starts text editor of your choice to enter the log message. update idletasks set command \ "$cvscfg(terminal) svn commit $filelist" gen_log:log C "$command" set ret [catch {eval "exec $command"} view_this] if {$ret} { cvsfail $view_this .workdir gen_log:log T "LEAVE ERROR ($view_this)" return } } else { if {$comment == ""} { cvsfail "You must enter a comment!" .commit return 1 } set v [viewer::new "SVN Commit"] regsub -all "\"" $comment "\\\"" comment $v\::do "svn commit -m \"$comment\" $filelist" 1 $v\::wait } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } # Called from workdir browser annotate button proc svn_annotate {revision args} { global cvscfg gen_log:log T "ENTER ($revision $args)" if {$revision != ""} { # We were given a revision set revflag "-r$revision" } else { set revflag "" } set filelist [join $args] if {$filelist == ""} { cvsfail "Annotate:\nPlease select one or more files !" .workdir gen_log:log T "LEAVE (Unselected files)" return } foreach file $filelist { annotate::new $revflag $file "svn" } gen_log:log T "LEAVE" } # Called from branch browser annotate button proc svn_annotate_r {revision filepath} { global cvscfg gen_log:log T "ENTER ($revision $filepath)" if {$revision != ""} { # We were given a revision set revflag "-r$revision" } else { set revflag "" } annotate::new $revflag $filepath "svn_r" gen_log:log T "LEAVE" } proc svn_patch { pathA pathB revA dateA revB dateB outmode outfile } { # # This creates a patch file between two revisions of a module. If the # second revision is null, it creates a patch to the head revision. # If both are null the top two revisions of the file are diffed. # global cvscfg gen_log:log T "ENTER ($pathA $pathB $revA $dateA $revB $dateB $outmode $outfile)" global cvs foreach {rev1 rev2} {{} {}} { break } if {$revA != {}} { set rev1 $revA } elseif {$dateA != {}} { set rev1 "\{\"$dateA\"\}" } if {$revB != {}} { set rev2 "$revB" } elseif {$dateA != {}} { set rev2 "\{\"$dateB\"\}" } set pathA [safe_url $pathA] set pathB [safe_url $pathB] if {$pathA != {} && $pathB != {}} { set command "svn diff $pathA $pathB" } elseif {$rev1 != {} && $rev2 != {}} { set command "svn diff $pathA@$rev1 $pathA@$rev2" } else { cvsfail "Specify either two paths OR one path and two revisions" return } if {$outmode == 0} { set v [viewer::new "SVN Diff"] $v\::do "$command" } else { set e [exec::new "$command"] set patch [$e\::output] gen_log:log F "OPEN $outfile" if {[catch {set fo [open $outfile w]}]} { cvsfail "Cannot open $outfile for writing" .modbrowse return } puts $fo $patch close $fo $e\::destroy gen_log:log F "CLOSE $outfile" } gen_log:log T "LEAVE" return } # Called from module browser filebrowse button proc svn_list {module} { global cvscfg gen_log:log T "ENTER ($module)" set v [viewer::new "SVN list -R"] $v\::do "svn list -Rv \"$cvscfg(svnroot)/$module\"" gen_log:log T "LEAVE" } # Called from the module browser proc svn_delete {root path} { gen_log:log T "ENTER ($root $path)" set mess "Really delete $path from the SVN repository?" if {[cvsconfirm $mess .modbrowse] != "ok"} { return } set url [safe_url $root/$path] set v [viewer::new "SVN delete"] set command "svn delete \"$url\" -m\"Removed_using_TkSVN\"" $v\::do "$command" modbrowse_run gen_log:log T "LEAVE" } # This is the callback for the folder-opener in ModTree proc svn_jit_listdir { tf into } { global cvscfg global cvsglb gen_log:log T "ENTER ($tf $into)" set cvscfg(svnroot) $cvsglb(root) #puts "\nEntering svn_jit_listdir ($into)" set dir [string trimleft $into / ] set command "svn list -v \"$cvscfg(svnroot)/$dir\"" #puts "$command" set cmd(svnlist) [exec::new "$command"] if {[info exists cmd(svnlist)]} { set contents [split [$cmd(svnlist)\::output] "\n"] $cmd(svnlist)\::destroy catch {unset cmd(svnlist)} } set dirs {} set fils {} foreach logline $contents { if {$logline == "" } continue gen_log:log D "$logline" if [string match {*/} $logline] { set item [lrange $logline 5 end] set item [string trimright $item "/"] if {$item ne "."} { lappend dirs "$item" set info($item) [lrange $logline 0 4] } } else { set item [lrange $logline 6 end] lappend fils "$item" set info($item) [lrange $logline 0 5] } } busy_start $tf ModTree:close $tf /$dir ModTree:delitem $tf /$dir/_jit_placeholder foreach f $fils { set command "ModTree:newitem $tf \"/$dir/$f\" \"$f\" \"$info($f)\" -image Fileview" set r [catch "$command" err] } foreach d $dirs { svn_jit_dircmd $tf $dir/$d } gen_log:log D "ModTree:open $tf /$dir" ModTree:open $tf /$dir #puts "\nLeaving svn_jit_listdir" busy_done $tf gen_log:log T "LEAVE" } proc svn_jit_dircmd { tf dir } { global cvscfg global Tree #gen_log:log T "ENTER ($tf $dir)" # Here we are just figuring out if the top level directory is empty or not. # We don't have to collect any other information, so no -v flag set command "svn list \"$cvscfg(svnroot)/$dir\"" #puts "$command" set cmd(svnlist) [exec::new "$command"] if {[info exists cmd(svnlist)]} { set contents [$cmd(svnlist)\::output] $cmd(svnlist)\::destroy catch {unset cmd(svnlist)} } set lbl "[file tail $dir]/" set exp "([llength $contents] items)" set parent "[file root $dir]" set dirs {} set fils {} foreach logline [split $contents "\n"] { if {$logline == ""} continue #gen_log:log D "$logline" if [string match {*/} $logline] { set item [string trimright $logline "/"] lappend dirs $item } else { lappend fils $logline } } # To avoid having to look ahead and build the whole tree at once, we put # a "marker" item in non-empty directories so it will look non-empty # and be openable if {$dirs == {} && $fils == {}} { catch "ModTree:newitem $tf \"/$dir\" \"$lbl\" \"$exp\" -image Folder" } else { # Newitem returns nothing if the item already exists, or an "after" from # buildwhenidle if the item had to be inserted set r [catch "ModTree:newitem $tf \"/$dir\" \"$lbl\" \"$exp\" -image Folder" err] if {! $r} { if {! $Tree($tf:/$dir:open)} { # If the node is already open, we don't need a placeholder catch "ModTree:newitem $tf \"/$dir/_jit_placeholder\" \"\" \"\" -image {}" } } } #gen_log:log T "LEAVE" } # called from module browser - list branches & tags proc parse_svnmodules {tf svnroot} { global cvscfg global modval gen_log:log T "ENTER ($tf $svnroot)" if {[catch "image type fileview"]} { workdir_images } set cvscfg(svnroot) $svnroot set command "svn list -v $svnroot" #puts "$command" set cmd(svnlist) [exec::new "$command"] if {[info exists cmd(svnlist)]} { set contents [$cmd(svnlist)\::output] $cmd(svnlist)\::destroy catch {unset cmd(svnlist)} } set dirs {} set fils {} foreach logline [split $contents "\n"] { if {$logline == "" } continue gen_log:log D "$logline" if [string match {*/} $logline] { set item [lrange $logline 5 end] if {$item ne "./"} { lappend dirs [string trimright $item "/"] } } else { set item [lrange $logline 6 end] lappend fils $item set info($item) [lrange $logline 0 5] } } foreach f $fils { catch "ModTree:newitem $tf \"/$f\" \"$f\" \"$info($f)\" -image Fileview" } foreach d $dirs { svn_jit_dircmd $tf $d } gen_log:log T "LEAVE" } # called from workdir Reports menu proc svn_log {args} { global cvscfg global cvsglb gen_log:log T "ENTER ($args)" set svncommand "svn log " # svn -g (mergeinfo) appeared in 1.5. It depends on the server # as well as the client, so we can't go by version number. we # just have to see if it works. # Do we want to do -g for all detail levels? Probably not for summary. if {$cvsglb(svn_mergeinfo_works)} { if {$cvscfg(ldetail) ne "summary"} { append svncommand "-g " } } set filelist [join $args] foreach file $filelist { set command $svncommand if {$cvscfg(ldetail) == "latest"} { append command "-r COMMITTED " } if {$cvscfg(ldetail) == "summary"} { append command "-q " } append command "\"$file\"" set logcmd [viewer::new "SVN Log $file ($cvscfg(ldetail))"] $logcmd\::do "$command" } gen_log:log T "LEAVE" } # called from branch browser proc svn_log_rev {filepath} { global cvscfg global cvsglb gen_log:log T "ENTER ($filepath)" set svncommand "svn log " # svn -g (mergeinfo) appeared in 1.5. It depends on the server # as well as the client, so we can't go by version number. we # just have to see if it works. if {$cvsglb(svn_mergeinfo_works)} { append svncommand "-g " } if {[regexp {/} $filepath]} { append svncommand "--stop-on-copy " } append svncommand "\"$filepath\"" set logcmd [viewer::new "SVN log $filepath"] $logcmd\::do "$svncommand" gen_log:log T "LEAVE" } proc svn_info {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] set urllist "" foreach file $filelist { append urllist $cvscfg(url)/$file append urllist " " } set command "svn info " append command $urllist set logcmd [viewer::new "SVN Info ($cvscfg(ldetail))"] $logcmd\::do "$command" gen_log:log T "LEAVE" } proc svn_merge_conflict {args} { global cvscfg gen_log:log T "ENTER ($args)" if {[llength $args] != 1} { cvsfail "Please select one file." return } set filelist [join $args] # See if it's really a conflict file foreach file $filelist { gen_log:log F "OPEN $file" set f [open $file] set match 0 while { [eof $f] == 0 } { gets $f line if { [string match "<<<<<<< *" $line] } { set match 1 break } } gen_log:log F "CLOSE $file" close $f if { $match != 1 } { cvsfail "$file does not appear to have a conflict." .workdir continue } # FIXME: we don't want to tie up the whole UI with tkdiff, but # if we don't wait, we have no way to know if we can mark resolved # Invoke tkdiff with the proper option for a conflict file # and have it write to the original file set command "$cvscfg(tkdiff) -conflict -o \"$file\" \"$file\"" gen_log:log C "$command" set ret [catch {eval "exec $command"} view_this] if {$ret == 0} { set mess "Mark $file resolved?" if {[cvsconfirm $mess .workdir] != "ok"} { continue } set command "svn resolved \"$file\"" exec::new $command } else { cvsfail "$view_this" .workdir } } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc svn_resolve {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] # See if it still has a conflict foreach file $filelist { gen_log:log F "OPEN $file" set f [open $file] set match 0 while { [eof $f] == 0 } { gets $f line if { [string match "<<<<<<< *" $line] } { set match 1 break } } gen_log:log F "CLOSE $file" close $f if {$match} { set mess "$file still contains \"<<<<<<< \" - \nUnmark anyway?" if {[cvsalwaysconfirm $mess .workdir] != "ok"} { continue } } gen_log:log D "Marking $file as resolved" set command [exec::new "svn resolved $file"] } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc svn_revert {args} { global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { set filelist "-R ." } gen_log:log D "Reverting $filelist" set command [exec::new "svn revert $filelist"] auto_setup_dir $command gen_log:log T "LEAVE" } proc svn_tag {tagname b_or_t update args} { # # This tags a file or directory in the current sandbox. # global cvscfg global cvsglb gen_log:log T "ENTER ($tagname $b_or_t $update $args)" if {$tagname == ""} { cvsfail "You must enter a tag name!" .workdir return 1 } set filelist [join $args] gen_log:log D "relpath: $cvsglb(relpath) filelist \"$filelist\"" if {$b_or_t == "tag" || $b_or_t == "tags"} {set pathelem "$cvscfg(svn_tagdir)"} if {$b_or_t == "branch"} {set pathelem "$cvscfg(svn_branchdir)"} set comment "${b_or_t}_copy_by_TkSVN" set v [viewer::new "SVN Copy $tagname"] set to_url "$cvscfg(svnroot)/$pathelem/$tagname/$cvsglb(relpath)" if { $filelist == {} } { set command "svn copy -m\"$comment\" $cvscfg(url) $to_url" $v\::log "$command" $v\::do "$command" } else { foreach f $filelist { if {$f == "."} { set command "svn copy -m\"comment\" $cvscfg(url) $to_url" } else { svn_pathforcopy $tagname $pathelem $v set from_url [safe_url $cvscfg(url)/$f] set command "svn copy -m\"$comment\" $from_url $to_url" } $v\::log "$command" $v\::do "$command" } } if {$update == "yes"} { # update so we're on the branch set command "svn switch $to_path" $v\::do "$command" 0 status_colortags $v\::wait } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc svn_rcopy {from_path b_or_t newtag} { # # makes a tag or branch. Called from the module browser # global cvscfg global cvsglb gen_log:log T "ENTER ($from_path $b_or_t $newtag)" # We're going to do some dangerous second-guessing here. If "trunk", or # something just below the "branches" or "tags" path, is selected, we guess # they want to copy its contents. # Otherwise, we'll copy exactly what they have selected. set need_list 0 set idx [string length $cvscfg(svnroot)] incr idx ;# advance past / set from_path_remainder [string range $from_path $idx end] set pathelements [file split $from_path_remainder] if {$from_path_remainder == "$cvscfg(svn_trunkdir)"} { set need_list 1 } elseif {[llength $pathelements] == 2} { set from_type [lindex $pathelements 0] switch -- $from_type { "$cvscfg(svn_tagdir)" - "$cvscfg(svn_branchdir)" { set need_list 1 } } } set comment "${b_or_t}_copy_by_TkSVN" set v [viewer::new "SVN Copy $newtag"] set comment "Copied_using_TkSVN" set to_path [svn_pathforcopy $newtag $b_or_t $v] if {! $need_list } { # Copy the selected path set command "svn copy -m\"$comment\" [safe_url $from_path] $to_path" $v\::do "$command" $v\::wait } else { # Copy the contents of the selected path set command "svn list [safe_url $from_path]" set cmd(svnlist) [exec::new "$command"] if {[info exists cmd(svnlist)]} { set contents [split [$cmd(svnlist)\::output] "\n"] $cmd(svnlist)\::destroy catch {unset cmd(svnlist)} } foreach f $contents { if {$f == ""} {continue} set file_from_path [safe_url "$from_path/$f"] set command "svn copy -m\"$comment\" $file_from_path $to_path" $v\::log "$command\n" $v\::do "$command" $v\::wait } } # Update with what we've done modbrowse_run svn gen_log:log T "LEAVE" } proc svn_pathforcopy {tagname b_or_t viewer} { # For svn copy, the destination path in the repository must already # exist. If we're tagging somewhere other than the top level, it may # not exist yet. This proc creates the path if necessary and returns # it to the calling proc, which will do the copy. global cvscfg global cvsglb gen_log:log T "ENTER (\"$tagname\" \"$b_or_t\" \"$viewer\")" # Can't use file join or it will mess up the URL set to_path [safe_url "$cvscfg(svnroot)/$b_or_t/$tagname"] set comment "${b_or_t}_directory_path_by_TkSVN" # If no file yet has this tag/branch name, create it set ret [catch "eval exec svn list $to_path" err] if {$ret} { set command "svn mkdir -m\"$comment\" $to_path" $viewer\::log "$command\n" $viewer\::do "$command" $viewer\::wait } # We may need to construct a path to copy the file to set cum_path "" set pathelements [file split $cvsglb(relpath)] set depth [llength $pathelements] for {set i 0} {$i < $depth} {incr i} { set cum_path [file join $cum_path [lindex $pathelements $i]] gen_log:log D " $i $cum_path" set ret [catch "eval exec svn list $to_path/$cum_path" err] if {$ret} { set command "svn mkdir -m\"$comment\" $to_path/$cum_path" $viewer\::do "$command" $viewer\::wait } } if {$cum_path != ""} { set to_path "$to_path/$cum_path" } gen_log:log T "LEAVE (\"$to_path\")" return $to_path } proc svn_merge {parent frompath since currentpath frombranch args} { # # This does a join (merge) of a chosen revision of localfile to the # current revision. # global cvscfg global cvsglb gen_log:log T "ENTER( \"$frompath\" \"$since\" \"$currentpath\" \"$frombranch\" $args)" set mergetags [assemble_mergetags $frombranch] set curr_tag [lindex $mergetags 0] set fromtag [lindex $mergetags 1] set totag [lindex $mergetags 2] regsub {^.*@} $frompath {r} from if {$since == {}} { set mess "Merge revision $from\n" } else { set mess "Merge the changes between revision\n $since and $from" append mess " (if $since > $from the changes are removed)\n" } append mess " to the current revision ($curr_tag)" if {[cvsalwaysconfirm $mess $parent] != "ok"} { return } # Do the update here, and defer the tagging until later #set commandline "svn merge --accept postpone \"$currentpath\" \"$frompath\"" set commandline "svn merge \"$currentpath\" \"$frompath\"" set v [viewer::new "SVN Merge"] $v\::do "$commandline" 1 status_colortags $v\::wait if [winfo exists .workdir] { if {$cvscfg(auto_status)} { setup_dir } } else { workdir_setup } dialog_merge_notice svn $from $frombranch $fromtag $totag $args gen_log:log T "LEAVE" } proc svn_merge_tag_seq {from frombranch totag fromtag args} { global cvscfg global cvsglb gen_log:log T "ENTER (\"$from\" \"$totag\" \"$fromtag\" $args)" set filelist "" foreach f $args { append filelist "\"$f\" " } # It's muy importante to make sure everything is OK at this point set commandline "svn status -uq $filelist" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] set logmode [expr {$ret ? {E} : {D}}] view_output::new "SVN Check" $view_this gen_log:log $logmode $view_this if {$ret} { set mess "SVN Check shows errors which would prevent a successful\ commit. Please resolve them before continuing." if {[cvsalwaysconfirm $mess .workdir] != "ok"} { return } } # Do the commit set v [viewer::new "SVN Commit a Merge"] $v\::log "svn commit -m \"Merge from $from\" $filelist\n" $v\::do "svn commit -m \"Merge from $from\" $filelist" 1 $v\::wait # Tag if desired (no means not a branch if {$cvscfg(auto_tag) && $fromtag != ""} { if {$frombranch == "trunk"} { set from_path "$cvscfg(svnroot)/$cvscfg(svn_trunkdir)/$cvsglb(relpath)" } else { set from_path "$cvscfg(svnroot)/$cvscfg(svn_branchdir)/$frombranch/$cvsglb(relpath)" } # tag the current (mergedto) branch svn_tag $fromtag tags false $args ;# opens its own viewer # Tag the mergedfrom branch set filelist [join $args] foreach file $filelist { if {$file == "."} { svn_rcopy [safe_url $from_path] "tags" $totag ;# opens its own viewer } else { svn_rcopy [safe_url $from_path/$f] "tags" $totag ;# opens its own viewer } } } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } # SVN Checkout or Export. Called from Repository Browser proc svn_checkout {dir url path rev target cmd} { gen_log:log T "ENTER ($dir $url $path $rev $target $cmd)" foreach {incvs insvn inrcs} [cvsroot_check $dir] { break } if {$insvn} { set mess "This is already a SVN controlled directory. Are you\ sure that you want to export into this directory?" if {[cvsconfirm $mess .modbrowse] != "ok"} { return } } set command "svn $cmd" if {$rev != {} } { # Let them get away with saying r3 instead of 3 set rev [string trimleft $rev {r}] append command " -r$rev" } set path [safe_url $path] append command " $url/$path" if {$target != {} } { append command " $target" } gen_log:log C "$command" set v [viewer::new "SVN $cmd"] $v\::do "$command" $v\::wait gen_log:log T "LEAVE" } # SVN cat or ls. Called from module browser proc svn_filecat {root path title} { gen_log:log T "ENTER ($root $path $title)" set url [safe_url $root/$path] # Should do cat if it's a file and ls if it's a path if {[string match {*/} $title]} { set command "svn ls \"$url\"" set wintitle "SVN ls" } else { set command "svn cat \"$url\"" set wintitle "SVN cat" } set v [viewer::new "$wintitle $url"] $v\::do "$command" } # SVN log. Called from module browser proc svn_filelog {root path title} { global cvsglb gen_log:log T "ENTER ($root $path $title)" set command "svn log " # svn -g (mergeinfo) appeared in 1.5. It depends on the server # as well as the client, so we can't go by version number. we # just have to see if it works. if {$cvsglb(svn_mergeinfo_works)} { append command "-g " } set url [safe_url $root/$path] append command "\"$url\"" set wintitle "SVN Log" set v [viewer::new "$wintitle $url"] $v\::do "$command" } proc svn_fileview {revision filename kind} { # This views a specific revision of a file in the repository. # For files checked out in the current sandbox. global cvscfg gen_log:log T "ENTER ($revision $filename $kind)" set command "cat" if {$kind == "directory"} { set command "ls" } if {$revision == {}} { set command "svn $command \"$filename\"" set v [viewer::new "$filename"] $v\::do "$command" } else { set command "svn $command -$revision \"$filename\"" set v [viewer::new "$filename Revision $revision"] $v\::do "$command" } gen_log:log T "LEAVE" } # Sends directory "." to the directory-merge tool proc svn_directory_merge {} { global cvscfg global cvsglb gen_log:log T "ENTER" gen_log:log D "Relative Path: $cvsglb(relpath)" ::svn_branchlog::new $cvsglb(relpath) . 1 gen_log:log T "LEAVE" } # Sends files to the SVN branch browser one at a time proc svn_branches {files} { global cvscfg global cvsglb gen_log:log T "ENTER ($files)" set filelist [join $files] if {$files == {}} { cvsfail "Please select one or more files!" .workdir return } gen_log:log D "Relative Path: $cvsglb(relpath)" foreach file $files { ::svn_branchlog::new $cvsglb(relpath) $file } gen_log:log T "LEAVE" } proc safe_url { url } { # Replacement is done in an ordered manner, so the key appearing # first in the list will be checked first, and so on. string is # only iterated over once. set url [string map { "%20" "%20" "%25" "%25" "%26" "%26" "%" "%25" "&" "%26" " " "%20" } $url] #regsub -all {%} $url {%25} url #regsub -all {&} $url {%26} url #regsub -all { } $url {%20} url # These don't seem to be necessary #regsub -all {\+} $url {%2B} url #regsub -all {\-} $url {%2D} url return $url } namespace eval ::svn_branchlog { variable instance 0 proc new {relpath filename {directory_merge {0}} } { variable instance set my_idx $instance incr instance namespace eval $my_idx { set my_idx [uplevel {concat $my_idx}] set filename [uplevel {concat $filename}] set relpath [uplevel {concat $relpath}] set directory_merge [uplevel {concat $directory_merge}] variable cmd_log variable lc variable revwho variable revdate variable revtime variable revlines variable revstate variable revcomment variable tags variable revbranches variable branchrevs variable logstate variable show_tags variable show_merges gen_log:log T "ENTER [namespace current]" if {$directory_merge} { set newlc [logcanvas::new . "SVN,loc" [namespace current]] set ln [lindex $newlc 0] set lc [lindex $newlc 1] set show_tags 0 } else { set newlc [logcanvas::new $filename "SVN,loc" [namespace current]] set ln [lindex $newlc 0] set lc [lindex $newlc 1] set show_tags [set $ln\::opt(show_tags)] } # Implementation of Perl-like "grep {/re/} in_list" proc grep_filter { re in_list } { set res "" foreach x $in_list { if {[regexp $re $x]} { lappend res $x } } return $res } proc abortLog { } { global cvscfg variable cmd_log variable lc gen_log:log D " $cmd_log\::abort" catch {$cmd_log\::abort} busy_done $lc pack forget $lc.stop pack $lc.close -in $lc.down.closefm -side right $lc.close configure -state normal } proc reloadLog { } { global cvscfg global cvsglb variable filename variable cmd_log variable lc variable ln variable revwho variable revdate variable revtime variable revcomment variable revkind variable revpath variable revname variable revtags variable revbtags variable revmergefrom variable branchrevs variable allrevs variable revbranches variable logstate variable relpath variable filename variable show_tags variable show_merges gen_log:log T "ENTER" catch { $lc.canvas delete all } catch { unset revwho } catch { unset revdate } catch { unset revtime } catch { unset revcomment } catch { unset revtags } catch { unset revbtags } catch { unset revmergefrom } catch { unset branchrevs } catch { unset revbranches } catch { unset revkind } catch { unset revpath } catch { unset revname } pack forget $lc.close pack $lc.stop -in $lc.down.closefm -side right $lc.stop configure -state normal # Can't use file join or it will mess up the URL set safe_filename [safe_url $filename] set path "$cvscfg(url)/$safe_filename" $ln\::ConfigureButtons $filename set show_merges [set $ln\::opt(show_merges)] set show_tags [set $ln\::opt(show_tags)] # Find out where to put the working revision icon (if anywhere) set revnum_current [set $ln\::revnum_current] set revnum_current r$revnum_current if { $relpath == {} } { set path "$cvscfg(svnroot)/$cvscfg(svn_trunkdir)/$safe_filename" } else { set path "$cvscfg(svnroot)/$cvscfg(svn_trunkdir)/$relpath/$safe_filename" } if {! $cvsglb(svnconform)} { set path "$cvscfg(svnroot)/$safe_filename" } # We need to go to the repository to find the highest revision. Doing # info on local files may not have it. Let's start with what we've got # though in case it fails. set highest_revision [string trimleft $revnum_current "r"] set command "svn info $path" gen_log:log C "$command" set ret [catch {eval "exec $command"} output] if {$ret} { gen_log:log D "This file $path must not be in the trunk" ## cvsfail $output } foreach infoline [split $output "\n"] { if {[string match "Revision*" $infoline]} { set highest_revision [lrange $infoline 1 end] gen_log:log D "$highest_revision" } } # The trunk set branchrevs(trunk) {} # There's nothing especially privileged about the trunk except that one # branch must not stop-on-copy. Maybe the file was added on a branch, # or maybe it isn't on the trunk anymore but it once was. We'll have # to use a range from r1 that case, to find it set range "${highest_revision}:1" set command "svn log -r $range $path" set cmd_log [exec::new $command {} 0 {} 1] set log_output [$cmd_log\::output] $cmd_log\::destroy set trunk_lines [split $log_output "\n"] set rr [parse_svnlog $trunk_lines trunk] # See if the current revision is on the trunk set curr 0 set brevs $branchrevs(trunk) set tip [lindex $brevs 0] set revpath($tip) $path set revkind($tip) "revision" set brevs [lreplace $brevs 0 0] if {$tip == $revnum_current} { # If current is at end of trunk do this. set branchrevs(trunk) [linsert $branchrevs(trunk) 0 {current}] set curr 1 } foreach r $brevs { if {$r == $revnum_current} { # We need to make a new artificial branch off of $r lappend revbranches($r) {current} } gen_log:log D " $r $revdate($r) ($revcomment($r))" set revkind($r) "revision" set revpath($r) $path } set branchrevs($rr) $branchrevs(trunk) set revkind($rr) "root" set revname($rr) "trunk" set revbtags($rr) "trunk" set revpath($rr) $path # if root is not empty added it to the branchlist if { $rr ne "" } { lappend branchlist $rr } # Branches # Get a list of the branches from the repository set command "svn list $cvscfg(svnroot)/$cvscfg(svn_branchdir)" set cmd_log [exec::new $command {} 0 {} 1] set branches [$cmd_log\::output] $cmd_log\::destroy # There can be files such as "README" here that aren't branches set branches [grep_filter {/$} $branches] foreach branch $branches { gen_log:log D "$branch" set branch [string trimright $branch "/"] # Can't use file join or it will mess up the URL gen_log:log D "BRANCHES: RELPATH \"$relpath\"" if { $relpath == {} } { set path "$cvscfg(svnroot)/$cvscfg(svn_branchdir)/$branch/$safe_filename" } else { set path "$cvscfg(svnroot)/$cvscfg(svn_branchdir)/$branch/$relpath/$safe_filename" } # Do stop-on-copy to find the base of the branch set command "svn log --stop-on-copy $path" set cmd_log [exec::new $command {} 0 {} 1] set log_output [$cmd_log\::output] $cmd_log\::destroy if {$log_output == ""} { continue } set loglines [split $log_output "\n"] set rb [parse_svnlog $loglines $branch] # See if the current revision is on this branch set curr 0 set brevs $branchrevs($branch) set tip [lindex $brevs 0] set revpath($tip) $path set revkind($tip) "revision" set brevs [lreplace $brevs 0 0] if {$tip == $revnum_current} { # If current is at end of the branch do this. set branchrevs($branch) [linsert $branchrevs($branch) 0 {current}] set curr 1 } foreach r $brevs { if {$r == $revnum_current} { # We need to make a new artificial branch off of $r lappend revbranches($r) {current} } gen_log:log D " $r $revdate($r) ($revcomment($r))" set revkind($r) "revision" set revpath($r) $path } set branchrevs($rb) $branchrevs($branch) set revkind($rb) "branch" # build a list of all branches so we can make sure each branch is on # a revbranch list so there will be a full set of branches on diagram lappend branchlist $rb set revname($rb) $branch lappend revbtags($rb) $branch set revpath($rb) $path set command "svn log -q $path" set cmd_log [exec::new $command {} 0 {} 1] set log_output [$cmd_log\::output] $cmd_log\::destroy if {$log_output == ""} { cvsfail "$command returned no output" return } set loglines [split $log_output "\n"] parse_q $loglines $branch # If current is HEAD of branch, the count is one too high because of the # You Are Here box, so the branchpoint would be too low set idx [llength $branchrevs($branch)] if {$curr} { gen_log:log E "Currently at Top" incr idx -1 } set bp [lindex $allrevs($branch) $idx] if {$bp == ""} { gen_log:log E "allrevs same as branchrevs: decrementing branchpoint" set bp [lindex $branchrevs($branch) end] set bpn [string trimleft $bp "r"] incr bpn -1 set bp "r${bpn}" } lappend revbranches($bp) $rb } # Tags # Get a list of the tags from the repository if {$show_tags} { set command "svn list $cvscfg(svnroot)/$cvscfg(svn_tagdir)" set cmd_log [exec::new $command {} 0 {} 1] set tags [$cmd_log\::output] $cmd_log\::destroy set n_tags [llength $tags] if {$n_tags > $cvscfg(toomany_tags)} { # If confirm is on, give them a chance to say yes or no to tags if {$cvscfg(confirm_prompt)} { set mess "There are $n_tags tags. It could take a long time " append mess "to process them. If you're willing to wait, " append mess " press OK and get a cup of coffee.\n" append mess "Otherwise, press Cancel and I will draw the " append mess "diagram now without showing tags. " append mess "You may wish to turn off\n" append mess "View -> Revision Layout -> Show Tags\n" append mess " and\n" append mess "View -> Save Options" if {[cvsconfirm $mess $lc] != "ok"} { set tags "" } } else { # Otherwise, just don't process tags set tags "" gen_log:log E "Skipping tags: $n_tags > cvscfg(toomany_tags) ($cvscfg(toomany_tags))" } } foreach tag $tags { gen_log:log D "$tag" # There can be files such as "README" here that aren't tags if {![string match {*/} $tag]} {continue} set tag [string trimright $tag "/"] # Can't use file join or it will mess up the URL gen_log:log D "TAGS: RELPATH \"$relpath\"" if { $relpath == {} } { set path "$cvscfg(svnroot)/$cvscfg(svn_tagdir)/$tag/$safe_filename" } else { set path "$cvscfg(svnroot)/$cvscfg(svn_tagdir)/$tag/$relpath/$safe_filename" } # Do log with stop-on-copy to find the actual revision that was tagged. # The tag itself created a rev which may be much higher. set command "svn log --stop-on-copy $path" set cmd_log [exec::new $command {} 0 {} 1] set log_output [$cmd_log\::output] $cmd_log\::destroy if {$log_output == ""} { continue } set loglines [split $log_output "\n"] set rb [parse_svnlog $loglines $tag] foreach r $branchrevs($tag) { gen_log:log D " $r $revdate($r) ($revcomment($r))" set revkind($r) "revision" set revpath($r) $path } set revkind($rb) "tag" set revname($rb) "$tag" set revpath($rb) $path # Now do log -q to find the previous rev, which is down # the list. For tags, it's only one down, so we can limit # the log to 2. It only speeds it up a little though. set command "svn log -q --limit 2 $path" set cmd_log [exec::new $command {} 0 {} 1] set log_output [$cmd_log\::output] $cmd_log\::destroy if {$log_output == ""} { cvsfail "$command returned no output" return } set loglines [split $log_output "\n"] parse_q $loglines $tag set bp [lindex $allrevs($tag) [llength $branchrevs($tag)]] lappend revtags($bp) $tag gen_log:log D " revtags($bp) $revtags($bp)" update idletasks } } # This is better than it used to be but there are still more propgets than there # could be, I think. We could match all the properties from one query instead of # just the one we're looking for if {$cvsglb(svn_mergeinfo_works) && $show_merges} { gen_log:log D "Finding all mergeprops" set bdirs {} lappend bdirs $cvscfg(svn_trunkdir) foreach b $branches { set b [string trimright $b "/"] lappend bdirs $cvscfg(svn_branchdir)/$b } gen_log:log D $bdirs set mergeprops {} foreach b $bdirs { gen_log:log D "$b" set cmd "svn propget svn:mergeinfo -r HEAD $cvscfg(svnroot)/$b/$relpath/$safe_filename" set cmd_prop [exec::new $cmd {} 0 {} 1] set propget_out [string trim [$cmd_prop\::output] "\n"] $cmd_prop\::destroy foreach mp $propget_out { if {[lsearch -exact $mergeprops $mp] < 0} { lappend mergeprops $mp } } } gen_log:log D "All merge properties: $mergeprops" #puts "All merge properties: $mergeprops\n" #puts "$bdirs" # Figure out where each property first appeared foreach mp $mergeprops { gen_log:log D "----------------" #puts "looking for $mp" gen_log:log D "looking for $mp" set found($mp) 0 foreach b $bdirs { #puts "looking for $mp in $b" set bt [file tail $b] # We don't need to look for merges from BranchB in BranchB do we? #puts "Searching /$b for $mp" if {[string match "/$b*" $mp]} { #puts " don't need to look in $b for $mp" continue } foreach br [lsort -dictionary $branchrevs($bt)] { regsub {^r} $br {} br if {$br eq "current" || $br == 1} continue set cmd "svn propget svn:mergeinfo -r $br $cvscfg(svnroot)/$b/$relpath/$safe_filename" set cmd_prop [exec::new $cmd {} 0 {} 1] set propget_out [string trim [$cmd_prop\::output] "\n"] $cmd_prop\::destroy if {$propget_out != ""} { #puts " $propget_out found in $br" foreach mr $propget_out { if {$mr eq $mp} { gen_log:log D " $mp found on rev $br of $b" #puts " == $mp found" set found($mp) 1 set spl [split $mp ":"] set fromrevs [lindex $spl 1] gen_log:log D " to r$br fromrevs $fromrevs" regsub {^.*-} $fromrevs {} lastfromrev # I don't understand something like /trunk/File1:3-10 when those revs aren't # on the trunk if {[lsearch -exact $branchrevs($bt) "r$lastfromrev"] > -1} continue set revmergefrom(r$br) "r$lastfromrev" gen_log:log D " revmergefrom(r$br) $revmergefrom(r$br)" } } } if {$found($mp)} break } if {$found($mp)} break } } } # sort the list in rev number order set brlist [lsort -dictionary $branchlist] gen_log:log D "init branches $brlist" # rebuild the list set branchlist {} foreach br $brlist { lappend branchlist $br # add to the list any revs that are in the branch revs # that also have revbranches if {[info exists branchrevs($br)]} { foreach r $branchrevs($br) { if {[info exists revbranches($r)] } { lappend branchlist $r } } } } set branchlist [lsort -dictionary $branchlist] gen_log:log D "branches $branchlist" # add any branches that are not on a revbranches list to the one closest # in numeric value # counter of branches in the list set brn 0 # get the length of the list so we can tell when we are done set brlistlen [llength $branchlist] while {$brn<$brlistlen} { # get the branch name set br [lindex $branchlist $brn] gen_log:log D " branch $brn is $br" # look at all the branches up to branch $br set subbrn 0 set subbrwithrevs r0 set subbrwithrevsnum 0 set foundinrevbr 0 while {$subbrn<$brn} { set subbr [lindex $branchlist $subbrn] # check each revbranch for this branch if {[info exists revbranches($subbr)]} { # remember the highest number rev with revbranches set subbrnum [string trimleft $subbr "r"] if { $subbrwithrevsnum < $subbrnum } { set subbrwithrevs $subbr set subbrwithrevsnum $subbrnum } foreach r $revbranches($subbr) { if {$r==$br} { # we found it in a revbranches incr foundinrevbr break } } } if {$foundinrevbr>0} { gen_log:log D " found $br in revbranch of $subbr" break } incr subbrn } if {$foundinrevbr<=0 && $subbrwithrevsnum!=0} { # we only want to attach a branch & not a rev that a branch is attached if { $revkind($br) eq "branch" } { gen_log:log D " put $br in revbranches of $subbrwithrevs" lappend revbranches($subbrwithrevs) $br } else { gen_log:log D " branch $br not attached because not a real branch" } } incr brn } pack forget $lc.stop pack $lc.close -in $lc.down.closefm -side right $lc.close configure -state normal set branchrevs(current) {} [namespace current]::svn_sort_it_all_out gen_log:log T "LEAVE" return } # Parses a --stop-on-copy log, getting information for each revision proc parse_svnlog {lines r} { variable revwho variable revdate variable revtime variable revcomment variable branchrevs gen_log:log T "ENTER (<...> $r)" set revnum "" set i 0 set l [llength $lines] while {$i < $l} { if { $i > 0 } { incr i -1 } set last [lindex $lines $i] incr i 1 set line [lindex $lines $i] gen_log:log D "$i of $l: $line" if { [ regexp {^[-]+$} $last ] && [ regexp {^r[0-9]+ \| .*line[s]?$} $line] } { if {[expr {$l - $i}] <= 1} {break} set line [lindex $lines $i] set splitline [split $line "|"] set revnum [string trim [lindex $splitline 0]] lappend branchrevs($r) $revnum set revwho($revnum) [string trim [lindex $splitline 1]] set date_and_time [string trim [lindex $splitline 2]] set revdate($revnum) [lindex $date_and_time 0] set revtime($revnum) [lindex $date_and_time 1] set notelen [lindex [string trim [lindex $splitline 3]] 0] gen_log:log D "revnum $revnum" gen_log:log D "revwho($revnum) $revwho($revnum)" gen_log:log D "revdate($revnum) $revdate($revnum)" gen_log:log D "revtime($revnum) $revtime($revnum)" gen_log:log D "notelen $notelen" incr i 2 set revcomment($revnum) "" set c 0 while {$c < $notelen} { append revcomment($revnum) "[lindex $lines [expr {$c + $i}]]\n" incr c } set revcomment($revnum) [string trimright $revcomment($revnum)] gen_log:log D "revcomment($revnum) $revcomment($revnum)" } incr i } gen_log:log T "LEAVE \"$revnum\"" return $revnum } # Parses a summary (-q) log to find what revisions are on it proc parse_q {lines r} { variable allrevs set allrevs($r) "" foreach line $lines { if [regexp {^r} $line] { gen_log:log D "$line" set splitline [split $line "|"] set revnum [string trim [lindex $splitline 0]] lappend allrevs($r) $revnum } } } proc svn_sort_it_all_out {} { global cvscfg global current_tagname variable filename variable lc variable ln variable revwho variable revdate variable revtime variable revcomment variable revkind variable revpath variable revname variable revtags variable revbtags variable branchrevs variable revbranches variable revmergefrom variable logstate variable revnum variable rootbranch variable revbranch gen_log:log T "ENTER" # Sort the revision and branch lists and remove duplicates foreach r [lsort -dictionary [array names revkind]] { gen_log:log D "revkind($r) $revkind($r)" #if {![info exists revbranches($r)]} {set revbranches($r) {} } } foreach r [lsort -dictionary [array names revpath]] { gen_log:log D "revpath($r) $revpath($r)" #if {![info exists revbranches($r)]} {set revbranches($r) {} } } gen_log:log D "" foreach a [lsort -dictionary [array names branchrevs]] { gen_log:log D "branchrevs($a) $branchrevs($a)" } gen_log:log D "" foreach a [lsort -dictionary [array names revbranches]] { # sort the rev branches to they will be displayed in increasing order set revbranches($a) [lsort -dictionary $revbranches($a)] gen_log:log D "revbranches($a) $revbranches($a)" } gen_log:log D "" foreach a [lsort -dictionary [array names revbtags]] { gen_log:log D "revbtags($a) $revbtags($a)" } gen_log:log D "" foreach a [lsort -dictionary [array names revtags]] { gen_log:log D "revtags($a) $revtags($a)" } gen_log:log D "" foreach a [lsort -dictionary [array names revmergefrom]] { gen_log:log D "revmergefrom($a) $revmergefrom($a)" } # We only needed these to place the you-are-here box. catch {unset rootbranch revbranch} $ln\::DrawTree now gen_log:log T "LEAVE" } [namespace current]::reloadLog return [namespace current] } } } tkcvs-8.2.3.orig/tkcvs/svn-import.tcl0000644000175000017500000000666311664612512015754 0ustar timtim# # Tcl Library for TkCVS # # # Adds a new document to the repository. # proc svn_import_run {} { global incvs insvn global cvsglb global cvscfg gen_log:log T "ENTER" cvsroot_check [pwd] if {$insvn} { cvsok "This directory is already in Subversion.\nCan\'t import here!" .svn_import gen_log:log T "LEAVE" return } elseif {$incvs} { cvsok "There are CVS directories here.\nPlease remove them first." .svn_import gen_log:log T "LEAVE" return } set cvsglb(imdir) [file tail [pwd]] # This is just a default. The user can change it. if {[info exists cvscfg(svnroot)] && $cvscfg(svnroot) != ""} { set cvsglb(imtop) $cvscfg(svnroot) } else { set cvsglb(imtop) "< URL Required >" } # Can't use file join or it will mess up the URL set cvsglb(imtop) "$cvsglb(imtop)/trunk" if {[winfo exists .svn_import]} { wm deiconify .svn_import raise .svn_import grab set .svn_import gen_log:log T "LEAVE" return } toplevel .svn_import grab set .svn_import frame .svn_import.top message .svn_import.top.explain -justify left -width 500 -relief groove \ -text "This will import the current directory and its sub-directories\ into SVN. If you haven't created a Subversion repository,\ you must do that first with \"svnadmin create.\"" label .svn_import.top.lsvnroot -text "URL of SVN Repository" -anchor w entry .svn_import.top.tsvnroot -textvariable cvsglb(imtop) grid .svn_import.top.explain -column 0 -row 0 -columnspan 3 -sticky ew #grid .svn_import.top.lnewdir -column 0 -row 1 -sticky w #grid .svn_import.top.tnewdir -column 1 -row 1 -sticky ew grid .svn_import.top.lsvnroot -column 0 -row 2 -sticky e grid .svn_import.top.tsvnroot -column 1 -row 2 -sticky ew frame .svn_import.down -relief groove -border 2 button .svn_import.down.ok -text "OK" \ -command { grab release .svn_import wm withdraw .svn_import svn_do_import $cvsglb(imtop) $cvsglb(imdir) } button .svn_import.down.quit -text "Cancel" \ -command { grab release .svn_import wm withdraw .svn_import } pack .svn_import.down -side bottom -expand yes -fill x pack .svn_import.top -side top -expand yes -fill x pack .svn_import.down.ok -side left -expand yes pack .svn_import.down.quit -side left -expand yes wm title .svn_import "Import a Project into Subversion" wm minsize .svn_import 1 1 gen_log:log T "LEAVE" } proc svn_do_import {imtop imdir} { global cvscfg gen_log:log T "ENTER" set imdir [pwd] set cwd [pwd] set commandline "svn import . $imtop -m \"Imported using TkCVS\"" set v [viewer::new "Import Project"] $v\::log "\nSVN Import\n" $v\::do "$commandline" $v\::wait update # Now check out the new module cd .. gen_log:log F "CD [pwd]" # We have to move the original stuff entirely out of the way. # Otherwise checkout won't do the whole tree. gen_log:log F "MOVE $imdir $imdir.orig" if {[file isdirectory $imdir.orig]} { file delete -force -- $imdir.orig } file rename $imdir $imdir.orig set commandline "svn checkout $imtop $imdir" $v\::log "\nSVN Checkout\n" $v\::do "$commandline" $v\::wait if {[catch "cd $imdir" err]} { # If we didn't check out the new dir sucessfully, put the old one back file rename $imdir.orig $imdir cvsok "$err" .isvn_mport } else { gen_log:log F "CD [pwd]" } setup_dir modbrowse_run svn gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/tkcvs/ui_misc.tcl0000644000175000017500000000704711664612512015263 0ustar timtim# Bindings to make canvases scroll. Canvases have no bindings at all # by default. proc scrollbindings {bindtag} { # Page keys bind $bindtag [list focus %W] bind $bindtag [list %W yview scroll 1 pages] bind $bindtag [list %W yview scroll -1 pages] bind $bindtag [list %W yview scroll -1 units] bind $bindtag [list %W yview scroll 1 units] bind $bindtag [list %W xview scroll -1 pages] bind $bindtag [list %W xview scroll 1 pages] # Middle button dragging bind $bindtag [list dragbind %W %x %y] # Wheelmouse bind $bindtag [list wheelbind %W %D] bind $bindtag [list %W yview scroll -1 pages] bind $bindtag [list %W yview scroll 1 pages] } proc dragbind {W x y} { set height [$W cget -height] if {$y < 0} {set y 0} if {$y > $height} {set y $height} set yfrac [expr {double($y) / $height}] set width [$W cget -width] if {$x < 0} {set x 0} if {$x > $height} {set x $height} set xfrac [expr {double($x) / $width}] eval $W yview moveto $yfrac eval $W xview moveto $xfrac } proc wheelbind {W D} { eval $W yview scroll [expr {-($D/120)*4}] units } proc bind_show {w {mode "-verbose"}} { puts $w foreach tag [bindtags $w] { puts "\t$tag" foreach spec [bind $tag] { puts "\t\t$spec" if {$mode == "-verbose"} { #bind $tag set comd [bind $tag $spec] set comd [string trim $comd "\n"] regsub -all "\n" $comd "\n\t\t\t" comd puts "\t\t\t$comd" } } } } # start and stop busy cursor proc busy_start {w} { foreach widget [winfo children $w] { catch {$widget config -cursor watch} } update idletasks } proc busy_done {w} { foreach widget [winfo children $w] { catch {$widget config -cursor ""} } } # Take a color like $d9d9d9 and darken it proc rgb_shadow {color} { set rgb_color [winfo rgb . $color] set shadow [format #%02x%02x%02x [expr (9*[lindex $rgb_color 0])/2560] \ [expr (9*[lindex $rgb_color 1])/2560] \ [expr (9*[lindex $rgb_color 2])/2560]] return $shadow } # See if two colors might too close to distinguish, for highlighting proc rgb_diff {c1 c2} { set rgb_c1 [winfo rgb . $c1] set rgb_c2 [winfo rgb . $c2] set r1 [lindex $rgb_c1 0] set g1 [lindex $rgb_c1 1] set b1 [lindex $rgb_c1 2] set r2 [lindex $rgb_c2 0] set g2 [lindex $rgb_c2 1] set b2 [lindex $rgb_c2 2] #puts "$r1 $g1 $b1" #puts "$r2 $g2 $b2" set maxdiff 0 set dr [expr {abs($r2 - $r1)}] if {$dr > $maxdiff} {set maxdiff $dr} set dg [expr {abs($g2 - $g1)}] if {$dg > $maxdiff} {set maxdiff $dg} set db [expr {abs($b2 - $b1)}] if {$db > $maxdiff} {set maxdiff $db} #puts "maxdiff: $maxdiff" return $maxdiff } proc is_gray {color} { set rgb_color [winfo rgb . $color] set r [lindex $rgb_color 0] set g [lindex $rgb_color 1] set b [lindex $rgb_color 2] set isgray 0 if {$r == $g && $r == $b} { set isgray 1 } return $isgray } proc static {args} { global staticvars set procName [lindex [info level -1] 0] foreach varPair $args { set varName [lindex $varPair 0] if {[llength $varPair] != 1} { set varValue [lrange $varPair 1 end] } else { set varValue {} } if {! [info exists staticvars($procName:$varName)]} { set staticvars($procName:$varName) $varValue } uplevel 1 "upvar #0 staticvars($procName:$varName) $varName" } } proc nop {} {} tkcvs-8.2.3.orig/tkcvs/diff.tcl0000644000175000017500000000466311664612512014544 0ustar timtim# NOTE: tkdiff exit status is nonzero if there are differences, so we # can't take it to mean failure proc comparediff {args} { # # This diffs a file with the repository (tkdiff ) # global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { cvsfail "Please select one or more files to compare!" .workdir } else { foreach file $filelist { regsub -all {\$} $file {\$} file gen_log:log C "$cvscfg(tkdiff) \"$file\"" set ret [catch {eval "exec $cvscfg(tkdiff) \"$file\" &"} view_this] if {$ret} { cvsfail $view_this .workdir } } } gen_log:log T "LEAVE" } # Two files or two SVN URLs proc comparediff_files {parent file1 file2} { global cvscfg gen_log:log T "ENTER ($file1 $file2)" gen_log:log C "$cvscfg(tkdiff) \"$file1\" \"$file2\"" set ret [catch {eval "exec $cvscfg(tkdiff) \"$file1\" \"$file2\" &"} view_this] if {$ret} { cvsfail $view_this $parent } gen_log:log T "LEAVE" } proc comparediff_r {rev1 rev2 parent file} { # # This diffs versions of a file, using one or two revisions (tkdiff -r1 [-r2] file) # global cvscfg global insvn gen_log:log T "ENTER (\"$rev1\" \"$rev2\" $file)" if {$rev1 == {} && $rev2 == {}} { cvsfail "Must have at least one revision number or tag for this function!" $parent return 1 } if {$rev1 != {}} { set rev1 [string trimleft $rev1 {r}] if {! $insvn} { set rev1 "-r \"$rev1\"" } } if {$rev2 != {}} { set rev2 [string trimleft $rev2 {r}] if {! $insvn} { set rev2 "-r \"$rev2\"" } } set commandline "$cvscfg(tkdiff) $rev1 $rev2 \"$file\"" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline &"} view_this] if {$ret} { cvsfail $view_this $parent } gen_log:log T "LEAVE" } proc comparediff_sandbox {rev1 rev2 parent file} { # # This diffs two revisions of a file that's not checked out # global cvscfg gen_log:log T "ENTER (\"$rev1\" \"$rev2\" $file)" if {$rev1 == {} && $rev2 == {}} { cvsfail "Must have at least one revision number or tag for this function!" $parent return 1 } if {$rev1 != {}} { set rev1 [string trimleft $rev1 {r}] set rev1 "-r \"$rev1\"" } if {$rev2 != {}} { set rev2 [string trimleft $rev2 {r}] set rev2 "-r \"$rev2\"" } set commandline "$cvscfg(tkdiff) $rev1 $rev2 \"$file\"" gen_log:log C "$commandline" cvs_sandbox_runcmd $commandline view_this gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/tkcvs/mkindex0000755000175000017500000000115311664612512014504 0ustar timtim#!/bin/sh #-*-tcl-*- # the next line restarts using tclsh \ exec tclsh "$0" -- ${1+"$@"} puts "making tclIndex" auto_mkindex . \ annotate.tcl \ branch_diagram.tcl \ style_params.tcl \ cvs.tcl \ dialog.tcl \ diff.tcl \ dircanvas.tcl \ errors.tcl \ exec.tcl \ filebrowse.tcl \ gen_log.tcl \ help.tcl \ import.tcl \ import2.tcl \ joincanvas.tcl \ modbrowse.tcl \ modtree.tcl \ modules.tcl \ picklist.tcl \ rcs.tcl \ ui_misc.tcl \ static.tcl \ svn-import.tcl \ svn.tcl \ tkcvs.tcl \ tkcvs_def.tcl \ tooltips.tcl \ vendor_merge.tcl \ workdir.tcl puts "done" exit tkcvs-8.2.3.orig/tkcvs/tkcvs0000755000175000017500000003677711664612512014223 0ustar timtim#!/bin/sh #-*-tcl-*- # the next line restarts using wish \ exec wish "$0" -- ${1+"$@"} # # TkCVS Main program -- A Tk interface to CVS. # # Uses a structured modules file -- see the manpage for more details. # # Author: Del (del@babel.dialix.oz.au) # # If we can't get this far (maybe because X display connection refused) # quit now. If we get further, the error message is very misleading. if {[info exists starkit::topdir]} { package require Tk } if {! [info exists tk_version] } { puts "Initialization failed" exit 1 } if {$tk_version < 8.4} { puts "TkCVS requires Tcl/Tk 8.4 or better!" exit 1 } if {[info exists starkit::topdir]} { set TclRoot [file join $starkit::topdir lib] set ScriptBin $starkit::topdir } else { if {[info exists TclRoot]} { # Perhaps we are being sourced recursively. # That would be bad. return } set Script [info script] set ScriptTail [file tail $Script] #puts "Tail $ScriptTail" if {[file type $Script] == "link"} { #puts "$Script is a link" set ScriptBin [file join [file dirname $Script] [file readlink $Script]] } else { set ScriptBin $Script } #puts " ScriptBin $ScriptBin" set TclRoot [file join [file dirname $ScriptBin]] #puts "TclRoot $TclRoot" if {$TclRoot == "."} { set TclRoot [pwd] } #puts "TclRoot $TclRoot" set TclRoot [file join [file dirname $TclRoot] "lib"] #puts "TclRoot $TclRoot" # allow runtime replacement if {[info exists env(TCLROOT)]} { set TclRoot $env(TCLROOT) } #puts "TclRoot $TclRoot" } set TclExe [info nameofexecutable] if {$tcl_platform(platform) == "windows"} { set TclExe [file attributes $TclExe -shortname] } set TCDIR [file join $TclRoot tkcvs] set cvscfg(bitmapdir) [file join $TclRoot tkcvs bitmaps] #puts "TCDIR $TCDIR" #puts "BITMAPDIR $cvscfg(bitmapdir)" set cvscfg(version) "8.2.3" if {! [info exists cvscfg(editorargs)]} { set cvscfg(editorargs) {} } set auto_path [linsert $auto_path 0 $TCDIR] set cvscfg(allfiles) false if {! [info exists cvscfg(startwindow)]} { set cvscfg(startwindow) "workdir" } set cvscfg(auto_tag) false set cvscfg(econtrol) false set cvscfg(use_cvseditor) false set maxdirs 15 set dirlist {} set totaldirs 0 if { [info exists env(HOME)] } { set cvscfg(home) $env(HOME) } else { set cvscfg(home) "~" } # Read in defaults if {[file exists [file join $TCDIR tkcvs_def.tcl]]} { source [file join $TCDIR tkcvs_def.tcl] } # This helps us recover from a problem left behind by tkcvs 7.2 set cvscfg(checkrecursive) false set optfile [file join $cvscfg(home) .tkcvs] if {[file exists $optfile]} { catch {source $optfile} } ::picklist::load # Set some defaults set cvsglb(sort_pref) [list $cvscfg(sortcol) "-decreasing"] set cvsglb(commit_comment) "" set cvsglb(cvs_version) "" set cvsglb(svn_version) "" if {$cvscfg(use_cvseditor) && ![info exists cvscfg(terminal)]} { cvserror "cvscfg(terminal) is required if cvscfg(use_cvseditor) is set" } # Hilight colors. Get the colorful ones. entry .testent set cvsglb(textbg) white set cvsglb(textfg) black set cvsglb(hlbg) [lindex [.testent configure -selectbackground] 4] set cvsglb(hlfg) [lindex [.testent configure -selectforeground] 4] if {$cvsglb(hlfg) eq {} } { # This happens on the Mac set cvsglb(hlfg) [lindex [.testent configure -foreground] 4] } set cvscfg(listboxfont) [lindex [.testent configure -font] 4] destroy .testent set WSYS [tk windowingsystem] #puts "Windowing sytem is $WSYS" set theme_system "unknown" if {$WSYS eq "x11"} { # If X11, see if we can sense our environment somehow label .testlbl -text "LABEL" if [get_cde_params] { set theme_system "CDE" # Find out what the default gui font is if { ! [info exists cvscfg(guifont)] } { # Find out what the tk default is set cvscfg(guifont) [lindex [.testlbl configure -font] 4] } # Put the Help menu back on the right if {$tk_version >= 8.5} { tk::classic::restore menu } #set cvsglb(canvbg) [lindex [.testlbl configure -background] 4] set cvsglb(canvbg) $cvsglb(shadow) } elseif [get_gtk_params] { set theme_system "GTK" if { ! [info exists cvscfg(guifont)] } { set cvscfg(guifont) [lindex [.testlbl configure -font] 4] if {$tk_version >= 8.5} { font configure TkDefaultFont -size 9 set cvscfg(guifont) TkDefaultFont option add *Menu.font $cvscfg(guifont) option add *Label.font $cvscfg(guifont) option add *Button.font $cvscfg(guifont) } } # in KDE or Gnome or some such. It rather rudely sets all the Tk # backgrounds the same which I don't like, so I'm going to use the same # trick I use for CDE to give the canvases a little shading. set cvsglb(bg) [lindex [.testlbl cget -background] 0] set cvsglb(fg) [lindex [.testlbl cget -foreground] 0] set cvsglb(canvbg) [rgb_shadow $cvsglb(bg)] } else { set bg [lindex [.testlbl cget -background] 0] set fg [lindex [.testlbl cget -foreground] 0] set hlbg "#4a6984" set hlfg "#ffffff" set textbg "#ffffff" set textfg "#000000" shades $bg set cvsglb(bg) $bg set cvsglb(fg) $fg set cvsglb(textbg) $textbg set cvsglb(textfg) $textfg set cvsglb(hlbg) $hlbg set cvsglb(hlfg) $hlfg option add *Canvas.Background $cvsglb(shadow) option add *Canvas.Foreground black option add *Entry.Background $textbg option add *Entry.Foreground $textfg option add *Entry.selectBackground $hlbg option add *Entry.selectForeground $hlfg option add *Entry.readonlyBackground $bg option add *Listbox.background $textbg option add *Listbox.selectBackground $hlbg option add *Listbox.selectForeground $hlfg option add *Text.Background $textbg option add *Text.Foreground $textfg option add *Text.selectBackground $hlbg option add *Text.selectForeground $hlfg option add *Button.activeForeground $fg option add *Menu.activeForeground $fg # checkbuttons and radiobuttons if {$tk_version >= 8.5} { option add *Menu.selectColor $fg option add *Checkbutton.selectColor "#ffffff" option add *Radiobutton.selectColor "#ffffff" } else { option add *selectColor $hlbg } if { ! [info exists cvscfg(guifont)] } { set cvscfg(guifont) [lindex [.testlbl configure -font] 4] if {$tk_version >= 8.5} { # This makes it look like tk8.4. Want? dunno. font configure TkHeadingFont -size 9 set cvscfg(guifont) TkHeadingFont option add *Menu.font $cvscfg(guifont) option add *Label.font $cvscfg(guifont) option add *Button.font $cvscfg(guifont) } } } destroy .testlbl if {! [info exists cvscfg(dialogfont)]} { set cvscfg(dialogfont) $cvscfg(guifont) } if {$theme_system == "CDE"} { # This makes it consistent with the rest of the CDE interface option add *Menu.font $cvscfg(guifont) option add *Label.font $cvscfg(guifont) option add *Button.font $cvscfg(guifont) } #puts " Theme system: $theme_system" } else { # Find out what the default gui font is label .testlbl -text "LABEL" # Find out what the tk default is set cvscfg(guifont) [lindex [.testlbl configure -font] 4] set cvscfg(dialogfont) $cvscfg(guifont) set cvsglb(canvbg) [lindex [.testlbl configure -background] 4] set cvsglb(bg) [lindex [.testlbl cget -background] 0] set cvsglb(fg) [lindex [.testlbl cget -foreground] 0] set cvsglb(canvbg) [rgb_shadow $cvsglb(bg)] destroy .testlbl if {$WSYS eq "aqua"} { # keep evertyhing from being blinding white # button highlightbackground has to be the same as background # or else there are little white boxes around the button "pill" option add *background #ebebeb userDefault option add *Button.highlightBackground #ebebeb userDefault # That totally screws up the menus unless you fix it with this option add *Menu.Background white option add *Menu.Foreground black option add *Entry.HighlightThickness 2 userDefault option add *Entry.highlightBackground $cvsglb(hlbg) userDefault option add *Canvas.background #eeeeee userDefault option add *Entry.background #ffffff userDefault option add *Text.background white userDefault } } # Fonts for the canvas "listboxes" set cvscfg(flashfont) $cvscfg(listboxfont) set fsiz [lindex $cvscfg(listboxfont) 1] set lbf [font actual $cvscfg(listboxfont)] #puts "$tcl_platform(os) $tk_patchLevel [winfo server .]" #puts "listboxfont: $cvscfg(listboxfont)" #puts "actual listboxfont: $lbf" set ffam [lindex $lbf 1] set fsiz [lindex $lbf 3] regsub -- {-} $fsiz {} fsiz if {[tk windowingsystem] eq "x11"} { if {$tk_version >= 8.5} { # Put the help menu back on the right #tk::classic::restore menu if {$tcl_platform(os) eq "Linux" && [lindex $tk_patchLevel 2] < 8} { # FIXME: check for X.Org in server string? #puts [winfo server .] # Overstrike and underline fonts in Xorg were too big from 8.5.0 to 8.5.7 set fsiz -$fsiz } } } set cvscfg(flashfont) [list $ffam $fsiz underline] #puts "requested flashfont: $cvscfg(flashfont)" #puts "actual flashfont: [font actual $cvscfg(flashfont)]" #puts "try underline: [font actual $cvscfg(flashfont) -underline]" # Prefer underline, but it isn't working at all in tk8.5.0 on linux if {! [font actual $cvscfg(flashfont) -underline]} { puts "Underline font not working. Trying $ffam $fsiz bold" puts " (known problem in Tk 8.5.0 on Linux)" set cvscfg(flashfont) [list $ffam $fsiz bold] } #puts "final flashfont: $cvscfg(flashfont)" option add *ToolTip.background "LightGoldenrod1" userDefault option add *ToolTip.foreground "black" userDefault # This makes tk_messageBox use our font. The default tends to be terrible # no matter what platform option add *Dialog.msg.font $cvscfg(dialogfont) userDefault # Sometimes we'll want to override this but let's set a default option add *Message.font $cvscfg(dialogfont) userDefault # Initialize logging (classes are C,F,T,D) if { ! [info exists cvscfg(log_classes)] } { set cvscfg(log_classes) "C" } foreach class [split $cvscfg(log_classes) {}] { set logclass($class) $class } if { ! [info exists cvscfg(logging)] } { set cvscfg(logging) false } if {$cvscfg(logging)} { gen_log:init } # # Add directory where we last ran to the menu list if { ! [info exists cvscfg(lastdir)] } { set cvscfg(lastdir) [pwd] } # # Command line options # set usage "Usage:" append usage "\n tkcvs \[-dir \] \[-root \] \[-win workdir|module|merge\]" append usage "\n tkcvs \[-dir \] \[-root \] \[-log|blame \]" append usage "\n tkcvs - same as tkcvs -log " for {set i 0} {$i < [llength $argv]} {incr i} { set arg [lindex $argv $i] set val [lindex $argv [expr {$i+1}]] switch -regexp -- $arg { {^--*d.*} { # -ddir: Starting directory set dir $val; incr i cd $val } {^--*r.*} { # -root: CVS root set cvscfg(cvsroot) $val; incr i } {^--*w.*} { # workdir|module|merge: window to start with. workdir is default. set cvscfg(startwindow) $val; incr i } {^--*l.*} { # -log : Browse the log of specified file set cvscfg(startwindow) log set lcfile $val; incr i } {^--*[ab].*} { # annotate|blame: Browse colorcoded history of specified file set cvscfg(startwindow) blame set lcfile $val; incr i } {^-psn_.*} { # Ignore the Carbon Process Serial Number incr i } {^--*h.*} { puts $usage exit 0 } {\w*} { # If a filename is provided as an argument, assume -log set cvscfg(startwindow) log set lcfile $arg; incr i } default { puts $usage exit 1 } } } if {[info exists lcfile]} { set d [file dirname $lcfile] set f [file tail $lcfile] set lcfile $f cd $d } # If CVSROOT envvar is set, use it if { ! [info exists cvscfg(cvsroot)] } { if { ! [info exists env(CVSROOT)] } { #puts "warning: your \$CVSROOT environment variable is not set." set cvscfg(cvsroot) "" } else { set cvscfg(cvsroot) $env(CVSROOT) } } # This helps with Samba-mounted CVS repositories # And also completely messes up SVN repositories #set cvscfg(cvsroot) [file join $cvscfg(cvsroot)] # If SVNROOT is set, use that instead. SVNROOT isn't # known by Subversion itself, so if it's set we must have # done it for the present purpose if {! [info exists cvscfg(svnroot)] } { if { [info exists env(SVNROOT)] } { set cvscfg(svnroot) $env(SVNROOT) } else { set cvscfg(svnroot) "" } } set cvsglb(root) $cvscfg(cvsroot) if {$cvscfg(svnroot) != ""} { set cvsglb(root) $cvscfg(svnroot) } # Thought better of saving this catch unset cvscfg(svnconform_seen) if {![info exists cvscfg(ignore_file_filter)]} { set cvscfg(ignore_file_filter) "" } # Remember what the setting was. We'll have to restore it after # leaving a directory with a .cvsignore file. set cvsglb(default_ignore_filter) $cvscfg(ignore_file_filter) #foreach c [lsort [array names cvscfg]] { #gen_log:log D "cvscfg($c) $cvscfg($c)" #} # Load the images that are used in more than one module image create photo Log \ -format gif -file [file join $cvscfg(bitmapdir) log.gif] image create photo Checkout \ -format gif -file [file join $cvscfg(bitmapdir) checkout.gif] image create photo CheckoutOpts \ -format gif -file [file join $cvscfg(bitmapdir) checkout_opts.gif] image create photo Export \ -format gif -file [file join $cvscfg(bitmapdir) export.gif] image create photo Tag \ -format gif -file [file join $cvscfg(bitmapdir) tag.gif] image create photo Branchtag \ -format gif -file [file join $cvscfg(bitmapdir) branchtag.gif] image create photo Import \ -format gif -file [file join $cvscfg(bitmapdir) import.gif] image create photo Mergebranch \ -format gif -file [file join $cvscfg(bitmapdir) newmerge_simple.gif] image create photo Mergediff \ -format gif -file [file join $cvscfg(bitmapdir) newmerge.gif] image create photo Man \ -format gif -file [file join $cvscfg(bitmapdir) man.gif] set incvs 0 set insvn 0 set inrcs 0 # Create a window # Start with Module Browser if {[string match {mod*} $cvscfg(startwindow)]} { wm withdraw . foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } if {$insvn} { set cvsglb(root) $cvscfg(svnroot) modbrowse_run svn } else { # We still don't know if it's SVN or CVS. Let modbrowse_run figure out. modbrowse_run } # Start with Branch Browser } elseif {$cvscfg(startwindow) == "log"} { if {! [file exists $lcfile]} { puts "ERROR: $lcfile doesn't exist!" exit 1 } wm withdraw . foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } if {$incvs} { cvs_branches \"$lcfile"\ } elseif {$inrcs} { set cwd [pwd] set module_dir "" rcs_branches \"$lcfile\" } elseif {$insvn} { svn_branches \"$lcfile\" } else { puts "File doesn't seem to be in CVS, SVN, or RCS" } # Start with Annotation Browser } elseif {$cvscfg(startwindow) == "blame"} { if {! [file exists $lcfile]} { puts "ERROR: $lcfile doesn't exist!" exit 1 } wm withdraw . foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } if {$incvs} { cvs_annotate "" \"$lcfile"\ } elseif {$insvn} { svn_annotate "" \"$lcfile\" } else { puts "File doesn't seem to be in CVS or SVN" } # Start with Directory Merge } elseif {[string match {mer*} $cvscfg(startwindow)]} { wm withdraw . foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } if {$incvs} { cvs_joincanvas } elseif {$insvn} { svn_directory_merge } else { puts "Directory doesn't seem to be in CVS or SVN" } # The usual way, with the Workdir Browser } else { workdir_setup } tkcvs-8.2.3.orig/tkcvs/cvs.tcl0000644000175000017500000021365111664612512014426 0ustar timtim# # Tcl Library for TkCVS # # # Contains procedures used in interaction with CVS. # proc cvs_notincvs {} { cvsfail "This directory is not in CVS." .workdir } proc cvs_incvs {} { cvsfail "You can\'t do that here because this directory is already in CVS." .workdir } # # Create a temporary directory # cd to that directory # run the CVS command in that directory # # returns: the current wd (ERROR) or the sandbox directory (OK) # proc cvs_sandbox_runcmd {command output_var} { global cvscfg global cwd upvar $output_var view_this # Big note: the temp directory fed to a remote servers's command line # needs to be seen by the server. It can't cd to an absolute path. # In addition it's fussy about where you are when you do a checkout -d. # Best avoid that altogether. gen_log:log T "ENTER ($command $output_var)" set pid [pid] if {! [file isdirectory $cvscfg(tmpdir)]} { gen_log:log F "MKDIR $cvscfg(tmpdir)" file mkdir $cvscfg(tmpdir) } cd $cvscfg(tmpdir) gen_log:log F "CD [pwd]" if {! [file isdirectory cvstmpdir.$pid]} { gen_log:log F "MKDIR cvstmpdir.$pid" file mkdir cvstmpdir.$pid } cd cvstmpdir.$pid gen_log:log F "CD [pwd]" gen_log:log C "$command" set ret [catch {eval "exec $command"} view_this] gen_log:log T "RETURN $cvscfg(tmpdir)/cvstmpdir.$pid" return $cvscfg(tmpdir)/cvstmpdir.$pid } # # cvs_sandbox_filetags # assume that the sandbox contains the checked out files # return a list of all the tags in the files # proc cvs_sandbox_filetags {mcode filenames} { global cvscfg global cvs set pid [pid] set cwd [pwd] gen_log:log T "ENTER ($mcode $filenames)" cd [file join $cvscfg(tmpdir) cvstmpdir.$pid $mcode] set commandline "$cvs log $filenames" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] if {$ret} { cd $cwd cvsfail $view_this .merge gen_log:log T "LEAVE ERROR" return $keepers } set view_lines [split $view_this "\n"] foreach line $view_lines { if {[string index $line 0] == "\t" } { regsub -all {[\t ]*} $line "" tag append keepers "$tag " } } cd $cwd gen_log:log T "LEAVE" return $keepers } proc cvs_workdir_status {} { global cvscfg global cvs global Filelist gen_log:log T "ENTER" set cmd(cvs_status) [exec::new "$cvs -n -q status -l"] set status_lines [split [$cmd(cvs_status)\::output] "\n"] if {$cvscfg(showeditcol)} { set cmd(cvs_editors) [exec::new "$cvs -n -q editors -l"] set editors_lines [split [$cmd(cvs_editors)\::output] "\n"] } if {$cvscfg(cvslock)} { set cmd(cvs_lockers) [exec::new "$cvs log"] set lockers_lines [split [$cmd(cvs_lockers)\::output] "\n"] } if {[info exists cmd(cvs_status)]} { # gets cvs status in current directory only, pulling out lines that include # Status: or Sticky Tag:, putting each file's info (name, status, and tag) # into an array. set datestatus_seen 0 $cmd(cvs_status)\::destroy catch {unset cmd(cvs_status)} foreach logline $status_lines { if {[string match "File:*" $logline]} { regsub -all {\t+} $logline "\t" logline set line [split [string trim $logline] "\t"] gen_log:log D "$line" # Should be able to do these regsubs in one expression regsub {File: } [lindex $line 0] "" filename regsub {\s*$} $filename "" filename regsub {Status: } [lindex $line 1] "" status set Filelist($filename:status) $status # Don't set editors to null because we'll use its presence # or absence to see if we need to re-read the repository when # we ask to map the editors column } elseif {[string match "*Working revision:*" $logline]} { regsub -all {\t+} $logline "\t" logline set line [split [string trim $logline] "\t"] gen_log:log D "$line" set revision [lindex $line 1] regsub {New .*} $revision "New" revision set date [lindex $line 2] #puts "from Working Revision: $date" # The date field is not supplied to remote clients. if {$date == "" } { if {! ([string match "New *" $date ] || [string match "Result *" $date])} { catch {set date [clock format [file mtime $filename] -format $cvscfg(dateformat)]} if {! $datestatus_seen} { # We only need to see this message once per directory set datestatus_seen 1 gen_log:log E "No date supplied by remote CVS server. Using \[file mtime\]" } } } else { # CVS outputs time strings tcl can't handle, such as # ones with +0100. Let's discard them rather than # trying to convert them. set ret [catch {clock scan $date -gmt yes} ans] if {$ret == 0} { set juliandate $ans set date [clock format $juliandate -format $cvscfg(dateformat)] #puts "Clock Scan on $ans: $date" } else { gen_log:log E "$ans" } } set Filelist($filename:date) $date set Filelist($filename:wrev) $revision set Filelist($filename:status) $status } elseif {[string match "*Sticky Tag:*" $logline]} { regsub -all {\t+} $logline "\t" logline set line [split [string trim $logline] "\t"] gen_log:log D "$line" set tagline [lindex $line 1] set t0 [lindex $tagline 0] set t1 [lrange $tagline 1 end] set stickytag "" if { $t0 == "(none)" } { set stickytag " on trunk" } elseif {[string match "(branch:*" $t1 ]} { regsub {\(branch: (.*)\)} $t1 {\1} t1 set stickytag " on $t0 branch" } elseif {[string match "(revision:*" $t1 ]} { set stickytag " $t0" } set Filelist($filename:stickytag) "$revision $stickytag" } elseif {[string match "*Sticky Options:*" $logline]} { regsub -all {\t+} $logline "\t" logline set line [split [string trim $logline] "\t"] gen_log:log D "$line" set option [lindex $line 1] set Filelist($filename:option) $option } } } if {[info exists cmd(cvs_editors)]} { set filename {} $cmd(cvs_editors)\::destroy catch {unset cmd(cvs_editors)} foreach logline $editors_lines { set line [split $logline "\t"] gen_log:log D "$line" set ell [llength $line] # ? files will show up in cvs editors output under certain conditions if {$ell < 5} { continue } #if there is no filename, then this is a continuation line set f [lindex $line 0] if {$f == {}} { append editors ",[lindex $line 1]" } else { if {$filename != {}} { set Filelist($filename:editors) $editors } set filename $f set editors [lindex $line 1] } gen_log:log D " $filename $editors" } if {$filename != {}} { set Filelist($filename:editors) $editors } } if {[info exists cmd(cvs_lockers)]} { set filename {} set lockers {} $cmd(cvs_lockers)\::destroy catch {unset cmd(cvs_lockers)} foreach line $lockers_lines { if {[string match "Working file: *" $line]} { gen_log:log D "$line" regsub "Working file: " $line "" filename } if {[string match "*locked by:*" $line]} { gen_log:log D "$line" if {$filename != {}} { set p [lindex $line 4] set r [lindex $line 1] set p [string trimright $p {;}] gen_log:log D " $filename $p\($r\)" append Filelist($filename:editors) $p\($r\) } } } } gen_log:log T "LEAVE" } proc cvs_remove {args} { # # This deletes a file from the directory and the repository, # asking for confirmation first. # global cvs global incvs global cvscfg gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } set filelist [join $args] set success 1 set faillist "" foreach file $filelist { file delete -force -- $file gen_log:log F "DELETE $file" if {[file exists $file]} { set success 0 append faillist $file } } if {$success == 0} { cvsfail "Remove $file failed" .workdir return } set cmd(cvscmd) [exec::new "$cvs remove $filelist"] auto_setup_dir $cmd(cvscmd) gen_log:log T "LEAVE" } proc cvs_remove_dir {args} { # This removes files recursively. global cvs global incvs global cvscfg gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } set filelist [join $args] if {$filelist == ""} { cvsfail "Please select a directory!" .workdir return } else { set mess "This will remove the contents of these directories:\n\n" foreach file $filelist { append mess " $file\n" } } set v [viewer::new "CVS Remove directory"] set awd [pwd] foreach file $filelist { if {[file isdirectory $file]} { set awd [pwd] cd $file gen_log:log F "CD [pwd]" rem_subdirs $v cd $awd gen_log:log F "CD [pwd]" set commandline "$cvs remove \"$file\"" $v\::do "$commandline" 1 status_colortags $v\::wait $v\::clean_exec } } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc cvs_edit {args} { # # This sets the edit flag for a file # asking for confirmation first. # global cvs global incvs global cvscfg gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } foreach file [join $args] { regsub -all {\$} $file {\$} file set commandline "$cvs edit \"$file\"" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] if {$ret != 0} { view_output::new "CVS Edit" $view_this } } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc cvs_unedit {args} { # # This resets the edit flag for a file. # Needs stdin as there is sometimes a dialog if file is modified # (defaults to no) # global cvs global incvs global cvscfg gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } foreach file [join $args] { # Unedit may hang asking for confirmation if file is not up-to-date regsub -all {\$} $file {\$} file set commandline "$cvs -n update \"$file\"" gen_log:log C "$commandline" catch {eval "exec $commandline"} view_this # Its OK if its locally added if {([llength $view_this] > 0) && ![string match "A*" $view_this] } { gen_log:log D "$view_this" cvsfail "File $file is not up-to-date" .workdir gen_log:log T "LEAVE -- cvs unedit failed" return } set commandline "$cvs unedit \"$file\"" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] if {$ret != 0} { view_output::new "CVS Edit" $view_this } } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc cvs_history {allflag mcode} { global cvs global cvscfg set all "" gen_log:log T "ENTER ($allflag $mcode)" if {$allflag == "all"} { set all "-a" } if {$mcode == ""} { set commandline "$cvs -d $cvscfg(cvsroot) history $all" } else { set commandline "$cvs -d $cvscfg(cvsroot) history $all -n $mcode" } # FIXME: If $all, it would be nice to process the output set v [viewer::new "CVS History"] $v\::do "$commandline" gen_log:log T "LEAVE" } proc cvs_add {binflag args} { # # This adds a file to the repository. # global cvs global cvscfg global incvs gen_log:log T "ENTER ($binflag $args)" if {! $incvs} { cvs_notincvs return 1 } set filelist [join $args] if {$filelist == ""} { set mess "This will add all new files" } else { set mess "This will add these files:\n\n" foreach file $filelist { append mess " $file\n" } } if {$filelist == ""} { append filelist [glob -nocomplain $cvscfg(aster) .??*] } set cmd(cvscmd) [exec::new "$cvs add $binflag $filelist"] auto_setup_dir $cmd(cvscmd) gen_log:log T "LEAVE" } proc cvs_add_dir {binflag args} { # This starts adding recursively at the directory level global cvs global cvscfg global incvs gen_log:log T "ENTER ($binflag $args)" if {! $incvs} { cvs_notincvs return 1 } set filelist [join $args] if {$filelist == ""} { cvsfail "Please select a directory!" .workdir return 1 } else { set mess "This will recursively add these directories:\n\n" foreach file $filelist { append mess " $file\n" } } set v [viewer::new "CVS Add directory"] set awd [pwd] foreach file $filelist { if {[file isdirectory $file]} { set commandline "$cvs add \"$file\"" $v\::do "$commandline" $v\::wait $v\::clean_exec cd $file gen_log:log F "CD [pwd]" add_subdirs $binflag $v } } cd $awd gen_log:log F "[pwd]" if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc add_subdirs {binflag v} { global cvs global cvsglb global cvscfg gen_log:log T "ENTER ($binflag $v)" set plainfiles {} foreach child [glob -nocomplain $cvscfg(aster) .??*] { if [file isdirectory $child] { if {[regexp -nocase {^CVS$} [file tail $child]]} { gen_log:log D "Skipping $child" continue } set commandline "$cvs add \"$child\"" $v\::do "$commandline" $v\::wait $v\::clean_exec set awd [pwd] cd $child gen_log:log F "CD [pwd]" add_subdirs $binflag $v cd $awd gen_log:log F "CD [pwd]" } else { lappend plainfiles $child } } if {[llength $plainfiles] > 0} { # LJZ: get local ignore file filter list set ignore_file_filter $cvsglb(default_ignore_filter) if { [ file exists ".cvsignore" ] } { set fileId [ open ".cvsignore" "r" ] while { [ eof $fileId ] == 0 } { gets $fileId line append ignore_file_filter " $line" } close $fileId } # LJZ: ignore files if requested in recursive add if { $ignore_file_filter != "" } { foreach item $ignore_file_filter { # for each pattern if { $item != "*" } { # if not "*" while { [set idx [lsearch $plainfiles $item]] != -1 } { # for each occurence, delete catch { set plainfiles [ lreplace $plainfiles $idx $idx ] } } } } } # LJZ: any files left after filtering? if {[llength $plainfiles] > 0} { set commandline "$cvs add $binflag $plainfiles" $v\::do "$commandline" $v\::wait } } gen_log:log T "LEAVE" } proc rem_subdirs { v } { global cvs global incvs global cvscfg gen_log:log T "ENTER ($v)" set plainfiles {} foreach child [glob -nocomplain $cvscfg(aster) .??*] { if [file isdirectory $child] { if {[regexp -nocase {^CVS$} [file tail $child]]} { gen_log:log D "Skipping $child" continue } set awd [pwd] cd $child gen_log:log F "CD [pwd]" rem_subdirs $v cd $awd gen_log:log F "CD [pwd]" } else { lappend plainfiles $child } } if {[llength $plainfiles] > 0} { foreach file $plainfiles { gen_log:log F "DELETE $file" file delete -force -- $file if {[file exists $file]} {cvsfail "Remove $file failed" .workdir} } } gen_log:log T "LEAVE" } proc cvs_fileview_update {revision filename} { # # This views a specific revision of a file in the repository. # For files checked out in the current sandbox. # global cvs global cvscfg gen_log:log T "ENTER ($revision $filename)" if {$revision == {}} { set commandline "$cvs -d $cvscfg(cvsroot) update -p \"$filename\"" set v [viewer::new "$filename"] $v\::do "$commandline" 0 } else { set commandline "$cvs -d $cvscfg(cvsroot) update -p -r $revision \"$filename\"" set v [viewer::new "$filename Revision $revision"] $v\::do "$commandline" 0 } gen_log:log T "LEAVE" } proc cvs_fileview_checkout {revision filename} { # # This looks at a revision of a file from the repository. # Called from Repository Browser -> File Browse -> View # For files not currently checked out # global cvs global cvscfg gen_log:log T "ENTER ($revision)" if {$revision == {}} { set commandline "$cvs -d $cvscfg(cvsroot) checkout -p \"$filename\"" set v [viewer::new "$filename"] $v\::do "$commandline" } else { set commandline "$cvs -d $cvscfg(cvsroot) checkout -p -r $revision \"$filename\"" set v [viewer::new "$filename Revision $revision"] $v\::do "$commandline" } gen_log:log T "LEAVE" } proc cvs_log {args} { # # This looks at a log from the repository. # Called by Workdir menu Reports->"CVS log ..." # global cvs global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] # Don't recurse set commandline "$cvs log -l " switch -- $cvscfg(ldetail) { latest { # -N means don't list tags append commandline "-Nr " } summary { append commandline "-Nt " } } append commandline "$filelist" set logcmd [viewer::new "CVS log ($cvscfg(ldetail))"] $logcmd\::do "$commandline" 0 hilight_rcslog gen_log:log T "LEAVE" } # called from the branch browser proc cvs_log_rev {rev file} { global cvs gen_log:log T "ENTER ($rev $file)" set title "CVS log" set commandline "$cvs log -N" if {$rev ne ""} { append commandline " -r$rev" append title " -r$rev" } append commandline " $file" append title " $file" set logcmd [viewer::new "$title"] $logcmd\::do "$commandline" 0 hilight_rcslog gen_log:log T "LEAVE" } proc cvs_annotate {revision args} { # # This looks at a log from the repository. # Called by Workdir menu Reports->"CVS log ..." # global cvs global cvscfg gen_log:log T "ENTER ($revision $args)" if {$revision == "trunk"} { set revision "" } if {$revision != ""} { # We were given a revision set revflag "-r$revision" } else { set revflag "" } set filelist [join $args] if {$filelist == ""} { cvsfail "Annotate:\nPlease select one or more files !" .workdir gen_log:log T "LEAVE (Unselected files)" return } foreach file $filelist { annotate::new $revflag $file "cvs" } gen_log:log T "LEAVE" } proc cvs_annotate_r {revision file} { # # This looks at a log from the repository. # Called by Logcanvas when not in a CVS directory # global cvs global cvscfg gen_log:log T "ENTER ($revision $file)" if {$revision != ""} { # We were given a revision set revflag "-r$revision" } else { set revflag "" } annotate::new $revflag $file "cvs_r" gen_log:log T "LEAVE" } proc cvs_commit {revision comment args} { # # This commits changes to the repository. # # The parameters work differently here -- args is a list. The first # element of args is a list of file names. This is because I can't # use eval on the parameters, because comment contains spaces. # global cvs global cvscfg global incvs gen_log:log T "ENTER ($revision $comment $args)" if {! $incvs} { cvs_notincvs return 1 } set filelist [lindex $args 0] # changed the message to be a little more explicit. -sj set commit_output "" if {$filelist == ""} { set mess "This will commit your changes to ** ALL ** files in" append mess " and under this directory." } else { foreach file $filelist { append commit_output "\n$file" } set mess "This will commit your changes to:$commit_output" } append mess "\n\nAre you sure?" set commit_output "" if {[cvsconfirm $mess .workdir] != "ok"} { return 1 } set revflag "" if {$revision != ""} { set revflag "-r $revision" } if {$cvscfg(use_cvseditor)} { # Starts text editor of your choice to enter the log message. # This way a template in CVSROOT can be used. update idletasks set commandline \ "$cvscfg(terminal) $cvs commit -R $revflag $filelist" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] if {$ret} { cvsfail $view_this .workdir gen_log:log T "LEAVE ERROR ($view_this)" return } } else { if {$comment == ""} { cvsfail "You must enter a comment!" .commit return 1 } set v [viewer::new "CVS Commit"] regsub -all "\"" $comment "\\\"" comment # Lets not show stderr as it does a lot of "examining" $v\::do "$cvs commit -R $revflag -m \"$comment\" $filelist" 0 $v\::wait } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc cvs_tag {tagname force b_or_t update args} { # # This tags a file in a directory. # global cvs global cvscfg global cvsglb global incvs gen_log:log T "ENTER ($tagname $force $b_or_t $update $args)" if {! $incvs} { cvs_notincvs return 1 } if {$tagname == ""} { cvsfail "Please enter a tag name!" .workdir return 1 } set filelist [join $args] set command "$cvs tag" if {$b_or_t == "branch"} { append command " -b" } if {$force == "yes"} { append command " -F" } append command " $tagname $filelist" if {$b_or_t == "branch" && $force == "yes"} { set too_new 0 # As of 1.11.2, -F won't move branch tags without the -B option set versionsplit [split $cvsglb(cvs_version) {.}] set major [lindex $versionsplit 1] set minor [lindex $versionsplit 2] if {$major > 11} { set too_new 1 } elseif {($major == 11) && ($minor >= 2)} { set too_new 1 } if {$too_new} { cvsfail "In CVS version >= 1.11.2, you're not allowed to move a branch tag" .workdir } return } # If it refuses to tag, it can exit with 0 but still put out some stderr set v [viewer::new "CVS Tag"] $v\::do "$command" 1 $v\::wait if {$update == "yes"} { # update so we're on the branch set command "$cvs update -r $tagname $filelist" $v\::do "$command" 0 status_colortags $v\::wait } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc cvs_update {tagname k no_tag recurse prune d dir args} { # # This updates the files in the current directory. # global cvs global cvscfg global incvs gen_log:log T "ENTER (tagname=$tagname k=$k no_tag=$no_tag recurse=$recurse prune=$prune d=$d dir=$dir args=$args)" set filelist [join $args] # # cvs update [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev] # set commandline "$cvs update" if { $k == "Normal" } { set kmsg "\nUsing normal (text) mode." } elseif { $k == "Binary" } { set kmsg "\nUsing binary mode (-kb)." append commandline " -kb" } if { $tagname == "HEAD" } { append mess "\nYour local files will be updated to the" append mess " latest main trunk (head) revision (-A)." append commandline " -A" } if {$recurse == "local"} { append commandline " -l" } else { append mess "\nIf there is a local sub-directory which has" append mess " become empty through deletion of its contents," if { $prune == "prune" } { append mess " it will be deleted (-P).\n" append commandline " -P" } else { append mess " it will remain.\n" } append mess "\nIf there is a sub-directory in the repository" append mess " that is not here in your local directory," if { $d == "Yes" } { append mess " it will be checked out at this time (-d).\n" if {$dir != " "} { append mess "($dir only)\n" } append commandline " -d $dir" } else { append mess " it will not be checked out.\n" } } if { $tagname != "BASE" && $tagname != "HEAD" } { append mess "\nYour local files will be updated to the" append mess " tagged revision (-r $tagname)." append mess " If a file does not have the tag," if { $no_tag == "Remove" } { append mess " it will be removed from your local directory.\n" append commandline " -r $tagname" } elseif { $no_tag == "Get_head" } { append mess " the head revision will be retrieved.\n" append commandline " -f -r $tagname" } } if {$filelist == ""} { set filemsg "\nYou are about to download from" append filemsg " the repository to your local" append filemsg " filespace the files which" append filemsg " are different in the repository," if {$recurse == "local"} { append filemsg " in this directory only.\n" } else { append filemsg " recursing the sub-directories.\n" } } else { append filemsg "\nYou are about to download from" append filemsg " the repository to your local" append filemsg " filespace these files if they" append filemsg " have changed:\n" foreach file $filelist { append filemsg "\n\t$file" append commandline " \"$file\"" } } append filemsg "\nIf you have made local changes, they will" append filemsg " be merged into the new local copy.\n" set mess "$filemsg $mess $kmsg" append mess "\n\nAre you sure?" if {[cvsconfirm $mess .workdir] == "ok"} { set co_cmd [viewer::new "CVS Update"] $co_cmd\::do $commandline 0 status_colortags auto_setup_dir $co_cmd } gen_log:log T "LEAVE" } proc cvs_merge {parent from since frombranch args} { # # This does a join (merge) of a chosen revision of localfile to the # current revision. # global cvs global cvscfg global cvsglb gen_log:log T "ENTER (\"$from\" \"$since\" \"$frombranch\" \"$args\")" gen_log:log D "mergetrunkname $cvscfg(mergetrunkname)" # Bug # 3434817 # there's an annoying bug in merging: the ending revision is ignored. # Example: there are revisions 1.1, 1.2, 1.3, 1.4 and 1.5 (HEAD). You are on # a branch made from rev 1.1 and want to merge revisions 1.2 to 1.4. When you # click in the merge diagram left mouse on 1.4, right mouse on 1.2 and click # Diff it will correctly use the following command: # /usr/bin/tkdiff -r "1.4" -r "1.2" "Filename.ext" # However, when you leave the revision selection as-is and click the Merge # the following command is used: # cvs update -d -j1.2 -jHEAD Filename.ext # Obviously the second "-j" parameter is wrong, there should have been "-j1.4". #set realfrom "$frombranch" #if {$frombranch eq $cvscfg(mergetrunkname)} { #set realfrom "HEAD" #} set filelist [join $args] set mergetags [assemble_mergetags $frombranch] set curr_tag [lindex $mergetags 0] set fromtag [lindex $mergetags 1] set totag [lindex $mergetags 2] if {$since == {}} { set mess "Merge revision $from\n" } else { set mess "Merge the changes between revision\n $since and $from" append mess " (if $since > $from the changes are removed)\n" } append mess " to the current revision ($curr_tag)" if {[cvsalwaysconfirm $mess $parent] != "ok"} { return } # Do the update here, and defer the tagging until later if {$since == {}} { set commandline "$cvs update -d -j$from $filelist" } else { set commandline "$cvs update -d -j$since -j$from $filelist" } set v [viewer::new "CVS Join"] $v\::do "$commandline" 1 status_colortags $v\::wait if [winfo exists .workdir] { if {$cvscfg(auto_status)} { setup_dir } } else { workdir_setup } dialog_merge_notice cvs $from $frombranch $fromtag $totag $filelist gen_log:log T "LEAVE" } proc cvs_merge_tag_seq {from frombranch totag fromtag args} { global cvs global cvscfg gen_log:log T "ENTER (\"$from\" \"$totag\" \"$fromtag\" $args)" set filelist "" foreach f $args { append filelist "\"$f\" " } set realfrom "$frombranch" if {$frombranch eq $cvscfg(mergetrunkname)} { set realfrom "HEAD" } # It's muy importante to make sure everything is OK at this point set commandline "$cvs -n -q update $filelist" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] set logmode [expr {$ret ? {E} : {D}}] view_output::new "CVS Check" $view_this gen_log:log $logmode $view_this if {$ret} { set mess "CVS Check shows errors which would prevent a successful\ commit. Please resolve them before continuing." if {[cvsalwaysconfirm $mess .workdir] != "ok"} { return } } # Do the commit set v [viewer::new "CVS Commit and Tag a Merge"] $v\::log "$cvs commit -m \"Merge from $from\" $filelist\n" $v\::do "$cvs commit -m \"Merge from $from\" $filelist" 1 $v\::wait # Tag if desired if {$cvscfg(auto_tag) && $totag != ""} { # First, the "from" file that's not in this branch (needs -r) set commandline "$cvs tag -F -r$realfrom $totag $filelist" $v\::log "$commandline\n" $v\::do "$commandline" 1 $v\::wait } if {$cvscfg(auto_tag) && $fromtag != ""} { # Now, the version that's in the current branch set commandline "$cvs tag -F $fromtag $filelist" $v\::log "$commandline\n" $v\::do "$commandline" 1 $v\::wait } catch {destroy .reminder} if {$cvscfg(auto_status)} { setup_dir } } proc cvs_status {args} { # # This does a status report on the files in the current directory. # global cvs global cvscfg gen_log:log T "ENTER ($args)" if {$args == "."} { set args "" } # if there are selected files, I want verbose output for those files # so I'm going to save the current setting here # - added by Jo set verbosity_setting "" busy_start .workdir.main set filelist [join $args] # if recurse option is true or there are no selected files, recurse set flags "" if {! $cvscfg(recurse)} { set flags "-l" } # if there are selected files, use verbose output # but save the current setting so it can be reset # - added by Jo if {[llength $filelist] > 0 || \ ([llength $filelist] == 1 && ! [file isdir $filelist])} { set verbosity_setting $cvscfg(rdetail) set cvscfg(rdetail) "verbose" } # support verious levels of verboseness. Ideas derived from GIC set statcmd [exec::new "$cvs -Q status $flags $filelist"] set raw_status [$statcmd\::output] $statcmd\::destroy if {$cvscfg(rdetail) == "verbose"} { view_output::new "CVS Status ($cvscfg(rdetail))" $raw_status } else { set cooked_status "" set stat_lines [split $raw_status "\n"] foreach statline $stat_lines { if {[string match "*Status:*" $statline]} { gen_log:log D "$statline" if {$cvscfg(rdetail) == "terse" &&\ [string match "*Up-to-date*" $statline]} { continue } else { regsub {^File: } $statline {} statline regsub {Status:} $statline " " line append cooked_status $line append cooked_status "\n" } } } view_output::new "CVS Status ($cvscfg(rdetail))" $cooked_status } # reset the verbosity setting if necessary if { $verbosity_setting != "" } { set cvscfg(rdetail) $verbosity_setting } busy_done .workdir.main gen_log:log T "LEAVE" } proc cvs_check {directory} { # # This does a cvscheck on the files in the current directory. # global cvs global cvscfg gen_log:log T "ENTER ($directory)" busy_start .workdir.main # The current directory doesn't have to be in CVS for cvs update to work. # Sometimes, cvs update doesn't work with ".", only with "" or an argument if {$directory == "."} { set directory "" } if $cvscfg(recurse) { set checkrecursive "" } else { set checkrecursive "-l" } set commandline "$cvs -n -q update $checkrecursive $directory" set check_cmd [viewer::new "CVS Directory Status Check"] $check_cmd\::do $commandline 1 status_colortags busy_done .workdir.main gen_log:log T "LEAVE" } proc cvs_checkout { dir cvsroot prune kflag revtag date target mtag1 mtag2 module } { # # This checks out a new module into the current directory. # global cvs global cvscfg gen_log:log T "ENTER ($dir $cvsroot $prune $kflag $revtag $date $target $mtag1 $mtag2 $module)" foreach {incvs insvn inrcs} [cvsroot_check $dir] { break } if {$incvs} { set mess "This is already a CVS controlled directory. Are you\ sure that you want to check out another module in\ to this directory?" if {[cvsconfirm $mess .modbrowse] != "ok"} { return } } set mess "This will check out $module from CVS.\nAre you sure?" if {[cvsconfirm $mess .modbrowse] == "ok"} { if {$revtag != {}} { set revtag "-r \"$revtag\"" } if {$date != {}} { set date "-D \"$date\"" } if {$target != {}} { set target "-d \"$target\"" } if {$mtag1 != {}} { set mtag1 "-j \"$mtag1\"" } if {$mtag2 != {}} { set mtag2 "-j \"$mtag2\"" } set v [::viewer::new "CVS Checkout"] set cwd [pwd] cd $dir $v\::do "$cvs -d \"$cvsroot\" checkout $prune\ $revtag $date $target\ $mtag1 $mtag2\ $kflag \"$module\"" cd $cwd } gen_log:log T "LEAVE" return } proc cvs_filelog {filename parent {graphic {0}} } { # # This looks at the revision log of a file. It's called from filebrowse.tcl, # so we can't do operations such as merges. # global cvs global cvscfg global cwd gen_log:log T "ENTER ($filename $parent $graphic)" set pid [pid] set filetail [file tail $filename] set commandline "$cvs -d $cvscfg(cvsroot) checkout \"$filename\"" gen_log:log C "$commandline" set ret [cvs_sandbox_runcmd "$commandline" cmd_output] if {$ret == $cwd} { cvsfail $cmd_output $parent cd $cwd gen_log:log T "LEAVE -- cvs checkout failed" return } if {$graphic} { # Log canvas viewer ::cvs_branchlog::new "CVS,rep" $filename } else { set commandline "$cvs -d $cvscfg(cvsroot) log \"$filename\"" set logcmd [viewer::new "CVS log $filename"] $logcmd\::do "$commandline" 0 hilight_rcslog $logcmd\::wait } cd $cwd gen_log:log T "LEAVE" } proc cvs_export { dir cvsroot kflag revtag date target module } { # # This exports a new module (see man cvs and read about export) into # the current directory. # global cvs global cvscfg gen_log:log T "ENTER ($dir $cvsroot $kflag $revtag $date $target $module)" foreach {incvs insvn inrcs} [cvsroot_check $dir] { break } if {$incvs} { set mess "This is already a CVS controlled directory. Are you\ sure that you want to export a module in to this directory?" if {[cvsconfirm $mess .modbrowse] != "ok"} { return } } set mess "This will export $module from CVS.\nAre you sure?" if {[cvsconfirm $mess .modbrowse] == "ok"} { if {$revtag != {}} { set revtag "-r \"$revtag\"" } if {$date != {}} { set date "-D \"$date\"" } if {$target != {}} { set target "-d \"$target\"" } set v [::viewer::new "CVS Export"] set cwd [pwd] cd $dir $v\::do "$cvs -d \"$cvsroot\" export\ $revtag $date $target $kflag \"$module\"" cd $cwd } gen_log:log T "LEAVE" return } proc cvs_patch { cvsroot module difffmt revtagA dateA revtagB dateB outmode outfile } { # # This creates a patch file between two revisions of a module. If the # second revision is null, it creates a patch to the head revision. # If both are null the top two revisions of the file are diffed. # global cvs global cvscfg gen_log:log T "ENTER ($cvsroot $module $difffmt $revtagA $dateA $revtagB $dateB $outmode $outfile)" foreach {rev1 rev2} {{} {}} { break } if {$revtagA != {}} { set rev1 "-r \"$revtagA\"" } elseif {$dateA != {}} { set rev1 "-D \"$dateA\"" } if {$revtagB != {}} { set rev2 "-r \"$revtagB\"" } elseif {$dateA != {}} { set rev2 "-D \"$dateB\"" } if {$rev1 == {} && $rev2 == {}} { set rev1 "-t" } set commandline "$cvs -d \"$cvsroot\" patch $difffmt $rev1 $rev2 \"$module\"" if {$outmode == 0} { set v [viewer::new "CVS Patch"] $v\::do "$commandline" 0 patch_colortags } else { set e [exec::new "$commandline"] set patch [$e\::output] gen_log:log F "OPEN $outfile" if {[catch {set fo [open $outfile w]}]} { cvsfail "Cannot open $outfile for writing" .modbrowse return } puts $fo $patch close $fo gen_log:log F "CLOSE $outfile" } gen_log:log T "LEAVE" return } proc cvs_version {} { # # This finds the current CVS version number. # global cvs global cvscfg global cvsglb gen_log:log T "ENTER" set cvsglb(cvs_version) "" set commandline "$cvs -v" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} output] if {$ret} { cvsfail $output return } foreach infoline [split $output "\n"] { if {[string match "Concurrent*" $infoline]} { set lr [split $infoline] set species [lindex $lr 3] regsub -all {[()]} $species {} species set version [lindex $lr 4] gen_log:log D "species $species version $version" } } gen_log:log D "Split: $species $version" regsub -all {\s*} $version {} version gen_log:log D "De-whitespaced: $species $version" set cvsglb(cvs_type) $species set cvsglb(cvs_version) $version gen_log:log T "LEAVE" } proc cvs_merge_conflict {args} { global cvscfg global cvs gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { cvsfail "Please select some files to merge first!" return } foreach file $filelist { # Make sure its really a conflict - tkdiff will bomb otherwise regsub -all {\$} $file {\$} filename set commandline "$cvs -n -q update \"$filename\"" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} status] set logmode [expr {$ret ? {E} : {D}}] gen_log:log $logmode "$status" gen_log:log F "OPEN $file" set f [open $file] set match 0 while { [eof $f] == 0 } { gets $f line if { [string match "<<<<<<< *" $line] } { set match 1 break } } gen_log:log F "CLOSE $file" close $f if { [string match "C *" $status] } { # If its marked "Needs Merge", we have to update before # we can resolve the conflict gen_log:log C "$commandline" set commandline "$cvs update \"$file\"" set ret [catch {eval "exec $commandline"} status] set logmode [expr {$ret ? {E} : {D}}] gen_log:log $logmode "$status" } elseif { $match == 1 } { # There are conflict markers already, dont update ; } else { cvsfail "$file does not appear to have a conflict." .workdir continue } # Invoke tkdiff with the proper option for a conflict file # and have it write to the original file set commandline "$cvscfg(tkdiff) -conflict -o \"$filename\" \"$filename\"" gen_log:log C "$commandline" catch {eval "exec $commandline"} view_this } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc cvs_gettaglist {filename parent} { global cvs global cvscfg global cwd set keepers "" set pid [pid] gen_log:log T "ENTER ($filename)" set filetail [file tail $filename] set commandline "$cvs -d $cvscfg(cvsroot) checkout \"$filename\"" # run a command, possibly creating the sandbox to play in set ret [cvs_sandbox_runcmd $commandline cmd_output] if {$cwd == $ret} { cvsfail $cmd_output $parent cd $cwd gen_log:log T "LEAVE ERROR ($cmd_output)" return $keepers } set commandline "$cvs -d $cvscfg(cvsroot) log \"$filename\"" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] if {$ret} { cvsfail $view_this $parent cd $cwd gen_log:log T "LEAVE ERROR" return $keepers } set view_lines [split $view_this "\n"] set c 0 set l [llength $view_lines] foreach line $view_lines { if {[string match "symbolic names:" $line]} { gen_log:log D "line $c $line" for {set b [expr {$c + 1}]} {$b <= $l} {incr b} { set nextline [lindex $view_lines $b] if {[string index $nextline 0] == "\t" } { set nextline [string trimleft $nextline] gen_log:log D "$nextline" append keepers "$nextline\n" } else { gen_log:log D "$nextline - quitting" break } } } incr c } if {$keepers == ""} { set keepers "No Tags" } cd $cwd gen_log:log T "LEAVE" return "$keepers" } proc cvs_release {delflag args} { global cvs global cvscfg gen_log:log T "ENTER ($args)" set filelist [join $args] foreach directory $filelist { if {! [file isdirectory $directory]} { cvsfail "$directory is not a directory" .workdir return } set commandline "$cvs -n -q update \"$directory\"" gen_log:log C "$commandline" set ret [catch {eval "exec $commandline"} view_this] if {$view_this != ""} { view_output::new "CVS Check" $view_this set mess "\"$directory\" is not up-to-date." append mess "\nRelease anyway?" if {[cvsconfirm $mess .workdir] != "ok"} { return } } set commandline "$cvs -Q release $delflag \"$directory\"" set ret [catch {eval "exec $commandline"} view_this] gen_log:log C "$commandline" if {$ret != 0} { view_output::new "CVS Release" $view_this } } if {$cvscfg(auto_status)} { setup_dir } gen_log:log T "LEAVE" } proc cvs_rtag { cvsroot mcode b_or_t force oldtag newtag } { # # This tags a module in the repository. # Called by the tag commands in the Repository Browser # global cvs global cvscfg gen_log:log T "ENTER ($cvsroot $mcode $b_or_t $force $oldtag $newtag)" set command "$cvs -d \"$cvsroot\" rtag" if {$force == "remove"} { if {$oldtag == ""} { cvsfail "Please enter an Old tag name!" .modbrowse return 1 } append command " -d \"$oldtag\" \"$mcode\"" } else { if {$newtag == ""} { cvsfail "Please enter a New tag name!" .modbrowse return 1 } if {$b_or_t == "branch"} { append command " -b" } if {$force == "yes"} { append command " -F" } if {$oldtag != ""} { append command " -r \"$oldtag\"" } append command " \"$newtag\" \"$mcode\"" } set v [::viewer::new "CVS Rtag"] $v\::do "$command" gen_log:log T "LEAVE" } # dialog for cvs commit - called from workdir browser proc cvs_commit_dialog {} { global incvs global cvsglb global cvscfg gen_log:log T "ENTER" if {! $incvs} { cvs_notincvs gen_log:log T "LEAVE" return } # If marked files, commit these. If no marked files, then # commit any files selected via listbox selection mechanism. # The cvsglb(commit_list) list remembers the list of files # to be committed. set cvsglb(commit_list) [workdir_list_files] # If we want to use an external editor, just do it if {$cvscfg(use_cvseditor)} { cvs_commit "" "" $cvsglb(commit_list) return } if {[winfo exists .commit]} { destroy .commit } toplevel .commit #grab set .commit frame .commit.top -border 8 frame .commit.vers frame .commit.down -relief groove -border 2 pack .commit.top -side top -fill x pack .commit.down -side bottom -fill x pack .commit.vers -side top -fill y label .commit.lvers -text "Specify Revision (-r) (usually ignore)" \ -anchor w entry .commit.tvers -relief sunken -textvariable version pack .commit.lvers .commit.tvers -in .commit.vers \ -side left -fill x -pady 3 frame .commit.comment pack .commit.comment -side top -fill both -expand 1 label .commit.comment.lcomment -text "Your log message" -anchor w button .commit.comment.history -text "Log History" \ -command history_browser text .commit.comment.tcomment -relief sunken -width 70 -height 10 \ -bg $cvsglb(textbg) -exportselection 1 \ -wrap word -border 2 -setgrid yes # Explain what it means to "commit" files message .commit.message -justify left -aspect 500 -relief groove -bd 2 \ -text "This will commit changes from your \ local, working directory into the repository, recursively. \ For any local (sub)directories or files that are on a branch, \ your changes will be added to the end of that branch. \ This includes new or deleted files as well as modifications. \ For any local (sub)directories or files that have \ a non-branch tag, a branch will be created, and \ your changes will be placed on that branch. (CVS bug.) \ \ For all other (sub)directories, your changes will be \ added to the end of the main trunk." pack .commit.message -in .commit.top -padx 2 -pady 5 button .commit.ok -text "OK" \ -command { #grab release .commit wm withdraw .commit set cvsglb(commit_comment) [string trimright [.commit.comment.tcomment get 1.0 end]] cvs_commit $version $cvsglb(commit_comment) $cvsglb(commit_list) commit_history $cvsglb(commit_comment) } button .commit.apply -text "Apply" \ -command { set cvsglb(commit_comment) [string trimright [.commit.comment.tcomment get 1.0 end]] cvs_commit $version $cvsglb(commit_comment) $cvsglb(commit_list) commit_history $cvsglb(commit_comment) } button .commit.clear -text "ClearAll" \ -command { set version ""e .commit.comment.tcomment delete 1.0 end } button .commit.quit \ -command { #grab release .commit wm withdraw .commit } .commit.ok configure -text "OK" .commit.quit configure -text "Close" grid columnconf .commit.comment 1 -weight 1 grid rowconf .commit.comment 1 -weight 1 grid .commit.comment.lcomment -column 0 -row 0 grid .commit.comment.tcomment -column 1 -row 0 -rowspan 2 -padx 4 -pady 4 -sticky nsew grid .commit.comment.history -column 0 -row 1 pack .commit.ok .commit.apply .commit.clear .commit.quit -in .commit.down \ -side left -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1 # Fill in the most recent commit message .commit.comment.tcomment insert end [string trimright $cvsglb(commit_comment)] wm title .commit "Commit Changes" wm minsize .commit 1 1 gen_log:log T "LEAVE" } proc cvs_ascii { args } { # This converts a binary file to ASCII global cvs global cvscfg global incvs global cvsglb gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } set filelist [join $args] gen_log:log D "Changing sticky flag" gen_log:log D "$cvs admin -kkv $filelist" set cmd(cvscmd) [exec::new "$cvs admin -kkv $filelist"] auto_setup_dir $cmd(cvscmd) gen_log:log T "LEAVE" } proc cvs_binary { args } { # This converts an ASCII file to binary global cvs global cvscfg global incvs global cvsglb gen_log:log T "ENTER ($args)" if {! $incvs} { cvs_notincvs return 1 } set filelist [join $args] gen_log:log D "Changing sticky flag" gen_log:log D "$cvs admin -kb $filelist" set cmd(cvscmd) [exec::new "$cvs admin -kb $filelist"] auto_setup_dir $cmd(cvscmd) gen_log:log T "LEAVE" } # Revert a file to checked-in version by removing the local # copy and updating it proc cvs_revert {args} { global incvs global cvscfg global cvsglb global cvs gen_log:log T "ENTER ($args)" set filelist [join $args] if {$filelist == ""} { set mess "This will revert (discard) your changes to ** ALL ** files in this directory" } else { foreach file $filelist { append revert_output "\n$file" } set mess "This will revert (discard) your changes to:$revert_output" } append mess "\n\nAre you sure?" if {[cvsconfirm $mess .workdir] != "ok"} { return 1 } gen_log:log D "Reverting $filelist" # update -C option appeared in 1.11 set versionsplit [split $cvsglb(cvs_version) {.}] set major [lindex $versionsplit 1] if {$major < 11} { gen_log:log F "DELETE $filelist" file delete $filelist set cmd(cvscmd) [exec::new "$cvs update $filelist"] } else { set cmd(cvscmd) [exec::new "$cvs update -C $filelist"] } auto_setup_dir $cmd(cvscmd) gen_log:log T "LEAVE" } proc read_cvs_dir {dirname} { # # Reads a CVS "bookkeeping" directory # global module_dir global cvscfg global cvsglb global cvs global current_tagname gen_log:log T "ENTER ($dirname)" if {$cvsglb(cvs_version) == ""} { cvs_version } set current_tagname "trunk" if {[file isdirectory $dirname]} { if {[file isfile [file join $dirname Repository]]} { gen_log:log F "OPEN CVS/Repository" set f [open [file join $dirname Repository] r] gets $f module_dir close $f gen_log:log D " MODULE $module_dir" if {[file isfile [file join $dirname Root]]} { gen_log:log F "OPEN CVS/Root" set f [open [file join $dirname Root] r] gets $f cvscfg(cvsroot) close $f # On a PC, the cvsroot can be like C:\DosRepository. # This makes that workable. regsub -all {\\} $cvscfg(cvsroot) {\\\\} cvscfg(cvsroot) gen_log:log D " cvsroot: $cvscfg(cvsroot)" } if {[file isfile [file join $dirname Tag]]} { gen_log:log F "OPEN CVS/Tag" set f [open [file join $dirname Tag] r] gets $f current_tagname close $f # T = branch tag, N = non-branch, D = sticky date set current_tagname [string range $current_tagname 1 end] gen_log:log D " BRANCH TAG $current_tagname" } } else { cvsfail "Repository file not found in $dirname" .workdir return 0 } } else { cvsfail "$dirname is not a directory" .workdir return 0 } set cvsglb(root) $cvscfg(cvsroot) gen_log:log T "LEAVE (1)" return 1 } proc parse_cvsmodules {modules_file} { global cvs global modval global modtitle global cvsglb global cvscfg gen_log:log T "ENTER" # Clear the arrays catch {unset modval} catch {unset modtitle} # Unescape newlines, compress repeated whitespace, and remove blank lines regsub -all {(\\\n|[ \t])+} $modules_file " " modules_file regsub -all {\n\s*\n+} $modules_file "\n" modules_file foreach line [split $modules_file "\n"] { if {[string index $line 0] == {#}} { # gen_log:log D "Comment: $line" if {[string index $line 1] == {D} || [string index $line 1] == {M}} { set text [split $line] set dname [lindex $text 1] set modtitle($dname) [lrange $text 2 end] # gen_log:log D "Directory: {$dname} {$modtitle($dname)}" } } else { # gen_log:log D "Data: $line" set text [split $line] set modname [lindex $text 0] set modstring [string trim [join [lrange $text 1 end]]] # A "#D ..." or "#M ..." entry _always_ overrides this default if {! [info exists modtitle($modname)]} { set modtitle($modname) $modstring } # Remove flags except for -a. Luckily alias modules can't have # any other options. # gen_log:log D "{$modname} {$modstring}" regsub -- {^((-l\s*)|(-[ioestud]\s+((\\\s)|\S)+\s*))+} \ $modstring {} modstring if {$modname != ""} { set modval($modname) $modstring gen_log:log D "{$modname} {$modstring}" } } } gen_log:log T "LEAVE" } proc cvs_lock {do files} { global cvscfg global cvscfg if {$files == {}} { cvsfail "Please select one or more files!" .workdir return } switch -- $do { lock { set commandline "$cvs admin -l $files"} unlock { set commandline "$cvs admin -u $files"} } set lock_cmd [::exec::new "$commandline"] auto_setup_dir $lock_cmd } # Sends directory "." to the directory-merge tool # Find the bushiest file in the directory and diagram it proc cvs_directory_merge {} { global cvscfg global cvsglb global cvs global incvs gen_log:log T "ENTER" if {! $incvs} { cvs_notincvs return 1 } set files [glob -nocomplain -types f -- .??* *] regsub -all {\$} $files {\$} files set commandline "$cvs -d $cvscfg(cvsroot) log $files" gen_log:log C "$commandline" catch {eval "exec $commandline"} raw_log set log_lines [split $raw_log "\n"] foreach logline $log_lines { if {[string match "Working file:*" $logline]} { set filename [lrange [split $logline] 2 end] set nbranches($filename) 0 continue } if {[string match "total revisions:*" $logline]} { set nrevs($filename) [lindex [split $logline] end] continue } if { [regexp {^\t[-\w]+: .*\.0\.\d+$} $logline] } { incr nbranches($filename) } } set bushiestfile "" set mostrevisedfile "" set nbrmax 0 foreach br [array names nbranches] { if {$nbranches($br) > $nbrmax} { set bushiestfile $br set nbrmax $nbranches($br) } } set nrevmax 0 foreach br [array names nrevs] { if {$nrevs($br) > $nrevmax} { set mostrevisedfile $br set nrevmax $nrevs($br) } } gen_log:log F "Bushiest file \"$bushiestfile\" has $nbrmax branches" gen_log:log F "Most Revised file \"$mostrevisedfile\" has $nrevmax revisions" # Sometimes we don't find a file with any branches at all, so bushiest # is empty. Fall back to mostrevised. All files have at least one rev. if {[string length $bushiestfile] > 0} { set filename $bushiestfile } else { set filename $mostrevisedfile } ::cvs_branchlog::new "CVS,dir" "$filename" gen_log:log T "LEAVE" } # Sends files to the CVS branch browser one at a time. Called from # workdir browser proc cvs_branches {files} { global cvs global cvscfg gen_log:log T "ENTER ($files)" if {$files == {}} { cvsfail "Please select one or more files!" .workdir return } foreach file $files { ::cvs_branchlog::new "CVS,loc" "$file" } gen_log:log T "LEAVE" } namespace eval ::cvs_branchlog { variable instance 0 proc new {how filename} { variable instance set my_idx $instance incr instance namespace eval $my_idx { set my_idx [uplevel {concat $my_idx}] set filename [uplevel {concat $filename}] set how [uplevel {concat $how}] variable command variable cmd_log variable lc variable revwho variable revdate variable revtime variable revlines variable revstate variable revcomment variable revmergefrom variable tags variable revbranches variable branchrevs variable logstate variable cwd gen_log:log T "ENTER [namespace current]" set sys_loc [split $how {,}] set sys [lindex $sys_loc 0] set loc [lindex $sys_loc 1] switch -- $sys { CVS { set command "cvs log \"$filename\"" if {$loc == "dir"} { set newlc [mergecanvas::new $filename $how [namespace current]] # ln is the namespace, lc is the canvas set ln [lindex $newlc 0] set lc [lindex $newlc 1] set show_tags 0 } else { set newlc [logcanvas::new $filename $how [namespace current]] set ln [lindex $newlc 0] set lc [lindex $newlc 1] set show_tags [set $ln\::opt(show_tags)] } } RCS { set command "rlog \"$filename\"" set newlc [logcanvas::new $filename "RCS,loc" [namespace current]] set ln [lindex $newlc 0] set lc [lindex $newlc 1] set show_tags [set $ln\::opt(show_tags)] } } proc abortLog { } { global cvscfg variable cmd_log variable lc gen_log:log D " $cmd_log\::abort" catch {$cmd_log\::abort} busy_done $lc pack forget $lc.stop pack $lc.close -in $lc.down.closefm -side right $lc.close configure -state normal } proc reloadLog { } { variable command variable cmd_log variable lc variable revwho variable revdate variable revtime variable revlines variable revstate variable revcomment variable revmergefrom variable revtags variable revbtags variable revbranches variable branchrevs variable logstate gen_log:log T "ENTER" catch { $lc.canvas delete all } catch { unset revwho } catch { unset revdate } catch { unset revtime } catch { unset revlines } catch { unset revstate } catch { unset revcomment } catch { unset revmergefrom } catch { unset revtags } catch { unset revbtags } catch { unset revbranches } catch { unset branchrevs } set cwd [pwd] pack forget $lc.close pack $lc.stop -in $lc.down.closefm -side right $lc.stop configure -state normal set logstate {R} set cmd_log [::exec::new $command {} 0 [namespace current]::parse_cvslog] # wait for it to finish so our arrays are all populated $cmd_log\::wait $cmd_log\::destroy pack forget $lc.stop pack $lc.close -in $lc.down.closefm -side right $lc.close configure -state normal [namespace current]::cvs_sort_it_all_out gen_log:log T "LEAVE" return } proc parse_cvslog { exec logline } { # # Splits the rcs file up and parses it using a simple state machine. # global module_dir global inrcs global cvsglb variable filename variable lc variable ln variable revwho variable revdate variable revtime variable revlines variable revstate variable revcomment variable revmergefrom variable revtags variable revbtags variable revbranches variable branchrevs variable logstate variable revkind variable rnum variable rootbranch variable revbranch gen_log:log T "ENTER ($exec $logline)" #gen_log:log D "$logline" if {$logline != {}} { switch -exact -- $logstate { {R} { # Look for the first text line which should give the file name. if {[string match {RCS file: *} $logline]} { # I think the whole path to the "RCS file" from the log isn't # really what we want here. More like module_dir, so we know # what to feed to cvs rdiff and rannotate. set fname [string range $logline 10 end] set fname [file tail $fname] if {[string range $fname end-1 end] == {,v}} { set fname [string range $fname 0 end-2] } set fname [file join $module_dir $fname] if {$inrcs && [file isdir RCS]} { set fname [file join RCS $fname] } $ln\::ConfigureButtons $fname } elseif {[string match {Working file: *} $logline]} { # If we care about a working copy we need to look # at the name of the working file here. It may be # different from what we were given if we were invoked # on a directory. #if {$localfile != "no file"} { set localfile [string range $logline 14 end] #} } elseif {$logline == "symbolic names:"} { # FIXME: old RCS can have a tag on this line set logstate {T} } } {T} { # Any line with a tab leader is a tag if { [string index $logline 0] == "\t" } { set parts [split $logline {:}] set tagstring [string trim [lindex $parts 0]] set rnum [string trim [lindex $parts 1]] set parts [split $rnum {.}] if {[expr {[llength $parts] & 1}] == 1} { set parts [linsert $parts end-1 {0}] set rnum [join $parts {.}] } if {[lindex $parts end-1] == 0} { # Branch tag set rnum [join [lreplace $parts end-1 end-1] {.}] set revkind($rnum) "branch" set revbranch($tagstring) $rnum set rbranch [join [lrange $parts 0 end-2] {.}] set rootbranch($tagstring) $rbranch lappend revbtags($rnum) $tagstring lappend revbranches($rbranch) $rnum } else { # Ordinary symbolic tag lappend revtags($rnum) $tagstring # Is it possible that this tag is the only surviving # record that this revision ever existed? if {[llength $parts] == 2} { # A trunk revision but not necessarily 1.x because CVS allows # the first part of the revision number to be changed. We have # to assume that people always increase it if they change it # at all. lappend branchrevs(trunk) $rnum } else { set rbranch [join [lrange $parts 0 end-1] {.}] lappend branchrevs($rbranch) $rnum } # Branches for this revision may have already been created # during tag parsing foreach "revwho($rnum) revdate($rnum) revtime($rnum) revlines($rnum) revstate($rnum) revcomment($rnum)" \ {{} {} {} {} {dead} {}} \ { break } } } else { if {$logline == "description:"} { set logstate {S} } } } {S} { # Look for the line that starts a revision message. if {$logline == "----------------------------"} { set logstate {V} } } {V} { if {! [string match "revision *" $logline] } { # Did they put just the right number of dashes in the comment # to fool us? set logstate {L} } else { # Look for a revision number line set rnum [lindex [split $logline] 1] set parts [split $rnum {.}] set revkind($rnum) "revision" if {[llength $parts] == 2} { # A trunk revision but not necessarily 1.x because CVS allows # the first part of the revision number to be changed. We have # to assume that people always increase it if they change it # at all. lappend branchrevs(trunk) $rnum } else { lappend branchrevs([join [lrange $parts 0 end-1] {.}]) $rnum } # Branches for this revision may have already been created # during tag parsing foreach "revwho($rnum) revdate($rnum) revtime($rnum) revlines($rnum) revstate($rnum) revcomment($rnum)" \ {{} {} {} {} {} {}} \ { break } set logstate {D} } } {D} { # Look for a date line. This also has the name of the author. set parts [split $logline ";"] foreach p $parts { set eqn [split $p ":"]; set eqname [string trim [lindex $eqn 0]] set eqval [string trim [join [lrange $eqn 1 end] ":"]] switch -exact -- $eqname { {date} { set revdate($rnum) [lindex $eqval 0] set revtime($rnum) [lindex $eqval 1] gen_log:log D "date $revdate($rnum)" gen_log:log D "time $revtime($rnum)" } {author} { set revwho($rnum) $eqval } {lines} { set revlines($rnum) $eqval } {state} { set revstate($rnum) $eqval } {mergepoint} { set revmergefrom($rnum) $eqval gen_log:log D "mergefrom $revmergefrom($rnum)" } } } set logstate {L} } {L} { # See if there are branches off this revision if {[string match "branches:*" $logline]} { foreach br [lrange $logline 1 end] { set br [string trimright $br {;}] lappend revbranches($rnum) $br } } elseif {$logline == {----------------------------}} { set logstate {V} } elseif {$logline ==\ {=============================================================================}} { set logstate {X} } else { append revcomment($rnum) $logline "\n" } } {X} { # ignore any further lines } } } if {$logstate == {X}} { gen_log:log D "********* Done parsing *********" } return [list {} $logline] } proc cvs_sort_it_all_out {} { global cvscfg global module_dir variable filename variable sys variable lc variable ln variable revwho variable revdate variable revtime variable revlines variable revstate variable revcomment variable revmergefrom variable revtags variable revbtags variable revbranches variable branchrevs variable logstate variable rnum variable rootbranch variable revbranch variable revkind gen_log:log T "ENTER" if {[llength [array names revkind]] < 1} { cvsfail "Log empty. Check error status of cvs log comand" $lc close invoke return } set revkind(1) "root" foreach r [lsort -command sortrevs [array names revkind]] { gen_log:log D "revkind($r) $revkind($r)" } # Sort the revision and branch lists and remove duplicates foreach r [array names branchrevs] { set branchrevs($r) \ [lsort -unique -decreasing -command sortrevs $branchrevs($r)] #gen_log:log D "branchrevs($r) $branchrevs($r)" } # Create a fake revision to be the trunk branchtag set revbtags(1) "trunk" set branchrevs(1) $branchrevs(trunk) foreach r [array names revbranches] { set revbranches($r) \ [lsort -unique -command sortrevs $revbranches($r)] #gen_log:log D "revbranches($r) $revbranches($r)" } # Find out where to put the working revision icon (if anywhere) # FIXME: we don't know that the log parsed was derived from the # file in this directory. Maybe we should check CVS/{Root,Repository}? # Maybe this check should be done elsewhere? if {$sys != "rcs" && $filename != "no file"} { gen_log:log F "Reading CVS/Entries" set basename [file tail $filename] if {![catch {open [file join \ [file dirname $filename] {CVS}\ {Entries}] \ {r}} entries]} \ { foreach line [split [read $entries] "\n"] { # What does the entry for an added/deleted file look like? set parts [split $line {/}] if {[lindex $parts 1] == $basename} { set rnum [lindex $parts 2] if {[string index $rnum 0] == {-}} { # File has been locally removed and cvs removed but not # committed. set revstate(current) {dead} set rnum [string range $rnum 1 end] } else { set revstate(current) {Exp} } set root [join [lrange [split $rnum {.}] 0 end-1] {.}] gen_log:log D "root $root" set tag [string range [lindex $parts 5] 1 end] if {$rnum == {0}} { # A locally added file has a revision of 0. Presumably # there is no log and no revisions to show. # FIXME: what if this is a resurrection? lappend branchrevs(trunk) {current} } elseif {[info exists rootbranch($tag)] && \ $rootbranch($tag) == $rnum} { # The sticky tag specifies a branch and the branch's # root is the same as the source revision. Place the # you-are-here box at the start of the branch. lappend branchrevs($revbranch($tag)) {current} } else { if {[catch {info exists $branchrevs($root)}] == 0} { if {$rnum == [lindex $branchrevs($root) 0]} { # The revision we are working on is the latest on its # branch. Place the you-are-here box on the end of the # branch. set branchrevs($root) [linsert $branchrevs($root) 0\ {current}] } else { # Otherwise we will place it as a branch off the # revision. if {![info exists revbranches($rnum)]} { set revbranches($rnum) {current} } else { set revbranches($rnum) [linsert $revbranches($rnum)\ 0 {current}] } } } } foreach {revwho(current) revdate(current) revtime(current) revlines(current) revcomment(current) branchrevs(current)} \ {{} {} {} {} {} {}} \ { break } break } } close $entries } } gen_log:log D "" foreach a [array names branchrevs] { gen_log:log D "branchrevs($a) $branchrevs($a)" } gen_log:log D "" foreach a [array names revbranches] { gen_log:log D "revbranches($a) $revbranches($a)" } gen_log:log D "" foreach a [array names revbtags] { gen_log:log D "revbtags($a) $revbtags($a)" } gen_log:log D "" foreach a [array names revtags] { gen_log:log D "revtags($a) $revtags($a)" } # We only needed these to place the you-are-here box. catch {unset rootbranch revbranch} $ln\::DrawTree now } [namespace current]::reloadLog return [namespace current] } } } proc sortrevs {a b} { # Proc for lsort -command, to sort revision numbers # Return -1 if ab foreach ax [split $a {.}] bx [split $b {.}] { if {$ax < $bx} { return -1 }\ elseif {$ax > $bx} { return 1 } } return 0 } tkcvs-8.2.3.orig/tkcvs/bitmaps/0000755000175000017500000000000011664612512014556 5ustar timtimtkcvs-8.2.3.orig/tkcvs/bitmaps/stat_okol.gif0000644000175000017500000000016111664612512017242 0ustar timtimGIF89a Т‡Цч„„„ЦЦЦџџџџџџџџџџџџ!љ, 6x'мЎp@‰CФьМD U>IUЁЅ+ыНŠК--y>­  (ќЅŠЦђ2r8Š;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_merge.gif0000644000175000017500000000021711664612512017377 0ustar timtimGIF89a Т„„„џџџЦЦЦ’мџь‹џџџџџџ!ўMade with GIMP!љ, BxмЎp…I JЪlРP ƒе„AjЉ!Z.QKC"@ўƒЪЬэ"лmP0fx‚t*=n*•'вСe(;tkcvs-8.2.3.orig/tkcvs/bitmaps/arrow_up.gif0000644000175000017500000000013711664612512017104 0ustar timtimGIF89a Ёџџџ!ўMade with GIMP!љ , ” Ї—ЫбЊКTЃжm`і,ЄР)ф•Е.фЦ;tkcvs-8.2.3.orig/tkcvs/bitmaps/delete.gif0000644000175000017500000000032411664612512016506 0ustar timtimGIF89aуПППхххџџџ“““ЇЇЇМММааа777OOOџџџџџџџџџџџџџџџ!ўMade with GIMP!љ,o№ЩIЋЕ ˆ}ыа[@A'…ЁЊ‚љhk”цЏm-м,gm6№I9 iV(ž‡‘œ$L'THX‰ьгE\-ё–JЋ,›уЈЁJ рлn|з’ 5}qt xd^.GC.’;tkcvs-8.2.3.orig/tkcvs/bitmaps/updir.gif0000644000175000017500000000026311664612512016371 0ustar timtimGIF89aуoooџь‹џџџHИџ’мŽдџkkk^^^џџџџџџџџџџџџџџџџџџџџџ!љ,`№=@k•8ЯР{x$gш)ЌЌ@ фP’Џ7ђl0w…p˜ дa,0‹ЕфrкByП)uP~АBхSиНіЄЯnљ{[ео]яР…зv‡Nцѕ::)ƒ(;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_modml.gif0000644000175000017500000000017411664612512017412 0ustar timtimGIF89a ТУ„„„ЦЦЦџь‹џџџџџџџџџ!љ, Ax'мЎp•@‹CФь˜EBg@иTKЄл/ЪюC РкWј) бxL’Š€ЈдIєXw ‡vuH;tkcvs-8.2.3.orig/tkcvs/bitmaps/modbrowse.gif0000644000175000017500000000051411664612512017246 0ustar timtimGIF89a((у‹ЂЙачџoooџь‹џџџџь‹џь‹џь‹џь‹џь‹џь‹!љ ,((љ№ЩIЋН8ыЭћ;`zф‡œ(z”м‘ОЇ(В“lIЎ'+§%А`ЊGУ D–ёL––Ь—“Њ|?Ћ[еN=]ыЗŽŽ[@эеWfž7m$їFŸa1€;-ЏЇў€‚ƒ‚|‡ˆ†‰x‹‘c•—–•™ŸЁIЄ Ђ’‰ЌЎАВДЏБ”ИА=М­ЙПСНХШ|ЬЮаЭУАЯкЁйлнпалморСАжпРэфЪьэщ_іх~њъс§“>N•БO!,ї“&ƒы6ѕ™HБЂХ‹ћD;tkcvs-8.2.3.orig/tkcvs/bitmaps/anglerfish_med.gif0000644000175000017500000002713511664612512020224 0ustar timtimGIF89aЅ€чџ  && 4*AO/#9!)))^4"*.&%*n*f$$]#-68*&,U(2TE+428#%:C'7QD4,:83.g+2h>QD3#?4301t49A?5@3/…>-v Y$.G?2=t&J_U@T?+2GgU>8Z:C@FP5FpHERF=—LIHEQ2GFs9EŸ0_-4U]UK>=M€>LŒ?XO3XqZIZ=M@R{jNhL88TfKDNPlPIŽ>V{NU_z4_D“mITUScBRА=in:h„‘8BgzGc‡ccCM\Й€^%Ufpv[Sy\G€]4`cpIbК€VfEtl_ido_rWi„ri;HlІlgb^Z=v—[gЇHy~lhˆMwNlШLoРraЇkh—_s~‘j)Ysš‡iU‡jbnr€“dtJ‹‹L†ЋN…}PV|жUˆœUŠ•ФNc„šЅw.XЭ`ФŽu\…Сv€™|€˜yiЂr‚z}ЗfŽ“t‰‘lˆЏ‡„„”„i‹zЙœ~xo’^ŒхWŸWŸ•Ж‚1U™Сa˜ЇЄ„^b–ЕŽŒ††Аs”Њg”ЯŠІˆw‹‹ЦŒ”ЉЧ4dЅЮbЌЏЖ†—m›ёcАЉiЊЧ­šqА–‚vЊМ—žГyЅт„ŸцnЎжƒЅв Ўй˜8uЉіЄ™ж›ŸвХ“Ѕ‡АЖЋСЅЅЃœЉЇlПЗqОШ’ГЊzЗнНЄvЛиšАИОЙ†ЛЭŠДђяЇ=˜ЕфБВБЈГЬЏГРжЂДБЏпuеЦМ­ьЫДž‰вЧЪс–Уќ†ауЃЩЦ—Эж§ЕC˜ЬэвФЧЗ№ЄШѕМРсМУгЇЮоВЧчРЦШ€щгоСЊЬПцїЩJЪЬШИжФпыщСвВолСзиЉушхЬШадзЬетФиѕКсѓНштгфЦЬщзЪщѓмшђљныцщьщычмєї№љќ№§ыўєњљїќіќєџџџ!ўCreated with The GIMP!љ џ,Ѕ€ўџ HА Сƒ*\(F†#JœHБЂХ‹БЩБЃЧ CŠДHfдF FЊ\ЩВeD6Ът@ёУЅЭ›8Cމ† Dr Jє`'6l”`Р`ЦLбЇPoЦЩGЩ"fEнЪ5dЋj|˜yEЧ ЂГwвzёТ„IзЗp ЦaЃF CŽ–%kwOŸп~~янkЗlж/5т*ŠHcЇNo’ЅLзВeн2w—йЛОўќщ#ŒˆIтХЈEоYцЎYœEАЩX1є и(D fЭrхJ7япЎ–ЕЋчW8PˆS+ЏЊ[Пv<_њЫOžю |7янЃtў+ыжїоВ;n—ЋO8ЋžhwтЈ§ЊўЋiифhелћnнЛщтŸwЎИSO=н Хz  q§№гЭTвнђ‰lUшЂЫrŒJ#З}Š'#zПЭВЬЫ(и rˆИгO?ї(уJ|‘tВШ+ŸМ"‹,­xУ ^Єeф‘i…‰г›.П)c +ЅAu/ДŒ>4vУ[!ё™AuЏГ‰У cZ[L˜pƒ pо0У kщ—фYс…чŸ+Rvƒм EЭPe–џќ %|BIд0ƒ&аQпЄBJ0Ё8'ŸЬЪЬУJ(’ aФ €ИЋnКAЩлxЎмa+NЊјСЫа*=fAС< ѕЌ№С +p№д(€Œ5@17h<ƒ 5œa‚3lЂ‹7Œаƒ$&Џ Эўо+ЃМr0-ГŒ$„ЯЊ&йHЊѓюц%z9IЃJ#Ќ‡2tаƒ@Q&<ђzlzЁ›ЅIАщvїpЦЌ@ЬY{з.ћп.ю2Ь„Ѓ1ТІ€^$"ЋB9‹—ƒЖ4 =Ту‡.Hд‰+!d0ž[t8рЬЂ$(  ’фЊ7№B‚?Т ${яВЬ8…Фn;пИГ‚KЩЌоћм Зr€~Ћ–x"Т№…0Tс‰6Є!$ AА‡PЦKПёPZ@qLРЁЪN&hTЭxЦ3тWЛМ§MpЌxЬ`f;  HшпЭ,Р„§№@ўЪhQzTB†Rј!Ю D+ш‘8tзѓ(юсeшIЗбЮ4Ѕ.tсd"!4ЪqphC…каF'дЗВdр-w1”DўєЇПЂA7Лй ДАИYАЊ7МIžHЊрˆ44OŽаƒ*ŠQшŒ–ѓмHч7qƒ-РP†Б E"! њ”žOи"pГ 3ИCЊ\сЄн ЧЁСќ bдƒд0Ф|QL˜Aѕ& A а№Ш2њ+KЮТт“ƒHcаI“ž4ї№†,\JЮfШx†-np†UР1Ї:•OkIO "!‡Рg№j@PИт‡ѕ‚,В4i@Ђz8‚* ЊŠЉ.”Ё-+‚ж‹€BАВўІaЃКhтЖ'с0ђБOМєлЈЧ6^q rvb Ћˆс, Kž&Т‰@C ЗT$lAЈOИ%.‘ЊдY8I@отDŽАеЯЂVДЄ=&>ћ‰#Д6"-HУ ЭОX’?Yд‚H_мЂƒьсŠxTмr"АјDб8ŽN(уВxDswњм:иžѕDТ+ D6ВСKЊЊ'ДŠАр­0ЏVЙJ L!Ÿ5ƒ{)’+\Ф9Ъа,@ЁŸЙЂ/"А“QW Т/mЦ; Š—кТш”…# ЋгBWКvИ.щљиЧЦ Вј№>‰‡_о gГ9ёўhcьƒЯъсk>‡X€f‰HЁІsќб №шB|фin v№Уі№;^A$—SЩƒ „“бid:дtаND6]‡ы^зК^ށ tx3ьэГџЁо ‘АРчˆ55ŽjФzЮХѕhаHŠTТЮАФDю0%т03Р™4Q2vДƒќА2ёQМєяˆt9mсW#УТl A(Мiж uр…Њa$ФрнРоvp‡Qxb­2 T%RРкжЊТtЭ[ŸЃCB+*В‚^„ƒ`{ˆШZЉt-Эl58С 2^S;Ь‚ŒьpХ7ўд`эrbћ jpД`ЙёШ”|А›ЎАІЯM.zЁЫЁŽёfys Л~м "М@пˆTЙŽ5@‰XћЂ-ˆѕ9єP‚@œЃA СDV`‰‡‡#•XюdY…ИУ‘(сBH2~ †—ŠтGxC9я>ї’ЇQЭ8ƒ АNo \0‚PoЮсwыМЈИŒ,?УƒХЫ–иш085~€jЌу› €bН,@чXB@„Xб †5Zй!РrУНvјўїРџ§etŠ;ј`,ЈС:ЁТHр*З71•?ЃiМE ў`xФзМЫ1Е O”Кда‚eAбдQ=" ШР:>пŠ РрѓыАrёљb” ѓЗ а g Н`,zV@9…№qјшќ€(xјР‡№l vdУ‹ ?}ЭА%Pr}% бв 50.фВiф2.7'.Žќ“GE…=GeѕжyrРД аЉ— а ѓw!Аж0~0Х@T0iuV0 эАЁ1†cˆbh†ў`†bx†gh†јPјPFо%q@NЖp ,xЂ Œв Эр/Р4x[ў‰wƒe:јnЄ&х‡KћTvЦƒZPtFѓч ,h  MШP(…  а4рSА‡Ёсd8†kИЖШ†шЕx‹З˜†ј0 I0LOі Їd Р 9‚x^P.]P.2#.8шx@ˆKa„zbIХЗoFЈѓч !@ŽыžPr0Й3€DАЌ6ЌhУ§0‹ќ‡q(|ср`юРфСЙЈ‹Ё1Ы :7ЎPugN‚е н‹€}‚Ш ур ‰—ˆ7с‚.1А)кh~<иsР rYBЗўj Œы@РѓgРё€“:Y ˜ŠСŠd\"cXїp Уq b žС ž‘мР іp•у T$\‚ Ъ€!P9`rЎа ‚И–Э№'`piсђn/ ƒ™‚)‘И’`Ис$fvu†?Q“ж@"ще`˜‰ЙDае`“@”a”ю ^Љљ2е оа Ыр юšѕщєЏђWљюp•юа Ќy•в™Ђ–IL0Hж–€7ЭР mЙ 9p1РMp™œ/аœ$йЂ)`*Љ’F%^Рў$4–I‘ ъ`“ё тЙёРK 6Y dаѕUєр•™№№п  ћщ Ђ!о№ о№ п№`Вљ`і WЩ я€ м№єpŸѕ№ЪPшёЪ@l)ˆIс.чвœuI’рввI(y3<{Щ3M5tёIAае  ‘‰˜;™щ№Ѓщ Ž-€ŠAHюС™+ЅŸШ№ Р  žp‡‡аџЉ иАŸВp Y* Mњ ШЁпА л€ яА `њ№@єpš* žЪ`V ˆТ)œЃрЎШ d=0u‰рrў„ъ)*К)+Њ’№ђ˜ б-РУPу‰˜Йаr€ЉmРžё ~РнЙdр №р+г0 a*eŽ@žрQŠрЅ› КІaJІeКЋга Лњ Ќ: М ЦšŸША t@-Рly КрdрФЙ№:ЃЈ@ˆ „њ­ъЂ)™ВЂEХ–uY:ЃњЋш ™Р=Z ŒЏˆЩR ѓP яjT1В№~qšи0 Ц€ ЦрЊŽА Ў ђ ђ Sz^zАЋZАЦpБ e‚БЛЊЛ иŸиА ШBU0ЮZъа‡pўЁЎ›Бщ=№m  :пZЈ†Њ.†ЊC{й—х—ЅQЃьZ y№R№ ё0ё№ 0 ёаДy†Љk`ыšŸА є яpІ[ А€ „№Њ†А ЋpW 8А ђР t№ ВАБЏ) ЖЕpЗЦ vЋЗx{БЋJІ#;\Ÿ№ †0р ъАŠЭ`EўyЁХ kР œ ­р €Ђр™›Ђб‰Й…ЊC@p?0C XЎўQ ъCP=$Pыdа ќ№ „№•ЌъD>UьСUўъQ@ф{ьШNфР™0ж@А–zњзIи 0Ка ­рž Та@Њ ТА mP Р ~аzФШB:0o0ЗSв mb№ а№ yЮŠ-ащ!ш`ш№ Я Иp x0BK@ЉЎьC@ь_P„p гфЗ п-_0Рўѓ>я6`Q ђЎОђ-:ађ?ѓЊы3?€эˆч4АZ` 6ЙmРU€еР А`UDАSPd@i єЫB­% >>ЂpяшФšRЖ7Œœ0 ­0Sа"С Я№И0ї №шР Iu,€(`R‘№ѕЗ0Фђ_€iр Њ0"аC€: љ)_љoљŸљšoёЊ>я, 9хH=™Žщи ™A€%PЬRcщ4PЖOћ% љŸяXŒ‚mу№Љд !пL,7,Ž@іРnў/ `cx€гŠ ИР ЬpїЇIPAР)C ‚ ‹ˆьяC`вО _"№ј!рї~?њпџўпџЁХР%–, hX  LЊХƒ˜. 9щжХ“иBA‚8‹дјР№C‰J Q"Ф!p|АшдЌЗq2m‰)œ8?*TР€!‰#:(рпRІM: `рС а у ЌЇЙ$ИA0ХE0ŠўСa€fоNэНїМ|ѓЕјт‹‹-Ё‚[э%†У˜фyЖ–ЧKbђ˜ФЎП ƒƒ№ЗимRШ­mbMлиp+–ЮhOж6e•ы™A[О?\Ц Ікё&hъ|о„h`†–—оX>Х$T}—ЁN JЈ€ѓЈKtаAXЂ‘IВ[ž}ИЎ&SўBыЎ[џƒЮf t5w!…аK(ШїЈ!‚ЯEN’INрdНљdТDœ~Љe,`jІ–9с€хљ”'6СЦёyc9šrМDHуШ h@„сФ!A+Мdˆ4№иАхv H@”ЈЁ ‘e,уKrР‚Ќ‘ˆk\#H6d” dрЮ#!Ÿ”НzыZhсRЊїЧD@wјоЙjH>ѓ #}Ic_Окз%С’>  ,™",С DЈ` Е› ”ly P@*7вMZђ ­Д$–шZ2‰D eqщ%ЅHТ/“ў“G>!т„Lё‚YФ 0Ѕz'Јї4€tѕLЊ€мМЙCP‰Ъ‡—Ar+”г“хДB–„ €‘y`мˆ*Si$!VИg9ѓ‰Ю) г T,'>iЩAI'0a`“O^ф?–)H?>€)§Ч 4`ШF]3‚‚уІ>ЄЉ}-РћђMСЅd˜BЊа™VЁ 3р@Nhˆ@/AXТІ0S2Ш”І3•)ва†)С d CшP€ўSŸ0˜yАР:t8&˜ASЪђ*t "`H LГšŠxB ВiД’І4 ?LZ࢘ЂbЉ‹l`CгЦ0ІƒЅ)cлХ.v1zhdяšП2•Љg!UЊ 4дЋ|ъ[SЬњб>ХlmыЊI!”‚h‘ы”њМižіq  ‹ЩФќлLdЂНЭm%ў \пfBА~јУr›\ц.ЗЗЪЭ-rw иM0&€ЌLy`S9HЁГ]§ьp0ZД.ЅМУAm[Љ‰ >Ё D;_)&Ї>їI2_RPюoM‘‰VЅЂUџ5E€ьл§ўЖЗПO€+Ёр+X>Х§C+і \уЎa ›­ТЄєн№ъmД­–Fe0bUsFз|мHKzIЖ/ЂтAјK5ўbЃ7Цq/Lqcџў—ПАŽћ[ЕTYШЙШФШp%пAmЎr§А)рw?o‡‰cжˆVK1s žРГžЩ ‹o]EuWRс…kш…/zЁ0gєТIuЦsэЌ0§№xЧ;Юёљ<ч^ш‡щPGЂе1ŒaЈЃЫр„Њќ‡KсYжђ–—жчЕ52јДтвЉМ–ЎВез]й'*)ЄТЮwЌeэ$Aыи€62эœу/щЧ­P…*|С#z4ТЊh…7†бˆJЄпЭ[ІS&8Ї№‘)ж^JVРl„(вЃx8&аЌŠљђА}>\Z^Ѕ шpўДлэ†7МяLk"ы'зVЋš%\]ЕBџК˜№У&‚­ŠbHЃiі&ZЁ‹*XРU‘vAё”.7Г)†4 z`еŽхŠ0sщEЙЛœЇ‡0Ж„“bяxЯлЯ– ЯђАšяa7gицѓ[d chЅаƒАYŒXнчœZТЅЃq=bћшђ1—"l $рТL ђ6M^ЅрfхNЊъЗ€)_ЙМcMoKмœц•аљ|к>wКЗ}>~аƒ<щ_дEЏ0ƒ„/C М.ŸШРв™ОЇvPЛ)LpŠ Ќ­€\Ё= ж=ђ  ™Є$OiЩQjі0єўТнwОqнщ^dЋэ<ч4§ЬУ %?т _ј2A о У>hХс _”Hё‹ИžrШ7…к_&С ‰+Žeш†A2$/ШРU€„3@„3X…UАƒ;@‡~А‡Б`Џ6Ќ‚X€-а;kŸрЫЎ/™„ŒП@tCœA„KИR€Цg$…f„9˜ƒ,РЦCDAєРFˆТCШ(ŒOШ€\`—$PL™;ЈPРŠ-CHИ]>Uм‚G(Д~PC[ўьpK]œЛ="БœВЦ5Јb˜)ˆС №A,>XЦK Z8†cИ†<мHRИ„fДЦlМЦBфЦ‚\Аˆ\pLЭX‡tрOdЧШ+I(4рI;А4иGtAt№GZД‡dЏђ™<Ѓ!Й˘-oЪлb?р‰LЦe$r№ЪЏєЪkшHмHgЩkЬFl$DAдA‚5xˆЩXƒ\Р)ј^РД›lŠ'@{№ЫПќK~И{hЄќИЏшŠЎ UC7ій ’€є€"˜iKАЫБЬHZˆFfЌЦДФЦ2(ў№€@€X†\№•xј…<0йЬ-АIНd ­PЬЏ№MEHЬр N0ƒ'иŠЎ\tЪќ>ЪT€€И€­ФHr8ыМNЯќЬЬHhЩГЬ‚kь‚lьЖl€}шкœKsиƒ<бЭ=Б€у$Юќ$Nу|‚34˜&и 9№­р€ІtJйк‹ЛRh‘ NЫМ†ЏќLаьЮя|Цj<Щl\ЫtM}ЈWјƒI‡Gри–<№Ѓљ Pа&а-р1а)9h„F№]а…Vh  `и„ЂJГЇ,СЅй а,ўhфHВфШ %‡эфЮюtFвdЦ.@ЫЕtьpш‡{p…Yƒ5И 0Лмs‚м„бЅАУыj…\(†a(†b J™”>ѕ]]P…Чё9Щ)H^”‚№ЁН ’@ЭtFRШHБ$Ы ­ама.ѕвkDKnМєд4W8/˜№‚Xyш€­г-›-„Fа…\P†bШU]рU]вMVO жM0V&•-ћrдМ"вJЩБдTLхT.ѕTeЦ“tШ3Ј‡~(QWp…aD`‚;hЅрOœеjс&hƒ@№„Lа…L№FИе?0Ж@ўЈ‘ХИ.šJƒ5˜Ќ,HЭГ#€ аLf|ЦаєHi­ааєЮkЕЦ’,‚2г Pа‡~pqзQ&KзЅ#:mWІxW9ИUyѕ„i-и,XUŸв Ий ˜)@ЗЛrЉeЕ4@€щ|Vi|Ц†еHмRбŒFŠ-Щ,(ЯFМdаш†4…YаD`ВОAй”%žbšU №№2[žъ)›U)№ЉxЮ­lЦяlЦJЕTZИаjXЇб”NNИ‡ŽНZ­u…Q…;@ЏлћZБЊU•и ŸZ;”AЄ[/eXГ„FНeZГЩ‘єаў.ЯдЬСƒnши=еSЎНƒHРЦU8WP8„•ƒН]X†SьеЬLг…Zkф\МЅ…Ѓ]вДи"xМ1X†~№ѕY­]Хe\лMY Ш]O˜‘…Sэ†zш†Qа$ˆо@,‚ё4нф…FM M ]г5]дХС о{№‡~ИZеP(W9@„ШKяеЫP\Op]pP€`W8пzа…;Иˆ^ЭMF#0]MШM№RЈЭ_QХСр„vшX}peHгaШZVм;і^VНPЌYWh‡z`сYР` ЮAœH­tђмF#опў…eјж~а‡vрсУYЎE-Ј]Жн8зF рQ˜`эwа о!цжі Фш}7юVtwЈј_єY1Wf2P(Y.цbжaPакЌ-пS…bџ%бv‡YаaD€dH~„Y(cwр‡Žэ‘ьa‘•ƒ–ƒSwшЮZqнe+цки§cR&х_ХEФ_]п4рSFež…] фGасУ•]HfrM\Х…(+ЉцХ;уr6чs.чaX Ю‡{рЅ(foЎц4E„]PсrўЖуы­кrцTV‡|V9€нrэcDИШ‡}ц…Ѕ кrЮ9Ž](чFgт@ч‹ЦчahЁrИо{јШе‚S&рFh}Цh“n&…\(ч{‚fВFИƒяz„rЎ‡X ^8c}сащы…шŠŽ„Цш‹@Q}hg4мe2-Ђощ3–j‡~„…@ш‘3†0рйe€єLhfhs n ‰>c1jсеЭ№Ш‡€–ыЙfЄVjzјhсP№VЛV‡Fш]-шQФžы§И-ТžkІx„„мdhЛІjoБœў}PыЗ&1Ш >jХS‡гІtŠd8эz ‡Š5ЦЋЩfŽsu2zЈ‡| 9шЊЈІ}0ыЅˆьzmп.6ЇHю‡&эiz№эЃu№mxјkІр^р„F+oш‘z№†‡Г  x€є~ p8ГяЩ…ѓb ьІn9№Ѓaoё–moР6ўЮяшN nz(Ye ё†uР6ќюЊz€x№ьЇHŽ o ?€‡zЈ№wv ^ pz№lmся з‚L8pFh ‡zа'FЈpxh_ ёІ` pЈpoатЅШ…Wx№Їлi(ўrx hЇXru„Мr чЌЙиqІђ"qŽIJёџk˜‹bр#oјri8œiƒСУJ‘kШ…\†9ŸsOјro0ѓІh„?-,kŠ\ƒуи4oђШ…7їчrЇ`„x†Mѓb№937€a єНЮ›ё9ŸJЁЇўр-ˆW~н„ык„RyQvЇxЏbЅh„Ё‰…@(­wr›їЦ†RЋL€Џr7ї?@Yа1oY(ŽяŽчјX ƒсHГUbv„Žяј xŠ”чј‘џ‡bљXКLPљ`Š*Py=јFИx–7wІhƒO z:ѓR њЄWњЄЯѕjЅŸYЕ‚Єза‚X(zћ^ŠMHzœg =HzzЂїњ џ‡Б73ѓO0„ЖЧЇƒ ЖgћxЖo{"PpC{№Л‡Х ЙЇ{І€Жџ3ќЖ‡Г_ +B 38>C„Чх0ƒЪаў9јЭ'‚ЅЫќЧ‡ќЧ7ƒ: §Сg 3€ќз‡ќХg|3xƒк?‚uŽAЈ§7H€І8‚нЇЏѕќнƒФ{Š ўнїу3ƒ>Ј§ p фOўтgќ8‚>ј7ј1ПН/ј‚ѓ:7№~јCшƒѓGџє?џ/xёџўс№~7ёясPёwшwŠ˜љџ§ˆ,h№ Т„ ќхсн`љ2$С#н` №!ШП„’B#+В№ˆаaE ‡T)“!Юœ:wўˆтsЪƒX|FСиph”A‘BГЉO%'ъh:сЂ7 В L:tыЮАbУbУ‚ Эž•8Аьй.Яв­kWСЕэКмKW_КFЧ.Ь€^„‰.ў‡ј1фЦ ч”Ь˜ВсЬš7sюьйs@;tkcvs-8.2.3.orig/tkcvs/bitmaps/newmerge.gif0000644000175000017500000000023511664612512017056 0ustar timtimGIF89aТ‹‹џџџџџџџџџџџџџџџ!ўCreated with The GIMP!љ ,IHКмў0:@kˆЭ`зЂuл'Nd Ђi–)#I t=˜ЎeзfьДфФхfEX`Щ )ЪцR(ŒJЉP‘tŠБlТ3L.3;tkcvs-8.2.3.orig/tkcvs/bitmaps/edit.gif0000644000175000017500000000022611664612512016172 0ustar timtimGIF89aТ’мHИџОООŽдџџџџџџџџџџ!ўMade with GIMP!љ,@IxКмО€ˆI*†Э9Юб$NСї„B ЌkvА0ьЂнf:шXКЏ4’7žp8,ШЫЬїл|*йѓ— 6h+#—д*r:я+#;tkcvs-8.2.3.orig/tkcvs/bitmaps/mod.gif0000644000175000017500000000027511664612512016030 0ustar timtimGIF89aуoooЏрџ‹чаџЙЂ‹‹‹‹‹‹‹!ўMade with GIMP!љ,X№ЩIk8ыЭƒџ`јab’" @‰ŽТ@{Ж `q‚0иѕ~œчŒО"8R6 и‚QZ‰П‚ћЕ6УуЎїKлжЉ•y97Сэ.Ž^;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_ok.gif0000644000175000017500000000017211664612512016711 0ustar timtimGIF89a Т„„„џџџЦЦЦчџџџџџџџџџџџџ!ўMade with GIMP!љ, -HмЎp…I Ъ:AР‘ЧYM8V$ž–I ъNАЌВн|ІЪКы#оТAd(;tkcvs-8.2.3.orig/tkcvs/bitmaps/rcsdir.gif0000644000175000017500000000016111664612512016531 0ustar timtimGIF89a Т„„„ЦЦЦџь‹џџџYџь‹џь‹џь‹!љ, 6xЬњpAEQYщmр Ž$Щ ]кjЫ0ЧТ+wŽSяОЫ< ч#юX­2Y9ŸŒ;tkcvs-8.2.3.orig/tkcvs/bitmaps/newmerge_simple.gif0000644000175000017500000000020111664612512020420 0ustar timtimGIF89aЁ‹џџџџџџ!ўCreated with The GIMP!љ ,9œЉЫ­c|„‹@ѓи|mо†YV•ёЧ‚ЈJr,6Р чЬЮѓ-€7!ЅцH*—ЬІѓ M;tkcvs-8.2.3.orig/tkcvs/bitmaps/tkcvs-help.xbm0000644000175000017500000000361211664612512017350 0ustar timtim/* Created with The GIMP */ #define tkcvs_help_width 48 #define tkcvs_help_height 48 static unsigned char tkcvs_help_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x10, 0x00, 0xfc, 0x0f, 0xa0, 0xaa, 0x28, 0x00, 0xfe, 0x1f, 0x50, 0x55, 0x11, 0x00, 0xfe, 0x1f, 0x00, 0x0a, 0x28, 0x00, 0xc2, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x80, 0x1f, 0x00, 0x0a, 0x28, 0x28, 0xc0, 0x1f, 0x00, 0x04, 0x10, 0x14, 0xc0, 0x0f, 0x00, 0x0a, 0x28, 0x0a, 0xe0, 0x0f, 0x00, 0x04, 0x10, 0x04, 0xe0, 0x07, 0x00, 0x0a, 0x28, 0x02, 0xf0, 0x03, 0x00, 0x04, 0x10, 0x01, 0xf0, 0x01, 0x00, 0x0a, 0xa8, 0x02, 0xf0, 0x00, 0x00, 0x04, 0x10, 0x05, 0xf0, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x70, 0x00, 0x00, 0x04, 0x7f, 0x04, 0x60, 0x00, 0x00, 0x8a, 0xff, 0x08, 0x00, 0x00, 0x00, 0x84, 0xff, 0x11, 0x00, 0x00, 0x00, 0xca, 0xff, 0x2b, 0xf0, 0x00, 0x00, 0xc0, 0xff, 0x03, 0xf8, 0x01, 0x00, 0x40, 0xf8, 0x03, 0xf8, 0x01, 0x00, 0x00, 0xf0, 0x03, 0xf8, 0x01, 0x00, 0x00, 0xf8, 0x03, 0xf8, 0x01, 0x00, 0x00, 0xf8, 0x01, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xfc, 0xc0, 0xf0, 0x0f, 0x80, 0x7d, 0x7e, 0x60, 0xfc, 0x0f, 0x40, 0x82, 0x3e, 0x60, 0x12, 0x08, 0x20, 0x01, 0x1e, 0x60, 0x0a, 0x00, 0xa0, 0x00, 0x5e, 0x30, 0x0a, 0x00, 0x50, 0x00, 0x4e, 0x30, 0x12, 0x00, 0x50, 0x00, 0x4c, 0x30, 0x64, 0x00, 0x50, 0x00, 0x80, 0x18, 0x88, 0x01, 0x50, 0x00, 0x80, 0x18, 0x30, 0x06, 0x50, 0x00, 0x9e, 0x18, 0xc0, 0x08, 0x50, 0x00, 0xbf, 0x14, 0x00, 0x13, 0x90, 0x00, 0x3f, 0x0d, 0x00, 0x14, 0xa0, 0x00, 0x3f, 0x0d, 0x00, 0x14, 0x20, 0x01, 0x3f, 0x0b, 0x00, 0x14, 0x40, 0x02, 0x9e, 0x04, 0x06, 0x0a, 0x80, 0xfd, 0x80, 0x04, 0xfa, 0x05, 0x00, 0xfe, 0x80, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; tkcvs-8.2.3.orig/tkcvs/bitmaps/cvs-says.xbm0000644000175000017500000000353111664612512017040 0ustar timtim#define tkcvs-says_width 48 #define tkcvs-says_height 48 static unsigned char tkcvs-says_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0x28, 0x00, 0x00, 0x00, 0x50, 0x55, 0x11, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0x03, 0x00, 0x00, 0xea, 0x2b, 0xa8, 0x0f, 0x00, 0x00, 0x7c, 0x10, 0x14, 0x3c, 0x00, 0x00, 0x1e, 0x28, 0x0a, 0x70, 0x00, 0x00, 0x0e, 0x10, 0x04, 0xe0, 0x00, 0x00, 0x0f, 0x28, 0x02, 0xc0, 0x01, 0x00, 0x07, 0x10, 0x01, 0x80, 0x01, 0x80, 0x0b, 0xa8, 0x02, 0x00, 0x03, 0x80, 0x05, 0x10, 0x05, 0x00, 0x03, 0x80, 0x0b, 0x28, 0x0a, 0x00, 0x03, 0x80, 0x05, 0x10, 0x04, 0x00, 0x03, 0x80, 0x0b, 0x28, 0x0a, 0x00, 0x03, 0x80, 0x05, 0x10, 0x14, 0x00, 0x03, 0x80, 0x0b, 0x28, 0x28, 0x00, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x07, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x0e, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x70, 0x00, 0x00, 0x78, 0x00, 0x00, 0x3c, 0x00, 0x00, 0xfe, 0x3d, 0xc0, 0xff, 0x0f, 0x80, 0xfd, 0x29, 0xff, 0xff, 0x0f, 0x40, 0x82, 0xa9, 0xff, 0x12, 0x08, 0x20, 0x81, 0xa9, 0x61, 0x0a, 0x00, 0xa0, 0x80, 0xd1, 0x31, 0x0a, 0x00, 0x50, 0x80, 0xd1, 0x31, 0x12, 0x00, 0x50, 0xc0, 0xf0, 0x30, 0x64, 0x00, 0x50, 0xf0, 0xfc, 0x18, 0x88, 0x01, 0x50, 0xfc, 0xbf, 0x18, 0x30, 0x06, 0x50, 0xfc, 0xa3, 0x18, 0xc0, 0x08, 0x50, 0x00, 0xa0, 0x14, 0x00, 0x13, 0x90, 0x00, 0x40, 0x0d, 0x00, 0x14, 0xa0, 0x00, 0x40, 0x0d, 0x00, 0x14, 0x20, 0x01, 0x40, 0x0b, 0x00, 0x14, 0x40, 0x02, 0x81, 0x04, 0x06, 0x0a, 0x80, 0xfd, 0x81, 0x04, 0xfa, 0x05, 0x00, 0xfe, 0x80, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; tkcvs-8.2.3.orig/tkcvs/bitmaps/amod.gif0000644000175000017500000000022411664612512016163 0ustar timtimGIF89aуoooЏрџЂ‹ча333џЙџџџџџџџџџџџџџџџџџџџџџ!љ,A№ЩIk8ыЭƒџ`јab’fŠІхF,ЧИЮИ<ж^ќљ=Ую' ‡См љZfМWДY N],л4Ыфю8`N;tkcvs-8.2.3.orig/tkcvs/bitmaps/Anglerfish.icns0000644000175000017500000015244211664612512017526 0ustar timtimicnsе"ICN#џџџџџџџџџџџџџџџџџљџџџі?џџЯЯџ№?ѓћЯџ§эПџўьБџџ>ЛџџПŸџџПŸџџП?џџџџџџўџџџ§џџџџџџџяџџџџџџП?џџП?џџППџџџюџўџџ§џџŸїџџсџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљџџџі?џџЯЯџ№?ѓћЯџ§эПџўьБџџ>ЛџџПŸџџПŸџџП?џџџџџџўџџџ§џџџџџџџяџџџџџџП?џџП?џџППџџџюџўџџ§џџŸїџџсџџџџџџџџџџџџџџџџџicl4џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўџџџџџџџџџџџџџџџѕояџџџџџџџџџџџџэ|мЬЮџџџџџџџџюююХЭююмпџџџўџџўџўнэнЬЭюмяџџзџџяџэЮмЬЭнохоџ§|ЯџžџьэЬЬЬНЛљнџѕЭ~џїм Ь\млZЏьџ§}Юџэ |ЬЬ нЮОў§п§Ьоџм|н||||\оъЎzчЬќЧохЧнuЭ|}оэЮ}ЭџэмUЬUW]eЬн]Wмняџъ^нзЧхцмЧ…юUџџџяяюм}ою}ы•X]юџџџџўњн}yзн}}зз_џџџџяџнЧжмЧмзмЭџџџџюэмн}и{Иыз_џџџџннMзчћ!ТЛОЭŸџџџџ§нн}ZНОмžџџџџџџџџЅнЛВыМЭџџџџџџџџџљнЬмЬЭяџџџџџџџџџџљюноџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџicl8џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћЌ§ўџџџџџџџџџџџџџџџџџџџџџџџџџўќ€yVјјV§џџџџџџџџџџџџџџџџў§§§ў§љVyzњVјџџџџџџџ§ўџџџџџЌ§§§§љћћљV22W]ќћјљўџџџџџ€Ѕџџџџ§ўџџџћїњ],+*їVV]]ЌЌVњџџџўЅyyЅџџџ§ќџџ§јњћїUTUTU]]ћўЌVћџџєyyyzўџџџќzњїі++їјVUUUњ‚‚ˆ§рћVўџєzyzyЌџџџћіѕ++***і+VUUUћˆЌ§ў§VћџўzOVzєџџќ*NzњљxxxxNNyyyUzЌ­­­њњџЅOOOЌџџ€NyЄІ{yyyyyyyyњћћњzќzOOЅџџўњњњ€Є€UzЊЊЊЊЊЊЄzUU€€€€€yU€§џџџўќЌќћњVOyЄЋЋЋЋ€OOOOЋЋЋЋЋЋ§єџџџџўЌ§§ўЌ]UyyЄЋЋЋ€њЅЅЅЋЋЋЋЋЋЌєџџџџџџўџџџўˆ\yyЄЅЄzzzzzzzzzz€Ѕўџџџџџџџџ§џџўќ\zyžЅžssszzzzzyyy§џџџџџџџџџќЌћљ2€yžЅЅz^^‚‚ЌUyўџџџџџџџџџ§љVVVљyyЄЅЌ^4 4^^ˆVVzџџџџџџџџџџџ§ЅЅЅЄЅЌ‚44^‚‚јјЌўџџџџџџџџџџџџџџџџџћVVњ‚^^^]VїVЌџџџџџџџџџџџџџџџџџџџџЌњјјјјїјVћўџџџџџџџџџџџџџџџџџџџџџџџ§ЌќћќЌўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџil32 џџˆџšџS(€•џ ‹UQФЇmџƒ џРzgІЈЌІ‹„џџW]pb,hžtofkgp‘ ‘Qџ(Z\џџR26C2rwTVŸЙНЋ‡SV˜Nџџn^џ?#€УЌnЮЧПЖЈЏž†H0Ž„37skl`џ?R™‚ЄlfЬЌЌЂЌ•‹Œxg-•n qrt`0џLfЅЮйЭЩИ ˆ• Ё‚~o\' RBpzu{1џ„беЛОТРХаС‰ˆ –l`L&d\_QTWY[dadsjb_X]]tXq”Š5[ƒ†ƒjH^‹_0489:;F‚„ZTRWe{Š[P%џ–\QUh‡‰n=$'))R‘Є˜›T;9@F>+ €џ…ZI@1?“‹nW2*8;SbGHC3::885% џ!+`Іtc>+HebWUVZbdhfS8‚џ*Q&VЊyfE,F}vj\TSWYpwf"ƒџwy†ЁФrcD.3TWl‡{aCN€v-ƒџn‘ЂЂ˜†i]<(5–мћќлБ`‹‰u„џ<^\QHGK8.ЭщяЯІsІœ@&†џ‚-c‰–…z‰˜ˆ…œБ‹Fџ]z˜ЄЅІЊЇŒc1џ CV^_WD*“џ…џџˆџџџˆџšџW€•џ sW^ФЇHџƒ џР~‚žЏЏГ­ƒџџw“ОЅMkЂ‡™ƒ_cdtšЈ—QџmlџџUAVrZ|}VM‘”ЂЅkDXЁ•U€џЇŽџO%€УГpgЕФИЖš•~e30–‹3YЈІЎ—џq›џ‡ЊpUЖЎЊЊ­™vmZK.žuЈЉЋAџ˜–ЋбнЯЩКЁ…›št^QBV˜E•ЃœЃ>џŸфчЫгжабр㓉šWK9•j[–ДЇ–ВжК€ƒu…ЇЋІЇЖФЃž•ЋŠ7'$n‚ЊСЭР^’ОvXy…uy{€„ކЋŸ€p]`w”€ЉТФmHonlbPh‘ˆI:=@ALbЅЋЏndegqŠdc>џЂ\OKObŠИВy;{ТЯФЦe**.2- €џ‘^KE<2jŸВgGDIeqW[XFJJFDA. џ$!?|ЅЉ{ZxІІ—ŽšЃ “Y‚џ*VAyŠЎ…]‚ркЧЋ››ž”ЅЌœ3ƒџdkuŠ~Ќ…__Ž|xqaQ8cБЁ:ƒџ]“—”‚•І{Y;nšНБ˜~\J“І“*„џV}|tqlxc>YЄЊu\bЉЄB)†џ‚ i™{ejtmvšИ“Jџa‚ЁЌЋЌВЏ”i3џ HZef\I*“џ…џџˆџџџˆџšџyQ€•џ s…†НЏHџƒ ьЭ­ДВООРЙƒџџ—œБЁiЙЏг^di~ЊКЈ\џ(}sџџдyY[jZŠ”jNŠ–˜~`AaГІaџџОЄџx?€ЮУxS›а№м‘ƒr`.3Ї›3УХЯЖџ•Ь–КJЎаыфшРi]QG 0АƒXФЧЩЌ\џЃКглЮШТЙБЧзгƒHA9^ЊNDЋЗБЙSџБяѕзсщшщёрВГЬМM<) ЇwrЃКАŸжі§ЌxuŠщєђєјљвНЕМœ) yІЛТЙ^'Пълк5sебййклрЪИЦП†dJF€І’ЏЕМi[‚„Š’——МВ‚™ššœАЖИˆ’“˜ЅЖС‘vHџЂbRQUrЉид•`fkjЕУЙИyqqx}zc?€џgSL?9wКкФhtw€omihwzywnJџ()HŒЦвœtˆЌЊœ—›ЃЈЇЃЂc‚џ*^#;„ЅжЉzˆЮШЛЇ™‘•ІХЧЖ;ƒџšhhnƒœ˜дЉ{mŒlSB7.)'wУДGƒџiŠžЃІ–Даu617D@80 #”ІŽ*„џbŒŠ„†‚“{@3=@5(!D­ЗJ,‡џ‚'uЁЁjA69?[•ЦЄSџm‘ДНИЙССЅt8џ PfqsgR0“џ…џџˆџl8mkC л#bСўћљђе–7 9;9.Xжџџџџџџџџў O!x‰gbfЩџџџџџџџџџџџџи*БЌ]ycџџџџџџџџџџџџџџн%–єџ”X{3ѕџџџџџџџџџџџџџџџДїџџч/|кКшџџџџџџџџџџџџџџџџџT%ѕџџџЧƒџџџџџџџџџџџџџџџџџџџџКCџџџџlkџџџџџџџџџџџџџџџџџџџџџёџџџЌ Вџџџџџџџџџџџџџџџџџџџџџџџ‹кџџФ5ўўџџџџџџџџџџџџџџџџџџџџџџџџџЏЅІЦмџџџџџџџџџџџџџџџџџџџџџњstenaбџџџџџџџџџџџџџџџџџџџџ}LE6cџџџџџџџџџџџџџџџџџџџ€^"!HѕџџџџџџџџџџџџџџџџџИ|zЙфџџџџџџџџџџџџџџџџџџdhџџџџџџџџџџџџџџџџџџџџї0mееОЖчџџџџџџџџџџџџџПP 'ЧџџџџџџџџџџџЕ ~чџџџџџџџнq_НУ˘Yich#Hџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџыџџџџџє?џџџџ‰GџџџќnгџџўЋб<чїѕH­яџъЅVПгџЦЋŸ•їмIWoЋљЄЛпЩќWчЅ№Єoя“ѕJЄ•ЗKТёRЏы'­І­RUO–лж•ЊПљЇ}&еџЎJъKџџї•оЗ[џПЪЋк­џџбU%WџџmрЊЏџћвЅUOџэ)vЛЏџъJщ&џѕЅr[oџ§[ЅTПџџќиЊџџџџЅџџџўШSџџџџѕ_џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџРјџў0џџџ€8џџџР<џџџр<џџ№џџџ№џџџјџџџџќџџџџќџџџџўџ?џџџџџџџџџџџџџџџўџџџџџќџџџџџјџџџџџрџџџџџРџџџџџ€џџџџџџџў7џџџќ?џџџќџџџќџџџјџџџјџџџрџџРџџ€џў?№ich4ˆџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпџџџџџџџџџџџџџџџџџџџџџџ§хўџџџџџџџџџџџџџџџџџџџџџе| ЬпџџџџџџџџџџџџџџџџџўмзЭнЭЬнџџџџџџџџџџџџџюžџмзЬюохнЭЮџџџџџ§џџџџўнчеэххомЭЫюзмяџџџџЇяџџџ^џџљЭныЬЭЬмНЎнЮџџџџь~џџѕŸџџьЭыЬЬЬТлнъхмяџџ•ЧмяџўџџњЬемЬЬЬмЭЛољэЮџџїЧаoџљЮџўЭ^МЬЭнЭЛЕОю§нџџќмwоџџїнм Ьн\ЭЭнЛыџяЭяџїмнЩџџ§РРР Э\ЭЭОъъџзпџќ|зџџм ЧЬЬЬЬ |\н[ъџяќпџ]ЭЭџ•Рwмм||||Ь}ЭЭпЎЊ§нњ|Ь|ЏџPЧХюзЧЧЧнЧ\|^ъўЎХљЧЬЩџ• }Wиэ}]]]з\pм|олн|]ŒЧŸџьмнUWЭжuuu]uзЭЩ]~]зЧнюџџўњ]Эн\}ю^UжнЬзХн]}н^^џџџќ§ююмЧ]ѕюm|ЬЬШ^Uх^яяџџџ§яо§эP}~yцэм…••ю^цхџџџџўџяџэЭЧХх~йYY]}Wнзюяџџџџџўџџын|зчЗЧЭ|ззн}~оџџџџџџяўџўMЧ}йм||знн}Ь}Ÿџџџџџџўџўым\нuчЧн}иxззм_џџџџџџ§§юнЭзЧш]ИЛОЊн|Ÿџџџџџџ§ЭЬмв|u•ŽЛВЋЙмЬяџџџџџџџэнЭзЧЧй[ТТЛЛ­Эџџџџџџџџ•н]^н^žВМ!"ЛКмХџџџџџџџџџџџџŸ§нныВЫЛЛмЭйџџџџџџџџџџџџџџь|ЭОЉэЬЬнџџџџџџџџџџџџџџџўнмЬЬЬЭеŸџџџџџџџџџџџџџџџџўэнмн^љџџџџџџџџџџџџџџџџџџџџяяŸџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџich8 џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§ЅВџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ†Ћ§§ўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓћzO+++јњ§єџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌVїzy€VyјU1OUћџџџџџџџџџџџџџџџџџџџџџџџџєаЌЌаЌ§ѓњO[€sT‚ЋˆІћ€VVOVЌєџџџџџџџџџџ€ъџџџџџџџџЋЄЅž{ЅЋ€Ћ‡Ѕќyћ]2,2V]‚§љUUџџџџџџџџџщyџџџџџџў€§џџџџрЌVVzˆ]3Pїј2,2W\ЌЌњUјќџџџџџџџџЅszЋџџџџџЋєџџџџп‡їOљ‚2,O*0*ј\WW]]ˆўћzUћџџџџџЋ€yyysЋџџџџЅџџџџџојUVќˆїN1TOT1P\]W^‡‚§рћUјЌџџџџъyOyzNЩџџџџаzЅєџџћOљЌљUU*UTїUy]]]‚ˆ§ўъњј€џџџџѓzyss€Ќџџџрщzz€љ+ііі+јVљ€TјT1Uњ^ˆћˆЌпў§zјЌџџџєy€Iz€NЅџџџџџЄ+ѕѕ$і*іѕUљ€UUU*ˆ‚ˆў­ўъћј€џџџѓјzїyzOўџџџъ€O$*OUPN+ONON*$їUz[UV€‚ˆћЌ­пўў§Vz§џџЅzOzјUzєџџєЅ*$Ty€VљzTrTTTyTNNU€tUUUz€­­§­­ўUќџѓzzOOUtўџџў€*NxyЃІІаљyxyxyxyxxrNyЄsOyžˆˆ­§ˆ­ћUњџЅtOPOsЌџџѓ€NUyЃ€ЅФІUЃЃЃЃzOЄOyzz]ћzzyЅ{yVOyІџџџЌј\V\€€Њ€€OU€ЅЊЄЊЃ€ЊЊЄЄЄUzO€OЋ€ЄЅЄ€Є€UUUЅ€Ѕаџџџџєќўˆќ]љ€Ћ€†ysЄЩБЋЋАЋЊЊžOіzOz€€‡€[€\€†еЋЋџрџџџџўUў§ˆ]VUOOyyЄЅеЋЋБЋzzOOPOOїЅЋЋЊБЋЋБЋЯВжъџџџџџџўњ§аЌЌЋˆ\VxsyЋЅЋЋЋЋЄVzњЊЅќЋЊЋЋЅЋЊЋЋЋарџџџџџџџџќўў§ъўўЌ2zTszЄЄЋЄЄ€ЅЋЋаІЋЅЄњЄ€Є€ЄЅЄЋЌџџџџџџџџџџ§џўѓўєџЌˆ2\syyЄЅЅžzztytzyzyzžzzzzzz€Ієџџџџџџџџџџџ§ўџўўџ§ˆ]V€syЅЄЅOmOmssztzstytyUyzЅџџџџџџџџџџџџџўћє§ъ§Вњ8љxsЄЄЅЅssszzzž€ŸЅ€ŸЊyszyЋџџџџџџџџџџџџџєњўќћњ]W+2ЄsyzЋЄЅz{Ѕ‚‚_‚^ƒˆ‰ЌysVќџџџџџџџџџџџџџџ\22V2\\]zyyЃЅЄ§Іˆ44 4:^^‚­OVOаџџџџџџџџџџџџџџ§€zљzsUzxyžžЄЋ­^  44^^ƒˆњ+ЅљџџџџџџџџџџџџџџџџъЅzњž‡ЅЅЅЅžЋЅа^44444X^ƒˆV+ј€рўџџџџџџџџџџџџџџџџєџџєџџџџ§€њњљˆ‚^^4X^^ˆ‚љ+UzъєџџџџџџџџџџџџџџџџџџџџџџџџџџєћVUї+]ќЇˆˆ‚ћ,*OVVєџџџџџџџџџџџџџџџџџџџџџџџџџџџџџєЋ{љUO+1O+ї+UVV€§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџраќ€zVљљ€ќўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџрџџрўўж§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџъџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџih32 ќџџџŠRЊ`G4Ѕ PgbŸЬШПЌw# .ŽЈxls€‘’“™ЉБ–R ‰‡ #!oБ†gu†`MQONVlˆ“Ѓ:†T…DG@FMK?6SHGB?odŽТШРАŽX3^ˆ–ЅW…kFƒ V€=œ}`Q‰БЛЖЖТЛЗЏЂ|?1u“˜J„@m`'‚/sДŸ†WtЭдТММСšЋАЄ˜†{QQŽ”M$Uua_{+-D—Џ‡LqгфБЊКЄ­ЕЃ’œti$ P”“-`zd`‡*! X'HВƒ[FвГ–И‘ Ў—†……ymb6t•t€bo€_‡O& VWc‰ЦйомеЪЋ€‘lЈЁŽЛ y…peZ4%‹“5€q`Ѕak•=‚*JТњіэчхнйиф№№ЋqpИ•‘ГiskaW)X”hm™vgЅ€MЖмТŸŽ›Ў­ЎЎАВОаД„~‚Г”qfYXG,#‹†+jk—€hCХЖ†lpЃˆv„‚‚ƒ„‰’Ÿya…Œ‰še^2:..6l’D YsŸ—| LЮ­oeK:7ueeccegkllŒ_Ds†iI:Q?=[-hR“d3WІБk'PКˆZTRE1+€WMNQSSTVWV\i]\yhmt^[mg\xt6Tr{Ћa-8ІЅЂ•pB>@?=?yll7ILEDRIe†ž”AgALFXВuBRO|y=#(+,€-:LUŸЋsЈf`_{JZŠ}~gP*,.‚)™We'IЁ ŒaT4&$(*++Ps–œ›œЊF171363,,'ƒ!h+a))JQЏ€|l]L-)+478Q„†c>`H'.7677€61(…E $ ;}МxueU3/,DHII6(',4BHI€JIGF>4)†% 7WОŽwjZ<3,F_eecc`^]]_`ceea[NF/ˆ# )Y˜Гin]E.0>w}{ytjda_`ejmz‡vk:‰"D$%+lŒПen]G-15xtg_[XSMHFFC:_phc3Š"tG\p‚—йЙal[E-0*UXOOYo…qaH9Ps~{.‹!uІЏВКЛЉ’`gV@/-$ImЕпєќђмМ”€sGoЁ‡’‹!Rlsuy|‚ek`Q82'=Ѕчє§џѓмСЉ’SuТ_IƒŒ AkgVF?;B@:--,‰ОацѓьеСЊ’}UР“bށ`qq‰gjzЈУЛЄŽpdРЂŒc ˜ X•ЎД„XQZ^[tЎеЋ•„Wš8q‹–ІЕЖЕИНЗЃ’‡n+ Ggz…‡‡ƒxb=Ѓ џџпџџџŠ!UЊ\IDЅ Rl}ЌаЭФАz& 4‘Љ}ˆ•‰ššœЁБИV ‰‡ (4=<0 qДŠpŸЏjFHGJWrœЋ–=†j…Ibgs…€fHXIIHG‘h{ЌЏІ”qC-bŸ­\…ЅWƒ Y#*Ÿ•„bCpžГЌЃЎЅ‘‚_//{œ M„VГ”C‚7 ‚@ВЇŽVWЌПЪДАФ›–“†yg];U–œQ;}Ќœ˜ФGJ  ™ЗŽLXДФОЈ­ЛЊЇЄ‰}p`VM Tœ›0 ”М›•ЮD!<ЇQ JЗ‰]9|ШГ­ЁЁЌЇŠlrf[QG'zžz€ К‰Фr5 ™‚r‹ШнтпиЫЌ‰‹nšŒ­™qcdSJA% &”œ8€˜~т€ŽЫM‚*sЬџўїђяхолцјњЊnjЋ“ŒА_VOG? ]nЇŒШ˜…з€mЪёкЕЄГЩФФТСТвъЪ”‡‚АrOHO4 ”ŽLŽФРЄŠ XнйЏŽ„Ѕ‹”ЏБЌЊЋЌГОЬЂЄ Жˆ…&+##)q›HЊІЮОВАgрЮЁ’mMb=†•–‘“™žžЎЗœbБРЉv9A0/E"iWk ]ВЮУрЪFhЩЁxt`SЄ~’{pnrsswz|}‚ˆƒв†Б——’khte€Ђ“c‚šЎЯЎX*‹†„~hOHUmЉŽ{KCCFHJJLMORЂˆРŠвDaabahaw‰ЃœVxa"€*I7=dƒgIHL—ЙxC%""$$%G‡ƒФкж`TpCOvkjU=#)3‚)žZj';]}ЈдХЄšgG!"ŠЊЦЫЧЧЦвc#*$$%" ƒm -g,+E:€‰И­ЂZT6;?@_’•uXq[77€?=;:<6-…&J & ,VŠŽДЈ›f`QailkM65=LcmnomliecWK;†% ):‹qДЌŸxiZtžЈЌЉЈŸ—™ŸЂ ЅšŒŒ‹|rLˆ# AnЎЁˆ_drдппмвМ­ЊЌЏБЏЋВИЇŸY‰"G'& \lŒ|ЏЂŒZdeеаЛЎІž‡}|t\“ЋЁ˜KŠ"y >OhsˆЕ†z­ Š[bO”pc^a`[RH8/c›ЊЈ:‹!`‚Œ‘–—…kgŽЉœ_]8QU‚ЉХКЊš†j]V6sЩВМ&‹! ^x‹–ЄГŽЊЄ˜qeP.sЃЈВМЈ™ˆzk^?rШnR›Œ VŒ‡ra[Zgmr\V4dˆ”ЂАІ•‰zk]C›ЦœhށeyyŽaR[iyˆwhTT‰УЋ”h ˜ ]—žЗЙPAGJNn­кВž‹\ š;x“žЎМЛЛОУНЋšt. JmŒŽŠg@ЃџџпџџџŠ*y1Њ„zd Ѕ !dГПлзЬЗƒ+Ÿ8™ОБПИ™ЋЌЎДРХЉ] ‰ ‡05:91($ЫЌЅмаnEEDK] ЎНЇD†{„ jƒrq|{l^wng[VЌel™–†e7,mЁБРh…Мaƒ5.ЇЄ“k<[‰ІЫЂš“‹ƒxZ)0‰ЎВV„gЫЎO‚\‚XЛИžZ@”ЈвшьъБ€€wnbZ7_ЇЎZb–УННйic ЂЦŸRBŸВахьшцтБ‰mdXQJ]Ў­6ЙзЛКсp!?L PФšj6oЪЧлщзрфмЖ[`WOHA"ˆАˆ€ЎРЬЌй•E –˜ŽЁЪлпниЬДЇЃЃнвЩщбŒNND>8'Є­>€Ўšщ›Јеh‚*ˆдќјђэющцфъѓђЕˆŸцООлp?<72 gЏ{)ВŸаЈ™м0€jжћяЭЙЧррттфчяљпБЋБпН‰=Fr“|zЛд•]kwyzyz‡™ŽСгŸа”|‹ЎŒИЈЎ‹g\R‚Ї_q*CiŽЙшсЬСƒd`f€j–ЃКМЗМЖХojpkmqolm`G3ƒw#2q//MEЁндЩЗtmc€r€™žŒy}fK^rquusrsgS-…&R)6dšЉлЮУ‚|l‚ˆˆ†dKGPa|ˆˆ‰‹ŒŠˆwbK†% 0Aœ‚игЦ™†tЈЌЎЌЌЇЄЃЅІЅЊЃœœ›}S ˆ##%;y‘ЌеШ­y€zШЬЫШТДЋІЃЂЇЎКЪЬРГc‰" K+ +$Jqœ–жШБs€pШСЌЁ›–ށusw†ЗЭРО[Š"‚@M^e€Т—”дЧЏt~\’’w[C1+'$!$}АНИH‹!l’žЂЄ’zv­аФЅzwBF*1@LF@;4*&$qУЎЖ&‹!$j„Œ–ЄЙаЊаЪР‘‚g,>?CG?:4/+'mдxW“Œ aœ—pkl}‡tj/(59>C?84/+&#—вЎtށ"qˆ‡™V'%)/95/)"5ЪЛЅt˜hЈАЧС{@&#&6cЌтУА›f šC†ЄАОЩШЧЫбЫЛЌŸ2 #SyœŸŸšŽsG!Ѓџџпh8mk žџёѕњфbbB љџџџџџџџџЛмџџџџџџџџџџџџџџq+`fda[V‡џџџџџџџџџџџџџџџџџџL(џъџѕў§§ўўўџџџџџџџџџџџџџџџџџџџџџџЏўћŒiSPV­џџџџџџџџџџџџџџџџџџџџџџџџџўџщhGџџџџџџџџџџџџџџџџџџџџџџџџ|Ыџџџџџџ ў’џџџџџџџџџџџџџџџџџџџџџџџџџџ0џџџџџџ5џџћџџџџџџџџџџџџџџџџџџџџџџџџџџџЦџџџџџџюлџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџuџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџhБџџџџџџџўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљџџџџџџџѕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ^Hџџџџџџїo+џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЦэџџџџџџcЧџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЖџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўВ•џўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџрQRћДњљіџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџрFQћUњѓЄВџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџС.з:кЬ‘dwџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЬл”ГЉ]I?џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљOЄг ˜@џџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓ?rљЫbЭџџџџџџџџџџџџџџџџџџџџџџџџџџџџџo5ўžџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџС"шџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяќД)…|Y4$(џџџџџџџџџџџџџџџџџџџџџє!џџџџџџџџџџџџџџџџџџйЇџџџџџџџџџџџџџџџџМ-yџџџџџџџџџџџіЦ*SДзййжИ”/ics#Hџџџџњ?‡Щцїџџџџўџ§ћћяѓпќџџџџџџњ?‡Щцїџџџџўџ§ћћяѓпќџџics4ˆџџџџџџџџџџџџџџџџџџџџџџџџџџџноџџџџюнннпџяў§нЬо§ў}ўЬЬннўюеќ}зЧнюнЩхнu]Э]Yџњэ}нчеŸџўќе}нŒoџўн}БЛнџџџџ­нняџџџџџўщџџџџџџџџџџџџџџџџџџџics8џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўЋћќўџџџџџџџџўўЌVљљњќџџў§џџ§єЌљљїї]ˆќџЅyўџўzї++UUњˆўўOЌў€yњyxxyyЌћzњџ§ћzyЋЊ€јzЋ€€ЌџџўўрyЄЄzzz€Є§џџџџ§ќ]yЄzzWњzџџџџџ§€Є^44^љќџџџџџџџџєћљљљњ§џџџџџџџџџџџўўўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџis32ЂџU€„џџc}ЊЏџџ[AJhuƒ•Œ}fd? ž„œЕЏš`ZkjLWžТПНž™ƒYeRqˆ@j˜z^ppvmxZB^o‚nrq^uY+/X‰jDQU=Q($|t4F_RORJ"џow“~8M}…hbnџџkytVF€оЧ’ƒ_€џ€Azˆ‘‹‚[…џMVKˆџ€”џЂџ‚„џџpˆГСџџHVƒwvŽŒƒ˜Œ]3Ѕ„ŒАЈ|G\‡ŸЃo НЪШФЅ™s@iK˜ГSjЌ›ƒ““žr?gГœkfR„‹9-tВ…FOTI+[2cЉhm•ƒue-џiis›y…Ќ’u€›1џџcmb›Šr„s€џ€A}‡‰‰ˆa…џUbUˆџ€”џЂџ‚„џџŸЃТгџџoŒ‹Є‰ƒ‹ЉЅЁr9ЏŒ‡еСlAe—ОС™ЪгвзЮЬw1sZЊФdŠкм}вчцЩКx*hЁА—zzuЄЕ~ƒ›Д—}ŒŽ`#[2pЫ††ž—ŠGџriuЛœ„ˆhRˆЌ1џџoŠ‘›„340E…u€џ€JІ†“n…џl{dˆџ€”џs8mk ƒŽT>:kзџџџ№wGL(Gџџџџџџџ~Žї9#Чэџџџџџџџї"ЛџЂЖџџџџџџџџџџ™эзGп№џџџџџџџџџџЛQS№џџџџџџџџК Uјџџџџџџџў)@ЮПфџџџџџџЈєџўз\!>3 icm#8џџџџўџѕ_в­Єyл_цЏЕ_њПџџџџтїџџџџџџќџј№Рicm4hџџџџџџџџџџџџџџџџџџњэяџџџџўеЭноџпўнЬмо§ў}ќ}ЧЧнюэЮюнu]н]Yџў§нзззŸџўн~вЛнџџџџ­нняџџџџџџџџџџџџџџџџџџicm8Шџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§Ѕ‡ћ§џџџџџџџ§ѓќњUVљ§џўЅ§џајјїUU]‚§€ъЋy€§UyzyTyUyЌќЅњPаќћ‡UžЋЊ€zz‡Є†арръўў]yЄzzzzyzЌџџџџћљzzЄ‚4X_€ъџџџџџџрўћњљW€Ќџџџџџџџџџєџџџџџџџџџџџџџџџџџџџџџџџџџџџit32U“џџџџџџџџџџџџџџџџџџџџџˆ<:7FјCic;1їbœe.54і_^vO*,9іd}n@.CVіKVP;-IW9PSSMIA?8ь\cOTRZ|‘КггаЫЦЕЃŒvaKA8фWsŠˆ‡cac‡’ЅЙФТТФЩгншюыгЏ[D3о:l РЌrto_U^€}€‘ƒ• ”•›ІКбъъС…XA/йH|Дцѓ‹mcxurz†c~“‰•˜ІФсзœgG*е.rЙюњд…`ntxnYZ{k„Œ€Š‹Œ’”ƒ•—ЋЮдfCžЏ?bЉыїгЄˆsvryn~q~Z\€l€klnrv|ƒŠ“‚• ФЦ^:œ3kI­%?‡еќмЄ…Œx[[qlwyxЅf5=:4/-,,16?KZfou}ˆ”•ЂТЏuL›Aq<˜;;JOVadhiicaYUJEB;8@wЁnd^TF=Vpry„“Ўk&:IRX[YUPE9,"!+@Xht€Œ“•ЈМ’_5š4^p”@?]YYbkt~„‡‡…€yqi`YSNLLJEDLE;AA68[u…Ё•Z™:_ƒ>“CY~e?2'(S_NEGJJH?FW>FdQB]–СЏЛЬЪЩЬЦСПОИЖГЏЃ‡Y12WnŽ”€•ЈЇvE–UЁpgl!zƒA' Š3F’˜ndfjja]pQ+2UtswŠ­М •ЏЩЩШШХРОМЙГВГ­ЅžŠ[+3[tˆ’€•ŸЉxH”NŒh_y>Ž „e"‹'"1~с虈3ˆqwh0/V–ЬгЦЦШФЖРУ‡ЕШЦЦФПЛКЙДЏ­ЌЁ™–”E>g€”••šІ|G“UtbnneWSfc‹?Њu„шнž••“„owF)G}ЙеиийЮгЩНООХЌЎЖЙЊАМЛЗБЌЉЅ—’Œ‰†_$'Vy‹”••˜ЃvE’ :[Wst_NtO‹1lŒЊtфпœ••’ql00a”ЩевгеЦпПМО#ЪгаРЉМПКГЎЉЅЂš”‰„€m$HsŠ”••˜žr; ‹K>.–ZLxy\Dx{g-ŠD@‹Њoдуž••’|pZ)>yžЯпмйбРсІД€О%НЕЧЬЈ`„АНЕЏЊЅЁž™‘‹…€{xxe :n‰”••˜™h1ŒGLp~ЛЌTC}~_=|}€ZˆF2‹]ЛюЂ••’}pS'EŠЄЯсрниСеЉ€О'ЕИжЅІВQ|ЕЙБЌЈЃž™–Žˆƒ~yusnU 5nŠ”••™^ ‹(‚‚e8€‚IˆA7‹^šѓЋ••“~mM&P•­ЦщфсзМТvЊ€О)ЎУЖzЊПО „ЊЄzЃŸš”‘…yvroif6 2n‹”••š„OŠ3gˆˆ‰[=‡ˆd6‡ˆE‰?JŠ™xыН–•”‚lP$I™ДПчэъсЖЖcЁ€О­М‡nЌ€ОИВЕ•~žЁ—’‹„}xtqogcV 6t€•˜u:‹,aŽŽS=\3Œ9‰7_FŠYШз˜••‰lV!A‰ЎТжёюъКЕ\€О&ЎЗ{pЕООЕЅСЄgTt”œ˜“ˆ„‚{wqrqc_]( Az€•e"Š%e““‘CK“•M<—“w09‰Mub?ˆ[ьЃ“‘ŠmZ$0p”ЏЦхѓяШИZ€О.ЋД}cЕООЉД­dˆžFf“—’‰…€€zupif`[Y; P„“••—€L‰!l˜˜‡2f˜˜9N™˜i+v)‡T€€_>(„B3^Жofc`ZSOQQRZg„ЇНЇdxНООЋБvYВОООtiЇОЛxs•r…‹…}~snjhf^XUD gŠ€•“p*ˆ pœn'†œ'jœN3Ÿ’O"ˆP>iˆ‡pZF;4./Cb˜ЉВЛССРНВЋЅ Œ{scJZ8„ЋНЙtXАОНšБS†МННЛФЇ_dˆ†‚~}{pjfdc\VSA 2w‘€•„U‡ Ё M2 Ђp"‹Ё–-YЂ›8‰&]ecgcS?FjЅбрхш‚ы#щшшчхтпзХ;rgZflwGВОПœ­?†ОННМЋ|]|]wƒ€ vpidbaYTQ8€  R‡”••“o,‡Ѕ“)cІІU- Ѕs"‹Ѕ† ‹JZJ5/,+-3;;, )‚–••Žl‚_SMKСФЎteФГ{BВQˆ]3>5E4|i†fT€OLKPQKTTSSTUTTX]WTUWUZV]o}J4Š––}7L•—Z3’r]fŸЖqS=ICTkmZeDZ5{k_‚Žh/"7CBX…‚[rХаšlSI5 ‡t @™У’o^MHHJKGKLJKGA8.9#>hƒГ’YPIFFEEDIKIKLLMMLKMLLNOONMPNLML8$„žžy,Dšž~$qžЃM_“ˆeŠЃ›`šSІf[‰“oSqVG6AYwЃВ‡PiГМšlK@-‡,:eupkh_QG@;:?=?CCEE=93-6D]ЕэЎ€O)8AB?=@>ACCDD€FEDEFEFHLG€F8E4qЅž^)UЁІ‘+IЄІ”'>snUwƒiN€€dI{oQCVHI6;JSgŸЫлЗtATŒ dB5&ˆ->КёёяээьувЙ“gH84589CKL\mq•ЯрГƒK8`,%39958:9;=<=€>A=>=?DA=>@><2%S­–J,}ЌЌ”8>ЅЌІF$5<-297;:<11/9,;HCB†ЊТЮЮК•f:;YmwO4.Š7:q‘””“˜ЂВХдушЯŸmJ601FjtŒ™›‘ƒ[7pИJ) &,400236465677668978€56382‹{AHЄДД™;JЌГБ`!RY>+582*/43*IY;@ŽŸ­ВНГЉŸ’‚f:*.7…УZ='#"*+,,-€.A-/10/0/0/01BH>JUD~ЖКЛ’KTВЛГp8E0~‰7>{<3€„B6•›oi œ—މ‡p[A0&!(T?!Ž-o_9H 3Ib–ЗЯН…[B10365;eЙЯ‰\TR;%("!#'(())*)ƒ*8++@^WLAQŒОСТВ…OoПТЅgMЁ 8rxXixw34fo]Sx~|ypg^L>74-%#"D9a$Ф~ Аjk,GiŽАЬЎ{vphtЕвЙˆf\VSM6"+#! &()&(('()((',NcVIU•ТШХД™oZžЧФ–^kЙШТf69AAKR9>AENC5-+#  # +5>>;=>>==;==@@; 9;>=64.'.0–es"0B'4(3G@.A˜РЩ c~€ysmf`ZSM@++6!71@?AEDADGECA/%0;1-77—dO/,#")<5:;eВЦРˆa‚|voib\VOF3!<),0 ?JJHKMNPQONJ?520./6<>CHJJILKIPOKNPNMNNMNPNHKLJIHE?43>=#˜"3€€, 08BGC“СЧЏjnxrke_XQJ:"74 :8MQWUT[][€ZWY\XVZUSUTTRTUSTWWTV€YW€VYTTWSRSQKA:;ED$š4% >INGkЙЦП”S}{tmg`ZSL@++;#4,&T^]`cdhh€e€ced]_a\€]\]^^_a]]ccb`][Y[`_Y]]UPI?AOL#› % „OBMSRQЇХХЌai}vohb[TND1!@,*6Jbjinrsppnllmmkjjeehgeegidfkjmppnns€„}usmpiXTMDJ^N (-€€ €M=NXYPˆТЦЙ€P~wpic\UOG5?3!; =fqvxyzxvuvutuwsknpijkjhijhkpqvyvpj~œЕНАЁ•›Š]VlI!ž<<#€Q% &KWX]kМЧР–Jwxqjd]VOI8!;8<)-fux}~~|}}{wsrpjhihddefacefilmotng\Z`obYhw|I$Ÿ Xa,€P$ DeNdhГЩУЂRhyqjd]VOI;%5;7/"^z}}„}{ztrpke`\XTSSRQQRQSVRSXZ\_q}yfUaquaTnlg3ЁY™€"6€P!*#7wkFnЏЭФЇ^[yrjd]VOI;'2;$32Ux}|~zwupibZVSTUXYYZ[[ZYYXVVWVOHI:>4/Dj‹‹l9sŽp:UЂ&Рt7;a 864]NqG‹ˆюЯФЇdMxpjc\VOI<).<',3AbaYSNS\ad_YMB:76789€:98530/.%#""&*rIB‘šy/y›~#ЂZ ‰К"3"iL[~~ohŸЈcZЇщ№ШТ ]Qvohb[UNH:(1;%.02HHLWeg\L>66=FP\fow}ƒ†„„|~xldOE>8/$OЂk4{І†-„ІvЃYBЬh"EZo]kЇЎq{ЈБгјївЦП—OZungaZSMH9&49#2-(H`oaH76>HWi~”ЉМЬжлигепД­Ћ—‰Š~jbZM<%xЏCqЎ‰;Ѓ­9ЃY„šh†•–‡~– ИЦк№ќќђпУОСЖŠ?gsle_XRLF6#87!4'CkX73>Nb|™ЗЯтяіћ§ўћ№ыђчЖВЁ‡€†sh^O4&@˜ЙЋR‚Лqn› Ѓ5LЉхщчусфыюъфмаУЗЊ ™—™ЉЁw8qpjc]VPJC4!;3 5!%2,1A\~ЃХп№љ§џ§іьржвХБАšŒ„}|…{eT;)\Б\ТТЎUЖВSXЄ29[„•™œœš˜”Œˆ‚|vl[RYv‘[Htmga[TNI@0"=.$0%=X‚ДпёїќƒџўјяфзЭТОМЋ•‰{~xr\>,Xнрc”дг|‘ЫmЅ2'9GPX[[ZVTSRUZ_cht}~kO=hqjd^XRLG;+)9(*(L{ЅЧчўієїќџ §ј№хлаХЙАЏЎ”ˆ€}~l]D/]ощ udR3d…иr Ѕ2-Kcu†‹Ž“–˜˜—•‘Š†ƒwqpkfa[UOJD6%12#-K‹ЏУвъўѓ№єљ€џ!ќїяцмвЧНВЈ ЁžŠ†m_L8eтыЃ•‚S7R7;ЂgЇU>Yn€‹‹…zuqmigd`^\XWUSPMJF?0#4,+9:ІДТйыёљєљ§ћўњ№ътйбЧНГЉ ˜”Ё‘‚mU;A‡яцЁ••“tXIL;ЈS *F]ilkjjifa]XTPMJHFECA?<::94,&<)-`•ЈИЪбЮвпёѓъыєьтмеЭХНДЊЂ™‘t^E5GЇјмŸ••“‚nTЋ (>LPJ;-"<  031-,*-5/'(4P}ЁЙСЗДЙПЦамщ§ёкгЭЧРКВЉЁ™‘ˆ„mO:9nкњШ™••“€oW В‰9 5Ua_UG:4>GH>9>VzžЊЃЁІЋАЖНФгяоЪФПКДЎЇŸ–Š|m_B:UБєыБ–••‘}pY Ф/?g‡…{rsžЗ‘`MQU\jy†‘™ŸІ­ГКЫЬМЖБЌЅ“†vcPFGVЁыљЭœ€•ŽwsR Ц58`}““’”ЁЦЩžlRGCGQ_lxƒ–ЋВЁ™’ˆ~oaQE@In­ыјкЇ–••”ˆruEЩ,Ttˆ’• ЅЪоТŒaJFDCFLS[aksf^VOHEDMg˜ЭѕљжЊ–€•~qv-ЬDd}‹“•œЛрэеЈ‚hYQNKKNXPQXdw’ДмѕјхСŸ–€•’…r{cЮ ,Uo“•–ЂЛжь№цвФЙЖЎВПЬкшєљ№кОЅ—•“Šuw~9б 6[p‹’‚•”•ЌМЩаеййжаЩНЎŸ–”•”‘‡vt€P е:\oz†Ž“‹•”„•“Œrw}Sй 1Pgs{ƒŒ“”‰• ”“Š~tsuj? н 9Uensy„‰‹Ž€ ŽŠ‡‚{tppocDт)@Q]fknpqqrqoomjbYG/щ#),-/-*#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџˆRC78јZuc4)їj_%5:і_`pH'4Iі_xh@:\rіDORF;7/&)A]n{‡“›žБХ™d9ši~ ”@Fmr“ЇЗШднроеШЗЅobYQKDCME;FH;?~ЊЛбГ].Z‡žЈЌЋЋІЂŸ˜Œy]A+"Bcv…“œ€žЂМЇqB™lЂ™J“CY„sVOE5*!&-020,'",V_PGKNOLCK[=XzZ=S‡­žЉЖДВГЎЈЅЄ™•…jB 2[tˆ–€žАЏ|J–…ЧМ€!{‰L2‹1K”šsimppfbvP :]ej}œЌžžЕДГБ­ЉЅЃž˜•”Ž…~lC3`zš€žЈА~M”hБЪИЂ—`-Ž …k+ ‹=)…фл —˜˜w~j'8uГОДВЖННЖБ{ЂГБА­ІЃЁŸ˜“‚zvr`3Am‡–žžЃЎ‚K“o›ІМНŒ-gi!Œ>UNŠысЅžžœŠv}B.VšОСТТОдгВЎЎГ™›ЁЌЅ›ЂЁ›”މ…}wqlheG '[“žžЁЌ}G’ `…ŒСТ“wРƒ.‹1u&ŒUUyчуЄžž›†vp) AiЋНМНПРфЮГЎ#ИШдЮЃЄІ —‘Œ‡„|toje`_Q Ly‘œžž Їx> ‹paEUР†{ФХ”lФФЁKŠLOUqичІžžšƒv] ,WtЏТСРМРэТЌ€Ў%­ЏарЙZr˜Єš“ˆƒyrlgb^ZYJ =tœžžЁЁm4‹tvЋЇзо‰nШЩšeЧЩШ‹/ˆXJŒYРђЊžžšƒvU2h}ЎТССОФшЕЉ€Ў'ЊРцЧЎЄIlž•Š…zwohc_[WUQ> 7t‘žžЂ–c"‹DdЙкжЮgЬЭЁ]ЪЭШy)ˆa[ŒW іГžž›„sO  8{•€ž |=ŠH’жжи_жзQджС[$‰b­†8Š\Эн žrZ/jЋШфжЩГЭ…€Ў&ЅШЂ‰ЈЎЎЉЌгСY`x~ytnjfc]YUUTHEC  D˜€ž˜j"Š:—ккжeqкнtZнкБHVˆ9•йН†3‡Q™яЋœš‘r^"Tv•ГуёпЙЪ~€Ў.ЂТœzЈЎЎЂСФ‡”–>Tvytojfaa\WSNJFB@+  U‹›žžŸ‡P ‰.œннФE”ннPpпн—<Ћ>8‡>ЇэшЖ]„B3cК’sic`YQMMKKQ]ЂЎВ~­ЎЎЁНmЅЎЎœЬ{ЁЏ­o^waklfb__VQNKJC@=1  m’€ž›v+ˆ %žпр™/ПпШ/–ррhDрЧa+ˆ0'~Ыхв­‰iTD7/BašЌВМСТСМБЉЄ‡to^NsD{­ЊЄ†bЄЎ­—Нf…­€ЏИ›\Uigc_^\RMJHHA=:/  4~š€žŒZ‡ Аср`>пт™$Ссб0yуж?‰NЕИЃ—…jLJjЅдфщы‚юьыъъчфриТƒBodW`h„KЄЎЏ“ДH€€ЏАЂ|`xK\c€aXRLIGG@<:(  VŽžžšv.‡ ХтШ(„ууk4му˜ НтЕŠ ˆœ|\`Яяљ€ћњњљ‚ј‚їіїїьпыгЇ…A(jЈЏ”ЋAp€Џ І“ƒdЌE\gcluTSPKLF>:7 !x˜€žŠV†+жф†&ЭуоFZфсXFрхxŠD‡x_Јю€џƒў ќњљјяюёъъыђэѕ§ў4ћч’‚sW€IZЋЏЏ˜Є|a­ВДЈxFV\\VSNKIE>93 Ožž—t!†:нсURуфФ-”хУ3ŽхпH‹‹smеџNўўћѕ№ъчуфцутмаадгФСеЯаыѕќўўјвЌ™GHIЋЏА–Ў`ZВВГГЇ‹/OSQOKHEAB@9&  w™žžƒF…/RфН?›хц1дх’4бхГ;ŠQozтџџўўќэзЪЩФХЯажсчеадЪХЫЪЧМФвТЪлъјў§чОtJ[‹ЎЉ]WЋВГГЁЃ]'CONLIFC@>>7 S’€ž‘b„FcцDзхрhbцпgdчрƒ1‰X‡kˆыўќњїсЬНЕЌ™šŸЋФЧЪЭЯУЛЪЪСКНЧОСЪШШФбцѕњъЧЙЂoiyZЎВГГЁ­swFDMJB?BL>;7*%  *„žžšv,„9^nОt‚чцЕ\Вч­YИчБa‰Jo™ь§іёпОЛЗІŒŠ‹ˆŒ–ЏЖДЌЖСИМСПМЏМЦВОЬЦИУицюъзЛЅx‡OВГГЁО‚‰ЗДsRMo;E{Q3*)+$ €  l™žžƒHƒZyВ^бцп˜kтм–iтЬ6‰Thšуњ№чбЗИЊ™‹„•КТУА„„šЄБДГММЗЗГЗЏЊИЗМИЗОСХХофмФ„jhV…АІЩ‘†ГЋИШЬЖuC<& ^Ÿž„EЦЭБŽффжЁЩыетЛчФa†aAQ|НэјрЫЕž—‘””€qcNWCUZYQ:~i“šˆ‡’’Ž‹…‹†‹ŒŽ“›”•™›˜–— ЊИДЂЂ‘u\\ЏЮЭЮ†lЮЮˆZ)B_39W9-+.9FNJ3Fšž–ž‰W<€ŒмО‰ОнпБДыорЩТ­l†u>V™нјъЭЛЁ“Šˆ€l]BY{™–—˜gƒj™‰ˆƒu~‚€ƒy€}~……ƒˆŽŠˆ‰ŽŠ‡”œІЏЌ”ZyжежБSЊеТPlПŒ3:i2*1;ŒR†ЂД‚ЎК^Dey~›СЄ˜мчЫСЇ‘a†u$,XЊЮЃ…yldfhgbjjeg`XO<@o”ta‘ХЇvmgca`^[dgbfeegigejigknonmspknkF)КсрЇ1[лсБ(žррlŠОЖЂКЮЦ—ИКЛЅЫІ”РЧГ‹­Œ„a`z•ЙЩПŽ‰ЦбСБ‘zN‡t8^je`^XTRSQPSOSYSXYPKB27C]ЛёЛfGPXWTNSOTVTVXXY[\XY][[]ca]]_]B˜тиy)oмуХ/]суЪ(Q˜š‡ЅЖИЙœyЖЊ‹qˆs}\MUf€ЋвуЭЂhjœБД›xa=ˆr+“лдЭЪЧТЗЈpXNNHBCCMTWbu{šдуКWXœV=DGD>BDDGHGHLJIKKJMQPKMQNL@,jцХ]4ЃхфХFMйфнZ,K[Y??JQUƒЋФбаМЃ}OJdy•xYK,Šq!Gcgfeir”ЂБГ›t^WI:4Bap~‰—›Ž€\JЅп‹S?835122376769:88:9;;=:9;DSIВЁX_бххХUaлхт‚0PXO778?9.3?6ALBF„™ЈАК­Ђ—‰{d@01At\<3Œo!) +@M[ep„šЁ†c[R54H[jjbR8HВфУІzOH;-#%%&''()()+*(*++,,/a|n|~d сцчПwsнцоŸ\W-s}7=zp>5vy@6†gb’Ž‹}ywjYH8+#']K)Ž%rd;J &2@Ug„›‰df^=-)+1IмыШЄœvJQF5;%XІЇ•|АфчшлО‹”хчвЉ}ХК-^cH[kg*+T]LC`cgc^UGG8)" B:/)Щ„ Дoo/4D`—~h—”ЅФпютЧЌ œ›•lEWH=‚6&ŠХА‡ИтщцлдК“СшхЮЌ˜лщр A"+-9>'+00+./.,+#"(‘*HмjVгI*Х4;r ?d‹’k™фщчсвП­ЈЃŸ›šŒaGZK@€5,v…ˆЉзруйЦБŸЙчшжХŸГъыжПj !’iWйFmЦOЪK*P~—vtПЩСЙДЏЋЇЂžš˜}RXST> !#%##&'$&)=nЇДЎ ˜›ЙжъчвПœ–иэхХЁS$%$#$&$#""$#"##"!#&%$#  “NUЪ/oЉaІЌ[-—Cw™~^ЉОЛЗГЏЊІЁœ˜’nEkHa7',,+.0/21242ZЁЗЫныђцдОЁ‰hЖрЭЗ‘i/,1€/..0/00,,..,+./-./)##!“O=Е#c„^o/bxAB|šZЅОКЖВ­ЈЄŸ›–ƒYShFg/48=9:>?<=CCM—АИИ­–|iVLTˆ“uY<'-;>>@€>=AA=;=;99;;9<=84.).-”XœJbEMQWL,WR†šyUЎНЙЕАЋІЂ˜nBtUZW2DGKMLMPOPSUWH:1.'#"!"$(%):GRRMOPQPOMOOPNH€I GHLKCA91;>$–ez%4E+7+5":5(j•pYДМИГЎЉЄŸš•WXoCr=EZXZb`]add^W<#  !%+5EW_bffbadebddcaf`^_\YY\[VTQC>LN, —CT2/%% ,8"$$B’[qНКЕАЊЅЁœ—ŠiC{S\c5^onmruuz{xvsaRMKGHRY[cl€puvt{zswzy€vstwtjnpljhf]KL[Z5 ˜%6€€+!" .#),*f“~G›МЗВЌЇЂ™‘vGrkAx?Yx€Š†‡‘”“*“”Ž‹†€‚……‚‡ŒˆŠ‘އ‹‘Šˆ‡Ž„„‡‚}}|tdZ\kj9 ša&  "-.2.G†’ŒfVЗЙДЎЉЄžš”€XXyHl[BŠ›› ЂЇЏБЌЌ­ЉЈЉЊІšžž”–™œ›žЃ ЂЄš›ЅЈЅЁš‘”Ÿš—–‡tfh|: › („O62767w‘‘{B–ЛЕАЊЅ ›•ˆeDƒZVp8}ЈДДОУФФЦТНРТПЙЗИЋЉ­ЏЌЌДИАДНЙЗЙНИЕЋ›Ž‰ƒƒ‚Ž‘†‹€oyœ€5-/€€ !€M33<=8^Ž’†TcКЖАЋІ ›–n?‚iD|BkВЧЯбдежввжаббвЩЛРТВЖККЙМПЛТЦСТФНБВПСШвдШЖЛЗЂ|‹Е{2 žB@'€Q'"1>?CK‰“ŒfBАЗБЋІЁœ–tDxr>}TQЖЬжсусрхцфчсиЯЬШЛЖЗЕБГЕЙБЕЗЗЙЛКЗЗЈŸ—ŸЏИЧИ‘ ­ІЄq1Ÿ ^g.€P& ,K@NO•r9›ИВЌЇЂ—‘xKmy@r`@ЇотрщюыфттдвЮТИ­Є›‘““’’”•“™œ“•››ššАМА—ŠЅМЕ“ŒЙДžKЁ` €%9€P$-!)_X=Z˜‘w=‡ИВЌЇЂ—‘yQezJhh9™всстлззЬСЗІœš˜˜ž››ЂЄ  ЃЃŸŸ™}wbcŸЪХИ‡yНУ“tЗФd ЁKб0€T R-K %3]yZFŽ›‘x>vЗБЌІЁœ—’{S_|N_j3†бЯФРНЎŸ› ЊВ­ЊЄ˜‰vlca^XWZ\ZZdif\MD` гвЁTЎжЈU~Ђ+ЦyS?f ._;lcD{ŒoОšv?rЖБЋІЁœ–‘zT^{Q[j2qЇЋ‹”Є­ГЋЁ‰s_MC=630/.,*((&'("#&()-—[XапЋ:ЎпГ'ЂZ Р$lJFhmqeŽ–]VеШ”q=yЖАЊЅ ›–‘wQcxM_d1W~{•­Аž†hM?;:GPX`hns~ƒwqke]SI>52=hАэљоЎ€žœx|HЩ/X{Žšž ЎгцЩ’aD:5359?EIPWMGB<745Aa™бїњкВŸ€ž˜„w|1ЫGiƒ’›žЅУфёиЌ„gUIC?>AHBEM]s“ЙсїљшЧЇž›Œx‚hЮ /Yt‡•œ€žŸЊСляђшеЧМЙВЖУаоыіћђоФЌ ž›‘|}„;б;_v†“šžЅДТЮекннкеЯУЕЇžž™Ž|z†U е>`uŽ–›ƒž€žž€ƒž›”‡x|ƒVй 3Umy‹“˜›‰ž ›—‘…{y|pC н Ub—№ъЙААЎš‚ŠD;ЇЉЈЅЇЪйуюэыГ–“œœ‹ŽŒ‰ƒ€}zspliebB *fŽЄЏААГОŒR’ wЏЅйкЕ–йœ9‹fІLŒUU‡ььЗАА­•„{%,K’ЇЄЃЄАуохю€э#№ьуж˜ŽŠ„€}ywqmifb^]LU‡ЂЏААГЙ‡F ‹U|\UаГœлмЙ–ллЙdŠ€}#ŒU}пюЙААЌ‘„e;R˜ЊЇЅЂЗ№зф€э%ьхъюзa`Š„{wtrmhea]ZVUI D‚ЁЎААГГ{;‹3ГТЖсьЗопП›монМOˆ?ˆn ŒdЪїНАА­’ƒ\#IU’ЋЊЈІСђип€э'ччѕтппUZ‚…}xtqmkea^[XUQP< =‚ЂЏААДЈo%‹v аычтЛ™рсХ–осмЙIˆ$Šv Œ^ЌњЦАА­“U+Sh†ЗВ­ІФёжт€э)х№юиуээТs€Б„tpmhfa\YUSPMKJ#:‚ЃЏААЕ›_Š5šЖффхМ•ффТсфкГ7ˆ…•)‹f‰ђдВАЏ™['\sŠФХЙЏСябп€эцђужф€эътруЊspmieb_[VRPNLHF< ?‰І€АГŠFŠŒ­ччщДŠчшКƒцчеž7‰pГ‰8ŠgжшГААЁd"Mn•ФцдПКэЫо€э&хькачээшуёшЭlOdhfb_\YWSPMLJDB@LЊ€АЉw'‰*{ЏъъцЂ‘ъьЈƒэъЫˆmˆA–ЪА3‡[ІѕПЎЌЂ€k?Z}­шѓрСхТб€э.тшгТцээсшшЫиЭBCaca^ZWTSOLIFDA=='_œЎААВ—[ ˆ8oБъъе†ЉыщŽŽыъЙsРIU‡>žеаЉ}]„:3oУЁ‚ytoj`^\YVXb‡ЈВаМРьээсуЦЕуээиыЦУпышNb]\YVSPOJGECA><9,€zЃ€А­„3ˆ*fЏъъЙgЬъжoЈъъškыжLˆG%wЛдЬБ–~oii\_sŽЄДЙТЧШЧТМЖГЌ˜…nnГxЃдьщдНЉтэьгрЉШъыъщыКxSVTROMLGDA@>;97+9ŒЋ€А›e‡*UНъщbшыВZЭъмmŽысx3‰MЏЗАЎЃ{xŠБврхщь€ы2ъшшчцфтпиЧƒnavМŽтэюби‹ПьыъъкЛœž?HOMLKGD@><;854#  `žЏАА­„5‡2NЯъд`–ыы‘XфыАSШъХU#ŠŽЊ•|ЃЯъѕїїѕѓёююэь1ыыьыъьытифдБ‘jaфюба~­ьыыпЧЕŸоЪGCLJNPCA?=<9421 €#†Љ€Аša †ASмъЂPдшуunъцƒaцъ—CŠU˜‘}Еыћ§ќјієѓђё№€ю ъъышшъэъэ№ђ€ё#яфІ“xdЌФ~”щыыбЪЃšпууЧzFBDDA?<:8630+ € € WЏААЉ&†JVсф}hцчЭ\žшЬa™штt-‹œ‡кўџќњјјїѓёэщъьюэющстхфкйцрцђѕ€ї"іпКЋd}шыыЭЭŽŽхутрвВL9<<:874332. € !…ЋААЏ“P…ZfфФfЂхцЄRжхІTвхНaŠQЃ‰’чџў§ќќѕчонезопфђєшфщсосутисысцяїћќќіжnlЖшеШ†ˆнутрЭУ&09865320/.' ]Ѓ€АЂn„'kpфЁ]зфо…qхн„sхп›F‰J–„Ÿ№џў§§єчлаФЎЎБНйорфшнжухрлощруъыщчяїќ§њтдК€ƒНš…рутрЫШ’šJ-4462.I0*& € ,“ЏАА­„1„LzwСŒ‰тсКsВтГpИтЗw"‰Z‡Діџў§ћьчпЪЊœІЄ ЅАЬжеЬлчрхшшцмшютъёэчьљћ§ќђоЧ”ЅkЫутрЩвšЊйЮtB<~3:ŒR" yЋААЏ“Qƒj“‡НkЬпиЃtлзЂqмЩ@‰K‡Пјџў§ћєђрЦГЊЗНЏЏ­ІСЮсшчѓѓ№ђюёьшё№є№ђіљљяќ§ќюЄ„lЈмЫиЃЂеШжмиР‚+ZІs   QЅБААŸnƒ Ѕ‚ноЛ†ЖоНŒЇдДd ‰DŒПјџў§ќњјыкЙœГЂ_sdVЊŠЙещѓ№ёєієїіѕїљіѕѕњјѕѓєјњ§§ђмдМ„wаЌЇжЗБРбЧЖŠ•ЬВˆ €€ *™€АЈ$‚EЌЈœzзлЬАŒкЮКsЬС‹%ˆWf†Щњџў§ћїњљы⭘”5ЌЙКw3…Ёзёіњљёяіёяљјђѓјљѓ№љ№ьјљњќ§§њшгЛЪ€ЏиЕІЅЖЪЃВЛзЖŠ  …€А­Œ9‚ХБ‰ВквХŠЫиУЋƒаАE†3U{žлўџ§ќљіі№яёЩЈ{Q_aaIhŠщхшэѓїёщяєээѓёю№ѕђыюієђѓіѕ€њ)эЫЃЁ{wЊтчтЪЃЫмЙ… #&hЏБАЏ”NЖУЋˆггЫšМйЧвГдИ\†apƒМё§ўњїє№ышэькУЇo8'-,"Qфщслзщэычтьътчъъщъяъъээыщчя№єэАЬс͘›ХтстЖ™тсЗ…!-T5  LЌБЅАš`9€…ЫЖ…АЫЭІЇжЫбРКЇh †uoйљўћєящхутулмпПЇk71>==>(T—шрмзгЦирртинроллтпнрфсппупкуфхшэьлЃœчцчЫ”РчзЕvН‡ `&-3L=ЅГ›Љ m™3ЉЖŠzГЦІ˜гЮЪРДžf †uc‚вњќёщфндЪЯджзгЩСЅŠ>3MVVG:ZЬхлЪЩЧЩСУбежаЭввбЯбжЯЭезббеевддезоzйъъЮŽŽшыЇyфА„ŸЗЪЎi#;P !GM*[—ДГАОЙ^]}Œ‡‘ЋЛŸŽФЭСОЊ”a †uHWЁтюлЩФНЖКМЙИХШФЦЛЋ˜p04@?N ,Vk|{svz|zzx}~€wyz{wx}{mi\M[]7 –eˆ)9L/</ :$E>$2zžІ€kдсокеЯЫЦТМЁpoU“Qc€}}‡ƒ|„ƒ}tW=:97630,.3;G\t}†…ƒ…‚„†……އ„ˆ…‚†„}xr_Ugf; —d]73)) 1A+..N‘Ѓžk‡рпмзбЭШУПА†Užjv‚ExŒ‹ˆ’Œˆra\XUWdou†ˆ‰ˆ‹‰’‘ˆ‘‘–•‹‘‹ˆ…ya`rl@˜(<€ €G$ &4-363užЄŽUИсойдЮЪХСИ—Y‘‰R›RiŒ•š˜ ЁЁ  ŸžЁЁœ› ™”šš˜“—›–˜ž›–™ž ž€›Ђ››Ÿš––”Šxlm|xA ša+"  $7694S—ЃuhкплеаЫЦТМЃppœ]‹wL”ІЅЊЈЋАБЎЎ­ЋЋЌ­ЋЃІЉЂЂЃЄЃЄЇЄЅЈ ЁЊЌЊЇЂ›šЁЊІ ЅЄ—ƒssˆ„> › -„O>8:9<‡ЂЂŒPВрмзбЬЧУО­UЈtm’Fƒ­ЖГЙЛММНКЗИЛКЕДЖ­­ГГЏЌВЕЌАЕББДЗЕЕЎЃ™”’—™—Šx ƒ825"€ &€M;;:;7lŸЃ—bwпнивЭШУПГŒPІ‡V WnВСФХХЦЦУУХТУФХПЕКНДЖЗЕВГЖБДЙЕИЛЕЎВЦЫбикгМЦПЉ„Бz4 žJF*€Q+&;99;SšЄvPвнивЮШУПЖ”W™“N mUАУЩЮЮЬЫЯЮЭЯЭЩСРПДГЖВЌЋ­ЎЅЈЉЉ­АВБЖЕЗВКЪбйЬЋМЩЛИ{4Ÿ gr4€5)'6A9@L‘І ‚FЙойвЮШУПИ™`‹›S“}E ЮвЮбгвЮЯЭТСМДЌЄž–‹€Š‡Ž—Ž‘›Ѓ­ВФмжСГТегРАвЯЩj ЁiЏ€+@€P)1%.LG7OЉЁ‡JЁойвЮШУПЙ›g_‡ˆAФбаЭЧХЦМБЉ™—˜˜™š›•”––“•š˜’ЂЙЃЖомйНЅглХЄЭк‡ЁTл5€T [3T%(-2J]HFŸ­ЂˆLŽоивЮШУПЙœixŸdz‰<ХФКДВЃ’Ž“— ЇЁ œ“…uh][VPNSZTT]bwЁ ЙхфЩŠФчЬ–Ђ0Я„1/0*'%)& FЈчХwЁцНjйфYЃYŒ—Xejlcv‘šБЧу№ёпСЁœŸ”kFНкдЯЪХСМГ\’VŠi4kЂ„Q-&/=BEHJJIFB@@BA11-)'%&)# sПсд‰Ћу šО ЃXDŽбегбвжнроеЩЙЇ—‹zxzˆ]XгивЭЩФРЛЎ‡Vœ‡TŽX:O9$.6;88601+(&%%)& [Ш‘кмЬаЯŠiЄXEIfqvz||{xuqnjfc`[USY^sO„кжаЫЧУОЙІ}XŸy^‚B!/>@?ACEMQGDB@>;864450*(&$&%$TцьšЌпмЊЌиЁЅ8HRV€XPWVY\bjvŒ™АФЬЋpgСигЮЩХСНЖšqj–hpl! +4<9652012*'&%'!XшѓЛ–ŠZ:|Єж˜ЅWBpЅАЗМПХЪЭавггджззибЭгбЭЪЧУПЛБb‚„[yR %/47>F?>>@FJEA@><:7530.-..**)"aъєОАuA`DA™„Ї -]„ЁЗФЩШУНЖВЎ­€ЉDЊЉЉЋЋЌ­АВГДГЄ~[Štl‹"$.15;@AC@ACDGB>=;97531/-,+0,("ˆѕ№МААЎˆgVJ[ ЈAk‹œŸœžœ—”Œ‰‡††‡ˆˆ€‰:‹‘•‰sc—k."*049:88>A?<:86531/-,*+*#1ЏћъЙААЎ™d 6-Ћ =av{q[F6%€<0FPV\emt†sAS% %/78423558765420/-+)%!CЙјѓЩБААЋ“„i Ф/Kz• ’‡‡ЖбЉf2!%(*--/135::5421/-+'#BІђћоЗ€АЈ‹‡` Ц5Br“ЇЎ­­ЏНптЕt> !$'),.35/-+)&"%]ДђќчРБААЎ …‰PЩ6d‰ŸЌА Пр№гœb7#!% +YйњќфУБ€АЊ”„‰6ЫQv’ЄЎАЗбьѕнЕ‹gL;/)&%)'/;Pn˜ЦэќћюеКБ€А­œ…sЮ 6d‚—ІЎАБМахєѕьлЯУПМПЪкщєћ§їцвОВАЎЂŠŠ‘@бCk„•ЄЌЏАЏАИХайофччфплбЦКБЏАЏЌžŠˆ“] еFl‚žЇЎ‡АББ‡А­І—†Š^й ;`z‡šЄЊ­Џ‰А Џ­ЉЂ”‰†‰zIн "Cdx‚‡•› ЄЇЈ ІЃŸ™‘‰„ƒ‚tO"т3Laoy‚„…†…„ƒƒ€}thS6щ !)147762*  џџџџџџџџџџџџџџџџџџџџt8mk@DЂ“"шџџЎ ž§ћџёNМчњьв 3цејкфšpѕєцЛЛВf_bb_SB(/АёџћўџџџџџџџџџџілД}J jТљўџџџџџџџџџџџџџџџџџџќшЛp# UВіџџџџџџџџџџџџџџџџџџџџџџџџџџіИZoзћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќмv iмџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџуqCа§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§еO(цƒ—іџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљЃЗњa+H`dfedcca^[YVTUVZ‡чџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџсLyџъ PІмє§ў§§§§§§ўўўўўўўўўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓW§џ” ^зџѕиКЇ›’Ž’–™žЂЅЇЉЉЌкўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅЏџџюFЋћ№ЕŒvi\SMLLNPSVY]]dЦўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўО'гўџџџб"Ъўвc>"  БџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФ!eџџџџџџЧ ЏўЯq8 ­ўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЧO§џџџџџўЖWћщ™h# —џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџИ ˆ^ šџџџџџџџўЪ=ФџНs!|ќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўІ ЫўЩcЏ§џџџџџџџџџј`іў=Gїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћ|џџџџџџџџџџџџџџџ’ #ќў…#сџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓT0ўџџџџџџџџџџџџџџВ$ ђў’Ўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџд(ѕ§џџџџџџџџџџџџџТ5Гџп2^ќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўЁ№§џџџџџџџџџџџџџтDBяџйHмџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџєW ђџџџџџџџџџџџџџџћv sѕџћ­Q”џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЦ!њџџџџџџџџџџџџџџџюЦŒtл§џќъЛ…^RWv­уўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљy0џџџџџџџџџџџџџџџџџџГUИ§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџе(QџџџџџџџџџџџџџџџџџџН 6тџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§yuџџџџџџџџџџџџџџџџџћІоџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџШ&џџџџџџџџџџџџџџџџџш‰hћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџєhБџџџџџџџџџџџџџџџџўШb5щџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўЏ дџџџџџџџџџџџџџџџџѕЅ?"аџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџт=(ђџџџџџџџџџџџџџџџџЯ~Їўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљt]џџџџџџџџџџџџџџџџшЃK dќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋ џџџџџџџџџџџџџџџїЙoP№џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџг7 фџџџџџџџџџџџџџџ§Ь‰0jђџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџы^Hќџџџџџџџџџџџџџџр J FФ§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџї‰Іџџўўџџџџџџџџџџь­c+Вјџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§Ц(эџџќџџџџџџџџџџяИx&RюџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕE˜ѓ§џџџџџџџџџџџяЖ.;эџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЂjъўџџџџџџџџџџџюК€3ЧџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕђџџџџџџџџџџџџџщЕ‚1HўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўсВy1“џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќкЌs)[ћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџёЧЂd"•ѕџўјѓљџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќрИQ7щџцГŠиџѓъћџўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§ъЬЄj/ 1єћДV.пјТŸєілфџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџрСŠFFљю‰$/ђм‡fїв†•їїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџцЖy&RћкiFњК\cљЉHŠй™тџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџёЙx&QћШMRњ˜9iѓˆ+ВZNїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџјС‡.BјЛ<Uѓ|"iфmЄ>Cр§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџњЪ’@*ёД3Oшg\жYЂs+W ЪџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћЭLзИ1:кX@ЬJ ‘ZdwUњџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљЬœRЃХ4 ЭM ФB uLbZ зџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљЫžTQл? ГJ ЉA M>I?ЉџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљФ˜OЫ\ ‹RsM , "% Zћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћб”Kq”–c*]  гџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§х˜BЄ)3ЁX h5|џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓМ? GY\Б9  <ќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§нe 7yг< ‰`˜@ tіџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ№Ќr№X RЕbЉs§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџоoUљ”ЫqЭ^3дџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџњФ05юж/й/AСэёџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя„ Юњ} XѓшѕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕHŠўрgžйєџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓM6їџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќЪJC§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюBўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџгi ТўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџС5.ЛџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюхёџГ"‡шџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяО|tќД%OДѕџџџџџџџџўў§§§ќ§§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџєУ„zœ!#aЖщјїятгЦЗЌЂ˜“‘‘–БђџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓХ‹ * )Ty…|jYI=4-($""$(UрџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџєФ!  .ЬџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюФ‹#"ДќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўцН‚їџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќйЗl Zмўџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ№ЩЃN 1ЇјџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћмМ‡-]ШњџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќцФŸT#{дњџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќшЪЊn#*yШѓџџџџџџџџџџџџџџџџџџџџџџџџџџџџірЦІr*%`Јлі§џџџџџџџџџџџџџџџџџџџџ§їчЯИ•_%:qІЯшѕ§џџџџџџџџџџџџ§іымЪŘn</SyšДЦбзййййижЯХИЉ”yS/ 2BMSUUUUSMB1 tkcvs-8.2.3.orig/tkcvs/bitmaps/tags.gif0000644000175000017500000000021311664612512016177 0ustar timtimGIF89aЁџь‹џџџџџџ!ўMade with GIMP!љ,J„ЉЛp б™К‹ГuЁw8 ~€Љd˜‰&%žБ{ѕ-Зі“ $ЩD2 Я'1O9CQ‰i$;tkcvs-8.2.3.orig/tkcvs/bitmaps/folderopen.gif0000644000175000017500000000073211664612512017404 0ustar timtimGIF89a((Ѕ &&'G;YR0ZZZaabbbbjdHkkkoooqpo{zq}{q}|w}}}~q‚r†ƒsˆ…tˆˆˆŽ„˜’vš”w›–|šššЉЁyЏІ{ЉЉЉЖ­|З­}зИчКЗЗЗЩН€ОООгЦ‚зЩ‚ггг№пˆђрˆшрМџь‹џэ–їюНџё­№№№љђаёёёџѕФџѕХџљмџ§ѓџўўџџџ!љ ?,((їРŸpH,ШЄrЩl:ŸаЈtJ­ZЅРе X…ДзЌx,і‚Џ‚›zНю~Зй›эЮТoђљ­ОПчГ|}S † xs] $jgP %”%‰‚+$‰„’•”—mšŽtR 3 Ђ1cЇЉ•Ђ{_‹В  YБN‡  -3де0ІN"2+нопр+ ЧžL5съп6mO '.ыы*яO4єъ%b$Н`С/œƒ Іˆ˜Qм‡ Ѕ kј E„ˆQтQќЖc}Л™№(I™Щ“ЪЖЈ\ЩВЅЫ—0cЪœIГ&• ;tkcvs-8.2.3.orig/tkcvs/bitmaps/checkin.gif0000644000175000017500000000041311664612512016647 0ustar timtimGIF89aѓ ’мHИџ‹ЂЙачџОООŽдџџџџООО!љ !ў& Imported from XPM image: checkin.xpm,ƒ’мHИџ‹ЂЙачџОООŽдџџџџООО^ЩIЋН ƒ‹С9†‘q€œŸQiЂркbЧ‰Є+1Wо—Њ`№erBLHД„СŽЧRЮф`НbGЎ…зы$14]'иЬН–ЭЅєд%‡Чнѓa`џЖk4v;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_ques.gif0000644000175000017500000000020011664612512017245 0ustar timtimGIF89a Т„„„џџџЦЦЦ’мџџџџџџџџџџџџ!ўMade with GIMP!љ, 3HмЎp…I Ъ:AР‘ЧYMH ш@eˆNЏer1ЋˆЇкЭАОЗЃŠmгђ‚C‡ВDH;tkcvs-8.2.3.orig/tkcvs/bitmaps/dir_ques.gif0000644000175000017500000000016011664612512017055 0ustar timtimGIF89a Т’м„„„ЦЦЦџь‹џџџџџџџџџџџџ!љ, 5xЬњp‰A‡Q…Yщm !Ž$Щ нpV*Wo;ЬTВ.ѓНЪfѓн€4a —9Ÿ ;tkcvs-8.2.3.orig/tkcvs/bitmaps/dirbranch.xbm0000644000175000017500000000360711664612512017230 0ustar timtim/* Created with The GIMP */ #define dirbranch_width 48 #define dirbranch_height 48 static unsigned char dirbranch_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x30, 0x00, 0xf0, 0x80, 0x00, 0x06, 0x18, 0x00, 0xf0, 0xc0, 0x01, 0x0e, 0x18, 0x00, 0xf0, 0xc0, 0x01, 0x1f, 0xfe, 0x03, 0xf0, 0xe1, 0x03, 0x1f, 0xfe, 0x03, 0xf0, 0xe1, 0x03, 0x0f, 0xdc, 0x07, 0xc0, 0xe1, 0x03, 0x0e, 0x9e, 0x07, 0xc0, 0xc3, 0x03, 0x0c, 0x0e, 0x0f, 0x80, 0xff, 0xff, 0xf1, 0x07, 0x0e, 0x00, 0xbe, 0x00, 0xf2, 0x07, 0x00, 0x00, 0x1f, 0x00, 0x82, 0x07, 0x00, 0x00, 0x1d, 0x00, 0xfc, 0x7f, 0x00, 0xe0, 0x3f, 0x00, 0x03, 0xe7, 0x01, 0xf0, 0xff, 0xff, 0x01, 0xf6, 0x07, 0xfc, 0x7f, 0xc0, 0x01, 0xde, 0x0f, 0xfc, 0xf9, 0xc0, 0x0f, 0xce, 0x0f, 0xbc, 0xe1, 0x80, 0x0f, 0xde, 0x1f, 0x9c, 0xe1, 0x80, 0x0f, 0xdc, 0x1e, 0x8c, 0xe1, 0x80, 0xff, 0xdd, 0x18, 0x88, 0xe1, 0x81, 0xef, 0xcf, 0x00, 0x80, 0xe1, 0x03, 0x82, 0xcf, 0x00, 0x80, 0xb9, 0x03, 0x00, 0xcf, 0x00, 0x80, 0x9f, 0x1f, 0x00, 0xc7, 0x00, 0xe0, 0x1f, 0xff, 0x01, 0xc7, 0x00, 0xe0, 0x1f, 0xff, 0x03, 0xc7, 0x00, 0xf0, 0x1f, 0xfc, 0x0f, 0xc7, 0x00, 0xf0, 0x0f, 0xe0, 0x1f, 0xc7, 0x00, 0xf0, 0x03, 0x80, 0x7f, 0xcf, 0x00, 0xf0, 0x01, 0x00, 0xff, 0xcf, 0x00, 0xe0, 0x01, 0x00, 0xfc, 0xdf, 0x00, 0xe0, 0x01, 0x00, 0xf8, 0xff, 0x00, 0xe0, 0x01, 0x00, 0xf0, 0xff, 0x00, 0x80, 0x01, 0x00, 0xc0, 0xff, 0x03, 0x80, 0x01, 0x00, 0x80, 0xff, 0x07, 0x00, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; tkcvs-8.2.3.orig/tkcvs/bitmaps/modbrowse_svn.gif0000644000175000017500000000146111664612512020136 0ustar timtimGIF89a((іFooo‹ЂЙачџ€œШШƒžЩ… Ъ‡ЁЫŠЄЬŒЅЭЈЮ‘ЉЯ“Ћа”Ќа–­б›БгžДе ЕжЁЖжЃЗзЅЙиІЙиЇКйЉМйЌОл­ПлЎРмАТнВТнЕХпЙШрЛЪсМЫтџь‹СЯфФбхХвцЧгцШдчЪешЬзщЭищЮйъгмьдоьеоэжпэлуямф№ущђчьєшэѕъяіэёїюђїяђјяѓјёєљђєљѕїњіјћїјћњћ§ўўўџџџ!љF,((ў€F‚ƒ„…†‡ˆ‰Š‹ŒF‘‘Ž”&—˜˜•Œ™Ÿ—’’œƒЂІEЉЊE›ЄEБВГ!.7CЌЎЏДБ"2?6* ЛЎЈВ ,<>, ДЩЄЫ/BD3ОВжœЈ:ЊВ $+05с•ЈЋ@9=КЋЋ­зјD@74.P!#/T 6Px РƒЉvРЃД Ш7 +‚iQСDGe9шрТŽ€г'Ў„ %b№асТƒƒoЁъAу„†пfхьtЊш(^ˆ %Ѕt)!PЃJJuЊЃІ‰АfНКHkRЎŠМ{(T–„Ь@[ъЌ"Г А6-€ИsыЖХK—­ 0@€€VР‚ ~xpaВ4V, UфЩ…+ Кœ83[0аДhвCw-@зF ћvьГKл–MЛPфТ…qk~$8oЫХ ї @yюЇЮ{џNіб]сz7_ЯЭ.vПmГћv[–МгѓшгЋ_Я~i ;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_modol.gif0000644000175000017500000000017411664612512017414 0ustar timtimGIF89a Тџ„„„ЦЦЦџь‹џџџџџџџџџ!љ, Ax'мЎp•@‹CФь˜EBg@иTKЄл/ЪюC РкWј) бxL’Š€ЈдIєXw ‡vuH;tkcvs-8.2.3.orig/tkcvs/bitmaps/cvsdir.gif0000644000175000017500000000020211664612512016531 0ustar timtimGIF89a Т„„„ЦЦЦџь‹џџџЂ!ўMade with GIMP!љ, 5xЬњpAEQYщmр Ž$Щ ]кjЫ Я1ХЪ8]нLљЏк  ZЉrj: ;tkcvs-8.2.3.orig/tkcvs/bitmaps/Ticklefish.icns0000644000175000017500000020113511664612512017523 0ustar timtimicns]ICN# H*‘‘$€jJ ”‘ %* в”T , 5 J€ U$*(’ŒHR %JPN‚Е%)JˆBU(P$Њ@"Щ()ˆ+ ЂP˜T€D((ј?џџџ€џџрџџ№џџјџџќџќ|?џќўџРџўџџќŸџџј?џџјџџјџџџјџџџјџџџјџџџјџџџјџџјџџј?џџјџѓќџРќў~?icl4 ммРРЬwЧЧЬЬЧз|||ЧpР w}}wwЧ|ЧЬР ззw||wЧ|w} x}}}wзЧЧЧЬРpз‡зwx|w|||ЧЬ ww}}|Ш|wн }мЧ‡‡зxРЭнм xw}ШxЬЧРзРРРм Р ,p РЬЭЬ Ь Р Р ЭмЭЩмСA Р Ь,а}мТРРТЋбЬЬЬшРЭа ЇовмСм xРь,ТР žмТЬ—МwыСЭРаЮэчмЭл}знсЭТаСЫз} бЬНИ|а{СЬ м лРСЬТСзнxэ,ТМЫСРЭЬлонМТЭа -ТЬВЬ,ТЬТЭТРвЬ"а,ЬбМ,бЭРЭВаЬЬСФЛМ,ТА ТТЭ,М Ым,АЫН,,ВЬТЬлЬЭЬ ВЫаЭЛ- icl8ѕOzјt+++$ѕtžss—ssssOіѕž—Pž—s—‘srmsOsOііž————————r‘smssP+ѕѕѕž˜Л˜Л——————s‘s–slssOѕž˜С˜Л—————l——ssmsssOlsOѕzМјј——žŸ————‘s–zt+$ssOO+OМ˜——Л——њїs————˜UѕѕOztzO*М˜ТЩЛž—˜—ž+ѕVVzzљ%+МžЛ—Ÿљ€ЩŸТzOіТ+OzžТOѕііѕz%ѕѕѕі,:2Ѕ$і+,++2їііїїіѕ,+Тѕѕ++ѕѕ,3љї+zmЅV+-2+іі,2- 2,,3zIyЅ‚ѕ,3ї2]‚33323 22POHzŸњї-2 3,2ŸЅЌ]3-2PHOsžŸUmЅ3239+ѕїXЌžЅЪ]-,3OO˜ЄŸљOIžЅW,32ѕ,,]ІЯЅžz--V{Ÿzzžž€‚3,3-3V2,9Ѕž—zѕ,-2 2W€ŸzјOIЄ3 23^ј--3јPі-2 2-{zžtžžЅ^233:ѕї]-,3--2 2-3]Ѕћ‚2-33]]32-,339-2 ,,-,33 3 2 242і‚333:]+-2--333323 2-9їі]^93++ 2--,39^ˆ‚32-3Wѕѕ,,2 3,33W2ї]W,3]іјћ^9493:39W+2]333]ѕ+PјјVї+$ѕP^33]јі\‚::љ+ѕil32 гџŠўќмЈ…‰ЗЯі‡ў†§јЧиъ|>GT_ejkošк„§ў…§рT3{B9BIQ[`hot{žйћ‚§ў§ўў§ўџџт?,24320127=DMYgzzˆС§ў‚џёсA)##(/7BGKPXZ^cdgwvzyixІх€§ўƒџ)Ч# &*(+,--)0657:;=CJJR>LqL{xv|ЃПэ§§ў‚џ*ї_!$'())+,*(77967:8?BHLjnCіє‚tu|}~ІЩ§§ў‚џ*Ђ!"#%'((**;?ЅЎc;789;8FI[z“{N:IGAКсропйЖ­§ўЕЮљхзw!'14zіч€рбЗ•vdXFGOIA=ETQGšм€пнкР›є€§,ўЧеюулІ;,4@X>бћцррснедЯРŠSDCKXqŽœG™лнроопЯЂг€§ўЩЩхтнЯ†<64@Ћ§єф€срспонЩsU[v‹Ђ™‚e1<МрнорозФž€§,ўДЊзсрогОЗХт§їцтссрсрсоол˜Q??FGC@D;3ЌннрппкЧ–€§,ўЯwРрссфыїјїярймннпмспопмЭ–cONOTONR‹анпоплсЁх€§ўћ’•дхт€у"фцмеРТоппсррсопннвЏ–zv‡ЃЬкмннройдЂі€§+ўџкlЊжотутсмрЮ€ДпррпсспспорорнлйкммнммооплХІ§+ўџџКiЏгимноцф…шхррсймрпрппрнлзгЬЭгимонприЗЇ§+ўџџ§šhЄЫбзыаЏПъћхппсспрртптмлмпЭЖЃЎФзнрронЗЏ§ў€џ(јБ‚ ЅЛТнЮгўѓуунстптуппклчюЖwospŠЖйпптмТё€§ў‚џ*ѓ№юєќЦЪљєхтрсрпрнлккфщПŠЌфёяЪ‚r­гоопг’šњ§§ў…џўДŠЙХЮйиммйлегесъПИч‚џ ўи~ЌЮопкБvн§§ў†џкw`ЊГМТЦЩЭЩЦСœ“Дђ…џ §йyœЬилЭ–…ј§ў‡џњФЛ ›‰•Š„™Лотў‡џ §§я†ƒЌЩгЧБ§ўžџў§ ђ{•ЎОЪЁŒуžџў‚§јиžŠ˜ЌХЄЗžџ…ўќ№ебтђјнџнџ‘§ћмЩЉ€•˜žЮЩзј‹§ўŒ§яьј§щВІКФЪбввЭЯЫПДХмѓˆ§ўŠ§ њдІХкд›Звж€з€жежжзйЮЬћ‡§ў‰§ ё­ЂРрЗŸСез€жззжй€ежжиждъ†§ў€§ўў§§ўўџџ№ЇДЧЫаЮЫбджиий€м оомизжзмЭРМв„§ў§џўџїВЏЧЩЬЫШЦЩЯйпоорртшцъьыыщтокоЧТы‚§ў…џэчЏЈССЙВДМЦЬббдз€ж€з€ж илмрфцътЛ№§ўƒџ%ўкЈАЅЖВБЛХШЪЬЮЯабгджежжззбежЮЮижзимтРш€§ўƒџз™ЕДБЕПФТЦЦШЦБРбЯбд€е жжзЏЖрЗОпйж€зсШэ§§ў‚џљБЎЗЗЛОПТУФЦЧЛwuГзббдв€жйФ™Ќ–љљжзезевлп§§ў‚џ*ЩЏЖЙЛННОПСУФФдgЎ‘ТдвгежжЭЦКїкќ§жЌ”™“ŒŠЪ§§ўџуЖЗИЙЕЎМПОРТТФЩЩlъѕЖ ЌБЌЉ›—П‚§ їюёяээњ§§ў€џцРВЕИЗ•`sЕКНПРТФФЋКџћХ œžŸœрŒ§ўџўоЛ ЖЗЕЙ›@jb}””–œ˜У№џў„џŠ§ўџкЖšЩ˜ЙКИУkкїЎPkЎЗнџ‰§ўзЁељЭš–ЊЋРџўњћџў‚§ њыщштм№šЗс€џўэфтрћ•џ€§ ъвгиоЛoK›О§Šџ§џ §§ййіќѕШŽˆЪі“•„џўјЦЫЊЖРРТИІŒžДлў€џќСЋ–М§џџ§оцћірИ’‹Ч§ўџўыйХвфіїіђэыэющЬž~|АсѓЕŒэЁ^˜їлЯѕљлПЏ•‹Џ§§ўkИўџџўд­хќ§іьнеЬЫЪЭеихцЯŸ{\liцщŽp@—ІљњиЙДЄŠ’ё§§ўј§џџябнјъжЬТКМЖЖЗВЕВДЖМШЬЗ”šфѕА…sEПѕѕвИГЏ›ƒь€§ў€џ'бМ№жujxЉДЖДГЕВДГАВЎЎХніёД‡‹_ЊћэЦЗЖЕЏˆœ§+ўџџфГњиyS[??UЏЕДЗВДГВВЎРщјїжŸ„‹”ЁиУЕЙЗДЋа§+ўџѓ›ѓъ‘J“žmFDdЂДЖВГГБЎЧъјьЭЋŠ‚ˆЎщфpjЋЗЕЕВЅvМ§+ўўПбѕЗk>€Š{@WcЗЕЕДГЌЬьнЏ‹€Š бѓж™uAЕЕЖАІx§+ўїŒёнЉZ;NTRRxsДНЖДДБЏХ­}€€˜кѕфЉ…†m=”ЖЕБГЇ|”§,ўАЋьРІaCH^p… ‘фФДЕЕЎІ“}uxƒ—ЅŸ“ˆ†Œ…OzАГГДАЊŒtђ€§,ўЙГаК­~dЖеЋз№ТЖЕЖД­ЊІ†x|ƒŒšДдх‰БАДВВГdФ€§ўЙžОЗЎЁrƒЄЊЇЧћнМ€ЖЕЗГББІ†Аецю№хРuG•ЕЏБЕВЂk‚€§,ўЌƒЋЗЖВЇžЖбщћцСИЗЖЕЖЖЕВБЏ’~—›—‡o9‡ААЕГ˜c„€§ўЯ^“ДЗЗОЫсфхвЕЋЏГЖГЕЖ€ВЏЉ„notyyqaJnЄАГБЕЊˆZс€§,ўћŠoЃНИЙКЛМПЎš‰”ГДГЗЕЕЗБВААЈ‰ybZjЁЎЎААЖБЇ{zѕ€§ ўџлYІБЙЛЙЗЋ›x[ˆВЕЖДЗЗГЖГВДБДАЎЌЌ€ЏЎЎ€ВЊv‘§+ўџџЙPЇ­Њ˜cvЫОЕДЗЖЕЗГЖДГЕАЋЂ——™ЄЉЎБАГЕЈy§+ўџџ§–Oq‡‹ƒwЕк№ПГИЖЗЗЕЕКГЗ­Ї–€rjh|•ЉАДЖББƒ”§ў€џјЎ^ZYtšзЮЧћпКЛЕЖЗЕИКГДЉŸ†i€Q LcˆЈГГИ­jэ€§ў‚џ*ѓэьєћТОђрПЙЕЖЖДЕЏЌІ–Ёrh сяэИsY‚ЁВВГЅi†љ§§ў†џ­o—šžЋЇЎЎЈЌ •‡~{phАх‚џ ўдc€›БДЋ†Tк§§ў†џмwM[jqx}~{xtpxt”Тё…џ §гbr™ЈЌœouј§ў‡џ ћЮПЃš€„†ŠŽІЩшъˆџ §§юs[|’ •gІ§ўžџў§ яyQ`p}zgrмžџў‚§їа†\_biT“žџ…ўќюаЩлэїнџнџ‘њ јеСŸƒ„„†ŒЪРаѕ‹њќŒњыщєњхЇ˜ЎКТЯбЮЪЫЦЛЌНдяˆњќŠњ іЬ—ОаЭŠ­Язж едгежззХУј‡њќ‰њэ“МлЏМджз€ждгежжзЮЭх†њќњњћќћњњќ§ўўэ–ЊЦЪЯЬЪбгежжй€л ммлжежжкЩЛВЪ„њќћў€џќў€џіЇЅЦШЫЪЦУУЫгзииппсххъыъчшснийПЙц‚њќў„џьхЄОПЕ­АИУЩаагже„ж еейлоттщмВьњќƒџ%ўж”Њ˜А­ЌИУЧЩЫЮЮЯбггеежзжжЭвеЬЫижжилрЗу€њќƒџв„­ЏЉАМТСХХЧХ­МаЯбддеж€зЁ­мЌЖлйж€зпПшњњќ‚џ*јЅЃЏБИМНРСТФЦЗmi­гббгвжжзкМ’Ѓ‹іібждздбзйњњќ‚џ*У ЏДИККМНПСУУг`—ЈˆЛгвгджжЬРГєжљњаЁŠ”‹~ФњњќџрЈАДЖБЉЙММОРРТШФgъєБœ›—ž™~„Й‚њ ѓыэьъъіњњќ€џуЗЊВДГ‹[oБЗЛНОРТОЄИџњП˜“”•”оћ‹њќџўиЏ–БДВЕ—?i]yŽ‹Л№џўƒџўћ‰њќџзЋТ”ЕЖДНfйїЎOj­Знџќˆњќе|šељЪ‘’Ѓ ‡Пџўњћџќ‚њ іссрлию­р€џўыуппћ•џћњњсКСРХ—M3}ИќŠџ§џ ћњШФюіфЇ_]Нѓˆ’„џўїСИЅЎЉЌЅ“}“Алў€џќЛЂŽЖ§џўњЯзѓфУ‹\\Йњќ’˜џўщгИФжщфслвЯавЯЋ{`kЊпђД‰щšYѕдМюыИ‘{_Y˜љњќdЗўџџўЮ•зјѓтвОБЃЁЁЃ­ДЧЪЊsZKgfучŒi7‚ŽђыЕ‡€qSnьњњќј§џџэХЮ№мИЄ—ˆ‹„ƒƒ~‚~€„žІ‘pŽсѓЌ}g=­ыцЋ…{fUф€њќ€џ'ЪЌчНuPITbv€ƒ€‚}{~|†Вжѕ№Џ~|TŸёж›…ƒ{S‚њ+ќџџтЃєРODS86@c|‚…~€€}ƒ”Бхіѕе}†—Л˜‚‡„€vPХњ+ќџѓŠьеd=‹›lB=Iq‚~€|…ЙціъЪЇ…zyЂхрkU{…‚‚~rJВњ+ќўЕЛу‰K:}‰z?SOp…‚ˆСшк­ˆyw}’Ш№д–m9fƒ|rQƒњќіkоНwC:OWSRub…Œƒ€€žЛЅyxutŒеѓсЇ€z`2lƒ}sU…њ,ќЊˆг’sJ@G\mƒ—|Ш—€‚‚€‡~mfkxЂšŽƒ|€xBZ}€~€|vd_ю€њ,ќЏЋ‰z\]‘ЖеЁФз”‚‚ƒ{xxtrkruw€ŒЊау†i|€~}rEН€њќЎqŽ…{rW{ЃЄ‘ТюМŒƒƒ‚„€|vƒЊатыютЛn=j{|}oCr€њ,ќЇ`z„ƒ‚€uŸСуёЩ“…„‚‚ƒ‚‚~|{{qxŒ’˜”ˆ|e0b{{~f>x€њ,ќЮJi‚†‡ЄТЦЩЋ„x{€…„ƒ~~z}h[aglkcT;Qt{€}vT>н€њ,ќћƒSsŒ†‡ˆˆ‹zf`h€„‚‚„}~{{ybXKCP]szy{{ƒ}sNhђ€њ ќџкK[t}ˆ‰‡†wfLDa‚ƒ€„„„}€}|{zx€zyy}~wK…њ+ќџџЗ?\jtxwbWPP^ЇŽƒ€…‡ƒ„‚€‚|voffiquy||€‚tRƒњ+ќџџ§’?NY[SLLЉР鑇ƒ„…‚ƒˆ…yr`PGGIWhu{€‚~}\…њќ€џ(јЊM<9XŠдЪЎшО‰Šƒ„…†‰tiVSE=?=9I`u€€†zgSщ€њќ‚џ*ђьыѓћЙЊтТ‡‚ƒ„€‚|wscWOKT™пэыБlG]o~xMyіњњќ†џЈXyulvrzztxncWMHJUЉу‚џ ўаR\k}ydBењњќ†џкq=BHLQSSOLJGQY†П№ў„џ њЮTRiuxoRgєњќ‡џ ћЮОž“svx}„ŸХчъ‡џ ўњњъhBXcngLŸњќžџўћ€њ ыl>EMTNHdиžџќ‚њєЪxFDAC8†žџў„§њьЬФзъѕнџh8mk џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџics#H  Є(@R P2(Tд!"JT! Šрјќ?ўюџŸoџўџўџўџўџўџўоics4ˆЬЬЧЧЬ ww|wЬЧз‡Ч|ЧР xwЬ}Р РЧ‡ЬРРРЬР ЬР ммЬ ЛМЬШ|ЬžСЫ|лЬРТЧЬ,ЗнРЬСТСЫМТР мЬЭЛЬР ТТм баРР ,ics8ѕ+%ѕ%OO——ssOіС—Л—m–smsOtј—˜———syHsіOЛžЛ˜y+stsI+OsžzVOOѕO$%іѕіѕ2,+ѕ%,,2PzV2ѕ]І33їszz,3іі3ЩЅ23,zŸsz3+,3zO,32{zsњ3-2ј33--3W]X3W93ї,33]333+ї33333++323ѕѕ++ѕW]2is32д„ўрРаё‚ў§Ъ“xDWeoЃє€§7ўџћС.),2;HVclБћ§џўf#,07;G^yŒyП§џ%)EЏ[S†јфДУ§Ќj,S’‹Лќ€џў§§љі†џјшшютчџіиџљѓшжБїчщ№ююмМTВђты§яуŠcЮпкЕ‰bfВтлё§пТ"'ЖрЖiFgZyрЯл§Южr‡ъснЫZ_rvола§ЙжцшЧпрпТ–“Оойи§ѕЇЧеаспсойЇИобй§џўњЕЫгжЬЬзјдЕйБ§€џіпеею€џўъЏЎЦ‹џѓљ„ўщзсѓ‚ў§уеШЪддЭзѕ€§7ўџќоЛФЭзкмннжоћ§џўСЕНЧГЮджЦЪнио§џкЖЂНТ­ЭКДПљэви§вОЏВНм§€џў§§іѕОџљяцшнхџірџєшЙДдїфлкабМЄКœЈтЕГ§эв…`ЂДВФгАŸБЙžт§гЂXnЃЕБ ‹АЃvДœа§ВЋ‘ЩгЖГІ‡ІКВЊЌ§ЊЋСР”ДЕВЁ‹…˜БШ§ѕ‹‡œНЙЖЖА”s‹Б Ю§џ§љЊЁžšŽšЧїЬ‘Њ•§€џјпбзђ€џўчtЄ‹џёї„§цб칂§њCнЮРХвбЫвђњњћ§§ћйЕТЫдймллвйјћџўИЏКЦЎЬдзТФлийћџжАžЛРЉЩГ­ЙіщЮгћЯЙЊ™БКи§€џ§њљёёИџљэрржуџѕнџэз•ЄвѕкФИЈЉ–ŠЗ–žЫ„”ћыСpLs€€ЇЩЊ–œ‡kкћФ€Vl€’˜„Љœ]€mШћ”|€СБ‚€yzŸДg~xšћ{”•g‚vkel}kОћѕw\}Є‡„„|bPb}rХћџ§љŸ~nja{НіЧqx€њ€џјоЫдђ€џќуzQ”ˆџ€ўяіs8mkџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџicm#8Pˆ, ‘"(”RЈ$RHЄ№ќ?ўџюќџџџўџўџўџўЯicm4h ЬЧЧwЧ Чx||||‡‡|ЧРРРЬРЬРРСЬЬмР оЬ,знЭЬСЭЫТРбЬмСЭŒР мЬ,ЭЫТР ,ЭРЭ Ьicm8ШѕіOIіѕs———‘srsOѕO˜˜———–tsHsіOМžТ—žіOOOѕі%OіOOіѕ,O$ѕ+,2,Oz,2їњІ3,PsztV,3ї3Ѕž,,{њszњ 322 ,2,3WWњW2+222,2 23339-2,+232,$23Wіїit32xкџџџџџџџџѕџЛ§ ˆ€rkc‚anŽЅјЎ§џЖ§ тдЫУРokc^XW X`lv†РСТЦЭйияЇ§џГ§јьч™nW@678BNaƒ€Пчы№ћЃ§џГ§зyTB1.5%3=AFSAR^`ege€d eW]_VYVLVu—яЃ§џВ§&Хq7:,1;=GKNOS]X]``bcdcdjqnpplekcbfdx•С §џІ§Р’•–щ€§ љМ–e6$;8BJGTW`Z^€`effgk€oruqusstmecg|ЂЌш§џЃ§!ыТАhA>PZо§§юКb5&)0?EKMKPSTTV\Y]^]a€fh€jlpr€v y}}zxlepwКщœ§џЁ§эпЏP(#*Em~х№тИH""+6D>BFHIJP€ST\_[Y`cijjppl‚v w~ˆ‚€––П№›§џЁ§ŸO($! 0V{dпВb2$!3‚= DIGJJPPLTT\„`bi€jl„v~ˆЙДœД›§џŸ§ђA'"#*13HkrdB( !-9=BFHH@EJNQLLSVY_W]dfbbh€j€lq€v€x}žП•ž›§џž§п†= ',045;HU6$!(/59;€=@ACF=FHJNPKPTW_U\bca`hjpmoƒvy‘ЈЉž“’ й—§џ“§…ў§§к1$%-112543:1+,3889:€;<=?BAGHIKPNORV[Y^`dh€j€pr‚v x‡ŠЄfYe–ЕЛЮѕ“§џ’§ўў„џ ўт2#'.1--12€3645€7#::898<<:;AACCBEHDDIIQRRTWRV\`aeemnlsvw~y†„pmc\c~Ыря‘§џ‚§ўў‚џўƒ§ˆџњ‚.",-01€-0…32678;7:5‚0)#/#„! )6"28:BSR[cghq€tuu}{vx‰‰xXa{Мљ§џ€§ўў…џў€§ў‡џ §єŸG "',--..223 65332$"‚  ƒ!!%4"! #'(:EM[ckw€‚‡‘mfš§џ§§ўˆџ€ў‡џ$њнL&%)60--.384.,*'  $"#**.€089:>€B>BPAHI€HDC@=?49<7.*,6ERVhu†Š—™’‹Ќч§џўў•џўл‘>) "(+40-,*)," €$.346==@DE@EFGJOOSQQVYZ^‚`_^^TSTPD>:67;<@CJT`u œ„}ЋпŒ§“џ ўѓуъјђЋ@%''),+)%  '//257€84689€=@A:AEFGIJOSRO\VY]]€`ch€jnssvpeXMI8.,1:CRk‡™€xЇт‹§“џ ёЦЉдФw#' # !)22/035€85689<‚=:A€FIJOSSTW`bii€jktuƒv o^C()I‡vœђќ‰§’џ§•7.-:3#)(# "'*,-..-0‚3€46€8‚9=C@B€FIKKL€TX_‚`aag€jkkq‚v y}zwocR=*j˜šї‰§‘џ§ЁJ("% "()+,/0.11/0‚346346€8;989=?<A?EFH€JNOQTTRIN\b``ebNMZp{‚vw|€}~ЇЏ~Єн‡§џюД8 # #(+)(()-*)*-..+-122484 6889;::;?@;ECF‚JNTTC6:Ngqge[70t€~Šzwv|{}‚‘АЄœНх†§ŽџўШ@%   $'„(),,**,€-//% %*-28€6347889<<::<=D=BIH€JMSTA>8JAPtnMMфњјіђЭsovwy‚ƒ…ЩЂЋ†§џ§­C $# %'„()(+**,-,&,41*%0?A1065588;9€8Bэ€§ћђ ‚y‚vy„ЇМЖД…§џК^   #%"!!$''ƒ(,**,€*.300BORL>64:A887668:99:8<;>ABFG€H Gs{ВљЛЅn5Aэ‚§‚entonsvw{{urv{s…€‡…§‹џўэt%  &"#&! #&&'‚(-*)*,(**0:?8GvŸž†[@8=B:9746878:8<9;<ADDAKKAgЧЊhTC?GF>;?@FG?J@FRz…§‹џѕP „"€&"'&'‚( )-)(*5PT87|џдv-><:<€8:<88<=D>::-*%$[Œ§ жL//258322/€*+}†§Šџоn € #"&&% !'&€'ƒ())+*(-:N4%QСјџџїхзi2&+)',,('%%&&',!&(!%$%'^Œ§ілжжззиз€жех†§‰џк‰" € "!#&%#""%'&&''‚( *)),0AA-&OЂшџ ЩŽV, "$##‚" ((zЌСЅ§ˆџлŽ5  !%+'""%€&''(('€(*,+3@;+#GЮўўџџўоІn[6/..€1 /--+..'# ЇнюяЬM9*‚.5*иџ ќќѓщчшђљ§ў§§ў”џў“џў§ џџшЅ7 .ЛщјћћђХЖ›o=!жў„џўўВџў§ џџбa =Ѓ§§ў€џљђфЫІxXC80>vчМџў§ълоевеййбШКЎЄšЇуџјП;:џў…џ ўїзЕЁ”„ia^n—лљœџўџўˆ§јъъюкЫдЫШЬбЯСЄ‡seRbЌџуЏ4 џў‰џўќќџџѓюѓћžџўўџў…§їіыЬЭиръѕњ§§њѓуЙŸ”Œ|yџКŒ#џўўТџў…§кбрьѓљќќ§ўў§ѓмМШлуиПЌпџВ •цИџў§“џўƒџў‚§іпрхюіњћќ§§ўўљчиищэђйЦ№ыїџfHПЃџњѕџўў€џўќ§‰џў™џўў§янтьѕћƒ§ њѓчлмщїїеЭф€§џ  Р”џўџѕиИИНУЭЭджгбййдаЬЧРКЗБЈЇч‘џњђчиШХЯш†џўў€§єфнђћƒ§ ћј№тйпьїїйШм§џУ“џ#ўѕеофыоЄhlw…š™ЈЌАЖГЖЊž—wjVA9ЗсмзбѕŠџ ќщжФЉ†g^pЅпэ§ƒџўў§§їцшљ§ў§њїѓьсжкьјѕъКс‚§џТ‘џќќ§д6b~žМЦжч№ѕљѕќў§€њј€і яыиПЏ›‹qW@#Э€ќ‡џ №•jYJ65).bЉёў‚џў§ќќтыј€§ў§ќјющскзрцёїыПђƒ§џП‘џ„ƒАЭФЮрэђєїњќќ§ќўў…§ћћіящоЯВšt‚w^n†џўтB+Bi…xU. 0Ušт‚џўьаођњќ§ў§ўїьчусмиеп№іьрђћƒ§џ Пџ щХ­аЕПиыѓєњ€§€ў§§‚ўћ„ў%§ћњїёсаУЏš‹unЁŒФ№ўџџљід~-xИТ“`3!(4Z о€џыгЬляњ§§ў§љђъфуомкиифѓєЮЋ…§џ Мќ‰џ іЋПЪРДеьіњ„§ў§„ќ€њќќ§€ў,§§ўўќїяхзЦИЉ—…g­ЌЃйёРn2#dЕиМY6-0*4fЏш№ЬРСгё§ќјюшфтслйийсюѕрФР…§џ6Хћ‡џїъдk’Хщљƒ§ў§§ќћћј€і€єѓђєђѓєѕјљ)њћќќњіюхзЩНА˜‡rWAИf!O­дРzI79;(%@|Кв˜Љгє€§ўќіюуттсркнйлъіюв膧џ"(Ѕъ†џ§їлВ™мёњƒ§ ў§§њљїєђэъъщ„утстч€щ)ъь№іњћљљїюуйдТЏ™„rH-BІйФœd:18<(%%L…АЛлѓ€§ў§ѕщустроомчтоыїйъ‡§џттуѓќ…џўч™žдыјћ§ў€§WїєящццшхуттссрртппрпотрстспуфхщюѓѕїєэфжЦЙЌš€aBQЄюУƒnG;8980'#HŒдьїќ§§ўўѓщупспооммизсєяѕћ‡§ŠџќёУОжђћ€§ќњђяъщъхццуррфтсрппт€сннп€снн€о&трнотфчьяяёшлвЧД„cv­оЧŸ…kN<:DKI:$1rСђњќ€§ћѓъфсопоонкклршьвѓˆ§‰џйВквшј€§ ї№щцрокйкйлоƒрсрртсрспмрсрпнмо€р"пмлнпсфхцььуШЄ‰zМлЭ‰lP=99NYO9*WЇы‚§њѓщуƒрпнирхчшріˆ§‡џQђйЇ‹йѓћ§ћїёыпдвбЬЫЬЭвазмпспопстстрпрспсрмнпопспнонмнрпрпноЮИ—xr–ЦлЪœ‚vT<;BBW\K6>†б€§ ўќјёщфрортс€рпжуцспћ‰§‡џЬ‹—н€§bћѕщииЭТОМАЏВКЪФЮзкороортсспопстспммрссрмкооноокивЩХ`a†ЕмЯЏ‘pWB9?VW^WF?aЎц§§ўќїюуттрпсуспонлдтциЩњ‰§‡џŒЏчћ€§юсщьРhNMUbwЃЙЪвекосутсн€о8пснпсрмрпрроммпсрмзАxNNo•ЖЪЪЄ‘‰‚pI>:9?V`]NBQ˜оќў§ўђыцтнор‚спнйисфбИљ‰§…џ^ъŠЗтљ§§ћштрЪ€]J>78@HII]‰ІЙЧекрсрсоорттсннпсрсонороррЫtjjnx’œЈЋЄ˜tbE689AR`aI4,,TЃсќїёшфутмстт€с ронйиеаеыќ‰§„џи–œрј§§ќшчиБvK/€&  $(.7Rw›ЗЫй€п;срорсппомосппоннпоаЇ€ojyŠ›ЃЃ›”Š|v^OC85 +.0JvЁЛемопртррпноппррононлб­•xrŠЄŸœœ“‘ƒxn^WC75:CHSagSmАšn0D‰Ъотомр€тсрсп€опблУЦ‹§‚џCѓˆmвіќ§ќєнкКy=.7Šžn)8,(-+)OŠЈЫкопптсрроорссромнмЭІ^]ŒОвЧЁ–Žƒ€xiYI8€9':AUU\acoЦхдІu2 !DЬоппусрсрпсспнрчвгНЛцќ‰§‚џ%И”Ўя§§ўіщрп€H0H„k9,:;*& @{ЅПертсспороноомнг L{ЬщеЋ›‹ƒ}rgJ=539ARVSTsАшеЏ“{T51.0LЄбртууопс омкжаЯВ†xі‰§‚џZ‡œнњ§ўўёоиО`/#5HE925/$%'C>3,6hНорнпс оомлкжРЖŸ‚‚ї‰§€џьpСэљ§ўјыпзЪc7€&1#*+HЈШмффсптсррпорспз˜‰ЂЖžzlZG82757€?.ISWS_j‰ЄЂ•…}sbM=9xРмрпссппсоппмйзФАЂŒ‚ї‰§џџ§ьŸеѕ€ў яцпжШT-€!%%"00)!AЊнучшусспопспрриТŽЃŸ‡zvgP>:$DSVSISej‚„‚ƒm]I707:FVVUG@CQ›Энпсусо€м рснйибЕЄƒї‰§eџџФPЋсјўќєчойбЛd*##&&"%.*5.'LКяящчцсспопстродИІА›}xfUMJIIWURSVS\ipjla[ULD;;<988HXa_G;;;:67B7BA=BHPTY__dgqu[8>uЗйтроопморпнсннооммЭКЊwнˆ§/џџд~Сьї№цуцпллвЎqE57$,110,=Ngr;(6‹эў§јьутпрсрпст€рммЯв€миЯШС›x`LCB=;>DDNUXY€]$`j€˜ТозІq49vТмрономрспмокосрмзеЧИЌ…зј‡§2џџи‹СфычууцунивУІm;9<69==FMZWA)0Œкќў§іьхсстрпросттр„о мккжТВ•xf]RPO€PT\fvЄЖгщпЮЛГЉ‡^/&G„Уйнн€орпм€о ртрзеШТС†Ч‡§6џџйŒТиуччтфтойкШКšmD*-8?>C:6/(6Œві§ўћёшфтпссрсрсрппррсро:нммжХЋ‹lW]XZdlrw‹’Њ­ГЗГЄ•Œ|_E(#4UžЮноррмномосоопойибЮЬЁyС‡§(џџе€ЗЯоцчсрпннргЫК €W?1,--*/Bf йє§ў§їэхтт„с€р2стсропнонномд͘qZRJJR]is|ƒ‰•–“„vng]RD;/+,9~Умпртмм€о троннйиикжЌ{Р‡§џџЯlЈХитхтоопптозЭУКžy`WXVew•У‚§јюшуссттср‚с€рсн€о8ннмЭЋcK>:=@HQX\bfhd`]XLC=99<91*1lИиоосмнпсотсомнйжнфм­xО‡§џџЧVœКЭкттпутортлидЪЪЫвзееяћ§ў§љ№хут€стт€с&прстспорсспоонноммЦ›nZD?A83159826853939€=D9/-:lБднмолосснрпноркжрчзЊpК‡§џ§РBˆБУЮкотууркрсфчссчёљќќ§€ўљ№ъфтстуспррпстпо…спо ммЫЉ‡oPGF:A55‚98899J€TR<00?~ТлмкмнопссннопркнлбО›lК‡§џџЛ0sžЙЬмтпрутнрпушыюєњ§ў§§ўљѓьцуррсрпспдгоптроппстсо€нппм4иЩІƒ^OOLQNLMMALOOQYY]`\ND74ChЁЬлммррононнрпмлзсузИ{Сэ‡§=џџс—cЉСмтоопрссррущшяіњќќњљљјѓ№ъуомлмнонпойкрнрпидсрсспмпоопом#еМЅƒkZGHILNPLX\\[VPNOL=?BSwЅЧелмнорп€н осснилчшдЉhкˆ§€џшQx˜БЬопопрсуурпфсшю€№я№ёючцтокжбЯбжкоопстнопйеу€р-посроопмнониЯЧГƒdZUQOMLQOPRHBCHKL_tЌЯмлмномп€о рорсожмцрФ“bиˆ§€џqъdg‡ЁБгмртупфцтотсцчфптуцшцромкзбФОПЧднпопсоорссуспопсутомнмпсокомлдФЎ˜ŠyjcVPGGRTU_is‰ЁОЫакммноомнносупоомджоб­}пˆ§Aџџў§џ|mЇНЯнхцсостучупуфпчфтухокззжЯПЛВДЩлспномртспсспртпутоорморп€мпттркзЮЦТЌІ—šЅЊМЦЫмонопмноммнопусмййибдЬІ‚ї‰§‚џY`GoЉРбнтснртфчцрртучцтонкйжваЪЉ ЄВЧлспоррпстрносутроонпхрноонрропсонолйлзгббвжезйлронмнмнмкнррнпсмножадК‹xі‰§‚џ9РoQv‘ЊРбломруутцтсуусфрмнцппскН}}”АШлрпоттпрропсрсттнропутнпспнмнртспопоппнонммнонмнмлмрсолроппзабГ…vі‰§ƒџ)ŸT[všЕЦбйлптсмтрсутмомйкшфшьи•KržНвнсрртур€п%сусрпротспптнопооспнмортсолнмоокййлммл€мнмл€мномополспоозШЬ­vі‰§ƒџ]ЩM\†ЈМЦеикопймнпсрйклккфъюфДT6‰Ржссфусруропсрнрпмортуснрнртророопсорпмкмйккзежжиидклмнмклмнор€н оптпнмжОЦЅxyі‰§„џ5ЪgE\‰­РведзлймнллнйпрфъяіёСjaЗхёыфшшунснпттнЪионтпсус€о рсронмостп‚мзд€гдЫЫЮХгзий€мннмоолнссутнйжРЛž|‚ї‰§„џ іЫMIe†ЅСде€д%йкзипохчьєѕщ”ЄИ+˜лїќљъхфсоорсухркндбуто5хупорорсрпсоййкззвбааЯСМОФЮЩШЩЭзмнонмоносснттнйзФОЉ‰‚ї‰§…џkєХ`Mb™НФЪгжггввиш№ёѓыСtЂпиsЭё§§љыфспостпсфсотнктуоппнфхснооттрнклкйиимжддаУНДЌЈЏВМТЦЭймннмннрттнмноокЧЖІ‚ї‰§‡џ7ДuYa|šЈЙШЫЮЮдлрээнЛЅвšзмГОюћўћѕыурпнеррпррпуурпрнсупрурп€рспкий€м&съчулЭНЖЎЄ™’—žЋЙУЭбинморпрсомпромЮГЅ“‹ї‰§ˆџ[У€[h…™АСУЮвкрпЯКНЃџџзЅŸёќў§љяштстпЭотпнрстуснпнтхунппруумнлийлручюіщеОЋŸ•“ˆ~~‰žЖРУЯкнртпо€п фтнмдЙЈ˜Яѓˆ§‰џв‰d‰›БСТЧЯЪЦР’bЙ€џяz†щ§ўў§ішурсфхорсррут€с<пурсфхонортткзжилрчьђіэСŠh`VXZ_golmlzŸЕКЧемнрпннстхунйеТЎ˜nмˆ§‰џWћєшOLblh]I;yљѕ№јџџќq“ећўў§њёфууттуспоптцупнопццсососснимзжксччёієфЇu^XYXXgmimfa^`m‡ŸЗЫзз€н пуорспйнЪБ˜‚pмˆ§‹џ ќЭЫбдгЯЩХи‚џXрmЄзє§§ўќєътццтртпсутрфуппсорхсмннпрнжжззрьђѓѕсЙ‹l]~ІМббеевбЛaLXk…ŸЗЩЪелптурнмопргКœ‚oг‡§™џнЂŽмї€§њёщфстфтрросфурппортпмомкнк€йиктчэѓєуХЄhЈЩуђ€џў§ћявЕ[Rby—ЕСакнптрмнпопзХЋznП‡§“џўў€џ5сЏ•пїћїѓ№ьтпррнрпоорпссплннорпйижзкйггзнуюђюсЫЉ‰qs›вєў†џўўр™„e_t—ГЦбжйоносснмйаМŸ†n о†§“џ6§§џџч›‰РлхшсжеддиопйкклотнммлйплйкмжедгдкеирччъоШЊ†s‚Ђƒџўƒџ€ўџџІwiq™БПЫдилртсоккзЧЉ‹wj †§—џhNezˆˆ™Є­ПЦЧЯилжжикммкзжз€йЯЪзбаавиуыђѕђуЖ|VbЋшїњћ‹џ§ќџџњјМVnЏРЧдлмноомниЭНЂŒj—†§—џГY7Slˆ“ŸЄГЕДИСРТЫЭХЩаШЩƒбЯдексффцеИ‘d?Цкэћўџўўџў§§яСYjАРЧгзкн€олеЪЋŽtp—…§—џ,щМƒSDj~“ЄІЕВВЗЛЕЛСТНПФХХЧЭЭбгвгноеЪПЃŠr`hІ бѕ”џў€§ёЗff„ ВЦбеиллмлзаУЂ‡kcЇюƒ§™џ)лŒ5?Ody„”–žЊЏЎДКНОУРЧШФЦШЦСИЏЎІ“–ЃXnxžѕ§ў•џƒ§Ѕh`}АУЮббжккиегЙž{\`сƒ§šџ&рjF?HU^lekƒŸ™ŸЂЏЇЉЊІšŸ“ˆƒsd]ƒИXЗшњџў—џ„§ ЅfbkŽЈЛЦФбж€идЧГ•qWТ№‚§ŸџэММ^5`Љ1-//2A91.(MППбъќџў…§ЖT[by‘ЊУЮвгеййЫСØmUМћ§–џў‚џ ўўџќѓђтлу№л€кломлйжж€зрєєїћўœџў†§ёХi^kt}‘ЅКСУЩгваХЋ€fh­§œџўџў‰џўўЄџўўˆ§ёЦ€lku† Ї­ЗИСЫбЬДš~~’Єё§џўўџўўРџўŠ§ћУЎt|{’›Ё­ЊБКЦвваР xЈПџўўџўўРџў§Э–‘t†•ЂЇЊЎЗХдщ№сМ‹lзџў§ёŠyrvˆ—ЃАИМЦЯйнаЇІЃзџў§ќюьГ{vxrt‰yŒ‘Ž‰wЪжџў“§щеввŽuuwqqВизибяџџџџџџџџџџџџђџџџџџџџџџѕџЛ§š…†„~…‰–їЎ§џЖ§ржжзк›‘„Ž ’•žллиедйжюЇ§џГ§"љюю ƒ„† ЅЊБВВДЕДГМЕВВАЂ–Œ„€…Чяю№ћЃ§џГ§кˆ‰›ІЇЃЊЅЋГЛПТЮгЮТбЦ€С РННМЛМІ™ŽŠ˜яЃ§џВ§&Яœ•šЋИОЬЪЬЮаабджебеиеббззгбааЪХСЛВБŸŸТ §џІ§ШЖРдиі€§ љСПЎ™žЎЙФЫЯ‚з„жйиежййзƒждЬСАЈЎНЗы§џЃ§юаг ŽЃУВя§§ђЬ–“ЂЏГУб€ж‚зжзж†е‚ж ззлйвТЌЂЭяœ§џЁ§юшЩ‡ŒЄВЬсЂыюхЯ‰ŸЃЌРжз‚ж‚з€жз‚жежеƒжзлнсштКЭ№›§џЁ§Ї†–ЊЂРол•щЈv…ЋžЗЭз‚жззжзз†ж‚е„ж€злђдЇД›§џŸ§ѕ›˜ššЄНЦвсшШЋšЇРЮе€зжз€жзжж€зжзжжинж„е„жйьфЌ­›§џž§щЏ™šІГМЫЯгктрЖЄЄАСЮге‚зжжзжзж€зжзжжзкж…еƒжиушбОМЊЊм—§џ“§…ў§§цК‘ЃЈЖЦЬЬЭЯвжмЬКЖУввгд€ежззжзжзж€зжзƒж„еƒж злфшжА›–КЯЩЪѕ“§џ’§ўў„џ ўчЕЂЅКЩЬШШЬЭ€ЮбЯЯ€бддвгвжзии€зийз€йкк€йккйлк€йжззеƒж змуулжЪДЁ•гтя‘§џ‚§ўў‚џўƒ§‡џ ўћšЂЁУШЫЬ€ШЫ†Юавгжгжй‚котлт„ф цщхыьщччлонжж‚зжз€ж ззлуоВŠˆОњ§џ€§ўў…џў€§ў‡џ ќ№Џœ›ЅНЧШШЩЩЭЭЮбЯ€ЮЯШЪбгоекƒстсуƒфыёюђщэ€ѓ єіѕѓ№№ьчфузежзилсумД•Љ§џ§§ўˆџ€ў‡џ њхВЁœЅЖТХЧШШЩЮ€ЩЦУПМКНФХХЪабжжкнмллк„лорпсмо€сттуутъъщы№юэьщхсойикосмЩДЋЊх§џўў•џўрЏšœЄГСХЦЧШЧХТКИЖДБВДИНУЦЪЮ€бгежж€зжжджзжжз†ж зноптчщщшш€ъ шцфтсхфеГВсŒ§“џ ўіъъіяО†Ђ ГРУЦХУРНИАЌЊЉЌАЗНФЪЪЭаб€вЯавгзжге‚ж€зжжззƒж‚е€жежлучыя№яьыфхэяЛ™Жц‹§“џѕзВЖйШЋœ ЙРССРЛДЋЅЅІЏДИСЭЭЪЫЮа€вЯавгж‚згеƒж„з‚жƒе…ж ксх№ѕѕђєћЮŸЉєŠ§’џ§П•ЎГЂЂ œБУСЙВЌЇІЇЌГКУЦЦЧЩЩШЫ…Юб€в‚гз‡жз…жƒеƒж зийккйнщєђиЏœј‰§‘џ§ЦЄЋСЏŸžЋГЖЕВАЎЉДМРТХЧЪЫЩЬЬЪЫ‚ЮЯбЮЯб€вегвгззеез„ж‚зжлкжеегЩб…жзимпрхжАчˆ§џ!іЛЂžІДФН­ЈАБАЎЏДЙЙПУХХШЦЧШЩЫЫЩЩЫЫЮ€ЯааЯЯаавдггеддеежд…ж€зЪПСбуижжЬЖЏУкук‚ж…зпфжžАŠџўџёЧ‰™ІАНКМЖБАЎГГЕЛТЦУТТУШХУФШЩЩЦСПСЧЮв€ЯЮбввгеддежжджееƒжззЙЅІПчумзЦЁ—ФдлхнзжзжƒзощгЌОф†§Žџўз{ŸЂВОЙ­ЏААБЎКРТССТУЧЧХФЧ€ШЪЩКІšЂИЭдбааЮЯбввгжждд€жддƒжзйЕЈ™–ФшчлЧЌ›уЧЛєынзж„зипєћЗД†§Žџљз•ГЗГГНГ­БНСОРРНОСТТФЧУТХЦУШМ”–ŸЈГЦтквЮЯввааƒвг€жегжйцп”XFU’кмИЎёћњљљчщнеƒжƒзйђЬЫ†§џќЬ™œІАИМЙАЗЛННОНР€СТУТ€ХЧШ РІ^RXi‘ЧттеЫ€аввег€вжзжжеж злч™•аЫpˆЊЋ“є€§ќјцфпƒж„зцшеШ…§џ Ш –ŸДИЗЗИЕЛОО€НО€ПƒТЧХФЧ€ХШЫМiJFNT\}ЇЮхйдбаавдггдвжеезƒж зюЕ•ГљМПЁrpё‚§ДПжкеЬвжЮЫЩХЫЧЬТН…§‹џ ўьЃ‘ЇЎИКИЖИˆНРСТ#ХФФЧТФФЪга_Ek—–x_k”ФпнзвЯбвбгдвжгеже‚жзэᕇ€§ЧŽ˜ѕ‚§ЙЁЈЛЧРТТРОРИЕЖДЖББЉЏ…§‹џ ћПЅЄБРВДИМНЛ…НОННРССТ+ФЦУЦЧЫжтvB~оњб˜xlxЛмпзаЯбабгвждеждззждвЯжвЊ›Š§Гˆ“žЅ—””“•š•œž˜І…§‹џ ї†ЅУПЋВКИЖЛГЛƒНПСНП‚Т УШУТХвюЛR*mџЩcRЃйхж€вджввжзждЭРА™Є˜œŒ§ жPQ]lwyshd^€QK†§Šџч™ВСЛВЙДКИЖНЛЖКƒНПСНРППƒТУЦФТЧеу“FIЙјџџѕсо~ЇХС€ЖЕГ­­ЎЌІЁŸŸЄЋЎЉ™t{Œ§ іммптффусрп€млш†§‰џ хРНСЙЕЖЗЙЗ€И НБЃ› ВОРПОПН€ОППСТ ФУФЧЫоЮ’XUЁчџ Ъ …}œЅЋ­­€Њ ЉЅž–Œ€ummЌЦЩЅ§ˆџъЭМЧИБЕКДМЖИКГЕ”ŽŠ›БФШСП‚НПРССР€ТХЧЦЭнЭœbcеўўџџўкЄ|†tuxz€| {zytqgUDJ‡жЇ§‡џэЭХШЃ­ВЗИДКЗИЙГЇŒ‡}kj€ІМСРЛМ€НСОПРПТУУФЦУСХЯбИfШїў€џ§цТГwjgdhhigffejkc]yхўўІ§џўў‚џўњИЩЦЂŸИЗГКИЖИЁœ]'>tЉЗВЙƒМОРРХТРСФМЋ––Е—Зфƒџќѓњњѕёѓєїњ§ў§ќќ€§€џўЅ§…џяЄЮОІЂАДЙИЕЛДКИЖПЩŠJ"$9>=GhІБВ­ЋЈЈЃЂ–Ž™Ÿ•ƒЕцћ„џ§€ў€§ўў‹џўЂ§„џ3фИШЪЇ—ІГЙЗЕДЖГЕИЙНр^BDzЌЅtC?OWg~” ЈЈЅЃŸЄЅЂœ•ˆrЃмдщљџўЁ§‚џ1јиЙВдЖ˜Ѓ­ККЙКЗГГЖЖКсЎzXcЗьъУ—ƒ?39HZ`lnnoio›–„ЬТ­а€џўџўž§џ.ўкЉЄЬН–™ЌЕІ ЎЛРОЗЖДДКбб˜eiКѓўј№ф{hK/+)H]iogiЊЇƒџўЁџўž§€џ§ю РЯšzšгьЎf†ЈЖ€Н ИЗНМрЛ~]ƒъ€џ§ыщŸRHQ•Ъъєщёјћќ‰џ€ў™џўž§џџўю†rЌШ’DЙтяёеkfr™ЙННММДЪлМtEпџ §§іюыьєњ§ў§ўў”џў“џў§џџфžSšК˜jЧыјќћєеПЄ‡roŠЅВ­ЄЂЋЄ~Eо…џўўВџў§ џџЫYdМЈГ§§ў€џњёсЫА“Šš›‘‘”‚ыМџў§пЦШФПСТОНЕЋЄž“ сџїЛDžУЋЙџў…џ §їнВЈАЖАББЉЌпљœџўџўˆ§ѕпнпРЈАЏЋААЇždSG4D“џзЎdи­вџўŠџўўџџє№ѓћžџўўџў…§ѓяо­ЊВСврыяєюлСiTG67Gџ|’м‡Л‘џўТџў…§СЉКгчѕћћ€ќљрБq{zj\^Чџo„ДЛд№Иџў§“џўƒџў‚§юЛФЩзщєљќ§ьЩЅ‹„ŠŒz‚йкђџg‰Ъ‰рЃџќјџўџў§§‰џў”џўў€џўў§щЩНжчѓћ€§ ћіютФІ…ސ}“Э€§џd‡Х`д™џјтХТСТЧЧШШЦХЯЫШЧХРЛЕВБЎЊш‘џїьовЬЬЯхџўў€џўў€§щаЧпєћ€§ њіёцдМЂ”Ž”‘Ш§џl†ВNа”џ"јптфцмГ{zz~ŠŠ”š•Œˆ…{oeZPF9АмкжгѕŠџќъаМЇ‘…Јоъќџџў€џўў§§№ЬЪы€§ќћј№флЭЗœ‘ސ’‹Я‚§џ†šИVв‘џ€§оfs|‰›­ЖЪжощьююь€шу€р дЫКЄxdXL:*Ю€ќ‡џ №˜cs’ƒiW`—яў‚џў§ќќПдѓ§љёхдЦЗЊ ”ˆŠŒoфƒ§џžЕgз‘џЄŸЏЎУдтьяёєіјљњњїђ€э€ьыъшткаРЅ‹vg\jeXt†џўфO_•ЦршД„aRYŒн‚џўхЛТсѓћ§§јѓфаТМЗЏЅœ“ސˆŒфљƒ§џЂЅjиџ ёбБХАЙЭнцюѕњ§ ћљјєѓђ№эъэ‚ю&ёђ№эужУЌ–ˆwgWX…Фђўџџњјм–O…УыїњПŒjYNS”и€џъЪЖНнєќ§ќјыйЩОКВ­ЊЁ“‹Šw…§џ’•”_бќ‰џ јРЦЩНЏШмьї‚§ ќњїєђ№яьыыъ€шъыьююё€ђ>юьудСБ~qdR„ЅЉЊнѓЩ‡_tЙшћћєЗ‰qfW=W тщЖІЅЙпјќ§ќішбФМИИ­ЉЅ›Š…‹|„Е…§џlt‚kбћ‡џ њяз„–Жгъїќ§њјјі№щусспнммкимийлну€ф*хчшыычпвРЎ ”„rgYKI˜Мz?eЋцњћірЄ„wq`;6hЈМj|Ау€§ћєцаКЙКИДЋІŸ’…‡„Ж†§џKWyЖэ†џўљхП•Н鹂§DќќћяшхунзаЭЪФЛЛКЛЛККИЖИСФХХШаесшщцчсаКЉžŽƒugaN53YЄфїћљьП€y`;(AmŠ—Ъю€§ќђпЩКЗЙЖВБЎЂ–Œ†Œ|Х‡§џшщюі§…џўэЌЁПйђјћ§EљєэумаЦРРФРЛЙИЗЗЕЖКГГЕГБЗЕЗИЖГЙМПЧгкптмЮНЈ—†pd[uЈрљћњ№жЁˆƒua;&8kЊг№ћ€§ѓкХЛГЖДГБЎЎЁ–Œ„~ві‡§Šџg§єЛВШцѕќ§§јьрзЭЩЩРСПКЕЕМЙЗЕГГИЗЖЖЏЏГЖЗЖААВГВЙЕАВЛОТЫвджШВЃ˜ˆ„’КуїћњявЊ‹„‚ƒ|_-/cЊрђћ§§ћяиЦМЖВГВВАЊЊœ…{hкˆ§‰џсМШОжюњ§§юмЭРЗБЊЉЋЇЋБДЕЖЗ€Е>ДДЙЗЕЕВЏДЗДДБЏБДДЖГЎ­БДЖЛПТЫЮСАЁБЫшњќњѓкВƒƒ‡Š}T-N™пљ§§ћїялЦКЕ‚ДГЎІ“ƒy~Ћъˆ§‡џiѕпГ’Лті§љэрФЊ™’Ž’“”–˜œІЎГИДВГЖЗЖЙЕГЕЖГЗЕЏБГБГЕГЏВАЎАЕВДГЏВМОЙЗТођћќљёмИ–ƒ……Ž‹qE?Ъљ§§ћєшзШОЖГДЗЖЕЕДВЂ{o€ї‰§‡џiв™–ШіќќјцСœ†|yuhrv{ƒŠšЃЉБЕББЕКЕЕДВГЖЙЗГЏЎЕЖЖДЎЉВВАВВЉЏБЌЊЌДШућќњћљёй­ƒ„Š‹Ž~[Hbx‡’ ЉЖИЕЕБВЖЙИЖАЏГЖДЖБАВЕБЖЕЏЇЉНЯлъђјћћљљыкСžŠ„ƒ…Š‘†€{uk~КцђфзЧНКЙЎЖЙИ€Ж ДГЏІ’‚o‡зћ‰§„џjк—ˆЫ№§ћђЦЃ„gORWacU?55DBJS[biqw{ƒŸЃ€wЗътЮСМДГДЕЕЖЕГБЎЇЉž“Œ‹unnss||}„Œ“› ЁŸ™—’‹†ƒƒ‚‚ˆŽŒ‰~cESfŸЎИЖАДИМТСЖЕЙЛТСЙВАЊЈЁ‘sotv€—ЌИДБЕДГИЙДАВЗКЙЕБВАДОЕАББЏДДБГЖГАВЏЎА­ЇІІЇЋЊЊЌЏДВ€ЏЎБЏ€ЎЉАЖЕАДЖ­ЏВ †uaRkі‰§‚џ9Тl@Sn€‘ŸЋБЎЕККЙСИЗКЛИМЕ­ЈЁœ’†yjQ]oƒš­ЕВВЙЙГЖЕБГЗЖЗИИЏЕВВКЙ€АЏДЖВЏЎАДИЖВБГБГГАБАЏЏАБАЎЏЎЌЏЕЖБЌДБГГЂ„|dQlі‰§ƒџmŸKBWn„“žЉЋГЙЗЏИЖИЛЙЎВЎЈЁ˜…sX9Vx“ЇБЖДДЙЛЕГВГЖКИДГЕВИЗГДЙАГДВВЕГЏЎВДЙИБЌАЎББЋЉЉЌЎ­ЌЎЎЏЏЎЌ­ЎЏАБЏВДБ­ЖГВБЄƒ€eNlі‰§ƒџmЪ~BBYwŠ”ЁІЋВГЉ­БДЗЖЉЄЃž•ˆyd;5h”ЏЙЗМЛЗЕКЕВГЗЕЙКДЏВЕЙЛЗАЕАЕИЕВДВВДЗГЖДЏЊЎЈЊЊЄЁЂЇЊЈЃЊЌЎЏ­Њ­ЏАВЕАЏАБГКДА­Є„€aJkі‰§„џIЬj>Be€Œ› žЄЋЈ­АЌЊЏЇ—’ƒ~Š…pL&VРзЭНУУКАЖАГИЙВЕЖВВКГИКЗВГГДЖЕВЏЏВЗКГЏЎЎ­­Єž€›Ÿœš™“ ІЇЉ­ЎЎАБЏВВЌЎЖЖКИЏЈЅŒ€aJiі‰§„џlіЬQAG^{‹‘™žŸЈЉЃ›•‰‡‡†‡zXЗ9РшѓхЧОМИВГДЖЛОЕАБДЗЛКГВВБПЛГВЕВДИЖДЖВЈЉЋЄЄ•‹„„ƒ‚†ŒšŸЅ­БВБЏВЏВЗИАЙИЏЈІ“xdWiі‰§…џkєЧ^>H_l€‹“”–—ˆ…„……~eN•мдdВујїцЫМЖГБИЙДЖМЖБЙИЕИЛВГЕАНПЗББВИИЕЏЉЋЊЈЁ•ŽzxurqmknuŠ’—ŸЉЎББЎЏАДИЙБЏАББ­š|j^iі‰§‡џГoKIRirx}€~]{{~|{wjkЄ™кмЌЁлѕћђнЫЛЕГГЙЛЕГЕДДЛЛЕГЖАЗЛДЕЛЖГЕЖЕЖВЋЅЈЃž–Š…}xsnkgb__bcn}‹“ЃЋЏЏВЕДЕЗВЎДЕВА pbeѕ‰§ˆџiПzOAX^ellnnpqrl`zŒџџмЄ‘йєћљыбЦЙЖИЙЕЙЗГАЕЖИКИАДБИПЛАГГЖЛЛЏЏЌЇЇЂ”Ž‹‡…uja`[ZXVURTbuƒŽ” ЋЏЕЙГБДГДНЙЏЎЂ‚uf_Хёˆ§‰џд…IVZbhg€ijkZFА€џUюˆ€ЩїќќјхФМЕЗНПВЕЖДЕЛЗЖИИГЛЖЗМОВАБЕЙЙЊЄЁЂЃ”‰ƒ‹‚mYKEFIKIFGNHIXqƒ†’Ё­АДГААЗЙОКАЇЂŠyl[Oеˆ§‰џEћѓхF2?C880*mёяэїџџћk„Цѓќ§љьжМЛКИИЛЖГВДИСМГБГДРСЗВЖВИЗЏЇЏЅЂœ‘ˆ†…‰ˆzcIDH€JSXVNF@?BLfx‰™ЃЃЏААДКБДЖВЉАšnaGвˆ§‹џ ќЫХЩЪЧЧФУз‚џXф~Цэќ§§ёмЦЙРРЙЖКГЗМЙЕНЛДЕЗВЖПЗЏАЏГЕЎЂЁ—•Œƒ†‹‡}mZPFpЕЭЭЯЯЭЧЄa:2:L_uŠ˜Є­ДЙЛЕЏЎБВЕЄ‡obNyЫ‡§™џYнЂ†ЪюћќјяйЧЛЖЙНЙЕЕВЗМЛЖДДГЖЙГ­В­ЋЏЋЇЇЂ–‡ˆ†‚„}sh[TŸФряџџў§ќњхЛЂ„K=HZqˆ‘œЉАГИЕЎАДВДЉ“zeWPГ‡§“џўў€џ8мЄƒНшђюхкЭКДЕЕЏЕДБГЕДЖЗДЌАЏВЖГЇІЃЄЉ •Š…„€€ynb_Zf’Ъђўџџўƒџўўп–}WJWq€‘ ЇЈББВЖИЏЏЊŸ‹p`Nл†§“џ6ў§џџц‰jœИЮЯФГЃЁЁЈБГЈЋЊЌГЙЏЏЎЋЉВЌЉЋЋ Ÿ›—”‹‚xtw‚€zoaUctršƒџ§ƒџ€ўџџiVQm€›ЁІ­ЕЙЗБЊЉЅ˜ƒiVT˜†§—џeJTezŒ–ЁЃ›’œž“„…~jšевшљџќЁњ‚џ1ѕв­ЂЦЇŽ‹šЈЕЕЖЗГААВГЗмЊvVaЗьщТ–‚>28ETYhlg`_j˜ŽuТНЋЯ€џўџ§ќќћžњџ.ўдž•Л­†‹žЊЁœЉЗНКГВББЖЬЭ’_gЙѓўј№фzhJ.*(G\hnfiЊІƒџўЁџ§ћњ€џќэ™–ИХ“q–гьЎdЁВЙИИДГИЗкБu[‚щ€џ§ыщžQGP”Ъъєщёјћќ‰џ€ў™џўћњџџўьnЅЛƒBИтяёе€a^mŒД€ИЗБШзЕmCоџ §§іэыьєњ§ў§ўў”џў“џћњџџу™P—ГdЧыјќћєгИ›|ao…ЋЇ•–ž•sDо…џўўВџќњ џџШQ^ЕЂ‰Г§§ў€џљюнФІx‚““„‚„€|ыМџќћŒњдАЎВ­Џ­ЈЋЄž›—™мџїИ:•КІИџў…џ §ѕиЋЃ­АЊЈЇЃЋпљœџўџќћ‡њюЮЮаЉ„‡‘’ƒ}bMC:(7ŠџвЈ[ЮЄЯџўŠџўўџџѓяѓћžџўўџќ…њэчЬ„Š“ЅИХбелдЛlL;0""6џck‰в|З‘џўТџ§…њЉ†˜ЕйяїїіыыфП‚XIFD@:?ЛџNjЉБаяИџў§“џўƒџ§‚њхŸЋЋПмыѓјњіяьдЌ[QZYMfЯаэџSxПнЃџћјџўџў§§‰џў”џўў€џ§ћњтЖ™ТзщіњњєюхзЧ{`R[^TxС€њџZ}ЛYв™џјсПЛКМССНМКИХПНОПЛЖАЎЎЌЉч‘џѕчзЫЧЦШсџўў€џ§ћ€њоМЎШыїїєє№фкЪБ“p`ZZcUwНњџa{ІLЯ”џ"їнпрсеЌjegmy|sprvvpqsj`SJD<0ЊиидвѕŠџќъЪЎ–‚zqrŸмцќџџў€џ§ћњњшЕЎињњјёшфзЧКІ†i[Z_b]gХ‚њџ}ŽЇSб‘џ§ќ§л[elq}—˜ЌЖПаржва€ЭЧУУТЖЂ˜Œq[HEA1%Эќћќ‡џ №–Ze†™™}eRT…ьў‚џўћјјЂПь€њ№хйЪЕœ…uk`MS_]Jкƒњџ‘Ј^д‘џŸ…‹‘‘ЏНЫйннсуфчъшси€ба€ЯЭЪЦПГŸyfZLGZYQq†џўуJZ‹КйцЎ~`QN~и‚џ§нЇЈбыїїюхлЦЋ•Œ„{qi_Z^Xgлєƒњџ“™œ_еџ№ЮЈИ˜ІПЮихъ№ѕєэьшфумлйебЯб‚в&ийзгХГЁˆjaYN@G„~Тђўџџњјл•K…СцѓјИ†gVGH‰в€џщСЂЂЭэјіюубЖž‰~yvn_P]YOk…њџŒ‰XЯќ‰џјМОН­œИЧођњњѕєєђэтпкзж€аKЮЬЭЬЮааввзййигаЦВ—…se[TKAyŸЅЇмѓЩ‡^qКшњјёА‚j_Q6K”лфЎšŒœЭђљіюсЬЌ—‹†…xurfQKWOgЌ…њџhmviбћ‡џњэбu‚ŸМжяјњљіішффсзЭЦФУП€МЙЖМЖИКМТ€У?ХЪЫЯЯЫТЏ”tn_UOG=A•Лy=bЇфјњѓнŸ~oh[5-[™ЋTb‘ЫњњїьнЩЋ‰ˆˆ…€vsl\ISNbІ†њџJSpЕэ†џўјфИ€žМт€њ[љљѓъщдЬЩЦОЗ­ЈЃ•ŠŠ‰‰Š‰‰…ƒ†’––—žАДУЫЬЫЬХЉ‹vmc^WQRC/0V рѕњїъЛŽ„}n[5#7XjvДфњњљэйПЂ‰„‡ƒ}|ypdUJZSГ‡њџшщьѕ§…џўэЄ’ЊПъђї€њ[№чнвШНЉ™’‘—‘‹‡…„„ƒˆ}…„†„‰‹БКСХНЈŽ‚oifRMLmЅміљјяг›ƒ~ylX5#/T‡Ефїњјюк؋‚€~|yyocXONСё‡њŠџg§єБšВкыјњњяиЪЕЃž““ˆ‚‹‡„‚†„ƒƒzz€„„ƒ{{}~}‡}Œ•Ё­ДДЇ†skbo†ДрѓљјэаЅ…~xvnR()U—ачѕњїьзГ™‹ƒ}~}{vvhWMKAЯˆњ‰џZоГЗЈСрѕљєоЦЎš‚xvytw~ƒ„‚‚€€ˆ„‚~z„€|z|€€ƒyx|€‚‰‘™ЃЊš‰€„ЇЦхјљјђкБ‡}}|znI'DŒгєњѕячзНš‰‚‚€{r[KES—фˆњ‡џiємЉЁЬэњѓпЦЂ}fa^bceffjry€…€}~‚…ƒ‡‚ƒ„z|~}‚{~|y{zš ЄЌЛнёљљї№зЖ”}|||d=5tРєњіьоЬДЁ‚~€…ƒ‚~nRDA[ђ‰њ‡џiаƒЗяјљђе™lVNMKBLPU[[diou|‚}}‚ˆ‚‚}~ƒ‡…zz‚ƒƒ€yu~~{~}u~‚|z—ЁМсљћљјї№зІˆ}|yz~pP3UЌхјѕъоЪЋŠˆˆ‚€„‰„€}|wjPD?Vђ‰њ‡џ[w~ЙьљњђКsNNE;554,1:CL\_dmu|…‰‡‚z|}}~ƒ|€ƒ€z‚€|yy~‚zpƒ“– Ящєіјљјїяжž†~}|ypVCIŠЯьщпеОЅ’‡z}‚ƒ€{r_JB=Pё‰њ…џ^чy„Оъїљн­wPD:@JPM@;6229FU_emu„…‚|}‚‡†ƒ{{‚ƒ}{}|ƒ‚„…‹ЎЪжуяїљјїїъиО›Š€}|{wyvldsАжлШЗˆ‡y„ˆ†€ƒ €~zr^QDkЮј‰њ„џjиwИчњѕсЂxW@9CP[^S;006<80EW`iu€ƒ€~‚„€€|z}‚€€}z{}‰Œ”ЉНЪоэѓљљјјєшйРЉ•‚€zz|}z„ЂДВЂ|€•ЅЂšŽ‚~ƒ‚ˆˆ…ƒƒ€~}zr_N?x‹њƒџkе`ДцїјхЎpYH::Y}Œ€aQB:=FF.4EWdrz}~…€{}‚}|}|~…‚‘ЅЛбэїљљјјєэхжНЉ”‡€}|wz|~†ВецхеŒdZeyˆzƒ‡‡…‚ƒ€~|yo[I:u‹њ‚џnє‰MˆфіљюШs^J<9EmИЯВ„Š~\BAVC+.G\hw~ˆ„‚€}}‚†…}z|}„•–ЉЮэјјљїѕёэнЭКЁ…~}|{{yx~‚ИьљјяиgH=Wo‚€‰„‚‚ƒƒ{vjSIeђ‰њ‚џn…jхњёсДz\M@66Dm–Ÿš–—’mG6CVJ;>[fq€ˆƒƒ~}‚‚ƒ}~‚ˆІецєљљѕьрПЄ˜„€€ttuwxxy‹Очєљј№жЏ‹yp\C2Go€‚Š}…†~‚yskVQA:bђ‰њ‚џn„Пєђча m[FA95Eu’ŒŒ†hF4CX]OE]fv„ˆƒƒ‚‚~|}ƒ~ŽЊЩтчьяъкИЈ–ˆ€xvxxvvwuzЗЮъљјјыЩЇŽzt_A.Em‚ƒ|‚€„‡„}zwuo]UB7]ђ‰њ€џpѕД€ЁлјьмЙjV@?72Dl{xrqqm]G:00HPWXV€UXPJMdlm`T_z†ŒŠ„†…‚~}‚‚‘ІОЭЩЫФІˆ{€|~zrtrrw{{’Лсіјѕ№ълУЁ„}}zutiS:.Mgz€„ƒ€„|~~ytraSICYђ‰њџџ§фcˆЧьчбВ–€lPB5+4SDOXY€U!PP_„„{oe`‡Œ’”‰ƒƒ€}~ƒ€||‘АИЏЈЅŒul‚r#tytu~–УяѕэъђхеКœ‡„}{yyx^9&:iv{„‰„}€y ƒ|tr`SLFYђ‰њeџџХKkЁгшк̘sj[A;02S@?LWZ]]adnˆŠwduЙГ “„ƒ€}~ƒ‡‚|~—˜ˆ†zmgeffnuvtzЈРаЯШРБІЃ˜’‰‚~…~}‚€^<,4Opw„~‚€| ~…wsjZOCQё‰њtџџО5gЊисХЄum^B=89EAISZ^dkuz€•“‘vdŽаЦЈ‘‹€€ƒ‚|}‚„…€|zof``eenos{ƒŒ–žЁš””Š…€~{xz‚}yqX;1;Pdx~ƒ„€{~|}‚…y|{uu{`RIF|нˆњtџџС=dЏиеБ’…|raJ;@KHKU^fquz…˜ЎОЉ“xxДпб­‰€~€„†ƒ~ƒ…{zwtsupskgleiioy~‚ƒƒy~€€xyxz{{yz‚…‚uNCTku‚€y|}~‰{yyxw€hWMC9ЭˆњџџФFjДгХ Š‚‚€wkV;E€]_emw„‡ЁСпоВ‰Ёкчг­Ž‰‚„†ƒ‡‹|zsoptt}xqrkggjnpvy||u~wz}tutv{‚†ЄВЙУ‘hfv~‡}~€y}|†|{}}z~r]NF4Ьˆњ6џџЦNvЖФЏ‘Š‘x{waBE`tx‚‡‰‹•БеьчР’p‘вырЫІŠˆ~‚ƒ~‚ˆ‚€yyzz€y;qtunyqjefjjorrttsvz{|~}ŒЎеюљњіж‰dgx‚}z|yƒz}v~ƒ{|gRF3Оѓ‡њ6џџФGjЃЅ“‹Š‘ˆ|skgWIVŠЊœЃГПЯсоЬЛК|›ЫщырХЅ„‚ˆƒ~ƒ‡†}}€€} yxyxzƒvlmoq‚rv~‡•ВЫрюђѕїљљѓПTG[x}{z€}z}|}ƒ‡€qwkYK3CЏ‡њ6џџФFh“‘…‹‡}uwl`VS]~›­ЙСОО­›ˆЊеяѓъеА—‹‡ƒƒ‚ƒƒ‚€‚€€};|zz{y}{vqr} ЌКФЮдилнцяѕљјїї№кВ‹qXB=^t{~y||z}…~}€~suiVD39Љ‡њ)џџУElo€‘’ƒ‚|{ƒ|l`WO\tƒ˜•†„”ГмђїяуЦЃŽ††„ƒƒ €ƒ†„€~€{|7yy‚‚xqu‰šЇЖЦдрчь№№эышйбЩКЄŠuo\B2Mix~†zy|~}ˆ‚}{zuqcP@3;Ћ‡њ+џџТ@hjwˆŽ…}~ˆŠzoh`XZcvŒŽŽ™ЈСёљїятЯЎ˜‰ƒ„‡†„€ƒF‚„…ƒ…ƒ€€‚{|}}{zy‚…}olu}…œЇБЙПЧШХСМЉ ”†}|tq[>/Cfx}}†zz€ƒ|†ƒ}z|un]K@3>Ќ‡њNџџП6Xcmz‡ˆŠˆ}ˆ†……~qsССЧцњњјјютаА‰†ƒ„ƒ‡…ƒ‚ƒ€…ˆƒ~€ƒ‚~}}|z}zy|}oqrrt|ƒ€}~ƒ€†ƒ}‚€|umR4*>j{{y|w}…†{{|‚ujXH?3CЏ‡њ1џ§Л.T]fow~‡ŠŠ„uƒ˜˜ŠŸЙЬббфѓњїээмШБ˜Œ…ƒ…‰ƒ~ƒˆ€~…ƒ} zy„vprmisu€€€}~~†}}{€ysbD+,Jjyzuy|}€ƒ‚{|}‚u\KA;7DЏ‡њRџџЛ*IXcnz‡‘†{‚€‰›­­ОЫбввзкруиЯНІ’‡‚ƒ‚ƒ~ˆ‰}~ˆ}~‚‡„~{{z~zzyyz{tg^dprw{xvyvyz{~€‚{gP;01>fqwyy€}|~{|ƒywpTF@;<­ш‡њ=џџт”BR]iy†}ƒ†ƒ…†‚Œ šБТЫЯЯЫЩЬЩБЌŸzxz||{~…ˆƒ{ƒ‰„€„ƒ~z~|~~y |…o]UUVX`eins€tsrjaZQ@0-7G[vuwz|}‚~€{ }†…{ofRF>8Cаˆњ€џqх9644AScp{wx{|y~|~‚~‚„}k_ND<9Gбˆњ€џqщ\GLXep{‚‡‰Œ‡~†„“‹~†‰”‚|xsmgf`dko{~€…|~‚ƒƒˆ„~€ƒ‰…~z{y€ƒ}w|zxtmbWQKD<7750*+5;@HR]iuzuyy{}|y|{}„Š€|~zj^J?=`ђ‰њ‚џTСf2=S^fnw}zƒ‰‰‡’†„ˆ‰†Œ‚yuoh[QKD=>=HV]dgovz‡€}€€‡z{}]RIKНэˆњ‰џг=@?@BAA??ENG:Ќ€џUэ€cŸсыыуЦ•‹„Ž~‚€‹…ƒ……~Š‚„‹Ž~{|‡ˆwolmn_NKRYOF@:5:<=:55<68BQ\^eox{{|„ˆŽˆ{uzaUMB=Юˆњ‰џhћѓф@'/1')$"gюэьїџџћdsЎмыьфгГ‹Š‰††‰ƒ~~€†’‹|~‘…~ƒ}†…{rzpmgZKIMUVLE66;;<9f•ЎЩЩЪЪШјP+&,9EUbikpx€‡Š‚{z}{cOKO[ejnsx‚ˆ…|vuxtcPBDކњ—џ^1?TPHKV_ajklqxnmqvyzupmo€tne]^^WMECJNPMBBLDN›фіљћ‹џ§ќџџјєЊ,>R\chkwy{}}y|~wdTC<…†њ—џДT%.9>AFJKPQSX][`bcfkdba_€XRKIJJGFFBDFFGOT—Фжщњўџўўџ§ћњщА/8GVbhlot{}|}zrhbPCM„…њ—џыН„Q88BEFHDFKXnmpwžЮ‚Ьяќџў—џ„њ›K54Џјњ–џў‚џ ўўџќѕѕчтшђноопрпотуффъјјњ§џќћ…њьЕ;34;CHM[adjlkiaSK7HžњЎџўўЄџўќћ‡њыВ`=59@DKNPTX\[WROD>X’ьњџўўУџ§ћ‰њїГ•kB9;ADDFHKJHHKLI@9F‹џўўџўўРџќћŒњУ|cAABB@BAB@?DKME:1"0зџќњ эuSE@@DBCE€DBA=33GзџќњјщфYOHJD‚?:6==Ажџ§ћ’њсЩЦУsVPLLI”ОНРРшџџџџџџџџџџџџђџt8mk@џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџtkcvs-8.2.3.orig/tkcvs/bitmaps/dir_ok.gif0000644000175000017500000000017311664612512016515 0ustar timtimGIF89a Тч„„„ЦЦЦџь‹џџџџџџџџџџџџ!ўMade with GIMP!љ, .xЬњp‰A‡Q…Yщm !Ž$Щ]кjЫЖщ[€*SД №Кї>p* ;tkcvs-8.2.3.orig/tkcvs/bitmaps/mdir.gif0000644000175000017500000000031311664612512016175 0ustar timtimGIF89aуoooџь‹џџџчаџЙЂ‹џџџџџџџџџџџџџџџџџџ!ўMade with GIMP!љ,f№=@k•8ЯР{x$gш)ЌЌ@ eьН%0 c Рп…ŽDы сpwŸRЩLфLPЮ€0]ЮO60(˜ _„p6$˜L„њ(jсђ5›gђх‡{57tb|2:)‰;tkcvs-8.2.3.orig/tkcvs/bitmaps/ticklefish_med.gif0000644000175000017500000002545411664612512020231 0ustar timtimGIF89a{„чџ !'%&! .,63(.':*!4-$ >=EC"?;@8-Z/ LJ:=3H8-Q6&UR?D*`G7dVda(``FYM5`Wmj>]ZpK9PYHH1aTHfT@lR>tP7{N7)nn*rcvs4ngIifnBob™M1‚agVUl_Crr’V;sbP{_K‚_C‹\A+ƒ…Œ7xA||=q4ƒB~v™–НO0–~LqZ|l_xuazlžcCА^<‰mWmye”jL3“}€r_(—Š›iIЉcC…rW6’‘oRŸœN‹{FN„#ЄŒb‰x!І—q…w[ŒŽa‚ ЉЄ{„vОmGБsLEŸ‡“cГА›}`ЩkD…†oЂ|^’pŸ[ЦrCбlFЌ|VЕЊFЃЃ%Жš_œŠWžмm>@ЉІЛЛжtB+КЉiЁФУŠ‘ЪНEГБ–ŠН†^~ŸŽ—–{­ŽmИ‹bЩ†VРŠZ,ТУЉ‘xЕncА™_ЏЏWДДQНІ(ЫЫп‡Pr­АйŠYжโPєƒIю‰Sи“]ˆЏЄ™Љ™в•jЬ™gЅЇ‰WФУНž}3ед/кЦХ{lОПШžsдœefУУКЃ‹ЯžviШЙ'упю–fПСtЦЧLиикЄlйЄrтЁsнЃyЊДЏeеМйЇ€иЊu\зкб­фЄ|Hсп*юьлЋqzЬЬЯЎŒиЌ‚ЧБ˜з­ŠлЋŠ~ЯаmздпА{лГ|ˆЮаxиЪ{зжцБ„уДƒежпЗ€ЯКžфЖ‡езсЙŽwротК‰пЛ——дж бб‹ммgэх’кмщСмФЋмрыЧ›щЩЅзЯЖ‡юыГллЌсхЯеЬхгПывЕДыщѓжА—њјТшшЇіђцпЩтциєсЦљсНєцдуюыС§ћўьЯжњј§ђч§іоўќ№њўћџџџ!ўCreated with The GIMP!љ џ,{„ўџ HА Сƒ*\ј‘2H"^™HŒ2dЉaШБЃЧ =ЊAУЇM›1cЪЄљc)%K“^Ц㆙(j"…мЩГ'H3NN–ЩsЈд2_G"­ЕЌ)ЌgА`ЩМbЦŒЯЋX=šQ*С''ЧффЋV­ВL}!]лtZгlЯ =лг&ЂЌxѓ"‚Xц%RIљЦьЉГLЭ.U›щ2ЗЫ Aѓ7ъ$DЎђjцIц JTЯюў‹тЙ ЄУfk>ЅVqкЃгg›– ZфЩЯтqНЙ7C3HRІyьš*9šŒysщ”ѓда“.KЫИЉ2Ч’ГOж ‹яяЭў”f~Њx0”e9_џ:jФдй>ЮЖЌЖфgэаEсОП@3M”Q!—@rIs›ђ‡љТtЉ•Е”tlљт–vаdЃл$`Б‘›=сsЭXb-šемzЖgи‹ Њ…”[6Э4йQЖ ,“№aˆ>Й"‰.рА$Hžв$(Ђx ’(žВ‰sСФRe,X#L,[ ух—[ }Hy#—7fъиу‡@vd #‘HтŠ+j™†‰цi`Bˆ!†l"ш–XnKybЎ&І•Zr &˜С”šоДѓЬ$fШбІBЉ˜тЪ1ти#j!84‘’Hšи' o Ч*ў‚ *%{ыA(!cж"І˜ ѓЭ3йxO<нxИiAЎЄтŠ8ыиГŽ.еT‰„ЄZэ­Іс*Ošиd­ЊНсY2О%Ydѕe“M•_ZљЭ9эdђуІІ˜rЬ:ћиCЯ1QиP…СН$!Ќj;м€ьyZŠNBЙ"kяE8]YJЩHdйVп7Z~љЭ,ѓ‚˜JЈћаSN2Ў$ЗЧc\‘EТйj‹pЕy:‰ЇŠзБjЇ45ЙŽЉЅЬ[ЗiW_,яъђwЉT#*;л$“L*"–Б`УЅ‘Ч['œЊ_лœЂУДBŒЯgMgЎОвJ}b˜fЅнTž+ХрЛNўдR#SХVУЬ5Ж3гќ5ž`›x “LђќИg—Keь’/й’EАœ3[Цйу 7Яx(Qєж)ОєlУ‹1ШР.GЉoBИЖyB0Њ~Нч%‡|Ёu‰Ю=YЖИсжRJЫ`2Œ81ЦСyД0 №їYэРyъ™EэЪЁњвПЮ;3Ы7кp фtЁ@N0ˆxœGвšРq:яљddћ`Чљ‘ иQ-@\3Dзњ„$@bwМC•‰і@‚ @`$Ч6aГГ\ЂEЬ(2 LФў9žёкm2”Ђ "В’Šfi0vPŒ<уОїСяŠ$$XЕц‡Њгb A\, "ЋQ­а–a@cAДР'ьzФX1`qPdb”шzr SаУQл “a ьA В"Џ˜АјбoOКB:*PС(ФЁŠ%чДBЛhƒ ‚$œФ$АЄ‰IЎ`ƒ єЄ^Ђr6ЬзСѓU! "LЋ(IЬvq~[МD,ў  РР pЈP0Tlтa7Ќе qˆ4”с ЋdШ P@$а 9˜ NР[ђ$сc‡д:˜ E.Ђ ЈўшТЌˆ-H гŸXь" Oё$€xцрРƒ<В}yP‰DН‰”4Ч:Ш1RaЈёт|Л<) ЉЯaY(BђP­јmбŸ[Dц)PБ` ƒАpрр ЪA PЏ€€ЭЈA’ЕyšЯRƒнGCБCa'eеˆŠ#ЁBA”ш1Ћ5г<| MЈМlр­pнР рJ5ˆ$ьA!^Фљ’Q R* иІbГ€@ 0’ FшЕЭiјТŒ€р #ТИ„€РA]Р€м5Џ1ўХГш‡9XA [,‘C,A˜ялч‚!0€hдˆbЭ`YјB€к$рИРMƒIРС“XЃ Bp` €э?$q/*DэЃЙXR WМaeј-VMЊ;C|@ @@€<@>m‚‚ёGнЎ’ ˆ0K€€v’H…$цр‚HЌ‚hФbр ф5Хр+ЈцЩ MhA‘hC,ђАр=фNБ ŠEўыXР0мc H ЧpЌ„%,8ЙТ#ш;‡eНТЦH…&ЖƒфUыXжQŽзё"ЏаAўЎ€п&8№Œ е7т)p€~ќ;€РBvВ“;{м%#ЗЩВ @8Ќbт(G–“С‹WlсЏШЈ$B• ЏЫЏx…8p@kZ[РШ=.АЎL  &€АНшФ)ыxндЖ‚Р&nS$\ШЉQ!Y’„V €lЂНŽpŒPˆB@A4@‰7Г04@ЕŽwŸo d$'РИKNtg#œш  пХv#№  Ь—bl˜Ё&58Œ€Р( п"€Щ%…:tЁ‹B№р>*ў’ьcyЫл$АЕ la€NuЫЃ<‡Tт‡$ъ„LƒdЃЮі†Љс ’pZ"‘С .€€ €й€%'` эИG?юЁPd!Я 2№n ”Рфh—ЁрrˆЩР`Р…`@ъЮцЅ$ н‘cЃл= Е:aˆ@єZэСІp№‚X€Š2„[м‚ј>№TœЂ `Вшс§œэЕVћАй.сDиРИћ]*РОLG0рeN!C№ћиG?ќб{”CkˆУІ№ƒD"|л`z=ЅŸ Yи ЈјР’!ў+TьТ‚n њ,˜Пzў€ h}њZЏ§џMРъ/џr–зх€#8ИH,spябІQл@Счћ@|ќ@Црž žA€tMѕTP„eЙ V'a`@5P3`zёіcFVkggІЇ‚gз~@жYУvyПжžЅoкИWOгWOЛз{q ШРірDH|ћх šР žр [€rBЙЗHO% wpy€Нl…€‚эGk№oІЇ7а<РXА†i˜†p Рr-Gˆ&z0З‹,БУ ’№p јB„§P|ўљPл€ Ÿа„žА lуГAг‡ žаPZШolgЅG†cЈ~&pj ‘` ХP ЬТЌШŠх еP Ў Яж aПlЂ‡‹6rIP)аtРTа>˜ лp€F˜љр'“ и€˜{•Hѕ;ЦР В@‚аX/ƒ №‰+8‚І73rр еР‡|ќPі˜іаX&Г ЋP^#А@aТ6d† Ѕ Ѕ&t№ Ша‚јХPЮиѕшс€ к€ Жр€ }CМ`‰"$АMpўqУІ\&0,˜‚эј|B(|Фwˆ§АїЈњШBщўШ:еe l‚6`@зрёА D`\0eA@/` ь0|Ф—ˆф` вР У` Ÿа LH 1dEgІƒпx@ееk1Јъwv) ‚j@ т0`iˆI”BЙ‡YњHю€ ПbV р‹cз3P њ яа D09№yИ xp  I д0 У№ Ш OЈй˜’Д’а†yжkЖjыh) Ч@€`yˆ†™ˆѕрЪIсаœфРœс œЪYдЙљ ЋаVў№IP`Њ ј№ч G6 Q ЦD(|бЈšкP–НР ˜Р„vРЧРхГAА# ~А&aЧd %№ЎРЮ蓆™œЯ в с * в0 J–ъќXф  Ћ šs.`”šy­аI 9ŒЦ0„˜cЉšЭР Эаš˜р œ  A‘E§й˜ 0 ХdаР9rР ` ”ŒYа9ŸŠЁд dЙЅdI–УРЅj дЪщ˜Џаn€с)žчр bI`Ÿ9wсcˆDYс` fкЃЌйˆ›ў с 7Xц“ Р\˜]M*l`=р ыP„%#iУp šк cjІѓЉЅЊйЅЬ  ]Z–ѓI–ЖР жа к  бIП ”p њрфЙ }@Ї‘ аЎPœDIва ФЪ ]Ъš˜р0  O4}цУ ЂРРЄv Pр4`ЩŸŸ№ Žр№Ђ0 Ќ@Ќд  УЎѓЩЃЄZ–Ќк дР Ц: ЃjЏж№ е ‡@ э™я№О ujh€'jp†(–_* ѓй ЋiŸ~a Љ’”–ЄРцЄЦ%l PI„ˆ$ [ ?№Зеў0 З Њ]:ЏЊ9ЊЃк 1›Ў=zГЭ№ П@ ŠpЫ›фY ЙZЇ{РК@б(Ќд Бщ:ЕЬ€–А| Чp2АЃ’ЗйБ:vІIВИ`}?o5RB€0 Ж` к`ІюкЊЌЩЎ;:ЕћГ|№ 9,уАщЂuJ|р(`Q Щ˜ж€ _JЊл XkБ !хА !ѕQШP Й``hй%l@аŒDHИ€ S@P3   0W! x0 <к ж@ЏЭа wkГ;JЏЬЋ 9 ч№FАžS z@I@‚Nў ­P DрЁРЇd™ЗЌjГЬ@БSРAаЙаš{Ђ еБlGP ЮИ­;nU”P•`~W9/рРЉЌкЊЋ)БЦГФЊ ЄРА ,УАф™ ­ a@ЇF``€b@ щ чp3n` Ђ*ŸХКЃУ@Б:РTаЙчѓЕМрJ*ЖLКŒ DШс№ kрVа щ@ ]аDр G ЗЏ  є:ЌУ: ЄjЕЧ •0™йМЯ[ F,НчщЫДЊдГЛu`  ЊякЃЌрqаТ Aх€ Ц@tћЧtš a}<Жў\€ Узь0 ˜0 \ЌFœd  Лћ срФЊЊЅf9ЏП •аўPИq НFœbСNа!O *:žчP i№ЋАЊЄJЏvKЌУ№ŽА-РpPЉ@n !Р’@УЛXh/7Пp€§с№Ц№ `EьШFlЭPРs БTЛРЂкЊŠžї`ИлGpСJ№ЮЦЁ€їАЂй№ьœ3№PNМЊ:л вp У0 РПЬzш‘ iЏ yVР"А‹љ†ЙжР Zp€&@ЩŽ3аP“(€УЋаўЊваЅкАЃЌЪЊ Œѓ0ъТХœС№ьO0нЂ\СD€Ъ*РЯuАЊЦšЎП ЈЃРlа| СьUfМPišАВ- 6<Ћ@ўЌуЋ@d‘l‘\жY(А ПрЊwkЫIнГ\Ёна Њ яœС4рzэœ Ы{чр аKGСd`š` ЬрЅ.=ІСл €ЬЪ НŸUOЂ 26,Хpˆбh З№V0€~ 0ж&№киL;€Љр!кЊ.=ЌЋj      }р6`9№Южћ}@ аиъR H+НІ bў@'0 РНPРzлЃ+н ЭR 1_ЎраЦ№ ŽРlžНЬVд@щ‘УPŸVцЇ­­ЏЭŽDрШ8аф€„!ъЛ;zЗФ:'ЄАrPQа zz@ ‡@ zаELи Lнs%Ћ€ЎРkЌФj ё= xАmу§xЉ№ ЩЃVеŠеќv’@бЬœ=; Z‘мк59Gвz €r ќр‘ЋКвKЌ=k ‘ Œрn€ о9`tŠС[ОхF KJp\А НРЅ=:рfйцйЉCрƒСХА•0Z№7Зў ЖТЦPќP­ки’э%п3в]P ‘RHP ?й˜-­в]Кж>‹#њauрU мJ`я|зNЯЋс”  Ъp5PТ;М;КЊRLЌЦа Н x #0ЄўmW бŒ П@ЂgZ–Ц€кфD]РШОЇš ш—KГ:Ммў ё§až9Pъ{­ж‹dРн€™ Т}№gЎЫФ БЌiЌє9 ‰ б ё— u №Зз!`У’€ ЭЩжœz э€P )АE аŒЃЄjЗ…ЮЊ~ЛРУрфНў žўz-bа|0  аг**žЮЋН&А^РиmМђ*ак`ыЃрv Ч ЁлA–Ч‰а@б`ьЋ€ !zЌ;: р=E9вYКРА ЂЄъвювХЪЃРпІ NzpРВМЬћзЬM б;№ŠнЛ;{дєЪšhє Љ№ЙSј -АЄК P~Щ"žЎНxРXžђG˜єpлUЛЊЌjЗd9Е]šТиЙŽаЎŒьмЫ@ йЋЭаНЯ§\МKои-МЖ@О ‘№МEЏ KЪЬрu№ П0Е 9ыФљМPђ`ƒўЭHшЇžŠРNžЎКфJђiNcОвKиFфtzЪN’Ls№ €:ЊъЪ НРЃЌ  рBŸu`ЧŠ;ZP­lw\ №1Ъ–4iдЈ5k6ЬЖrфъёЫЧ_П~ѓ‘ Їš6mЭЄY+иQ3ЭЌQ#9lТU‹Њ8qBУ #J’1’„І0hœ ˜РрEЃaЬ†ul†”Y3fЌ0ŠѓOъTЊSƒCfЬВdШВJЊ`€€Б@pСГJлЩ‘Ѕ…sчюa>Кuнa+И”™5ІkMz4ypщQjУz^хІGOžРь‰ 2hžєи Т8‡ўьХŒЄ5fЗXђ3ЅъjЉ/Мrѕšlы(Ц РЭ žF'I&еHnмzqЫ… '™­^Э‹LMšIх=ЎDјk*.<œ8Ё‚ёb'6L€@a—FЁ{)п;LzС^Ќ4ЁfЭКЃзZcЏbpа€МH+ЁŒЈСF:ƒГЦф’3l”QyФBыP2H#СЄбHЅZIЛFр№"0ш  :Р„„€Ѓ‘_TТ.!fz1ˆjlaх“@˜Шo5Ии֘Ў^ѓЊ n €AˆD!I›ТС$-1в&œЂXХ‘DиHdkДщЁ5Im-~ўЌ™а6КЅЙUiЄ?Ye•_j34[ŒjГ/бšzJ‡"WƒЁ˜­Мт^xY%† 7  +х(‚N"GKТAЩ([„\C +№՘‘ІЫ‹ ƒšЙEЙ7ђ Ё^: !mn,1!„t,Њ љАsъGЅЊIb[вЋTМ `@O P T-2lŽšpиbаK•>бЄ“@t `дW’‘/bлВИe“њqЏЕVRШЈчp ­з„ZUJЙUYЃjЉ‚РhВ‚Эвd Ё€o 0€ „РуV˜ЛЮGl†Ї8qЗцx՘5зьˆЄ -ЃЂіB !8›ў)LйalaЮечНЅЂ†еFЅ йpјсЉ`0eвIНzХ‹ЪР:ІdRYЩѓP•Lc—eOжЈР8ЖЉЇmШiѕЈ‘6т7)ГdjКƒœыH•G|”[†кgjк ‰зQи€ZъЉ0рЏ+%™lƒКцXƒY-Te ?С“Na‡@lѓkB‹Юш‚”­БЩ…,тА PE=B˜C†nQMІ,V€AЎЇ PБёŠ$JZVIP@n$PX X@0С>`0€+ТЖАШБсŽ@™ТЋ>MиМЈ)Џт ў Ј ЃuС‹`I•ЋшPѕФ П#Л“hnF!Œ Œ‘јf*ЖbŒmQABСЋi]tвcАУЭю8 o \ `3 Цœ‚<ќе3Ш№Њ•;aчVXЬљ&ТvP„"ф КИИЇЋQŽ˜QjЯЂрD и*5 |R М’ЪXб<EЄЪКŽ$5;-A“Q 8ШУdvјЋ{ЎtО?мч)˜о§Ює%|с oФ!ў€ˆcЌƒj”D$жёWМ}вкњѕV€ЧHA &Nпy‡А \ў щ Т1ЖбьTќ€.И–‰ы „9пчћˆРtоѓО№†?D q‰S?ŠhМ=иБ lQAХpЛы[ ѕ”Q*›БчЇРks ?A$„єЌ`Ёl‹~ЋЯm4XоdЎAюwПƒ.о№_HCўБ@\ТЈ€X@>sАzи†4B0;Пh ˆШ<ы› [1Šƒc˜–‡q№Р&Еы+u‹mи†bp №0žKКЅ+‚,Ј?=РП?8„7xH HШJрƒEh<~А‡rP#lAZЂСŒ@ \1\л‚ Д>rsў+Бт4}ђ ‰AмSр p PКњы‚пУП4јƒ,Ш>0ƒEpq˜‡}0Р4ТЌш S€*‚№kB'Djщ@ р1QЫ–­*к†r`q8†H€AcХШёhŒ*:HWX‡:ТDРў@0ЎxiЃ>ФU|˜є ‚šl(E3Av`‡upМj8†]t…^мEqX{и‡,<ф Eє)Уik­ЖbХg|”BИРW8qЈ]ЬЦmфFm<†js˜y˜‡{˜uИu˜‡u4lЬFwЬЦkьЦm,†b` ‚ƒhфЧ{H‡ўHtH‡nt0ШƒDШ„TШ„L\Ш‡„Ш„ь†nH€ДШ‹ФШŒМHALp‡kЩqp‘IpрTЩ“ Щ•4Щф†qˆЩ4In8I•ФI—єШ‘ф†‘фЩ’4Щ ЪЁ$Ъ—д'фU ZP…VhЅtЪVXЪІŒЪІœЪЅ|JЊЄЉЄЪЊ|ЪЈЌЪІЄJЅTЅмJЇdЪВьЪЅdЫЖtЫЗ„ЫЋ•бЌDKФ„HУ/(…t@ЭЉ<y­RU˜хR" ЕфбSm™DLЎ|ƒ7}ƒq˜"й7§JЇфQ;еRЅвТKЯ1нJп‡­„ЪГФVjЉЫkI“$IЅНXgЩ$S7]Uˆдн{Se Я‘ееЊ…‡Є5IжЄ…nЈKd]в/ …4u†f­вVHg8ЊэћtђД[ш\Z<кG‰ZдЄЭi@MР-г%­‘|MZ(гPэ‚­ед\гž]–%ЮзTкŽ=‚(<=(зр\Q7§‚ž„[Њ]šКў#шЯк\жМ†Н-УФJkeЫ0PT†U†VPЬYјXZнТEЬ>а;§‚Ё ЭИЬT]Jм5;екЭ„I­T М#шƒЖДнЅdнќpиН„d-М•‡ЛМtEе7ЈK,UТ-пјєЫL№Zнк6eR\НЫюдT­?нг>uТkшаЭ]†•X§пJ-М“]Ъ>Hо*=„кЪГьпВ4УMm…0а€}UZАЄ„J AJ§KвЊМ^жH‡Ђ|ЩпRJ“,гњ^рг5mп*й’ЄЩЂќШqи^uЈй†НIшМ†žЌ@0Мy­л›\]'Ъ#Ўл&vўк.Щ&&гЎЭмb§‚CЈлYU/нMЅѕbЁ”bgPю5МLжT]ттTкŸЌ„ym&NMˆ-#nZИlЪ0Hр%рTFeЪў‚V˜…(ЕйЅZ=аЌœЪ" =˜…„эcЊ%‚-.<™}ЫUJdнW_Mи^ьеЫэ]в•ЭЫ3pг.(x€ж}­„эDV;-‚0„ћь†L8‚-LШKPЎR=€R†ежthзXž_-u^‚ЭKNЎ УЫГЌcйнд.Ы~cUЊЅWU 3ЅU ЋаPЦЕйЋЄ„<.Мbu†iK3цRn6И ЈY~}KdІŠЪiўу‹]вRЈсkШU;Ѕ…ђ$eу]Sч§RuбZ о/žZCеАUк3Hо&Eh%TЦg|NbКМлJ}Y Щ2hюT}-щ5НUC=еЖѕш5х^Bх˜ЩAн*mщ7_ ІчЉЮ›-ZДьƒšU…Ÿ–бj6gЅцI§‚–mJи`T5жt8KБЄцSеƒ?р†FFЫ"]iT]‚CˆЩўэiЉ№dНЬ оXНф†A8„ ежю<ЭsfU'Kђяёію№Foєюа­tяБ,o}mONгŽO‘юшЛняћOщpџp§оo5§Ызл5Ыай^mpЕDmZ˜В”№ ?^І4mВдlВ|№Y Ч№ ЯO їUЕLdOKёэ0№#€ёё}ё?‚Пq†ёаѕЧёџёђїёq!ђпq"Зё!Џё(џX$а)Пq"Ш€;tkcvs-8.2.3.orig/tkcvs/bitmaps/add.gif0000644000175000017500000000027311664612512015777 0ustar timtimGIF89aђ’мHИџОООŽдџџџџ!љ!ў Imported from XPM image: ,Т’мHИџОООŽдџџџџIhКмn IŠИt‚Ї@јСЦ5q…# ЎІЎj ‹уRžДs_|Ї-TmH,>"”\m'<Ю‚ГмЮчkB@іЃd0ЛœЏ1j,&;tkcvs-8.2.3.orig/tkcvs/bitmaps/dir_ood.gif0000644000175000017500000000156111664612512016667 0ustar timtimGIF87a їџ„„„ЦЦЦџь‹џџџџџџџџџџџџ!љ, N@ Рƒ Рp€€ Xиp3 РБуХŽ ЈhЩ"I^<™’сХ—&Жtљ’фLš,7žмys'Уž>5 ;tkcvs-8.2.3.orig/tkcvs/bitmaps/man.gif0000644000175000017500000000013511664612512016017 0ustar timtimGIF89a€аџџџ!љ ,4ŒЉРš нM ьЅzg‰qHˆd4eee4ызЊЛy.\ПNiнšп“§„СZ‘Ж ‚”†;tkcvs-8.2.3.orig/tkcvs/bitmaps/ticklefish48.gif0000644000175000017500000000464011664612512017552 0ustar timtimGIF89a00чџ   !  '&&  ., .$641*#,-$7*"=+7. F*A??=K1(->8?7-LKH6)E7/X0!:=5U3%P6'US PP6KD-NOW?1,QGNE;@MCg>-_]aB.VG7JO@#`Sca_G8c[&_`0^W<\]zE0rpnP;cTDjQA…G-sO6zN9-mm’G-8ngEkZ‚pCpe‚€Mkj‚vYk^6{lsbO‚^D’W>|`K‰]@ДL-=}y9€w2‚€qj`™—ŽN}pVyvЊZ<–~Ђ_=—‰ss\ГZ6 aF•fOQ€€jyk…pW‹nX–kK8ސmRМ]4Fƒ ЈЁІ‰ ЂŸC•Š]‹|M“€UŽИjJŸwTv‡}ПnCВЏ>Є‹ЎvQ™~bށpЫkEЁ|aЕvK…ˆrCЂЂЖЄЖЋi’–y{Љ€\#К™лmEоn= ФЗЛЛcŸq›ŽлtAФТGЏЎ™†Ђqж~I^Ў”в€PР‡\ЎŽiЖ‹gАpЛ‹_NИ™Ы‡T/УФ.ШВ`АЏСŽ]#ЬЫnЎЏђFWЙИсм‰Љ›йФр‹XиYїƒU.ввЃЅ†SУУѓˆP[УИbППЭšhНŸ|з˜^ЦsЦ{Юœsгf6изо˜fм˜lіŽ^hХХŸЏЈLйЙsФХн k‚ПСLиитŸrлЅm>роВЏЉєšgлІtгЉйІ"яэиЊuУЏœЭ­_иимЌq{ЭЮз­ŠмЋŠ>ьнrзСkиЯкЎ…zдХпА{oиж‡Юа—ШШŽЬЯмД}{изфЕ€рИхЗˆеиГЧОтК‰XђжсЛ——еифМ‘’кнЂеишРкТЋbѕѓžнрэыэШœ“ыйъЪІЎррйвЩЪлещвЙњљєзБМщьгщцГљїћуОљфЦѕхЯбћњџюЮўѓмќічщ§њўћђ§џќџџџ!ўCreated with The GIMP!љ џ,00ўџ HА Сƒ*\ШАЁУ‡ЛH|Б"Т)“т :Хq’)бѕoŠ™QКŽƒ%­eВkзšЙ ™ˆ™LцЊ вK—ЯR*[ƒI"Э‚DЪ”Љ’ЎЅO{іL)m(LhŒhњ)ЗШ™C•2СAtˆщ(X(SkYuиА^Чдщо>,‘Ь ‚SцLЅАЃЮNѕЉv-Ьkш†™Уу№о7eXЮ *Гч/иА•PІѕщєLЫ8‡ЏmЃЧАе;eЯК\)чя ПMЋДјƒі,a]&ўйњ]Kбєf"дї Е2=‚)џ[щuЅSΘшхSАЎfLb4iR„ї–ў)/Žr8’џ…%(С‘/Uܘ‰ч‡‹яьŒЦ(Ъ DƒTЁРЎ1'HŒ№… .ŒAЉBG0ВЬбЧц™…pXХ‡?(@$`‡*^ДвˆЭБN1ЪШЁ(№СƒŒ2 (|0 ˆ@ €€Х<уˆ.<ф2@%аŒ?О @№ˆB ="@@ @_< 2ЫАHP1­ЌTŽxуљ№CфF=N№A `‚љ (Р3Ы—AїјгЯ:‹Ь2а:у)ГI№Щ#˜њфmД­ˆ‘С0>;„ JЪшšŽ5̘Ц@ЇŠаh =”у?њи“N:фƒMОщиS1!?ш0бУпа›M6Ш,bЅЈ-S‡‰@AИў№’J*иXc 6кdƒM4б cM7ё т:MЄB;ўф“M4ж cHAсАљLŠ2š0(u„p ˜гЩШDCЬвyƒ<Ч8ЁЦ1ьJN7йXѓЪэ'Œ@Р#ўЌТЗ„ёAЉX“Е6б`“‡?№ C‹NЄq ўСгM75‡‚AX,sBЎP%v yфЁ@˜`н ЭЕєЯнN‘†'є уФshу11˜49 эГL(>G?щмa0€CС\ 8)мhТRHС :шбr~M6Љш@ sУJPL=Ш`‚;ь0ШњЄw7Њ(2>0оyKбТйР}Д5rФJ $@­Ик+T0sьZс˜йпjFŒGHRр7ќAx €C6xA‰‚GТ(№Рuƒ’PD-,ŽўЭdй Fšр„0ф! x‹Сxa2mиЬƒў‘ZЦхb-ёа‡уaBkаЬc4S„ŠP„&H! …ˆ$’і7bЂkxвЂр‡Є=/_кр&0сЖПaуh€CFn  Ј ˆФPрV‘…‚и S‰ˆf m#'\DкpВ“! +$‰‘ДЅ•Œ\жpФ в€*УЧR@ 0ё ^М"І@‚єš™ЌpыУ^ё L8‚–ЦSŒ`ha e-Ш Дp‡74ТI@ждЉr$!NрыЮ@5DbO+2АФZФТшдx–qHС+0jТV.Т>cІ Iк`œеŽЧ^HС/їУ&a"ЗG$ )6…5IЯwˆ дщ(yB”РŠBРЎPj„ё‹, Лб€иР†œ lС€оŠЬ—ОѕЭЏ~їK“€;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_plus_kb.gif0000644000175000017500000000020511664612512017734 0ustar timtimGIF89a Т„„„џџџЦЦЦ’мџџџџџџџџџџџџ!ўMade with GIMP!љ, 8HмЎp…I Ъ:AР‘'ZV’Р0жIЉU1jЏLшнp>W0ŠL1JсJЇ"jИсuŽЈ,;tkcvs-8.2.3.orig/tkcvs/bitmaps/checkout_opts.gif0000644000175000017500000000034011664612512020114 0ustar timtimGIF89aуџџџЦЦЦчаџЙЂ‹ŽдџHИџ’мџџџџџџџџџџџџ!ў% Imported from XPM image: update.xpm!љ,d№ЩIЋН ƒ‹С‘qPœƒІ‘& 0 l–w)Мs§§ЏиЊ ќD:Ri’1Œс•&—ЬiBaUr2лnєV 1[Хћ+дgL xЋз$ §Цчѕi~€‚ƒzl;tkcvs-8.2.3.orig/tkcvs/bitmaps/unedit.gif0000644000175000017500000000024411664612512016535 0ustar timtimGIF89aТяџџџŽдџHИџ’мџџџџџџ!ўMade with GIMP!љ,@WxКм№Р B@И#К@љ Ј5 0QЈu=ŽD.q§pcy~n.ЙиьvA…ŒиlЩ$}С`JЭр ЇдuИыљ’ rI|‰Ш@B5гтdа•­жЌ3;tkcvs-8.2.3.orig/tkcvs/bitmaps/dir.gif0000644000175000017500000000016111664612512016021 0ustar timtimGIF89aЁoooџь‹џџџџџџ!ўMade with GIMP!љ,0м€™Ц}cމЁк|‹ ˆ•вhІhZЎ,цО@Ъ'mпuФЖ/О„ЌŽ‘S;tkcvs-8.2.3.orig/tkcvs/bitmaps/log.gif0000644000175000017500000000016711664612512016032 0ustar timtimGIF89aЁџџџџџџџџџ!ўMade with GIMP!љ,6”ЉЫ cT и‹- AцЮ}й–H&ЉœgЊ‚Ѕ+С*{аuЏ”Žтѕd­!1|4–ЬІ ;tkcvs-8.2.3.orig/tkcvs/bitmaps/dir_plus.gif0000644000175000017500000000020111664612512017057 0ustar timtimGIF89a Т’м„„„ЦЦЦџь‹џџџџџџџџџџџџ!ўMade with GIMP!љ, 4xЬњp‰A‡Q…Yщm !Ž$Щ]к)ЈlчТJЙј[ХC>яЕ–n” 6U Фl2;tkcvs-8.2.3.orig/tkcvs/bitmaps/who.gif0000644000175000017500000000040011664612512016034 0ustar timtimGIF89a„чаџŽдџHИџЙъеж(8Ђ=UŽ(8„=U’мС†QqRЃт˜Ќ‹“=UmzЊЂVVVџџџџџџџџџџџџџџџџџџџџџџџџ!ўMade with GIMP!љ,kр'Ždižh:, ’lћQьaИ)€ю@zР“А ЛEВДдЭ ЦІ+4‚GйЎД@˜W†8ЅPБXо№8нюО,nx&6/‰‰  Š$:Ž"“—˜!;tkcvs-8.2.3.orig/tkcvs/bitmaps/branch.xbm0000644000175000017500000000357611664612512016536 0ustar timtim/* Created with The GIMP */ #define branch_width 48 #define branch_height 48 static unsigned char branch_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x30, 0x00, 0xf0, 0x94, 0x10, 0x06, 0x18, 0x00, 0xf0, 0xc8, 0x29, 0x0e, 0x18, 0x00, 0xf0, 0xc5, 0x11, 0x1f, 0xfe, 0x03, 0xf0, 0xe9, 0x2b, 0x1f, 0xfe, 0x03, 0xf0, 0xe5, 0x13, 0x0f, 0xdc, 0x07, 0xc0, 0xe9, 0x2b, 0x0e, 0x9e, 0x07, 0xc0, 0xc3, 0x13, 0x0c, 0x0e, 0x0f, 0x80, 0xff, 0x28, 0xf0, 0x07, 0x0e, 0x00, 0xbe, 0x10, 0xf4, 0x07, 0x00, 0x00, 0x1e, 0x28, 0x82, 0x07, 0x00, 0x00, 0x1c, 0x10, 0x85, 0x03, 0x00, 0xe0, 0x38, 0x08, 0x00, 0xe7, 0x01, 0xf0, 0x7b, 0xd0, 0x01, 0xf6, 0x07, 0xfc, 0x7f, 0xc8, 0x01, 0x9e, 0x0f, 0xfc, 0xfc, 0xd0, 0x0f, 0x0e, 0x0f, 0x3c, 0xe0, 0x88, 0x0f, 0x1e, 0x1f, 0x1c, 0xe4, 0x90, 0x1f, 0x1c, 0x1e, 0x0c, 0xea, 0xa8, 0xff, 0x1d, 0x18, 0x08, 0xe0, 0x81, 0xef, 0x0f, 0x00, 0x00, 0xe0, 0x03, 0x82, 0x0f, 0x00, 0x00, 0xb8, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x9e, 0x1f, 0x00, 0x07, 0x00, 0xe0, 0x1f, 0xff, 0x01, 0x07, 0x00, 0xe0, 0x1f, 0xff, 0x03, 0x07, 0x00, 0xf0, 0x5f, 0xfc, 0x0f, 0xf7, 0x0f, 0xf0, 0x4f, 0xe1, 0x3f, 0xe7, 0x0f, 0xf0, 0x83, 0x89, 0x7f, 0x0f, 0x08, 0xf0, 0x01, 0x28, 0xff, 0x0f, 0x00, 0x60, 0x00, 0x50, 0xfc, 0x1f, 0x00, 0x60, 0x00, 0x50, 0xf8, 0x3f, 0x00, 0x60, 0x00, 0x50, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x90, 0xc8, 0xff, 0x01, 0x50, 0x00, 0xa0, 0x98, 0xff, 0x07, 0x50, 0x00, 0xa0, 0x18, 0xfc, 0x7f, 0x50, 0x00, 0xa0, 0x14, 0xf8, 0x7f, 0x90, 0x00, 0x40, 0x0d, 0xf8, 0x7f, 0xa0, 0x00, 0x40, 0x0d, 0xe0, 0x1f, 0x20, 0x01, 0x40, 0x0b, 0xe0, 0x1f, 0x40, 0x02, 0x81, 0x04, 0x86, 0x1f, 0x80, 0xfd, 0x81, 0x04, 0x3a, 0x1f, 0x00, 0xfe, 0x80, 0x07, 0x7e, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; tkcvs-8.2.3.orig/tkcvs/bitmaps/fileedit.gif0000644000175000017500000000030111664612512017024 0ustar timtimGIF89aучКџь‹џџџ&&'ЉЉЉОООзИG;ёёё!ўMade with GIMP!љ,\№=ЄНи‚ jўGф˜mн Ў+qЂЫчЫ*ХЖh”\mp RГ! x…;9а]x%D ЈЅžUЃGа+7–`Эhotœггš’žъћџ;tkcvs-8.2.3.orig/tkcvs/bitmaps/link_mod.gif0000644000175000017500000000160111664612512017037 0ustar timtimGIF87a ї„„„џџџЦЦЦџь‹џџџџџџџџџ!љ, ^@А AXШ0*dHаaˆ 40РaСŒ  аqaD„Š$™ђЄР”+G– @L’Jи8В'IŽ 9 JДЈQЂ “†д9а Сœ ;tkcvs-8.2.3.orig/tkcvs/bitmaps/Ticklefish.ico0000644000175000017500000002362611664612512017350 0ustar timtim00Јf00h Јv шh!(n&(0`€"*7#*1/7E9NzJjИ0Ez->gEkЫ1?W`jq%3U@]‰sЦ…ЎкŠ­з+=sœЮ7GVІйgšє6ZГ &g‹ЖŠЋмЙвщЉгK`|rŸтEmл(1KTwŸa|ЁWp….Ba8G_ЯхѕpАD^‚DTc@OJObs\€ЉЋТкђћўІЪъ^Žі)6HW^0€‚2€‚ސŸЂ ЁЈ ЋЖ€“Mчіќ—Лс{Ц!0X\‡Р­ЭCM@acprv‚—™ЏВЫЬ#зи6rˆ…œЏУFa 4]МKvЕCnПP€в'6PБзє;ENKLPP SU[c?A468>-qЂl˜мW’мѓўuЊи $-,^˜зp}Nl{6w€9|‹]Ž›q .7 _‹ЛЈЏŸkyjŽ8ЂЂCЎЏGѓѕbepC–’iОЧГїљГљњ]_Оућ=?mm-ЛЛp‚|ŸН\ssZkEy}=QvЎЩвй`_&†ЅЃЏЎnИЙWыэƒFDK6ХХh›Љ‰5=:ХФsЏА`њ§щ-G…ии_њћбЮюџgn8ЦфљON--G’™К#S`#RmŠ•CВШ.СзrGQ,вв.pŽ{y‰—~–”Ў^ФУ/ЗФ ‰І‹Є>ЄЖИУ[ор>ииL™ИNЙйLжиoзи{ие}‡vТФэя"йы“^kYХдzУУSvyVЮЭ{    !"#$%&'()*+,-./+012344567%89:/;+<'=>?@A@33223BCDEF8GHIHJKLMN %OPQR?@STTUV(>0S??@WXYO Z[[\O:%#]TPPP?R H^JJ_L``aSVb cdZefgh#OiTTUUUUjkjlmnnmg[gLopY''%&eqe<]:rUUrsstui,8-vnwvmnxn[_obyz {Zmvfn\| sri}#~~,8€nnfvmvnjfe‚Yh hƒefjwm Mr„i"Z[x[…‚†!‡wmfefnjmqfle[[Z<[[ˆffnƒ'‰su#Zfjv€nˆ`{fnfneelefjˆfˆemˆˆmefxf<stŠ*emmlvvjxZZflnnqnenfeb/!ˆˆmˆnf‹st!ˆne€N‡‡‡NnfnfmfffmfWŒŽŒ‘emnnˆ~’“|nfnˆ”W•k,wnnfnneflˆ–Ž1—˜—110yxefjea "/‰“:vn[C™š68‡wlmnnfeˆ|Œ›ŒœkžŸ ˆffeqx_z /t‰sЁeZPЂЃЃЄЅ‡€mqnІWzЇЈЉ2111Œ?zljmnefЊ'Ћ{‰‰“z,€J„PЌQЌ2vnnf­ЎЉЉŒŒЏААБŽВ*nfmlƒЇ‰“tu$8ВЌ˜˜iВZnlnmf•,ŸГ11ДžА1ЃzjffˆZ#‰u“u|k‹ЕPЖ—Rƒflfjmq˜kŸАГ11ЗИАPnnfe…Йw strs9kВ]#Zgnefemˆˆˆ˜КЛŸК17Ѓ08Nvnlf[I$‰sstЂkkxƒƒqffqjlqmmefnNЄkАŽВЋkМlmfea‰Tusi?Н+kk‡О€€vvqvv€ОЁˆ)ПИАЉН 9k‡lle`^ РСТr„???ЃІ$,,8‡М‡М‡М:У/srU—ЛЃВІ/k8€fx‚\-o5Tts  ?   Ї-$О-httsiiФS%Ok8N+ЈЅts˜Ф2 RTR?? ? Risstrsi$k,-`.k|/Х“tЦkc ЧuP   ? RrutU‰t’yG:-NУz-GЊCtiRСЄШT  RTUUit“’urr)G<У#ІЩXzЪЫCЄCЄ3iTiTЬЬЬЅitt„ І!:L Э3Є™ЄЬiСЮЮЄЮЮЮЯ5„ y# ‘$ЯCЄЄC4ЄЄЮЮЮЮEЯ&yЬааббЅV‡вЯЄЄЄЮЮЮЄЮЮгy>дееШEEеЦЅжЭжж:–Ы5CЮЮЄЮЮDЮЬ?СEШШEееее>зВАиийкžл$9Ї4Є56ЮЮмЮЮDЮШеШееееЮе4иибййййкž%$8Ї%аЄC54ЮЮDШШEEеееееКККийййАнšF*+8›4мЮDC™мDDШEШееККиииКнндКо›пмШDDЮеШDеШEеEееКиКйййрЭ›аDšЬЄеEееесККииКййž ›етаШеККEиииуййЦлтжб™гггжаŒ––– џџџџџџџџџџџџџџЧџџџџў?џџџрџџџРџџџџџўџќ?џјР№№рРР€€€€€€€€€€€РРрРрџ№џј?џќџўџџРџрџјџџ€џџРџџџђџџџџ€џџџџ№џџџџџџџџџџџџџџ(0`@BPT"KmЋd”бmŽЌ‰ВмyЎлцєљАЮж\_ œЃ yŠ:ЭЭ-ЃЏIикd 331VdAfd2fffdcfft1#ffff1FffdBfffhS˜fvft !fiHwfQ–AfXfg3 33A&—e"!W•F2DCvA!#544D36D`Gt2Ff!""hffgttC1D"7wtfc"""(‰ffwwCD3G—cVfB"!34"ˆ–fg–ttC34Gfsff""DDC8fgvfwwDDDGvAfc"!Gf—tvvwdffwwwwt3fq"$wwftDvffff1!wwwCfb"7g™ˆ‰ggvwvQ$wfDfB"WwtVˆ–ffwwQGvtB"vw†wffe˜fvDd""yu!†w—U6ggsv"Xc!v–w‘gwCB"(„!vvwh˜ftAfB"%„!GggqQfwCfb""˜C!wwwwaˆfgsfc""%ˆDGwfvwvi˜†fC fd""!ˆ‰–fii˜EˆfD0fd1!ˆˆˆˆ‰S"Qh†gC–f1!y™–s""a ˆ–D–gq!!""""ff0ˆd@d!!!"""!!f •i1–c!"""""c1eW1"!""%Ct 6f!!"†fA1ffif†aˆ5˜QQџџџџџџџџџџџџџџЧџџџџў?џџџрџџџРџџџџџўџќ?џјР№№рРР€€€€€€€€€€€РРрРрџ№џј?џќџўџџРџрџјџџ€џџРџџџђџџџџ€џџџџ№џџџџџџџџџџџџџџ( @€ESB3Lƒ%/@7GaNZAlvR2Cie…ЈUmNp­C\‰[s“;Pq7Qˆ?KO;JbhŒИ{ŸЫ†ЉдАмw—Нk˜ЩbЅ…ЌлxœвC_“$/G:PwlŠ­ŠЎк”Жн\tŽKa{›НсЃЫKmЋ%2K2=L‰ЉХ—œw‡jt|Dw}7€ˆ;ЄВRВИuЅПб,>c$7LtЕJ\tby’jƒŒ[fAfj'lrsv„‹ЁЇ0˜^—ЊЌ8W•=SvGi R{МCRXEGIKTWW\(FXiD[uJf”XyЅRtžTr’]qu†žКNjŠ[„ЕrЅбYЎUr—25'+() :F9ЈХкЅЦш‚ЕтsЈтmЃл_•рMyШ?`Јe„Є}Дс8FEWdgвхюЌгђŠЛу…ЙтuЋлe•Ц1Oš/71 rЇ‚Еп|ГоgšиvЊй]yš$+*wАн—УчVjzZŒЯЈТаЪхјxЃЪuŠ:zƒHv€PvœЧDvе gzs˜ЁYФШsифОЗиђkŠ˜ЊЌyЪЯŽЏЗiexc"%Zt‡V[ š7‡ŸЄlwF•LЇЊWDb’*6B ЪмщMU(ww(iy0ЛЩЗ\lWЁХ Leyt|Rch:АЖКеЦзиwŒМйёБЮьЩЭ‚“œRfnIСЮе;hЧNSГОВЄЯ№RZEGiŸ‹ЌгeiУкфДЩйŠšlcwŽсяјj™тX†еЏЧрOc~ЭрђйщєІПж=B’ЎЩŠ”­ВЕИ!“ГбЛМ'’™ŸЇУУ*ЦЩ6SZ=€šVзяћГК6жжCге;ИМGq…sŠ”~ЎЖRРЮ^ЖТZ~ŒU‚œЋГЮчЋ­ЖЙ#гд5ииLŸЇ0ВЖRезŠртЅЅ„ХЦ$аа*жжIийiсх^ЌМu{„<ийYДРm†”5ЁЎ2œЃˆУвJЙСbs„_s€h    !"#$%&!'()*+,-.//0123'456789989:;<=>%?@A ?# BCDEFGHIJKLLMM J$NOPQRSBTTUVWXYZZZ[\]^_ ` QaQOO# UTb > cdefggagh]^IIIiafjklkP[[]$m"nooZaaa\piqoZJrstJugvv[PQnZoooq`ww`QhuoxjyGzGqZfYd{gnonoo|)+}~c€ogpNrb‚ fqƒ„…†‡gZnqQˆ**‰Š‹Œquapw#J‚ŽeC}Xvg'‘ƒ’+“”<“Žooai•4mJ–—k˜|™š›F€ogœŠ”,“.…„nuJž zŸ(Y ЁЂBnoaЃЄЅЅ”“..ІngaAjЇ"KsЈU=Љ'`€naaaoЃЄЅЊЋЌ­{va]ЎyWTlЏА˜{‡eББeБ# В‰…64X{v\AГДˆЕŸDЕ7ЁІXЖЖЗ'wtsVВИƒ ЙК{vЛМН"Ž7Ј+‹7CD777FTЈЈЈtHm#ОПИYpПСЃrЏF„;9CЏЏЏТzzVUcK Уyy•С€ИФХЦ9E8:Ч:VtGLS УШШ„ЦЩЪЫЬЬЬЭЪBc}}›™ЮЯЯЯаШ';ХЦЬЬЬЭб6~Эвгвдежзий‹клмm;нЦЩЩЬобппврвдситЅЅуфжžСЃх;ЦХЩцччгшррщщщъщъил2yыьочЬЭчппврэщщщъйюЯя№бсЭшршщщщЅуёђжя-йдѓйтюыЯЯєѕѕєџџџџџРџўџјџ№џр№Р@Р€€€€`РџрС№јќџџрџј?џџџџџџџ( @Рiwbi #WgdCeІL\\VttІж‰ВкlŒ­u„VБЩЮЄ­]ae!РУ=#"EUUfx™y‘V y™˜™ˆRd™‘™ —wA—RUV)AEwwtw…!a‡`y„339™ˆˆUeˆ†™C4UT™™˜…UXˆ‚!™3G‰‰™ˆˆˆˆ˜)—3™™™‰ˆWˆˆR)ƒ4ˆ™™™˜‘H‰‚'t5˜™ˆ‰‚xS1™I™‚qƒ4‰™˜!•3A™‰‘ˆR!31q˜‘™‚)˜C1‡A• ™q131†PA11334`†™t33Aw ""‘4Ga™‘D)™!`a‘&aa!!џџџџџрџџўџќџјџ№љРРР€€€@Р`Рџрурјќџ€џ№џљ?џџџџџѓџ( @5Hp+9X4Iw-6K1AS[s’lŒЕq‘ОXtŸf„Дe†ЖOkЁ17=Ody™Р…ЂН~“n~a‘ІšЗЧ‚ЅбPk—(EBdЅBgІj„Ѓj†ЇWgfY^*dl7duVn…fxvЄeЈQmN]vV|ЄXДC_ŠVnˆ}žЧRer29#6GL_pšМн~БсuЈмg—аSyГYwЄTvxЉеb‹Жt“Жt“Й,52Tv’}ЏуqšЭ{ЃЩƒЗрЖт|Азf‘Б]‚ЃnŸЬzЏнT|Еx›ФRh‚yЉбŠЇЃНбК’ПсЕоqƒ‚‰P‹“`n’Ђ`“жo‹ЋATj6@?†ІТ^e9{?ˆГЯ…Гз™­œ‹’NžЄYžЅeu•ŸY‚ГБзa}Ѓ'+ƒ•—šПЬЖЧ™АЕn™Ÿd‚ЖсQТiЏд~ЁЯJZA@B‰^ЊЧеЄЦи ЙI\chnN‚Žn„™ЊКйяzЇоVЭЊУнŒЊЭaf,‰:W[\c-GXgdŸUk†ныѕЈХчfŠПŽЊХ„ЄЧŠŸmЌА‡Œ—™ œЂ#SZ"TbGl€cVi^y“$’ЌKЂСх•ЕЩ›ЊVВЖ$ТТ'ЙЛ,І:вв0ЧЫ@ГЙJЕО^ХЫgРЪx’Ÿj›Им–ЎЛ™ЅQЖЙ!СУ+ии@ииRдзeЯжfЖОrЂЋ4БЗ9КР?ТШ_ТЦPХЫhМУ}šЉk†—O„’_ƒ’b   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH-IJKLMINOPQERSTUVWXYZ[\]^_E`abc;dbMefghijklmnop[qrstuvwxyz{|}~}€c‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”“•–—˜™š›œžŸ ЁЂЃЄЅІЇœЈЉЊЊЋЌ“­ЎЏАБВГДЕЖЗјР€ €€Ррјџ( oƒ™q‡‰tœЭ^}Їv”ЋŽІЈs…b ЈaŠЕжxЉзVhgUbMВЬШЅЉ,ЭЯF!4DB5ex‘ATC2%DA04‘E0Da‘—…Q0A™hˆQ0Uˆ09vAxA3h&!‚ !1јџрР €€Ррќџtkcvs-8.2.3.orig/tkcvs/bitmaps/loop-ball.gif0000644000175000017500000000032311664612512017124 0ustar timtimGIF89aу ‹ЂЙЕач^^^kkk’мHИџŽдџџџџџџџџџџџџџ!ўMade with GIMP!љ ,n№ЩIЋН8?КяGЦ1J™Щ^iЂщ‰KЉœё‰T^§v)РjвZИLИФ`Ёй^РйєMЉŒgpž ƒEЅ н/Xьiа€ТZ0žЩяaz м ћщu~bz|,€{‡‚m‘;tkcvs-8.2.3.orig/tkcvs/bitmaps/modbrowse_cvs.gif0000644000175000017500000000073711664612512020130 0ustar timtimGIF89a((„oooџь‹џџџўыŠЖ4РN.Ыi=р\СN.Ж4пœ\е‚LщЗkъИlЫi>ѓбzеƒMєв{џѕХ‹чаџЙЂџь‹џь‹џь‹џь‹џь‹џь‹!ўMade with GIMP!љ ,((ўр'ŽdižhЊЎlћpЛєм8д,џ7™Œ7‚ЄRА#ОРhЎIDJЏTžuРНuЛpЖf= ‡†у†ˆœ ‘ ѓ мжˆс ИхNH9mj mSs‚„8ƒ j‰c4H }y›y ‘•‹PpЁg~ykЂP”i7fЋЌUWR–.VЙ@Л-GТBs)ХDЧШ$ЭЮЯаба.ЪЦ,е+и'клдйзСЭРт(фР"фш$ыэяYђю№лиєёѓP`П'P0шB… 18TЗpпС‰%&Tˆ˜A™FŽ7ZєзбФ (K-`Шсˆ“)WЖє˜R%K—%њ#йђ!…21ъмysзЂ3™!Хљr)7u(мФUъЬzQЇІ{Q.aWчЖ.KЖЌйГh]„;tkcvs-8.2.3.orig/tkcvs/bitmaps/link_modol.gif0000644000175000017500000000157011664612512017377 0ustar timtimGIF87a їџ„„„џь‹џџџ„„„„„„„„„!љ, U$@ Рƒ РА`Тƒ РрУˆPёЁB† 9ZDфF‘Mžр"bЪdI2ЃЬ™4АмЩГЇЯŸ=Z0ЇЧ„-;tkcvs-8.2.3.orig/tkcvs/bitmaps/arrow_hl_dn.gif0000644000175000017500000000014011664612512017536 0ustar timtimGIF89a Ёџџџџь‹!ўMade with GIMP!љ , „™c!DйшУT[оUТŠdieЅsЎъ:МC;tkcvs-8.2.3.orig/tkcvs/bitmaps/link_plus.gif0000644000175000017500000000155011664612512017246 0ustar timtimGIF87a їчџџџ’м!љ, EHА A‚/дjЁ = Т4рH#~ЏDџop?ƒ?nox(x6#ƒ;tkcvs-8.2.3.orig/tkcvs/bitmaps/delete_red.gif0000644000175000017500000000030311664612512017335 0ustar timtimGIF89aу zŠ 777™OOOЋ::РaaПППхххџџџџџџџџџџџџџџџ!љ,p№ЩIЋЕ }ЋвВH'…ЩЂЊ€љhkŒ”цЏm м,gm6№‡h „ЈСpHZ'TJ= ,…6J˜z ݘ›ЄU˜'yJPVФёmйј^ТyshLr\vWˆe~:GŠ.”;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_ood.gif0000644000175000017500000000020111664612512017052 0ustar timtimGIF89a Т„„„џџџЦЦЦ’мџџџџџџџџџџџџ!ўMade with GIMP!љ, 4HмЎp…I Ъ:AР‘ЧYMX C•Aт„Њ&ѕRЋвsЃ|jХ8 „БFF›cЩP$;tkcvs-8.2.3.orig/tkcvs/bitmaps/link.gif0000644000175000017500000000152511664612512016205 0ustar timtimGIF87a їчџџџ!љ, 2HА Сƒ*\ШАЁC"6@bФ‹/ЈHqЃЧ CŠќ˜БdG‰ )J ;tkcvs-8.2.3.orig/tkcvs/bitmaps/dir_minus.gif0000644000175000017500000000017311664612512017237 0ustar timtimGIF89a Т’м„„„ЦЦЦџь‹џџџџџџџџџџџџ!ўMade with GIMP!љ, .xЬњp‰A‡Q…Yщm !Ž$Щ]кjЫЖщ;tН(Uл• SО_* ;tkcvs-8.2.3.orig/tkcvs/bitmaps/files.gif0000644000175000017500000000021611664612512016346 0ustar timtimGIF89aЁџџџЉЉЉ!ўMade with GIMP!љ,MŽ™У Ѓ„ Й 2ВяЭ_]–1і&ъЅQxЖ.Жžи!$TЮЛСЂ$Ф_Ьg о9Lщ{к˜:šса‹rzCФЯ„E†Жb ˜Д(;tkcvs-8.2.3.orig/tkcvs/bitmaps/svndir.gif0000644000175000017500000000016011664612512016547 0ustar timtimGIF89a Тр„„„ЦЦЦџь‹џџџџџџџџџџџџ!љ, 5xЬњp‰A‡Q…Yщm !Ž$Щ]кjЫРЯTќжВLГy?л ч№ZЋ 2j: ;tkcvs-8.2.3.orig/tkcvs/bitmaps/paper.gif0000644000175000017500000000015211664612512016352 0ustar timtimGIF89a Ё„„„џџџЦЦЦ!ўMade with GIMP!љ, )м€ЉЦ Йј@ Mc™t_T1о(‰hjЎЌqŽ%ьЮjЧ_ЎєˆQ;tkcvs-8.2.3.orig/tkcvs/bitmaps/tkcvs48.xbm0000644000175000017500000000352011664612512016574 0ustar timtim#define tkcvs48_width 48 #define tkcvs48_height 48 static unsigned char tkcvs48_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xa0, 0xaa, 0x28, 0x00, 0x00, 0x00, 0x50, 0x55, 0x11, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x28, 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x28, 0x28, 0x00, 0x00, 0x00, 0x04, 0x10, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x28, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x10, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x28, 0x02, 0x00, 0x00, 0x00, 0x04, 0x10, 0x01, 0x00, 0x00, 0x00, 0x0a, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x04, 0x10, 0x05, 0x00, 0x00, 0x00, 0x0a, 0x28, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x10, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x28, 0x0a, 0x00, 0x00, 0x00, 0x04, 0x10, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3d, 0xc0, 0xf0, 0x0f, 0x80, 0xff, 0x39, 0x60, 0xfc, 0x0f, 0xc0, 0x83, 0x39, 0x60, 0x1e, 0x08, 0xe0, 0x01, 0x38, 0x60, 0x0e, 0x00, 0xe0, 0x00, 0x70, 0x30, 0x0e, 0x00, 0x70, 0x00, 0x70, 0x30, 0x1e, 0x00, 0x70, 0x00, 0x70, 0x30, 0x7c, 0x00, 0x70, 0x00, 0xf0, 0x18, 0xf8, 0x01, 0x70, 0x00, 0xe0, 0x18, 0xf0, 0x07, 0x70, 0x00, 0xe0, 0x18, 0xc0, 0x0f, 0x70, 0x00, 0xe0, 0x1c, 0x00, 0x1f, 0xf0, 0x00, 0xc0, 0x0d, 0x00, 0x1c, 0xe0, 0x00, 0xc0, 0x0d, 0x00, 0x1c, 0xe0, 0x01, 0xc0, 0x0f, 0x00, 0x1c, 0xc0, 0x03, 0x81, 0x07, 0x06, 0x0e, 0x80, 0xff, 0x81, 0x07, 0xfe, 0x07, 0x00, 0xfe, 0x80, 0x07, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; tkcvs-8.2.3.orig/tkcvs/bitmaps/conflict.gif0000644000175000017500000000024511664612512017047 0ustar timtimGIF89aТОООџь‹HИџџџџџџџџџџџџџ!ўMade with GIMP!љ,XHКмNД8хС*zѓє X˜Ц”сO)Šшг0+Ce~_cF›-˜KUйє""G9ж~Ь’іЪqъk”НК‘p8;!s6LђFНi_дgZКЋлe ;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_conf.gif0000644000175000017500000000023111664612512017221 0ustar timtimGIF89a Т„„„џџџЦЦЦчџь‹’мџџџ!ўMade with GIMP!љ, LxмЎp…I JЪlР г@NФP1eh/aгŠэЖАй УЅ,!ƒЫƒxюU"kCЫ3\{оь$ќ 9ЮЊC;tkcvs-8.2.3.orig/tkcvs/bitmaps/link_ok.gif0000644000175000017500000000154011664612512016673 0ustar timtimGIF87a їчџџџ!љ, =HА Сƒ"РP€Bpј0тФ‡Rlˆ1#†5ŠdрЃ€’(SЊ\ЩRхШ‘%A*< 2 ;tkcvs-8.2.3.orig/tkcvs/bitmaps/adir.gif0000644000175000017500000000022611664612512016164 0ustar timtimGIF89aТoooџь‹џџџ333џџџџџџџџџџџџ!ўMade with GIMP!љ,IHАмє0‚@kQкM›џž ŽТ"pЈeІьC,ЯSœ№ЌЧЕ™зГяuЫU‚”ЁoЧLV9Y’f;НЄЧa••rq7ояDі$;tkcvs-8.2.3.orig/tkcvs/bitmaps/annotate.xbm0000644000175000017500000000352311664612512017102 0ustar timtim#define annotate_width 48 #define annotate_height 48 static unsigned char annotate_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x08, 0x20, 0x10, 0x00, 0x00, 0x10, 0xa8, 0xaa, 0x28, 0x00, 0x00, 0x10, 0x58, 0x75, 0x11, 0x00, 0x00, 0x10, 0x08, 0x2a, 0x28, 0x00, 0x00, 0x10, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x08, 0x2a, 0x28, 0x28, 0x00, 0x10, 0x08, 0x24, 0x10, 0x14, 0x00, 0x10, 0x08, 0x2a, 0x28, 0x0a, 0x00, 0x10, 0x08, 0x24, 0x10, 0x04, 0x00, 0x10, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x08, 0x24, 0x10, 0x01, 0x00, 0x10, 0x08, 0x2a, 0xa8, 0x02, 0x00, 0x10, 0x08, 0x24, 0x10, 0x05, 0x00, 0x10, 0x08, 0x2a, 0x28, 0x0a, 0x00, 0x10, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x08, 0x2a, 0x28, 0x0a, 0x00, 0x10, 0x08, 0x24, 0x10, 0x14, 0x00, 0x10, 0x08, 0x2a, 0x28, 0x28, 0x00, 0x10, 0x08, 0x20, 0x00, 0x00, 0x00, 0x10, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x08, 0x20, 0x00, 0x00, 0x00, 0x10, 0x08, 0x20, 0x00, 0x00, 0x00, 0x10, 0x08, 0x20, 0x00, 0x00, 0x00, 0x10, 0x08, 0x20, 0x00, 0x00, 0x00, 0x10, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x88, 0xff, 0x39, 0x60, 0xfc, 0x1f, 0xc8, 0xa3, 0x39, 0x60, 0x1e, 0x18, 0xe8, 0x21, 0x38, 0x60, 0x0e, 0x10, 0xe8, 0x20, 0x70, 0x30, 0x0e, 0x10, 0x78, 0x20, 0x70, 0x30, 0x1e, 0x10, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x78, 0x20, 0xf0, 0x18, 0xf8, 0x11, 0x78, 0x20, 0xe0, 0x18, 0xf0, 0x17, 0x78, 0x20, 0xe0, 0x18, 0xc0, 0x1f, 0x78, 0x20, 0xe0, 0x1c, 0x00, 0x1f, 0xf8, 0x20, 0xc0, 0x0d, 0x00, 0x1c, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xe8, 0x21, 0xc0, 0x0f, 0x00, 0x1c, 0xc8, 0x23, 0x81, 0x07, 0x06, 0x1e, 0x88, 0xff, 0x81, 0x07, 0xfe, 0x17, 0x08, 0xfe, 0x80, 0x07, 0xfe, 0x13, 0x08, 0x20, 0x00, 0x00, 0x00, 0x10, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; tkcvs-8.2.3.orig/tkcvs/bitmaps/loop-glasses.gif0000644000175000017500000000046711664612512017664 0ustar timtimGIF89a„zzzџџџŽдџ’мHИџ‘S'ŽJ‡<І…nЌ™‹—b<Ѓ~d‹E›oOгггОПРНМЛВ ”ПРСŸ€iџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ!ўMade with GIMP!љ ,Ђр'Ždi’@ Ў+p~Љ0ЬDрšPќ…n(­€…œŠИѕ> NH4F2I§х RVК”$…aœKЬХ‘aёЦ…Рyќ“0`Б,k{s/rt# g% ‰Š~ˆ‹  #  ‹h# jh} j% ЂVЎ­Љ Ї ŠА‘’ŠРŒš%™ И…Я%!;tkcvs-8.2.3.orig/tkcvs/bitmaps/tag.gif0000644000175000017500000000017411664612512016022 0ustar timtimGIF89aЁџь‹џџџџџџ!ўMade with GIMP!љ,;”`ЉЫШŸœƒMк€о„a‡(’‰ІзbŠђљFѓЌ*ёэf|яух`; ЅEѓ|bУвFI„JЇŒ;tkcvs-8.2.3.orig/tkcvs/bitmaps/trace.xbm0000644000175000017500000000351211664612512016365 0ustar timtim#define trace_width 48 #define trace_height 48 static unsigned char trace_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x10, 0x00, 0xc0, 0x03, 0xa0, 0xaa, 0x28, 0x00, 0xcc, 0x01, 0x50, 0x55, 0x11, 0x00, 0x1e, 0x18, 0x00, 0x0a, 0x28, 0x00, 0x0e, 0x3c, 0x00, 0x04, 0x10, 0x00, 0xe0, 0x1c, 0x00, 0x0a, 0x28, 0x28, 0xf8, 0x01, 0x00, 0x04, 0x10, 0x14, 0xfc, 0x33, 0x00, 0x0a, 0x28, 0x0a, 0xfe, 0x7b, 0x00, 0x04, 0x10, 0x04, 0xff, 0x3b, 0x00, 0x0a, 0x28, 0x02, 0xff, 0x03, 0x00, 0x04, 0x00, 0x01, 0xff, 0x03, 0x00, 0x0a, 0x60, 0x00, 0xfe, 0x03, 0x00, 0x04, 0xf0, 0x00, 0xfc, 0x01, 0x00, 0x0a, 0xe0, 0x0c, 0xf8, 0x00, 0x00, 0x04, 0x06, 0x1e, 0x00, 0x00, 0x00, 0x0a, 0x0f, 0x1c, 0x00, 0x00, 0x00, 0x04, 0xce, 0x01, 0x00, 0x00, 0x00, 0x0a, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x0f, 0x00, 0x00, 0x00, 0x80, 0xf7, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0xfe, 0xe5, 0x8f, 0xe1, 0x1f, 0x80, 0x7d, 0xc9, 0xc7, 0xf8, 0x1f, 0x40, 0x82, 0x09, 0xc0, 0x24, 0x10, 0x20, 0x01, 0x28, 0xc0, 0x00, 0x00, 0xa0, 0x00, 0x50, 0x40, 0x0c, 0x00, 0x50, 0x00, 0x50, 0x00, 0x1e, 0x00, 0x50, 0x00, 0x50, 0x60, 0x0e, 0x00, 0x50, 0x00, 0x90, 0xf0, 0xc0, 0x00, 0x50, 0x00, 0xa0, 0x70, 0xe0, 0x05, 0x50, 0x00, 0xa0, 0x00, 0xe7, 0x08, 0x50, 0x00, 0xa0, 0xc4, 0x0f, 0x10, 0x90, 0x00, 0x40, 0xe5, 0x9f, 0x11, 0xa0, 0x00, 0x40, 0xf1, 0xdf, 0x13, 0x20, 0x01, 0x40, 0xf9, 0xdf, 0x11, 0x40, 0x02, 0x81, 0xf8, 0x1f, 0x08, 0x80, 0xfd, 0x81, 0xf8, 0x9f, 0x04, 0x00, 0xfe, 0x80, 0xf3, 0x9f, 0x03, 0x00, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; tkcvs-8.2.3.orig/tkcvs/bitmaps/export.gif0000644000175000017500000000017611664612512016572 0ustar timtimGIF89aТџџџŽдџHИџ’мŽдџŽдџŽдџ!љ,CxКмо Ъј`ƒЪ€р@lœт…"YžсXš_Ћr,шОuњљНЦЖYЅ'|W`ŠbМP"Ÿ#Ф)ЅДіЗХ ;tkcvs-8.2.3.orig/tkcvs/bitmaps/check.gif0000644000175000017500000000025411664612512016323 0ustar timtimGIF89aу’мчаŽдџџЙЂџџџHИџ‹ŽдџŽдџŽдџŽдџŽдџ!љ,Y№ЩIЋН8ыЭ5јdŸ УGŒPМц`Ќо[Фkи ЖЙ€ ˜„(Ќ$ЦdМи 8MЪ5›xЋ$rїbЭрЖUљщLфіМ~/‰;tkcvs-8.2.3.orig/tkcvs/bitmaps/unlocked.gif0000644000175000017500000000104011664612512017044 0ustar timtimGIF89aЦ@ ",*- 7&%%%>-!:::E(J1T- K:,m=o@uCiI+nL2EEEWVV~~~ƒ[3”]+šb)›f3Јm(Ђm9Џr*Їr:Д{8ЋyCВ~EЙ=И…IКŠRОWЛœtЩ@УŽSТ”\֘dЭЃmЭЂrвЉy„„„˜˜˜ЈЈЈИИИгЎ‚зАƒмО›кР›уЩЌхЮАыаЏьдЕЪЪЪйййэпЭёфдчччѕюфџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ!љ<,}€<‚ƒ„…†‚‰‰‡‡‰1;:‹Œ„0?>;0 “ˆ>>>:œ“˜Š›Ї‡:Š/0’‡­МНОНœŠ=-ЦЧШ-ЇТХЩЩЫ…ЮЯЧб„геШзƒйкЦмˆдкс<опхчфЎтпрьцђѓєѓ№Тјљ;tkcvs-8.2.3.orig/tkcvs/bitmaps/branch.gif0000644000175000017500000000021011664612512016473 0ustar timtimGIF89aЁ‹Eџџџ!ўMade with GIMP!љ ,GœЉkq ФƒЭ€KWлŒA‡Јl‰Щ4’хЕƒJъBcЂМЏїБѕf EИљ}R"е@Ъp&чt%h^KУmд[3 ;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_okml.gif0000644000175000017500000000016111664612512017240 0ustar timtimGIF89a ТчУ„„„ЦЦЦџџџџџџџџџџџџ!љ, 6x'мЎp‘@‰CФьМD U>IUЁЅ+ыНŠК--y>­  (ќЅŠЦђ2r8Š;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_kb.gif0000644000175000017500000000015411664612512016674 0ustar timtimGIF89a Т„„„џџџЦЦЦчџџџџџџџџџџџџ!љ, 1HмЎp…I Ъ:AР‘'ZV’Ѓg\•Aу ЃЏЯbНЕн™в>ЦЃшH!;tkcvs-8.2.3.orig/tkcvs/bitmaps/remove.gif0000644000175000017500000000024311664612512016541 0ustar timtimGIF89aђ’мHИџОООŽдџџџџ!љ!ў Imported from XPM image: ,Т’мHИџОООŽдџџџџ1hКмn I+/ыНџ`ˆqdљ„ ЎЌŒf,ЯtНˆјЧ m/МЛ€pH$ТlШЄ,;tkcvs-8.2.3.orig/tkcvs/bitmaps/folder.gif0000644000175000017500000000016511664612512016522 0ustar timtimGIF89a Т„„„ЦЦЦџь‹џџџ!ўMade with GIMP!љ, (HЬњpAEQYщmр Ž$Щ ]кjЫЖщ Wђ,д3ы.шџ€;tkcvs-8.2.3.orig/tkcvs/bitmaps/link_modml.gif0000644000175000017500000000160011664612512017367 0ustar timtimGIF87a їУ„„„џь‹џџџ„„„„„„„„„!љ, ]$@ Рƒ РА`Тƒ РрУˆPёЁB† 9@RР@ ˆ,iђ#J• ”H€Ъ›c (X'L1 JДЈQЂ<“jбуИ;tkcvs-8.2.3.orig/tkcvs/bitmaps/checkout.gif0000644000175000017500000000041611664612512017053 0ustar timtimGIF89aѓ ’мHИџ‹ЂЙачџОООŽдџџџџООО!љ !ў% Imported from XPM image: update.xpm,ƒ’мHИџ‹ЂЙачџОООŽдџџџџОООbЩIЋН ƒ‹С9†‘q€œŸQiЂркbЧ‰Є+1Wо—Њ`№…rBLHЃ! v<жBA$‡,…@ qРl&;dqЂђPšи'ЪШЮL2ЌГ”r#П §#|f ;tkcvs-8.2.3.orig/tkcvs/bitmaps/arrow_dn.gif0000644000175000017500000000013611664612512017060 0ustar timtimGIF89a Ёџџџ!ўMade with GIMP!љ , „™b!ƒ/ŠIAФy7ћpQјYЮeŽЂYІЈU;tkcvs-8.2.3.orig/tkcvs/bitmaps/arrow_hl_up.gif0000644000175000017500000000014111664612512017562 0ustar timtimGIF89a Ёџџџџь‹!ўMade with GIMP!љ ,  œ Ї—Ыб@ЋКyqб=`8|cP™ ZBшћК0\;tkcvs-8.2.3.orig/tkcvs/bitmaps/dir_new.gif0000644000175000017500000000021711664612512016674 0ustar timtimGIF89aЁџџџoooџь‹!ўMade with GIMP!љ,NœЉ{РЂ УН ВС.М ‘‘1•РЖ$*n‚gдаЩ4ЋнљdСtЛоикјjQВЖ ІœЃlv^Б2э•iCЅЦу‡љŒN+ ;tkcvs-8.2.3.orig/tkcvs/bitmaps/import.gif0000644000175000017500000000035011664612512016555 0ustar timtimGIF89aуoooџь‹џџџ‹чаџЙЂ’мŽдџHИџџџџџџџџџџ!ўMade with GIMP!љ ,ƒ№=@k•8ЯР{x$gш)ЌЌ@ eьН%0 c Рп@ŽDы ‡BwRЩ$фLPЮ 0]"ЮO600˜o‰„p6šХ@} Еƒƒ!‹yZz{|~X€ƒƒrl‡7‚}…)68(!y{r˜˜  œ7Ѕ˜;tkcvs-8.2.3.orig/tkcvs/bitmaps/stat_mod.gif0000644000175000017500000000021111664612512017051 0ustar timtimGIF89a Т„„„џџџЦЦЦџь‹џџџџџџџџџ!ўMade with GIMP!љ, ЄыџA­ђџ8•жџ1Гџ+l”џ$Meџˆ“џМЌЄџh]WэGC=})џџџџџџџџџџџџџџџџџџџџџџџџџџџui~Чƒ{˜џŒ‚œџ–‹Ђџ’ƒ•џ~m„џМšcџХXџq7џ}a/џ9;1џ1c€џ6’ЫџHПєџCГјџ:™кџ1~Аџ&_‚џJcџqvuџЌЎџ­ГŠџ::-_џџџџџџџџџџџџџџџџџџџџџџџџ?95 ”Š…‹OMTyZdoЁ\dpу~…–џЁ‘ТџЄ‡eџЩЂ^џ|>џ}b/џoe7џœ ]џz†Qџ]vWџHnlџ9dsџ/Yjџ,NXџ0ABџuYHџЙЃuџЙЃuџRB3Ÿџџџџџџџџџџџџџџџџџџџџџџџџ50-5OHDG+'$5($"-3+<]CIaџ”ƒЖџГ”jџЪЃ`џ™x<џ„g2џ‹‡JџЫн|џЧкxџИЧmџЊБdџЃЇ_џžЉ]џЂЋ`џЌ `џХЎsџЧЎ|џЙ jџVL2ыџџџџџџџџџџџџџџџџџџG@<# KEE+2*;Ё[PtџŽ|—џзВtџЧ [џŽo7џx]-џ‰yJџЃ™^џЅž_џ™’Wџ”‰Tџš‹Vџ“XџŸ–[џЂ˜^џ™‡_џ™…[џzOџugAџ0)ЙџџџџџџџџџџџџџџџЂ•ŽI/*(SSKHwXQMNGEs90Cё‹zЂџЦІ|џвЌjџМ–Qџv\-џfA*џuC7џuH:џeUџ„wkџrZJџjZHџdT?џb@1џvH:џxH:џyE:џvB8џp?6џV3*џ#ЙџџџџџџџџџџџџЉЂžM952SNLУQKRЯ_Upѓo‰џАzџлРџЩЃ_џˆl6џb7$џh'џl)џl +џ—ˆSџЌГ‚џТаЄџЖС”џЙЦ›џ‡~cџv2@џr-:џx/@џ~3Fџv(9џg+џIџ(ЇџџџџџџџџџjXnnˆ§‰s‘џp…џXVџ™MFџЁstџО‹‚џІ€Uџ‚C0џ“;5џš?9џ›A:џ›C;џšH<џ‰[Aџ­ŸzџЙЏ…џЖЌ…џw_LџŠ^Nџ_Lџ‘aNџ›hYџЏ|uџУސџЁtlџzaRџ[K1л Cџџџџџџ}D05кЎšытЌ’џз‚ZџаuSџfS4џ<џŒ€gџб~WџЯvRџжwTџжyWџзYџи‚[џйˆ_џбhџИcџОŸkџНŸkџ“ŒeџlwfџIYTџGehџplVџЎž~џ†xMЋЋŸfПЈЎ~џМЩœџˆŠHыKџџџџџџ–`@оМ ЏћвЏџьЂyџ‘wcџ…ŠџšˆrџюЉ{џёЇ{џђЅ{џѓІ}џѓЎ‚џјР‘џгЃzџЗ›uџЎŽxџЩЗ—џЄ‘cџ.8;џ.=џ$0џ(6џYRPџЃ’‰џ%!KАИaIЉЌtџМФ–џЙТˆџˆ‹aу'џџџџџџ‹tS СБ—ејюмџнЭЙџЧЗЄџпЬЕџсЩЕџсФДџфЦКџякШџьнЪџЎ‚џД‹‡џа ЃџОš”џFQdџ@K\џ*:Mџ (џ џџ€yџ„voэ _N, –†cїПЛ“џЎІџЎЅ€џH;,Ѕџџџџџџ ›šWЏЄ‘rчСДАнике§тчтџпснџежгџСЙГџЕЅІџЄvwџЪ’šџе™ џЯ™ џz…џK`џAPmџ8B[џ*џџ964џЌ›’џaWRџџџџџџЂŒjз­—sџ­˜sџ­—rџ‰sXчџџџџџџ‹†HЏПfGc`/џџџАІЁkЋ›•џf[ZџZh}џУУгџгЅœџцЋВџоЂ˜џуЊЋџЩ›“џds‹џ[j‰џOYwџDJeџ*џџ™‰џ‘{щ"џџџџџџЎ`СНŸlџНŸlџФЇrџŠvToџџџџџџgD5Ё#џџџџџџh_ЪРЛГ­œ”џ][^џbz™џ­ПгџйОЙџыГЛџцМИџ„Žžџ€ŽІџmy˜џ]cџBF_џџŠ|uџІ•ћ\RNIџџџџџџM.ГŽ]ЉТЂmуХЄgџвЖsя<#џџџџџџ–ki+hICЇ7)a ;=0,#Q™Šс‡xrџ]VVџN[lџ‚™џВЅАџŸЈМџ–ЈПџŠšЕџt€žџBG^џC@Bџ™‰‚џ­›“љwje[џџџџџџџџџџџџџџџЗŸpwГ›dяЊ’W-џџџџџџџџџџџџsD>œ{ZAЕГqYХв}YЊЈhW‡gMUšsp•А‘Žћ–kbџнЉ}џuriџY^jџX^kџWXbџ`[[џ{џДЂšџЎ•л}oj=џџџџџџџџџџџџџџџџџџxdL+†yaiџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџТЙД-ЧЕДŸВ‚yёЙŽnџЇ—ŽџЖЅžџОЎЇџПВЋ§ИЋЅЯЅ˜‘kMEA џџџџџџџџџџџџџџџџџџџџџ>+ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџY]‰VQщ~n“ИЏЋWœ“ŽEbXSџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ~YXd35!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ(0` €%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ #5-(%a71.…<52‘;42‘0+)‹#qIџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ#JB>…j_Zг‚unѕ•…~џŸ‡џЂ‘‰џЁ‘‰џ…џ‘‚{§|ojѕ\SOн730ЁIџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ)$"YOK‘ƒuoѓЂ’ŠџЏ”џКЉЁџФЕЎџЦЙГџЧКДџЪНЗџЭОИџФЕЎџЗІžџЋš‘џŒ…џ}qlѕ=85Г?џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџLC@AylfеЂ‘ŠџГЁ˜џСБЉџ­ЇЄџ{…џPblџ;Vfџ8Znџ;^qџJcqџm{ƒџ ЃЅџЫФСџХЗАџЏž•џšŠƒ§f\Wуw џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ 13B:7auhcя„vmџ€sjџŒƒџGV^џ&Slџ&^€џ*l•џ/zЉџ8Фџ5ˆМџ0zЈџ*j’џ"Vtџ-Oaџr~„џЭЧФџН­ІџІ•Œџylgѕ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџA9+EreLС—‡gћ’ƒcїrUщwgJнsbCеr_>лva?§h=џŒn8џv].џ]M)џ(1-џ'b‡џ5‡Мџ8‘Юџ<ŸуџB­ёџ>Ѓшџ8”гџ3‡Пџ.xЈџ*j‘џ$Ywџ$@QџœŸЁџЫНЗџЌš’џylgї‘;,*C7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџA88eh\Wё†yrџƒzџ•‰zџžyџЉ–wџЙŸwџ„dџШЄgџЧЁ^џО–Oџ”t9џz_.џgP'џ-9џ)lšџ< уџ?ЈєџCГќџFК§џ?Їђџ9˜лџ3†Пџ/yЉџ*k’џ%[{џCWџt{џЫОИџ…|jџcaTѕ•œƒя Yџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ€sŒЕ’ˆЊџ•БџЂ–ДџЇœОџЋŸТџ›Бџ€qšџvg“џЅ‡\џЯЈfџУ›UџЉ„Bџ|a/џu\,џE9#џHPFџ(Qgџ.{Љџ?ЇйџKХѓџGЛќџ@Њђџ:šнџ4‡Нџ*k•џ%\џ$Wuџ8IџprlџТЧžџЕЙџГЗŒџHG:Ÿџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ%! Ё—Ч94BCTT\­_foХbmwёixˆџ€†•џОГзџ”„ЖџŠr^џгЌkџЦŸZџБ‹Fџ|a/џy^.џ[L'џ‰†Oџ‘“Xџt~NџRgLџ:]Zџ._uџ*e‰џ(eŽџ&aˆџ"VwџKfџ;Mџ"/;џkUџ­˜rџЙЇ|џИЉ|џhVCб џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџWNI)Œ‚} D=:o#!=77k43@ЋJ\mџvq“џ™ˆЛџtbџеЎmџЧ \џГGџ}b0џ|`.џl_1џМШpџЛЪqџЎНhџЅГaџ›І[џŽ–Uџ†Oџu|KџkwGџjuDџmtDџynAџ•b?џЗ“aџЫЋpџОgџРfџeQ7ё;џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ.)'M930c$ $ 1'#!7/B‡:@Xџwm–џ}ЏџЈ‹gџеЎmџЧ [џАŠFџ~c0џ}b/џtj9џФЯtџЫо|џЪоzџШлxџТбtџЕОkџ­АeџЈ­bџІЏaџЅБaџЉГgџЏВlџЙЌnџЩГ|џЭК‹џОЈxџЏkџi`>§ Ÿ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ'#!Y #_3%!5-AЯ>7Sџ‘Вџ~l‰џзВwџвЋjџХžXџŸ}?џ~b0џw\,џ{pCџЈ `џЎЋfџАЏfџ­Ќdџ­ЊdџЈЂaџЅš^џЄ›]џІЁ^џЇЄ_џЅЂaџЋЇdџЃšfџ›‹fџœŒbџš‹[џ‹{Mџ~sGџPI.ѓ } џџџџџџџџџџџџџџџџџџџџџџџџQIE•/+(s/*(i$!GA,'+k4+:љ_Rwџ’‚ЏџА”xџкДuџЮЇdџСšSџ†i4џy^.џhM*џ_Bџ‡hGџˆlJџˆmKџmT;џW?-џS=+џXB/џcN4џu^>џ„kFџ‡mIџŠnJџŠmIџŒkJџŒhIџ‰dFџ†aEџwV=џdL5џG6&я }џџџџџџџџџџџџџџџџџџџџџMD?{qlЕF?<1^VR—mc^‘YTOe51/eIAGеI>Vџ‘€АџЉƒџнЗ{џд­lџШЁ\џГIџrY+џkP(џd4+џp92џq<5џq>7џ~ZMџšŠџІЁ‘џ“€mџ~aEџ†}kџndPџK8(џ\4,џq=5џp<4џt=6џt;6џq85џq84џr95џe40џQ,'џ6эqџџџџџџџџџџџџџџџџџџTJF˜‹ЯG{tpЭtmhГ-*)ЃE>IчaUu§~ЄџДЅ’џцбџпР…џЬЅaџП˜Rџ€e2џeK&џ_$#џf'џj)џj*џj#+џ’‚MџЇ­vџИФ’џКЩšџЛЪЂџИУ”џЗЧџНЪЁџbT:џh"/џl&2џi"/џj"0џn#3џm!1џk,џk+џ^&џGџ0хg џџџџџџџџџџџџџџџ$-%TMUх% -ЫQHYѕVKgѕobˆћ†uџ€dtџƒQNџ—Z[џЁfbџРœџвЗzџ“u<џbB%џk('џw"+џy#,џz%,џy%-џz&-џŠN;џ“{Hџ–cџЙК•џЩЮžџІЄ|џЩЮ џЁŸwџx[YџˆNXџ–Xaџ}?Fџ‰ITџЃ_qџ aqџЋgzџžXiџBSџk)0џ\+-џP3-ы “)џџџџџџџџџ^I^gŸ‰ЂџŸ‰Іџ„ џ—x‹џŒacџ’OAџžJ=џЃTLџ‘gdџУЈЅџЦŽ„џ wPџ‰M3џ–D7џžD9џЃG<џЅJ>џЇK>џІL>џІNAџЅO@џ O=џ}M<џЙЋ€џœ†iџЫУ’џ•cџЭФ“џ]?3џ‰dJџŽfLџŒgGџhHџ›hLџaGџ q]џКƒ}џаЅŸџЫІžџaKџŠmџ€lHљ90С=џџџџџџџџџZ/!ГzdзъЧЙџкƒџЭ|YџЮvRџаrQџА^CџRQ0џDЄџFƒ6џП–‚џЮzWџУnLџЦlLџаpPџЮpQџЯrRџЯtSџЯwUџбzUџб{TџбYџЭƒfџЈ}YџиЦˆџІYџЫЖ}џГ—iџ›”jџš›}џrwbџbl\џbyqџ_neџƒ_џВЅxџЊ–nџˆvCЅЇ`ХЈ uџЂЋyџОЯ­џЉЈa§UV-гAџџџџџџX=#Ф’tŸђжСїїЪЊџ№ oџы’eџЕmLџFS=џRsAџ6W2џЁ…pџц•eџш“cџъcџыcџъdџь’fџь˜jџюœkџюŸmџ№Ќ~џщАџРœdџŸkOџЦЌsџЮЖ~џФЇiџ—wMџ3DBџIWџDKUџ2Dџ!-џ #џ џџ$$%џІ•џЃ’Šџ@96џџџџџџ{e?›kџУУœџЄ›vџФУ™џЅŸ{џ†eѕ iџџџџџџџџџџџџ…tK•€UЭЫСДџђѓьџѕјяџяёчџыэуџэютџщхлџхоиџфлзџшурџэючџщьфџЗЉЇџ›}џžkpџтЈДџНŽ’џФ‘•џеЊ­џlanџ@Wuџ;Njџ6F`џ1=Uџ*џ џџ џcYUџЏ”џ†wqѕ$9џџџџџџX4)]КБ‡џ—ƒfџУЗŒџБЅџ–€dџЦМџ5"ЛџџџџџџџџџSX-YЎГeщ‰[ч“ufУЅŽбФТПћзидџнпмџлмкџдгбџЧФФџЏЇІџžyџЂ†џЊtrџе–Ÿџг›ЂџУ‡‡џцЋЗџвšЁџ‘szџM`|џMcƒџDSqџ>Idџ8@Yџ"(8џџ џ#""џžŽ†џЌš’џ\RMБџџџџџџE=ИЅzџ›}`џгЦ’џ›aџГ›tџТБƒџiI;уџџџџџџџџџ{u?MЎКbЫЋКcCc`/џџџџџџ˜ˆMЙЌІљ–…џj][џ>@Mџq|џШХЮџЩА­џмšџщЌИџжŸџтЂІџуЌ­џлЄ–џА……џYjƒџ^oџVeƒџO[yџGPlџ@E`џ%*;џ џ џ€rlџЏ”џ‹|vѕ#/џџџџџџџџџg*%Ж™j§ХЉuџЦАyџЃYџмЩ‹џœzVяdL[џџџџџџџџџƒf?ЁF=#eџџџџџџџџџџџџi^vФКЖТВЊџŸŽ‡џSOQџH_vџšЏЬџДТоџЯК­џхЌЋџъЎИџхЖЂџцЎЎџеЃ­џЊŸџ„‡’џl|›џboŽџX`џPVsџHKgџ%џ џcYUџЌ›’џЄ“‹§XNJџџџџџџџџџџџџ|D)/ПŸh§кР~џПŸgџГŒZџсЮ‡џ†\:Ы џџџџџџџџџƒTG4 ›џџџџџџџџџџџџ’‡вЩХЭКЉЁџ†џ[VWџ8Mhџ‹ЄХџЇОгџЭЫФџцЖМџыЏМџъУСџВЁ џ‚™Ўџ”Аџv†Єџmx—џbg†џY\zџ15IџџdZVџЉ˜џЎœ”џre`­ џџџџџџџџџџџџœiBWЛžkдЛˆСПŸcџЗ‘ZџкФ{ћЁzL}џџџџџџџџџџџџ‰ZY„]YЕ;'"—?JDF9ПЗГсЈ™’џ”…~џlcaџ:BRџg}–џЅЙџЊЕМџвЌЗџЄЁГџ™ЌПџ’ЄКџ‹Зџ­џv€ џ_dƒџ-3Dџ,+.џtnџЌš’џВ ˜џ€rlБ=53џџџџџџџџџџџџџџџџџџџџџЄ‰e[ШЏl§Ќ‘_ћ­•YcA5џџџџџџџџџџџџџџџ–mk“jaszYAГxkEГ‚…RЋ‹•X­…‹RГrlDЕgR?Хw[UэwUSџoQNџaJFџYGAџЉoџxysџlwˆџ‰›Бџ Йџ‡˜ВџyˆЃџ^j„џ@I\џ88?џh^[џœŒ…џБŸ–џЕЂšћˆysŸF>: џџџџџџџџџџџџџџџџџџџџџџџџЏž{ ЛІoЙ…w^™3(џџџџџџџџџџџџџџџџџџџџџџџџlH2zOЂŸbЏЙkЊВc•TzbBvUK!Д ƒбПЛљБ‘џЄphџй›sџЭЊџok`џJHMџHIOџHHNџQNPџf^\џ†ysџЂ‘‰џЎœ“џП­ЅџЏž–ч€rmcB:7џџџџџџџџџџџџџџџџџџџџџџџџџџџE72{kZЁh`UџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЈœ–'кгаРЉЊэЏ{uџНˆlџК˜vџ•…|џЊ™џЋ™‘џ­›“џВ ˜џН­ЅџХЗАџОБЊп—‰ƒ…MEAџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ>+ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџvg`АЃЅ…„gЁmfнЌy_љЖ ‘ѓЬРЛыЫРЛыЦЛЗхНЕБЩ­ЅЁ—„Mh]YџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŒcgyGEч‚VB“XPK]SNQHC џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ’vn5{TUнd25IџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџjR>bA:џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ(@€ Bџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ )++) џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ-"m>74ЋMD@гXNJх_UPыaWSы_UPыXOJщLD@п:41Чc-џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ '"![PGCПqe`ѓŽxџšŠƒџЃ’‹џЉ—џЋš‘џЌš’џЋ™‘џЈ–ŽџЂ‘‰џ˜ˆџ€zџujdїRKFпЇQ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ/)&S^TPгŠ{u§Ђ‘‰џ­›’џА•џЏ”џЗЅœџТВЋџШКГџЬНЗџШКДџУГЋџИІџА•џА•џЌš’џž†џ‚|џi_[ы"БGџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџTKG­‡xs§І•џЏ”џБŸ–џЧИБџзЭЧџЦОЛџЅЅЅџ…Šџt|џr|џ~†‹џšœŸџПННџфкжџсидџЩЛДџВ —џЏ”џЂ‘‰џ{§XPLу‡џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ71/;nb]сŸŽ‡џ­›“џГ —џЬЛВџОГЌџswyџ1CMџ=NџD[џ Pkџ%[{џ+jŽџ&_џ"SoџF^џ?PџHXaџ•›žџсмйџеЪФџДЂ™џЎœ“џ™‰‚џvkeѕБ-џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ;M7#0*(Yh]Xѓƒunџwi`џk_Uџ‹yџSWZџŸйџ5ŠРџ3ƒЕџ/zЉџ+n—џ%]~џH_џ#@Oџ|ƒ†џтнкџШКГџАž•џЃ’Šџ€smћ'#!У5џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ-'K`U?У…vXїŠz\љzlQэl_FпcV>г]O7Щ\M3Ч[K0бbQ6ћnZ9џu^3џf2џ}c1џcP)џ20"џ*3џ&`„џ4„Жџ6ŠСџ6ŒЦџ:™йџ>ЃшџDВѓџ;œоџ7“бџ4ŠУџ1€Гџ-tЁџ*iџ'aƒџAVџ*=HџАААџмбЭџВ —џЈ—Žџtnћ&" С/+%;џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ NE3™†x\љЏ |џТБџЦЖџФВџСЎ†џПЉџПЅxџРЃqџМhџОœbџН™[џЛ–SџИ‘Jџ˜w:џrY+џoW+џ^L(џ4Gџ,uЅџ6ЬџAЊёџ@Љєџ@ЌјџFК§џAЎљџ=Ѓьџ9—лџ5ŒЧџ0Вџ-s џ-rџ*fŠџ"Qmџ9Iџ|„џтйеџГЁ˜џ’wџWNJћ)'#Уvsgй kџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџB8Cƒ\Peџg[uџj^{џh\xџf[rџi^nџpdmџxkkџ‡ugџ‹wnџfV^џНœeџбЊhџШ \џП—PџЋ‡BџlU)џ…h3џoV)џџ4Jџ%d‘џ;žоџ?ЊєџAЏћџHПџџIСџџBАќџ>Ѕяџ8—йџ4‰Уџ2‚Жџ-t џ&`„џ%\}џ"Qlџ3Cџhmoџржбџ™—tџ“˜‹џЃ}џЙЧ џ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ{o„›Ѕ˜Дџœ˜ЙџœИџБЄЛџГЌЩџОВгџФЗйџЖЈЮџ•„ДџАџwg–џ‹sSџжЎnџЫЄaџТšTџЖGџrY+џ‘q7џpX*џKЅэџ<žсџ6ŒФџ.wЇџ'c‰џ%\~џ&^~џG_џ,9џki\џТЧ›џСФ›џЛП’џЄЄyџZYIеџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ДЈЁеNEEs;9QaTW`Яcciїc”џooqџq‡šџ€џЮЩпџГЂдџ}Ўџq]UџиВrџЮЇdџФWџК’Jџ}b0џŽn6џoV*џZG#џhc:џ’Xџ’”YџoxLџGZDџ*JKџ#Reџ%a„џ*oџ,vЉџ-wЋџ-vЇџ)k–џ%`…џ"VvџF_џ8Lџ*7џ•…cџЎšwџЎŸuџБ zџЛЏ‚џŠx]љ%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџG@<#Ѕœ—е WOKЉ?7;{GCK›BKZѕQesџZfwџВ гџ”ƒЖџlYYџкГtџЯЈfџХžYџМ”Lџƒg2џ‹m5џnV)џbN&џ–›XџАЖgџІ]џ—ЁYџšІ\џ›Ѕ[џ–Tџu~Jџ_hDџNW?џ=K;џ6F8џ0@5џ4A3џ4@1џ:D.џVD0џ9,џЄ|ZџШГzџРŸnџЕ›jџУЃpџF0%љaџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџWNIKpfa—1,)70+)Mџџџ*&$y/(5ЗCPeџDM`џ›‹Оџ’‚Дџzf\џлЕuџаЉgџХŸYџЛ”Lџ~c0џ‘q7џsY+џbP(џАМiџас|џЮхџЪпzџТдsџБРhџЄБaџ›Ѕ[џ˜žYџ”–Vџ“šVџ™Uџ›Uџ“œUџ”˜TџœRџЎƒOџИ›cџдЕvџТ›aџШЉjџФcџФІeџЅ„Vџ Л џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџA,(%E0+(y '# C  (#!1:1G­9=XџLIhџ ТџƒsЃџžƒcџкГtџЯЈfџХžYџЙ’KџqY+џ–u9џy^-џaT,џЙОlџШеxџЩл{џШлyџШмzџШкxџФвuџМЦpџЗМlџДЕiџАЖgџАИgџ­ИeџЏНiџВНoџДМrџВАoџО­sџЫК‚џеЦšџОЎŠџЛЉyџЁ”iџvtHџѓ[џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ‹  u Q  #91Fѓ83Nџk^‡џЁУџn^}џвАwџиБqџЮЇdџФœWџВŒGџmV*џ…g2џ€c0џj`8џЋЄaџЏ­fџДЗlџЖЙkџГЖiџВДgџВВhџЎЋeџЊЄbџЊЁbџЈЃaџЋЉbџЉЉaџЌЏeџЉЉeџААiџ­ЉhџŸ•oџš‹iџœgџ‘cџ˜‹Wџ…xJџxJџrlBџ гMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџLEA=.)'… … }%"5 +#Q5.1m.$:џ9/Jџ‘ГџŽ~Џџ„nџоИzџеЎmџЫЃ`џТšSџЁ~?џw]-џnV*џmS*џ~gCџ‘yNџ“}Pџ—…Uџ˜†Uџ”ƒSџ„uIџ|nEџyjCџ€lFџŠqJџŽxNџ|Oџ‘Oџ•ƒQџ’Qџ—„Tџ–ƒRџ•Rџ—Rџ˜|Qџ”xOџ’uNџŒpKџx`?џjW9џfV9§ЩUџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŽxuG@<#B<9Ё50-150-‘A;7S EF?;u2)9Ч80;џhZƒџžРџ‡szџнЙ~џкДtџаЊgџЧ [џН•Nџ|a0џ€d1џsY+џ]>*џzQ<џ}U?џ{V@џ}YAџ}ZCџpL@џS+'џL#!џE!џ? џ>$џ<)џH4&џdI3џ|Y?џ{X@џ~ZAџYAџX@џƒYBџƒWBџS?џ~Q>џ~Q>џ{P>џiF5џY=.џV=-§ ЭSџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџВІŸЁ"ƒofa-lb^СKC?Ke\WГ‹‚|a51/wƒyv›%џ\Ooџœ‹ОџŒx…џйЗџнЖxџдЎmџЫЄaџУ›UџЊ†DџoW*џoU)џ]=$џj1.џn30џo52џn74џo95џvG>џ Ї”џЖЦВџНШДџЌЇџ–€_џˆsQџЈЇ’џ†nџZN6џ6"џ[0+џo73џm62џl51џt85џr63џo43џo33џn33џp43џm42џ_.,џL&#џF#!§Х?џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџФЛЖЃA;8ЏJFC+ˆ„с k‰‚}л@><{MHGз7/@їgZџ•„ЕџЅ™œџчжЄџцЫџйГrџЮЇdџЦžZџМ–Oџw].џlT)џ];"џ_%џe(џi *џk!+џi!+џk&-џ„iBџ ЃnџЖС‹џАЛ…џГСџИЪЇџШиГџЉГ|џДХŸџЭпРџ›Ђlџ@)џd *џd *џj",џc)џd)џg+џj ,џh,џh+џl!,џj!,џY$џFџ;ћ Г3џџџџџџџџџџџџџџџџџџџџџџџџŒ‡ƒ‘LFBхqojfы ЯD=Eљ4,BяfXџ€Вџ‰w–џ€fYџaNџЂq[џХЈ‰џъмВџаЎiџТœSџp8џbL%џ[5"џf%џo(џk(џn(џm)џl)џo*џ”sGџЄЃUџ””]џЖН™џзуОџЩдŸџЂЇsџЧбЎџТЬ–џ ІvџШвВџz[Qџ€>RџŽK[џ}=Mџ}6Gџ‰AUџ‹@Wџ•Icџ’C\џ†4Lџ~-@џq1џj,џV!џM!џV01ї С[џџџџџџџџџџџџџџџџџџ(2F)37š’џДœБџЌ“ЋџЃ‰Ÿџ—v„џZ[џ‡O?џM:џІL>џЊRDџŸQEџVPџšƒџцвЭџЪ‹џ˜hDџ†N2џЁM;џžH:џЅJ<џЊM>џЋO@џЌQAџЎRAџЌQAџ­SAџ­VDџЌWCџЌVBџЃR>џm<2џбЩ–џ•x\џœˆiџркЁџaLџШОџЦЙ‰џI*#џ‹jNџpMџšzUџ—wPџЂVџ’oIџ›sJџ]@џ–bGџ’`NџРŠ‚џиГЈџфХНџЊŽrџx`Jџ•–„џ›ˆ]џgV1ѕ­7џџџџџџџџџP'E1ЛчЛЋџфКЋџг‘vџШwWџУoNџЩpNџЭoOџТfJџœS;џDM0џEЅџFЂ џctfџпІџЬyXџСlKџОgIџПeIџЮmMџШjMџЪlPџЪmPџЪoPџЩoPџЫtSџЫtRџЬvQџЬwSџЫvSџРv\џŽbIџхй—џЙšnџž}Xџрв’џŒdHџкЭ‘џŒ…[џФПœџЁlџ…qџuzaџzŒџduhџ‘Žjџœ‹eџСДџЊ•iџЏždџ|_8ЋЂXеЏž|џЎЊwџ™ЂzџРеКџДГoџ†Bљ%%Й;џџџџџџџџџqA*ІlOГщФ­§њчкџђСЂџыœmџцŽ`џф‰^џЮwSџŠT<џ.[*џ3wџ2vџ8hHџИ_џсŽaџз„Yџн…Zџф†[џтƒ[џт„\џт„]џфˆ^џхaџцbџц‘dџх‘bџу’cџъЁuџкІyџсЃxџДoMџИ–aџфб‡џН–^џШЎpџПšaџtcFџq{bџ6Eџ2JKџ,9џ7Dџ NdџEVџ+%џvldџЉ›‡џЄ“ŠџjhIэ()ЄЌa1АИ_ї˜uџСб”џЎНџЮсКџСЪmџšžOћ(*З5џџџџџџџџџџџџyK2—a@EжІƒз§ыкџќаЌџїЋyџє pџнˆaџšdGџL]Zџ‰…џv}sџImrџКƒ_џэŸmџєЄoџђmџя™kџђšlџђœoџђ˜lџєžqџё rџ№ЃsџѕЉuџїБџњТ–џѕК‹џбЋqџЃ’[џž{lџЈƒaџЩЗ‹џЦЗƒџЖ–`џШЏjџJ?4џ1=џ3Dџ6Fџ%1џ,;џ;Pџ*8џ џ“ƒ}џАž•џŸŽ†џ0*(ГџџџЛЦgЛ›ŸTџЬлКџПЩџКЦžџЭл џ­ЖfџІЎnћЅџџџџџџџџџџџџџџџbPЪЄе§ямџ§рЛџјК‘џхЅџГ…gџ­˜‚џšџ…˜—џ–…џЧ™sџщА†џюГ‰џѓЕ‹џђБŠџђЎˆџђЏŠџѓА‹џєБ‹џїЗџѓН”џњгЉџљвЈџМ—mџ xZџŸ|qџЧЕšџА–~џСЅ—џаЗЋџХЙЁџspaџŸbџDD@џ*9џ+:џ#/џ )џ!,џ&3џ џ "џЇ–ŽџАž•џ}wџsџџџџџџžžUeЂ›QџœrџиуНџŸŸmџЯйДџžgџТШŸџvtJяy џџџџџџџџџџџџџџџ‹tS3ЕЁƒѓћїыџќёжџёдЙџкЙŸџЖ›ƒџЉ’zџЌ—~џЬД–џлРЁџлНЁџрПЅџфТІџсМЅџуМЅџчРЋџыЦЎџъФ­џінСџћюЮџщаДџЗœ†џžpџ•ofџтБЕџкЎЏџПЃ–џЋŠ€џnlsџ9MdџFJTџOSXџ3?Mџ-=џ%1џ 'џ "џ џ џ џQJGџЎœ“џЏœ”џh]Xї/џџџџџџcS.!ƒqIћkџХШЁџХФšџ–tџУС—џ—tџПМ’џ>3#з=џџџџџџџџџџџџџџџџџџ“|OaЃs§ѕєяџ§ўєџјљыџэцйџхмЭџтйЪџцлЬџ№фгџшиЫџуЯХџтЭХџоХСџпЩШџщгаџ№хнџјїщџѓёцџЬКВџxSPџЁv}џфЊЗџК’‘џ orџуВЗџлЎАџВŒ‹џ6:Lџ:Pmџ7Kfџ4E^џ1@Xџ-;Rџ(џ !џ џџџ џŒ}wџАž•џЅ“‹џC<9Уџџџџџџ/hM:сЫХ™џ}aџуфВџ{bKџппЎџ|dMџопЎџ€iQ§ ƒџџџџџџџџџџџџџџџџџџ ™‰OяwXџТЗЎџьяыџіќїџєќіџёќєџюљђџэј№џыѓыџъђыџъ№ъџыѓэџэјёџчёъџцюшџХУСџ_aџ”w}џЫ˜Ёџ˜cfџлžЋџфЉЖџФ–šџГ}€џлЋАџrp~џGYtџIf‡џATqџЉ†klЩЎЈІїЩЦУџдваџйзжџжедџаЫЫџЪФФџЗЏВџЃ™œџaXџƒdeџЅ€ŠџХŽŽџЕuqџъЋКџкЂЈџДzrџтЄЏџъЏМџкЁЉџ›swџLYpџPcƒџL_џGVtџ@Kgџ=Gbџ8>Wџ.2Gџџџ џџxџЏ”џЊ˜џVMHб џџџџџџџџџџџџKŸиЮ•џЋŒjџВЁvџЦВƒџ˜}^џиЫ“џ…aLџодšџM%"лџџџџџџџџџџџџ:5œЃUеТеrЧ›ЅVAc`/џџџџџџџџџnb_-РЖБїšˆ‚џŒ{wџm^]џ=6=џRXiџy}џТНРџШОЦџШ‘{џхІБџъЊЙџмЌЁџЫŠzџыЌЛџфЎАџжЃџлš”џЁ|…џUg€џ_nˆџYjŠџSb€џN\zџHRoџCKgџ>B\џ26Kџ џ џ џdYUџЌš‘џАž•џ‰zt§$Qџџџџџџџџџџџџџџџg( ƒЭН„џиЦŠџ˜qQџхи–џŒaFџъпšџŒ_EџУД€зoJ;[џџџџџџџџџџџџzd;m‰€Gл**џџџџџџџџџџџџџџџџџџЈžšƒиЫХџАž•џšŠ‚џTNLџ*:Mџt’ЏџОЧлџФЬыџХОСџк „џщЊЖџыЋКџчИІџм†џъЌЙџщЌЕџщНГџП™џt{‰џj}œџcs’џ]j‰џV`џOWuџLSpџEGcџ'+<џ џ џF@=џЄ“‹џАž•џЅ”ŒџNEBЇџџџџџџџџџџџџџџџ€@(ЫЖz§шзџœmGџщйџškFџщиџБ…WџB2 ‡џџџџџџџџџџџџW?‰M8$Чџџџџџџџџџџџџџџџџџџg^w ШПЛЧЪЛДџЏ”џ”…~џRMMџ-?Tџc…ЈџЉОпџЉРнџЧЧХџрЙЁџщЌИџыЌКџыФЙџфОЇџцЋЕџyn|џ…šЏџŒ™џt…Ѓџlz™џeqџ]dƒџV[yџPTrџHIdџ џ џ?97џ…џЏ”џАž•џma\лџџџџџџџџџџџџџџџџџџT2ЋеРџфвŠџЉzLџтЯ„џЋ|MџсЮ„џШžcџ7 ]џџџџџџџџџџџџџџџyF?GjC:чe џџџџџџџџџџџџџџџџџџ~qv#ибЭхУВЋџЏ”џ•†џ_XXџ$3EџXs˜џЂКжџЃНгџОЫЯџпШОџыЏНџь­НџыЙСџхЭХџ‹“џ‰ЁКџ„™Дџ{ŒЉџuƒЁџlv–џekŠџ]aџUXwџ!/џ џJCAџŒ…џЏ”џГЁ˜џƒuoэ2,*3џџџџџџџџџџџџџџџџџџM. rI­МЇtMтдЋ“Ј~OџнЦ{џЊOџмЦ{џР gПO0џџџџџџџџџџџџџџџ=™olЃV60Ыo/  @€uv9кгЯыПЏЉџЌš‘џ™‰‚џwmiџ16Bџ—nYeŸƒZ™­ЄhБНТyБЦдБУб{ЏЕКo­ —`ЋtRЉZHЋ†YUлmmџŒZXџMKџmC>џžmVџцЋyџнС“џoqkџ5BTџVg~џ`sŒџ`sŽџ\l‡џO^vџ6BWџ"+9џ348џ[SPџ‹|vџЅ”ŒџЏ”џЕЂ™џСЏЇџˆztЧI@=#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ…k85œŽlљF@51џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџTJBЛБЋuчрнѓаТМџЋŽŒџЁmgџа‘rџсЇwџЛЄƒџwqdџYROџUONџRLKџTNMџ`WUџvjeџŽxџŒ…џЊ™џЏ”џВ —џЪИАџВЁ™ѓqd_{*%#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџr`Usre\yџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ…voЮХС—холѓД•˜џЈplџЦwџНŒfџНЃ‚џ‰{tџЉ—џЉ—џЊ˜џЌš’џЎœ”џАž•џАž•џН­ЅџгХОџРГЌѕˆzuMEA!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ>+ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџohПВЌsЉŠŠЫБ|§Ћt^џП“rџЎœџОЏЈџТВЋџУЕ­џЪМЖџгЧСџжЬЧџШРМљБЉЅЫ~snuKC@џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ}W[U’VSї\GчЋˆpлЏІЂБЛВЎБДЌЈЋЇžš›Ž„yi^YMMD@џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŒdh’mqзp;;яR?Й џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ›yЉtFL§e24џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџjS>a?:MnDJџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ(€ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ!3CMSUUUUSMC1!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ/Sy ›Е"Ч*&$б2,*з60-й82/й820й71.й3-+й+'$з!ЯХ ЙЉ•yS/џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ;qЇ4.+ЯMEAщbWRѕpc^§ylfџqkџ‚tnџ„vpџ…xqџ†xqџ…xrџ„wqџƒuoџƒuoџ€smџ}pjџuic§i_ZїTLHы720нЫЕ™o=џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ% a#ЉD=:лeZUїykf§‚tnџ‡ysџyџ•…џ›‹„џ ‰џЄ“‹џЇ–ŽџЈ—џЈ—џЈ—џЈ–ŽџІ•џЃ‘ŠџŸŽ‡џ™‰‚џ‘‚{џ‰{tџ„vpџƒvpџƒvp§ujdїPHEч# ЯЙ•_%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ+y<42ЩaVQѓzmgџ‡ysџ{џš‹ƒџЄ“ŒџЊ˜џ­›“џЏ”џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џЏ”џ­›“џЉ—џЂ‘Šџ”…~џ‰{tџ†ysџ‰|uџ{qkїJD@с ЧЇs+џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ#{G?<еma]ћ‚uoџzџžŽ†џЇ–ŽџЎ›“џА•џАž•џАž•џАž•џАž•џАž•џАž•џА•џА•џА•џБž•џБž•џА•џА•џА”џАž•џАž•џАž•џАž•џАž•џАž•џА•џ­›“џІ”Œџ—‡џ†xrџŠ|wџ‘„~§_WTщЫЋo#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ]D<8Щl`\ћ„vpџ•†џЄ“‹џЌš’џЏ•џАž•џАž•џАž•џАž•џЏ”џА•џИЅџХДЌџаТМџйЮЩџоеаџфкеџчнйџчнйџфкжџпеаџлЯЩџбУНџЦЕЎџКЇŸџБž–џЏ”џАž•џАž•џАž•џАž•џЏ”џЌ™‘џžŽ‡џŠ|vџˆztџ”‡§^VRч ХŸUџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ 170-ЇeZVљ‚toџ—‡џІ•џЎœ“џАž•џАž•џАž•џА•џБŸ–џМЊЂџаСЛџхлжџєяьџѕђ№џьшцџлевџЯЧФџУМЙџПЙЖџМВЎџПЖВџЪУПџкаЬџщокџєышџћієџ§ћљџїђ№џцокџвФОџОЌЅџВ —џАž•џАž•џАž•џАž•џЎ›“џЂ‘ŠџŠ|uџŠ}wџ’…ћB<:нН‡-џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ [RIEнwjeџ’ƒ}џЄ’‹џЎ›“џАž•џАž•џАž•џАž•џЗЅœџбУЛџьфрџѕёэџниеџЕЌЈџ‹„‚џgghџLUYџ;IQџ/CNџ)?Kџ&>Kџ%ANџ)HXџ'BPџ/EQџ;MXџP]dџnswџ˜“’џЦЙДџэсмџќїѕџћљјџюшхџеЧСџКЇŸџБž–џАž•џАž•џАž•џ­›’џœŒ…џ…xrџ‚{џtidёЩЃO џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ 70.‘eYUї‰{tџŸŽˆџЌš’џАž•џАž•џАž•џАž•џПЎЅџргЪџ№цоџгЩТџœ’Œџbaaџ7DJџ#:Fџ5Dџ3Cџ5Fџ9Lџ?SџE[џIaџ!Pkџ%Wsџ MfџG^џBVџKRџ 9Gџ4Cџ6Gџ>QџG_џ!Plџ$Xxџ'`ƒџ)hџ,n–џ.sџ3~Ћџ5ƒВџ/wЁџ-q™џ+k’џ)eˆџ&]~џ"SoџIaџ>Qџ5Eџ2@џ%=Iџ]hnџДА­џђэыџќљјџчокџРЎЇџБž–џАž•џАž•џЎœ”џ ˆџ…xrџŠ}vџQIFчНƒџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ  џџџџџџџџџџџџџџџџџџџџџ/LC@Эzmgџ•†џ ‡џ…џ’ƒ{џ‡yrџ‡ysџЖЅžџбРЗџЉ˜‘џfb`џ2CMџ?QџBUџF\џ!Ojџ%Zyџ(b†џ*j‘џ-p™џ-tŸџ/xІџ1}­џ3‚Гџ5‡Кџ:“Ыџ:”Ьџ5ˆМџ4…Жџ2Бџ1}Ќџ/xЅџ-rџ+l“џ'b†џ#XvџJcџ=Pџ6Fџ7GџBOVџІЂЁџђэыџћњљџогЭџЗЄœџАž•џАž•џАž•џЈ–Žџ‹}wџ‡zsџaXSя Х‹#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ) U y …}kYI=5-)%##%)U@96сeYUџqfaџre_џg\Uџ[PGџSG:џOC4џQH>џTLGџQKHџ,8>џ-9џ0>џAVџ&\zџ0vžџ3~Њџ0wЃџ.vЁџ/yІџ0{Ћџ1Аџ2‚Жџ4‡Нџ6ŒФџ;—гџGЕяџ> оџ7Ъџ6ŒФџ5‰Пџ4…Кџ2Дџ0}Ўџ/yЇџ-tŸџ+n–џ)fŠџ%\|џ!RmџH_џ3Bџ-:џCPUџЙДБџјієџѓюыџЩИБџБŸ–џАž•џАž•џЋ™‘џ“ƒ}џ„vpџj_Zѕ Х‘!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ # a?7)ЗbV?щwhMљ|mQїreKя\Q<уG>.г70#Ч'"З ­Ѓ ™ “ ‘ ‘ —Б1)!ѓF;0џPC3џVH1џ\K-џeP,џmV*џt[-џ†k5џs]/џA4џSG'џ%(џ (џ'4џЄъџ>ЃыџAЊєџ?Єьџ<тџ:™мџ8•еџ6Эџ5‹Хџ3†Нџ1€Дџ/zЊџ-tЂџ,o™џ*j‘џ+iџ*hџ#WtџG^џ4Eџ)5џ1@GџЏЉЇџћљјџърмџЙЇŸџАž•џАž•џЎœ“џ™‰‚џtnџeZUѕ У…:%$.{!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ#/*‡^S?щ„uYџЁnџЗІ€џФВ‹џЩЗџШЖџУБ‹џНЋ…џЖЄџВŸzџЎ›uџ­˜qџЉ”mџЉ‘iџЉ‘gџЊdџЉ`џЉ^џЋ\џЋXџЌWџ­UџАSџВPџГMџДŽJџГFџЄ?џ~c0џ[G#џŠl4џtZ,џlV+џ‹p9џ"$џ*:џ$\џ.wІџ1€Дџ5‰Тџ;™йџ@ІыџAЉёџCАљџ@ЊєџA­љџCВ§џDДћџGКўџBЏњџ>Ѕ№џ=Ђъџ;œтџ9—йџ7’бџ5ŒЧџ3†Нџ1Гџ/yЉџ-t џ,n˜џ+l”џ0vЁџ,l‘џ(`‚џ"Qmџ@Uџ.;џ5Aџˆˆ‡џѕёяџ№щцџМЉЁџАž•џАž•џЎœ“џˆztџg\XџWNJяП}LTNu\@<§ Е%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ/D<-ЛpdKџ€cџЅ–uџАЁџЗЉ†џМ­‹џПБŽџХЗ“џЪЛ–џЭ͘џаϘџвП—џгП•џгН‘џдМџжЛŠџзЙ†џзИƒџиЗџбЏwџЭЋqџгЎpџбЋkџЭЇfџЪЄaџЧ [џУ›UџП–OџЛ“JџБŠDџo6џbM%џ‚e1џ„g2џ[G#џy]-џR?џ џ6Kџ%b‹џ/|Џџ4ˆУџ7’вџ>ЃъџFЖўџ?Їѓџ>Ѕ№џ>Їєџ@ЌљџFКџџJТџџEЗџџAЏќџ@Љїџ>Ѕяџ<žцџ:˜мџ7“вџ5ŒЧџ3…Нџ0Вџ.yЈџ-s џ.uЁџ.sžџ*hџ*gŠџ)c†џ"QmџH_џ;Lџ+8џaceџъхтџєюыџОЋЃџАž•џŒ‚џueSџA:7џ`VRџE>8яBC<хšЋЃё„gџ Гџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ 93(УI@:џRHGџVLPџXOXџXO[џXO[џWOZџVNVџYRTџ\VSџbZRџjbUџvmZџv_џŒcџ™‰hџА›tџФЊ}џЬА~џЋ‘kџp^OџgU=џСŸhџиБqџгЌjџЮЇdџЩЂ^џХXџС˜RџН•LџЖGџšy;џqY+џjS)џ–u9џhQ(џpV*џlS(џ!џџ5Lџ V{џ+rЅџ4ŠЧџ< чџFЙўџ?Љіџ?Јєџ@ЋїџAЎќџCДџџMЩџџIСџџCЕџџBА§џ@Ћјџ>Ѕ№џ<žхџ9˜лџ6‘аџ5‹Хџ2„Йџ0}Аџ1~Џџ2~Ўџ*l”џ'cˆџ&^€џ%\}џ'^~џ!QlџF]џ4Dџ%/џX[]џшсоџѓьщџЛЉ џ–ˆuџŠ‰dџZXRџ:73џ|{dџЄЊ…џжю蟘rџ С5џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџF==CJ=\џfX„џqbџvg•џzk™џ|mœџ|mœџ{kšџxi˜џue”џqbџn_Œџj]ˆџfY‚џcW|џ`Tvџ[PlџUJ[џSHRџYMYџ^Pvџsd‘џOC[џ„mHџкГtџжЎmџаЉgџЫЄaџЧŸ[џУšTџО–NџЙ’IџІ‚@џ}b0џXF"џŸ}=џy^.џ^J$џ‚e0џB3џџ%џ+=џ=Xџ!Y‚џ/}Дџ>Ѓпџ@Ќёџ?ЋїџAАќџCДџџEЙџџMЩџџQгџџGОџџDЗџџBБўџ@Ќјџ>Ѕяџ;фџ8–зџ6Эџ4ˆТџ4‡Оџ5‡Мџ0{Ћџ*k•џ(c‰џ&^џ$Z{џ&]~џ%Yxџ$VrџE\џ/>џ",џTWXџцрнџьфрџš–cџЌЗ”џпьдџмшгџЊВ|џЌЗ‘џиъЫџЁЈmџ гiџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџGЄьџ;›рџ8•жџ8”вџ6‹Хџ0~Бџ1~Аџ+mšџ(eŒџ&_„џ%[}џ%Z|џ)d…џ&\{џ Keџ?Tџ-;џ")џ[\\џШУБџ‘’\џкхТџмчТџЬжЎџ‘UџакЖџЯиВџŠ‹SџjjYяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ7…ї—‰šџXNhџeh†џjy•џl|–џco‡џvm~џ‘ƒ–џ€ џš–ИџБІЦџЧНкџуи№џ№чќџёчќџпбђџСЏпџЁУџœ‹ОџŸŽСџ”ƒЖџk\ŠџF:?џНœgџкВsџд­lџЯЈeџЪЃ_џХXџС™RџМ”LџГFџp6џ\I#џ’r8џp7џVD!џŠl4џiQ'џ4+џkbCџЂ™kџ„~XџQN7џ-83џ3>џ8NџLbџ&c|џ/{™џ=ЗџBЌЯџEГтџHНяџJСіџJФћџIО§џFЙўџBАћџ@Ї№џ@ЅыџBЌђџAЇчџ1€Жџ1Вџ-sЁџ)hџ'a‡џ%]€џ&^џ)d†џ#Usџ MhџH^џџAHџHWџ Tiџ&c~џ+r”џ0Љџ4ŠМџ7’Ьџ9—жџ9šлџ8—иџ8”гџ9–еџ>Ÿпџ1Дџ/{­џ0{Ћџ*l—џ'c‰џ%^џ)dŠџ&^~џ NjџHbџCZџ;Mџ-<џF.%џЈŸxџчхЏџХКџwZCџЁ—qџцфЎџНВ‰џjQ;џйжЃџфс­џZI:ѓMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŸ’ŠЯЫСЛћ*%$}џџџ+!7 #YymjѓSKNщAG\ѕVh~џTm~џwqoџaehџkŽŸџq–ЈџN]cџRVZџˆЇџнещџиШ№џЅ”Шџ Тџq џJ=]џ’yQџмЖvџзАoџбЊhџЬЅbџЧ [џУ›UџО–NџИ‘Hџ˜w:џgQ(џc1џ›x;џbM%џz_.џd0џ;1џZW2џ‚~Hџ}{HџƒLџ”•WџЈ­eџЇАgџ•ž\џ†Lџah>џ@M6џ-?6џ#;=џ:Fџџ)8џ$/џ?$џ€hOџцпЂџЈŠkџd?4џЖЉ{џыфІџЦЖ†џ^5-џПД„џыфІџВvџ3ѕIџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ3.+5аЧСя…zuз/џџџџџџG@=qgbй//$<-"9A6/5Сk`^э9dџŽvPџоЗxџиБqџвЌjџЮІdџШЁ]џУœVџП—OџЙ’Iџœ{<џiS)џx_/џŸ|<џdN&џz_.џ‰j3џ<3џ†LџХбsџФЯuџКФpџДРlџВНiџЃЎ`џ’ŸXџŽ›Uџ“Vџ— Xџ Њ\џЇВbџЁ­aџ Њ_џœЄ[џ“˜Vџ…‰PџIџuvCџhl?џ]c;џ[a7џV^7џPX4џNW3џSZ4џZ\5џTZ4џTZ4џ]d8џbi;џwf>џЁ\>џ M4џD/џ`DџЙ jџхг‹џфв‹џЩЁlџŠT9џФЎsџчжŽџЬЈpџU:џ–~UџћХ1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ ka[sАЁ™ёY џџџџџџ,(%SA;8Еџџџџџџ*'$c2.+Љџџџ'"$s/*8§L_wџGXkџ7=FџOZnџЏџЉ˜ЭџЁ‘Фџ‡wЇџJ=^џЁ‡[џоИyџйВrџвЌjџЮЇdџШЂ]џУVџП—OџЙ‘Iџ›y;џgQ'џe2џz;џ_J$џ‡h3џˆh2џA9џ™UџФвxџбс}џас|џЭт~џЧлzџХзwџЦзuџМЬpџБСiџЉЗbџ™ІZџœVџšSџ˜Tџ˜Uџ—žXџ˜Yџ˜›Yџ™›ZџšЂ[џ›Є[џ• Zџ” Yџ–ЃYџ–ЃXџ“Vџ•ŸVџšŸWџ˜™Vџ’OџЂ}HџЙwIџЃb:џc<џЖŸcџоЪ~џмХ|џйИwџН‡YџЅyLџгНwџлУzџХ“^џЄtIџЭЗrџкФ}џ‡dDџ пoџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ 7i_Yysicг= џџџ50-‰!aџџџџџџ+'%™A (##u7-EїAKeџ9@Nџ@NdџLOhџ‘ГџІ•Щџ Уџ‚rЂџF9RџЙ›hџоИyџйВqџвЌjџЮЇdџШЂ]џУVџП—OџИ‘Iџ™x;џ`K%џ‹m5џ›y;џS@џ“r7џ}`/џE@"џ Ї^џЮоzџвт}џЮр}џбщџгю„џвыџЮф}џЯт{џЭтzџТдtџСвrџМЮpџДТkџЌИeџЄ­`џžЄ\џ–›Xџ‘Tџ“Sџ“Sџ‹’RџŠ’QџŠ”QџŠ•Rџ‡“QџŽ™Sџ—œVџŽ“Rџ‘•Sџ››XџЃ›Zџ­š\џВš_џФАqџмМ}џжАyџС—fџГŠUџТЅaџеМqџгЕuџР“aџАŒTџвЙnџЯДlџЩžgџjK3џ!ё­џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџGYLD?]HA=Б9џџџ,(% џџџ -(& џџџ)$(=<2L§9>Wџ9?Xџ;C]џSKkџš‰МџЄ“ЧџŒРџvf–џPBJџвАwџнЗxџиБqџвЋjџЮІdџШЁ]џУœVџП–OџЖIџ”t8џWD!џ™x;џ“r8џN>џ }<џmT)џUQ-џАЖfџУЬuџЩжxџЮс}џЮу~џЬс~џЫр|џЯх}џЮцџЭфџЯч}џЭс{џЩиwџСЯsџРЬrџПШpџДЛjџГЖhџЖЗiџВЕhџЌБdџЋГdџ­ЕeџЎЙfџЅБaџЈЕcџЉЗeџЉЗfџ­ЙiџАЛlџВКmџБЗoџЖЗtџЕЈnџЗŸgџВ—\џКŸZџЪЏ`џбИoџйЧџЬИџЋ‘bџМ YџЩ­hџЛІwџИЄ|џ{qIџ41$џ§нe џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅ)2-*361.ЁYџџџ $ iџџџ'#!5џџџ =5>};3Nџ:џcCџ†lHџˆpJџ‰pJџˆpIџ‹uLџvKџ‰tIџ’{Pџ‘zOџˆsKџwNџ‘zPџyNџvMџvNџvNџsMџ‘tNџ–wPџ•tNџ‹jHџnKџ‘pLџlJџ‹jIџˆhHџ…fEџy]?џaK4џ`L3џr[>џlZ=џA6$љ ЫŸUџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‰{tз*&$Й1:51;MFCлY1,*A=75ЭK 1-*‘ [<74e&# wUF;Hћ>5@џ$.џ2(Aџzj˜џžРџІ•Щџ€p џkYcџдД~џсМ€џоИyџкГsџеЎmџЯЉfџЫЄ`џЦŸZџТšSџМ•MџЁ@џpW+џoX+џo6џUC!џ“r7џQ=џcE1џ€Z@џ}X?џ}ZAџ‡bEџƒ`Dџ|]AџaDџ„dGџƒdEџ}^CџtWAџWџrQ;џ_C1џU>-џgL7џfN7џ<- љ ЭSџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ&"+Џ•ё#Е3UMHOod_щgOHC]VNJзYZRLЃs+cZTWUMHЁ#-Ы6,@џbWYџ(џ`R|џ—†ЙџЌšаџ‰yЊџeUmџЭЎ{џсН‚џпЙ{џмЕvџзАoџвЋiџЭІcџЩЂ]џФWџС˜QџЗJџŒn6џTB џ•t9џmU*џtZ,џrW+џN2#џrD6џtG8џyK:џ{M<џwL;џvM<џzP?џxO>џwP>џySAџ}UEџ}WNџyHCџo:5џh1-џe.+џ]'#џX# џV" џS!џP"џM$ џK(#џ?% џ/џ'џ)џ>)џV:+џkG5џ|R>џ{R>џsM;џvO=џzP>џ|Q>џzP=џzO=џxM;џ}O=џ~O=џP@џ€N@џwH;џyI;џzI;џ{I;џwG9џxH;џ}L>џ{K=џmC6џiA4џ\9.џM1'џ[;.џ]>0џ8%ћ ЭMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџH@;CЦЗЏљ($"Л=od_U”…ѓ}#i_Yi|pjхm‡Ѕ50-‘?mc]C†yvс )§$/џKAFџOBgџ|­џЋšЯџБџkZtџТЅyџсОƒџрК}џнЖwџйВrџд­lџЯЈfџЫЄ`џЧŸZџУ›TџО–OџЇƒBџrY,џjS)џ‡h3џZF"џ†g2џG/џf4-џn81џv=6џo92џm:2џt>6џt?7џn<5џo=6џsC;џtC;џzMCџš—‰џЄАЁџЎИЈџДИІџА­›џІ–‚џš|dџiNџV;џuL1џ{T9џŸˆtџІ“џu_џtYDџO<+џ4'џ%џ/џP-%џh;2џm>5џo>6џt@7џq>6џq>6џp>5џp=5џxA9џ|A<џu=6џq;5џu=7џu;7џp96џr94џu;7џt;7џp95џs<7џv=8џk83џ`4.џV.(џK)$џS.)џO-'џ1ћ Ы“A џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ_UOQзЫФћ50.ЩM{rmSЙЊЃћ™9nc\iЕЇŸѓ‰+Г­Їd]YГ[504OЁ˜”їџ&џPCiџ‡wЈџЊ™ЮџŽ~Џџp^wџЧЉ{џсОƒџрЛ~џоЗyџйГsџеЏnџбЊhџЬІcџШЁ]џФœWџС˜QџЗ’Jџn7џXE"џŠk4џ]H#џ~a/џM7џ]''џj,-џj,-џh++џk..џp00џl/0џl22џl12џm22џn44џm22џ}ZHџœЁ‰џЇЗžџЕЫДџТнЧџЬыжџвђоџЪцвџРдМџИОŸџЌЁsџ™‰UџnhEџЃЖŸџЧрЫџНЭЕџЕЗ—џ›‘hџtiAџ9/ џ"џ9џa,+џn11џk/0џk/0џi//џh..џi./џq02џl/0џp02џs02џj,/џj,/џn.1џn.2џk,/џg+/џn.1џp/2џj-/џl.1џj/0џ\)*џO##џFџN#$џJ!"џ+љС‡/џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџdYTSфкдћNGCлitokGбЧСћ"Л]YPJcеЫУљЉIЂž™‹™Šй™"уPKMџ2*=џ^PyџŽ~ЏџЉ—Ьџ†vІџ‚t€џжПŽџчЩџфСƒџпЙzџлДtџжЏnџвЋiџЮЇdџЪЂ_џЦžYџТšSџР˜Pџž}?џiR(џpX+џkS)џmT)џV> џU"џb 'џc!(џi#,џm%-џg#*џf#*џn&.џn'0џi$,џk&.џm)0џs=5џŠnJџЇЇgџЏДmџЈЎfџ› ^џ’˜]џ“›mџЊЙ–џРжКџбъвџЯчЮџТвАџИП‡џ™œ[џ–nџТиПџгэеџЮхЬџЙХœџžЁ]џWS0џ!џ;џg$,џh%,џd$*џd#+џe$+џl&-џg$+џg#+џe"*џe"*џm$.џj#-џh",џh#,џj#-џg",џd!*џj#,џp&/џl%.џg$,џf#+џ] 'џM!џCџK!џBџ# ёЙy'џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџSIEGхнйљvkfя‰%`ZU/мдаѓRJEн‡1+'gаЦРї;52г‡A=<•yspї ї#-џK?bџsd’џœ‹ОџЃ’Цџzk–џЃ™“џьфЗџёщМџёчКџюсЎџчв—џоП}џе­kџЯЈeџЫЃ`џЦŸ[џУ›UџСšRџЏŒGџ{a/џ[G#џsZ,џbK%џZ@!џW!џc%џe'џd&џf'џj)џi)џp,џh(џh)џh*џh)џn,/џŒvDџŠ…Gџ‰ˆ\џЄЉŠџЧзДџарЖџбу­џЪйџЛЦ‚џЇБjџ–ŸhџЋЙ–џдчЪџдшЬџЧж­џИХ}џ–ŸZџІГ‘џжъЭџзыЮџЧжВџВПqџhj6џ=џb)џb)џf+џe)џl -џk,џd)џ]&џ`'џf*џa'џf*џm.џl-џj,џf+џh*џl,џj*џl*џs!/џi*џd'џX$џJџ@џJ џ?џ"чЗy'џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ0+(1бЪХѕ‘…ћ ЕW/КЕБп{pkљУŸvplѕ50-їлх<4GџQDiџo`ŽџАџЉ—ЬџŽ~Ўџyh{џЈ—vџГpџ­”hџПЅtџйФџьпЕџіювџютЙџоЧˆџбЌfџШ \џФœVџТ›SџИ•MџŠl6џXE"џoW+џ\H#џZ=!џX џi&џm(џl)џg&џj(џi(џh'џn(џg)џh(џf(џi'џn&,џŠNџМХcџЋАVџŽIџ„‡UџЏИ•џетТџкщШџицХџЮлДџЦд™џЏКoџ“ZџЖСžџйшЧџзхФџСЮ–џЃЌ^џ‘˜kџЭлЙџкщШџдрТџ fџiA6џo"9џv+Aџx-Aџ9Kџ„>Rџk'9џs+<џ~0Hџ}0Hџ|+Cџ.Hџ€/Kџ€.Iџ,Eџ€+Bџx#;џp/џr-џp.џk*џm)џg)џc)џ_&џU!џHџBџX")џT(,џ%сС‹Gџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ7xspщkd_џчГ‹?<:йNJHџ ѓ ы-'4ћ<2IџMAcџcUџvg–џ•„ЗџЌ›Яџ™‰Нџud…џyf[џv^Bџi=1џp-0џr)3џv+6џ{15џ„I;џЎeџщмЙџѓыЯџнШ‰џЪЄ\џУœTџПRџ•v;џ^J%џhQ(џ[F"џW5!џ`#џk'џn(џq(џs)џo)џo*џt)џs*џp*џq*џu*џp*џq*џt+џr%+џˆX@џ­І^џЋЇWџ›•Lџ…Aџ~|QџЋАŒџйфОџнчСџншТџвлВџЙО…џ‹Oџ’”oџлхПџмчТџЪвЅџІЉgџ}MџНХЁџИК џM-8џ—^rџ cxџ~HXџ—[iџЉkxџЅgwџb*3џe+4џ TfџЄ]oџŠL]џƒCSџЈ`xџЊc~џЌgџЉc|џЇ^yџ Upџ—Ggџ”G^џŠ8Lџz)>џw"7џv 4џp-џd%џZ#џLџN"џuBDџh;:§1ы ЭЅk/ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ•'".ѕ.)4џ#)џ )љ(!3ѓ5,CљJ@^џ[Muџk[ˆџte“џ€pŸџ”„ЕџЊšЮџВЁзџ–†Иџtcџo[TџqR:џj5-џ„4<џ”HSџЁ[gџЊjsџЊjvџЅbnџ›RZџˆ8>џ…H7џЩВ…џяфУџйУџХІZџ›z=џeO'џ]H#џP;џ]-"џr#*џv%+џx%,џ{&,џ~'-џ~'.џ(.џ…).џ}(-џ)/џ†+1џ€*0џ|(/џ*0џ‚+/џ‚+0џ‚,/џ€,0џ~/1џŠaBџ’|Hџ„n>џ‘|Jџ~UџrdDџЂ ~џмсЖџрцКџсчЛџПП’џ‡wKџzsTџинВџсцЛџйоГџІŸpџl\8џaWEџC-0џІs~џВ}‰џl77џn=>џИzџБp{џz><џc53џЕv€џЛy„џu@Bџe66џУ†•џЧ›џ—goџ’biџЩ’ џЧŽœџУ‹—џЛ}ŽџИy‰џЖw‡џ­jџЁYpџ—H[џˆ8Aџs+0џa#&џS!џX'(џ„]TџzK?џL*"§+сЙQџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ/";[WHrћsc‘џvg”џvf”џue“џyi˜џ‚rЂџ‘ВџЅ”ХџДЂдџФБуџХГшџЌ›Яџ„tŸџo^mџlWJџnI6џ{:0џ…41џ˜BFџЌajџДptџМ~џТ‰ŒџЩ—™џЫ››џФŽ‘џМ€ƒџЉ\[џ‹J7џПЅpџыпИџЊ‹JџjS)џU? џj8&џw3,џ‰54џƒ10џ‡20џ‡22џŒ33џ‘76џ‘64џ76џ‘65џ•96џ•:7џ‘87џ‘86џ’:6џ98џ’;9џ”;7џ•=8џŽ:5џ‰95џŽ;5џD3џ|S8џgI2џЙВ‹џАЁ{џwXAџo_HџббЄџфхДџфхДџЩХ™џrU;џqaJџклЌџфхГџстБџ”‚`џK0!џ%џ‚PRџžXYџ…O>џc7+џw75џ88џt?2џd9*џr./џy34џx?3џf6*џ›AIџЅLYџwB;џsF@џЧ„Žџб™ŸџжЈ­џжАВџрКНџи­ГџбЂЉџЫ—ŸџТ‰’џЖ{‚џždfџz@:џg0*џZ1.џbA<џ‘taџ…\Dџ_<(џJ4&ёЧЃe#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ>-?“Ё“КџулёџтдёџпЭяџмЪэџкЧэџеТьџЪЗуџЙЈвџžЙџp“џiXgџfNHџtN8џ€H4џ‘B5џœC8џ›C9џЅMCџЋTKџ­WLџБb\џКumџН{qџЯš•џядЯџєурџрКГџЪƒџЋWKџX8џДœ`џpV,џc=%џŒD3џŸG9џ›D9џ>5џšB8џŸD:џŸD9џЃG;џЄH=џЂG<џЁH=џЉL>џІJ>џЄI>џЇK=џЅK>џЃJ=џЇM?џЊQDџЉPAџžK=џЃM>џЉQ@џЂN>џ L<џ„@2џ_,%џ{jSџшц­џЮХ–џ‚]JџW4,џЌЃ}џчхЌџчфЌџЬХ”џoF8џeM>џнйЅџчфЌџрнІџ{ZFџ/џ=џ_,$џqK5џy[<џf<-џlB2џwS9џuV7џzQ;џxO:џZ<џlL1џuA1џw>/џY9џh?,џŽ?;џžJHџ†QCџ}UBџЦƒ†џмЋЊџчФТџьбЮџьаЮџсМКџЫЃ•џЄ}fџvO:џsJ;џydYџ}ymџЁ•wџ–xOџwY4џcL/§4-л­s)џџџџџџџџџџџџџџџџџџџџџџџџџџџ?!I]9;џˆ^eџŒjuџ„epџ~`kџ|^hџwX_џvTQџRGџ‡S@џ‹Q;џ‘P:џЁS?џЁO=џЎS?џЖYCџЎSCџГXEџГYEџЅP=џ™K9џ†B3џN2-џ776џACDџj]]џЫЛЕџћёэџфЛЎџа€џЖfOџiG)џ•P8џАXAџАWBџЌT?џЁN=џЌS@џЉO>џГTAџЖVCџБTCџГVDџЖXDџЕXFџДYFџЗ[FџИ\EџГXDџДYEџИ]FџЕ[EџБ[FџВ]HџЙcLџИaGџГ]FџВ]FџЕ_FџД]EџŠB4џPџЅ˜qџътЅџсиžџ›y^џ\))џƒoUџумЁџъуІџбХ‘џ`/+џu]IџшсЄџъуІџдЪ”џW('џcQ>џ­˜sџЏšnџš‡UџМЅwџбЖƒџЗiџ“NџкИ€џлЙ€џИœdџyIџлЖ{џбЊoџЄ‹QџŒqCџГˆVџŸsHџ•}Iџv\6џ’M;џЄUJџšfSџЊ€gџпЋŸџ№вЫџѕулџнЭЗџГЂtџ|hAџxjTџ–œŒџЁБ џЕДџЋ›dџxBџtb6џD>'сГy1џџџџџџџџџџџџџџџџџџџџџџџџP'X.!ЧЁX@џтЊ™џюЮУџлЃ’џЩ…oџФy^џНlMџЖdHџКfHџМhJџЙgKџИbGџХjKџШjLџФeJџЦgKџЛ`GџЋXAџ˜O8џp<.џ0@9џ4o#џ@”џ?џџGahџБ‘ƒџьХГџрЇ’џЪvYџУmPџНgIџКcFџЗaFџЗ`EџД^EџД[DџХdIџШgKџРbIџХfKџУeLџУeLџФgMџЧiMџТgLџПeKџФjMџФiLџРgLџТkNџЦnOџЧoOџХnNџУmMџЩsPџФpNџРkLџУnMџРkLџŒF8џ\)$џШК„џъсžџърžџРЇyџm1,џz[DџхлšџысžџУБ~џa($џЏžqџърžџьрЃџylMџ”Š_џЭО“џХЖˆџЈЂeџШКŠџзЮЃџбЦ›џЂ—`џТИџМКšџХЛџ‡Sџ ЅџжЫІџЕІfџЈ”[џиР‰џпЧ“џОГoџ‹SџЮ­qџГŒVџ•„Gџxb7ѕ—aBѓЌzYџЛ•wџбЙЃџгЩВџРП‡џ’ŽPџ†‰iџБЦГџЙбМџЙСšџЗБlџ‘Kџ…z@џSO.щЕƒ1џџџџџџџџџџџџџџџџџџџџџџџџe6$;ƒI2эвŽsџњшрџќёьџёбСџщЗЁџфЈŽџн•sџд€YџЪvRџЯxSџдzSџжzUџз{UџгuRџЩlOџСgIџЅY>џŠL5џ>FEџ3sџMНџVжџVжџGЎџ:q4џZp|џЬ‰iџхЅ†џл‹fџЪvTџЩsOџЧsOџЩrOџСkLџУkKџбrPџеtQџжuQџаrQџЭnQџвtTџвtTџбtSџЯrSџбvTџж{UџЯvTџЭuTџе}Xџз]џб{WџбzTџе~Uџе€Wџв|Uџд€Zџд~Vџеƒ]џзoџоš}џ\JџzK4џйЪŠџъм–џъм–џЮЗ}џŽO7џŽoLџшк•џын—џЇƒZџyI3џфе’џА rџ„€]џŸŸfџЗЛŸџЪЭЖџЎАqџihSџ#6=џ;LIџPYCџ BTџ!PkџGlmџMgZџJeџ*>DџŒZџ[R5џ—†{џДЂџГДkџ‚_џА‚џОЎŽџЙКhџ__0ЃbD)f9k€DыˆzCџ‘~XџЋ›…џЛС‚џŸЄ[џŽ˜rџФмХџЭчаџСЫšџОСlџЊЇSџ”‘Iџbb6я Л3џџџџџџџџџџџџџџџџџџџџџџџџqA*S‘W<яй™yџљнЭџўјєџћъпџєЭЖџяЛ›џщЁxџх“cџу_џтŠ^џуˆ[џлYџм€YџпXџПlLџЇ]AџkB1џ7YJџ1{ џ>™џ=–џ=—џ>˜џ(gџTƒџ—jTџш™kџр‰_џмˆ]џзƒYџгVџЦuPџи~Vџр‚Yџр€WџтƒYџиyVџн€[џрZџо[џл}Xџл~Yџт…\џп…]џнƒ[џрˆ^џфŽ`џсŠ`џпˆ_џп‰^џуŽ_џпŠ]џк‡\џу_џф”gџхœwџшІ‚џэЏџьЌ‹џл”nџЃZ:џœyQџчжџцеŒџчжџЫБsџ”S3џРЊoџчеŒџзТџP2џЕhџvlRџНПІџ‡ŒUџ3@џ :Gџ`iKџ2<џ*8џ1:џ&;:џSџC[џџ->џ-<џ1Aџ.=џ'4џ#/џ".џ&3џ.=џ5Hџ8Kџ.=џ #џ џџ…wrџА–џАž•џАž•џ­š’џŒ}vџ:31ы_џџџџџџџџџџџџƒ…DIЦвo§БДXџ‰‰EџВ̘џкщШџврНџХаџŠŽSџЫиЖџичХџУЯ“џЋГ_џƒ…TџаиЂџАЗnџFF(сЁK џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџbPQРiёјиЖџџіъџўщвџ§нЗџќЬ™џњЕ‰џј­„џыŸzџк•qџЙ‚aџœqUџГŽsџЂКЙџ_zzџs€|џdtoџVz~џЊЮеџŠ‚mџЙˆbџеžtџщЊ|џѓБџ№ЏџёЏџєА‚џіБƒџєЏ…џїЎƒџіЊџѕЉџїЎ‡џљЎ†џіЈ€џѕЈџѕЈ‚џњЎ„џјА…џѕЏ†џѓБ‡џєКŠџјР‘џњЫŸџ§ж­џ§еЊџђЦ˜џмЈ{џдœpџМ‰bџ„aGџw\IџаФЉџЌЃoџЇpџжД­џЗ›џБœƒџРІ™џбЗЉџЧЕ›џЖЎџŠ]џ•’‚џЬИ}џВœ`џˆwbџ џ+;џ,:џ,:џ.>џ(5џ#/џ!,џ +џ"-џ&3џ,;џ+;џ !,џџ џ*))џ™‰‚џАž–џАž•џАž•џЈ–Žџrlџ% г7џџџџџџџџџџџџJD ­В`хЈЅSџœ˜MџzyKџзфСџлшФџЬжЎџАГtџŒŽeџкчФџЮйГџКС{џsrBџЬеВџСФџŒŒR§&%Э‰1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‰kNeР›x§јуЦџџњ№џў№иџ§чФџћбЇџєЗ“џђИ”џрЊˆџЦ™{џГ‹mџЊ„fџЗ•xџНК­џЏТЦџЏУШџ­АЉџ„lџІ„fџСšyџЮЄ€џсБŒџшДџчГџѓМ”џѓМ–џ№З“џђЗ’џюГџёЗ”џьЏџшЊŠџёИ”џ№З”џєМ˜џ№И•џђЗ”џіОšџљС›џљХžџяХЃџќоИџ§фПџќмБџюФšџЄ„aџj@џ„hOџlVHџЈ…‚џмАВџЫІџиЩЕџЃ‘kџЂ†pџеГЌџШЋЁџжИ­џмШРџиЬЧџРЖЄџ‚uWџ+Qџ*;Qџ&7Mџ*9џ%1џ +џ 'џ $џ "џ  џ џ џ џџ џ џ,*)џ“„}џЏ”џАž•џАž•џ­š’џ„vpџ2-+у=џџџџџџџџџџџџџџџџџџP<#){_;ѓwnSџСО–џŒtTџ‰‚dџтчКџсцЙџКЕŽџs\?џВВџтчЙџГ­†џpY=џИИ’џтчЙџЗБŠџwaEџ#ЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЄ’a5ŠpGщ’z_џчтзџџџ§џўџїџ§ўяџќўыџќќщџѕэлџчзШџоЪЙџнЩЖџеФВџзХВџоЯМџпаПџфжУџђсЩџєчаџшеУџфаПџщдТџсЪМџоХКџсЫПџуЪОџтЧОџиМИџсФРџывЩџсТПџцЪФџялбџїъмџћјцџќўъџќ§щџічзџжОДџtoџnJFџl[XџЖ‹“џшЎЛџеЈџШЉЅџ†]VџˆWYџнЋБџуВЗџтГЖџрГЕџЭЁЁџУЃŸџ]Zџ&'2џ0C\џ9Olџ8Njџ6Lfџ5Ibџ3F^џ2C\џ0@Xџ/>Tџ.>Uџ'7Kџ (џ 'џ $џ !џ џ џ џџџџ џ џ]SPџЃ’‹џАž•џАž•џАž•џЂ‘‰џoc^џЏџџџџџџџџџџџџџџџџџџ/ lH0еpcLџфцЕџЁlџ]D2џззЊџфхЕџорАџ…hMџqbJџхцЖџнпЏџ„gNџsdLџхчЖџпрБџ›ƒfџG2#ѕЅ?џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŸZiŽtGћ‡mRџкеЬџўџўџџџ§џќџљџњџѕџјўєџјўѓџїћ№џѓѕъџё№хџэъоџщчмџъужџьфкџюцлџэуиџютиџщмгџсаЪџтаЬџхдЯџфгЮџкФХџйСУџцежџрЯвџцадџђыцџѕѕюџїќёџїўђџїўђџіјёџпвдџКЌЊџЋ™–џdGHџHFџ}IQџшЋЙџыЏНџыАНџЭ–ŸџЭЎЌџŽ`_џŽZ`џхВИџуВЗџтГЖџрГЕџвЇЈџВ‹ŒџL/7џ9OkџVџ09Oџ+3Fџ џ џ џ џџџџ џ џ џWOKџ…џЏ•џАž•џАž•џЉ—џtnџ'"!Щ'џџџџџџџџџџџџџџџџџџџџџџџџK‘V:0џснЈџфсЊџ}UFџhRAџцуЌџчфЌџЭФ•џ\-(џž”qџшх­џЬУ”џa3-џ™Žmџшх­џтпЉџtH<џ.щ‰џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ 7‰KуЊœZџ•|Jџ|\<џ`MџЃ‡џЯЯЩџъяыџѕљѕџїћјџїћјџѕћїџѓњѕџёњєџюљѓџюјђџэјђџьјёџьјёџьјёџьї№џыї№џыї№џьїёџыї№џъі№џьїёџыї№џтьхџипйџфышџдгбџБЇЋџ‘…‡џjAAџa()џjrџфЈЗџюЏПџб”ЁџаЋЈџ~AAџ­pyџьЏНџыЏНџыЏНџпІБџЧ“—џЕƒџŸdfџоЌВџЪЂџGEPџC\uџLg†џJc‚џNlџPu›џCTrџASpџ?Plџ=KfџVџ2:Qџ17Mџ(џџ џџџџџ џџ#! џ†xqџЉ˜џАž•џАž•џАž•џšŠ‚џbWS§ y џџџџџџџџџџџџџџџџџџџџџџџџBuS+'џмжŸџъфЉџЂ†hџP&$џдЭ™џшуЉџуоЅџuF<џnZFџъфЊџцсЇџƒXIџaF9џцрЇџъхЊџ—x^џDћ ЇџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџUNO(ЙАЖ^§ЗИeџАЃcџЎ—gџЃ…cџjSџ{L?џxJFџŠjjџБЅЅџвдбџрфрџхщхџщышџьюыџыюыџыюыџыюыџъюыџшьщџшышџчъшџцъчџфчхџтфтџпрпџиизџЧТХџƒџB;џƒorџndgџaWZџv`fџhlџМ„wџŽKGџтЄВџэЎОџюЏПџб“œџиД­џ‹H?џП€†џьЏОџыЏНџъЏНџъАМџкЂЋџЛ||џœ`]џžx|џ?K]џH\wџOcƒџMaџLaџKaџGXvџDRpџ@Liџ>IdџHcџ;A\џ9=Vџ7:Sџ+/Aџ џџџџ џ џџ942џŒ~wџЋš‘џАž•џАž•џАž•џ›Œ„џf[Vљyџџџџџџџџџџџџџџџџџџџџџџџџџџџ/ 1UџНАџъсЁџщр џ`Mџb>2џшп џытЂџВ™pџZ$"џЭС‹џъсЁџмб–џm0-џŽyYџыуЂџсж›џx?8џ4Гџџџџџџџџџџџџџџџџџџџџџџџџџџџ @@!sŸЈUѕеэ€џбщћЊЗa­„BQc`/џџџџџџџџџџџџџџџџџџџџџ^SJqe`•УКЖџЁ’џ‚soџyifџtccџo``џjYZџ`QSџ^MOџ\MQџYKQџVKRџXQZџb]gџ‡„џЈЂЇџВЎНџаВЇџМdџР~xџь­НџэЎОџэЎОџсЁЋџуНБџЦvџЕmYџуЅВџэЎОџэЎОџиœџыЬОџЦtџУ{iџпЁЇџыЏОџш­ЛџoxџN^sџbw•џ]arџ\k…џYl‹џVf…џSbџP_}џO_~џJVsџGQnџENjџCKhџAJfџ>C^џ<@Xџ9=Uџ,1Dџ џџџ џ џџџzmgџЃ’ŠџАž•џАž•џАž•џ­›“џ„vpџ4-+Ч!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ. g&!ћЏžpџъпœџърџЙ™nџg/'џЬП†џъпœџжШџo/'џЈ–jџърœџърџšhNџkD3џырŸџзШ“яŽcPЧM-#џџџџџџџџџџџџџџџџџџџџџџџџџџџC=!C—–NяЪйuџБОcй€ˆCIQT(џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ_VaЇš’нѕяьџПЋЃџЎœ“џЌš‘џЂ‘Šџ€rmџk^Zџ$џ"0џ?TpџZv”џ}•Џџ­ГЦџшухџѓёѓџрпяџСЙШџхЪИџТ~ZџбџэЎОџэЎОџэЎОџтЂЋџшТДџгœ}џТzcџцЈЕџэЎОџэЎОџсЂЉџшСДџшФ­џЫ‡dџи”ˆџЭ–žџB>FџCTfџav“џcy—џat’џ^oџZj‰џWf…џTa€џSa€џO\zџLWuџISpџFNiџDJfџAF`џ=B[џ=@Yџ'+;џ џџ џ џ џџ_UPџœ‹„џЎ›“џАž•џАž•џВŸ—џ—‡€џ\QMѕ WџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџA p/"ѓБœlџъ혟ъ혟еФ‡џ†E2џЉ”fџы혟щ혟ŽP9џŽpNџып™џън˜џЙ—iџs<+џСЌwћK?+w'gO8 џџџџџџџџџџџџџџџџџџџџџџџџџџџqc8ГГ­_џŠˆHп<<3џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџh][_зЮЩ§шнзџГ ˜џАž•џА•џЁ‰џrlџdZVџ!џ"/AџMj‰џnЎџ•ЋТџФШжџцфёџджюџПЩъџКГКџэЭЕџЫ…\џоџэЎОџэЎОџэЎОџхЅЎџьШЗџкЂ{џа‰pџчЈЕџэЎОџэЎОџшЉЕџуЌЅџёгСџшСЄџЭgџlYTџO`tџdx”џh~œџfy˜џbt“џ_nџ\jˆџYf„џWc‚џS]{џPYwџMUqџLUrџJTqџDHcџBE_џ@C]џ(џ џ џ џ џ џLDAџzџЊ˜џАž•џАž•џАž•џЉ˜џxkfџ)$#ЁџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџP'|;&ёА˜f§ък“џък“џцж‘џЂeCџ‘qKџък“џьн•џЈtMџƒZ<џэн—џък“џЫБwџˆH0џnW:уEџџџџџџџџџџџџџџџџџџџџџџџџџџџ †j@ѓ–‚Kџ+'“џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџbWьmeЃŠyЏђэыџдФНџВŸ–џАž•џЏ”џ™‰‚џrlџ[SPџ$џ'6Iџ\z™џs–ДџŠІПџФЪчџХЬэџЙЧъџЏСсџСИЖџябЖџбŽcџпЁЁџэЎОџэЎОџэЎОџцЇ­џђаМџуА‡џж“nџфЇЌџэЎОџэЎОџэЎОџъЌИџтЖВџрЦЕџуИ•џЊ~џs„žџpƒЁџm}џix—џer’џbnџ_k‹џ[e„џV^}џRYxџPVtџNTqџLQoџHKgџFGcџ<>Vџ џ џ џ џ џ?86џ‰{tџІ•џАž•џАž•џАž•џГ ˜џŠ|uџG>;е)џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ% I-ѕЎ“b§чжŽџчжџщиŽџДSџŠ_=џчжџшзџК\џƒQ3џцдŒџчжџеСџž[9џ8%У5џџџџџџџџџџџџџџџџџџџџџџџџџџџ'#‹bB§w\8џ …џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџxъd[b#­Ё›сњіѓџЦГЋџАž•џАž•џ­›“џ“„~џsmџUOMџ&џ+Uџџ џ џ џ=75џ‚tnџЂ‘ŠџЏ”џАž•џАž•џДЂ™џЈ–џpd_ћ'#!}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџwF) d<џаЙzџыкžџчж•џтЮƒџЛZџ™g>џрЬ‚џсЭ‚џХЁeџ–]8џоЪ€џсЭ‚џмШџЙyIџJ+“ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџNEХ}O@џ$Нs!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџPFъ†~ё{чrp}рйе§ючуџЙІžџАž•џАž•џЌš’џ‘ƒ|џ„vpџe]Zџ )џ,>џ;WyџRtžџ˜ЏЯџЊТпџЇСмџЅРйџЂМбџЗРРџ№эсџзТІџфЌДџэЎОџэЎОџэЎОџь­НџхЏЕџъаЧџюрЬџзЙЈџaZ`џ`r„џ˜АџŠЄНџ„šЕџ“Џџ{ЊџwˆЅџtƒЁџržџmy™џhr‘џel‹џag…џ]b€џZ^{џVZxџUYxџIJeџ џ џ џ џD=:џ‚tnџЁ‰џЎœ”џАž•џАž•џГЁ˜џГЁ™џ|niџ<52ЇџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџG* ДvHЫžwMџУЌrЩЙЈ€cуиМЏэп­§З‰TџnCџоШ}џпЩ~џПš_џ›e=џмЧ|џоЩ}џнШ€џНŒ[љR2aџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџg42WЇvmћM'щ™i#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ…€ёnfЁ ˆ{v—ьчфџьупџЗЄœџАž•џАž•џ­›’џ•†џ„vqџ{plџ%)0џ 0џ,AaџKi”џ’ЋЩџЇНеџЄМвџЃНгџЄПеџАРЦџуфпџоЮПџхГМџюЎОџэЎОџэЎОџэЎОџ№ИЪџьШгџудаџжЮРџ˜ЃЉџЄМџŽІПџŠ Кџ„—Гџ€‘Ўџ}ŒЉџy‡Ѕџw„Ђџq|šџmt”џioџfj‰џbe„џ^`€џ]_џLQmџ$џџ џџULHџ‡ysџЂ‘ŠџЏœ”џАž•џАž•џГ ˜џЙЇžџ‡xrџH?<Й џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџV5 žqK‰d?_gS1 џџџ—zGвј›Г†Zџœ{LџлФxџмХyџЙ”\џ–lDџлФxџмХ|џКЂhЫfO0= џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ= —hfЏ•jdџE"Яq9џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџzoя†€ђf^~ ™‹†­ёьщџъснџЙЅžџАž•џАž•џЎœ“џšŠ„џ‚voџŠ}wџDBFџ)џ.Gџ;V}џšЙџЇОеџЉСиџЈТиџЅТйџЇОЮџЪдгџйгЩџуВНџюЎОџэЎОџыГХџГ™Ќџ–›Ўџ“ЁЖџœЌЙџœЅЊџ‹›АџŽЂМџŒЁЛџ‰›Зџƒ”Бџ€ŽЌџ}‰Љџz…Ѕџs}џpw—џlq’џilŒџeh‰џbe†џBG_џ$џ џ џ*''џf[VџŽyџЄ“‹џЏ”џАž•џАž•џГЁ˜џОЌЃџŒ}vџSIFЧџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџyc=OА†\§ЅŒWџйСsџкТtџЕ“_џ–wNџкСuџž…PЗB5џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџEЊ†…Ы•lfџU,#г c?#   ) 21-8‘†€БшфсџтлиџВ ™џЉ—џЉ˜џЊ˜џŸˆџ„wqџŠ~wџrjhџ"'0џ/џ%8VџWu–џšГЬџЈОгџ›ДЦџ™ВЦџЖШџ­НФџФНЖџоЖРџыБУџ{‡џŒЂЕџšГШџ™БЦџ™АЦџ—­Фџ“ІПџЃЛџŽЁКџŒŸЙџˆ˜Дџ„“Џџ­џ~Ќџx‚Ёџrz™џov–џnr”џ]`џ+3Eџ џџџHA>џzmgџ–‡€џЈ–џЏ”џАž•џАž•џЕЃšџСЎІџ’‚|џVLHХ!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ…qWeЖ›tџОІbџжМnџжНnџЌeџ˜WџЈŽTЧ=0џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџU$'Є}{ЋЏŠ„ћzMBёQ4(Е wi]SMMMOQSWY]]  eSLHЧ™•“џš˜џ~snџvidџymfџ}pjџ}pjџsfaџlb]џvpџTPQџ +џ 2џ&:UџH]tџXesџ^jwџn}Šџˆœ­џšЌМџŸž џЃ•џžЏџœЕЩџДЩџ›ГШџœБШџ™­Хџ•ЉРџ’ЅОџ‘ЃМџžЙџ‰˜Гџ‡•Вџ„”Гџ€Ž­џ|…Ѕџx~žџflŠџ:C[џ+џ џџ833џl`[џˆztџ ˆџЌš’џАž•џАž•џАž•џЛЈŸџФАЉџŽyџXNIПџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‚nQ'СВ‘гпЫџгИhџЙЂ_џЎ—yџua?б=0#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ> ˆ\[_Ћ…€зŸseџW@ѕnP3йVF)Л>6Ї.+›"!“ "%'“+.—01™23Ÿ32Ѓ1.Ѕ.)Ї,$Љ)Љ'­<-)лdWTџl__џ^PNџUGEџWKGџYNJџYOJџULHџJC?џRKFџb[Wџ?=>џџjXFџ…zdџZZQџ6=BџIS]џy‡–џ›­Сџ‘žЏџšЉЛџЂЖЬџ ДЪџŸВЩџ ГЬџœЎЦџ—ЈСџ•ЅПџ”ЄОџИџŒ™ЖџŠ•Гџ†Џџ{…Ѓџ_j‡џ4BYџ 1џ џџ722џf[Wџ‚tnџ—ˆџЇ–ŽџЏ”џАž•џАž•џАž•џФАЈџСЏЇџŒ|vџUKGЅ!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ †XиЩЃЏеМpџЂgџmя!GџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџS$# wHAQ™n_Ї›sZн €ZѕЋ”c§ГЈlџЛИu§ФЩ§Ые…§Яоˆ§асˆ§ЮЩжџРЩzџЖИrџЋІjџ ‘aџ—€ZџpTџ‰cOџ‡ZMџ†QLџ…KJџƒDEџ‚CDџƒMLџuEEџj;;џlFAџaHAџH;6џP?8џБ~[џщЊuџыЛ…џьбЁџКГ•џZ]Zџ$.<џJZpџt‡Ёџ‹žЗџ–ЈРџšЌФџ›ЋФџšЋХџ—ІСџ“ЂНџ’ŸЛџŠ˜Дџ~ŒЉџky–џP]yџ3AZџ+?џ+џ џ!"&џIB@џoc_џƒvpџ”…~џЄ“ŒџЎœ“џАž•џАž•џАž•џЕЂšџаМДџЙЇŸџ€rlѓMC@џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџˆn;WЛЃ`§Ђ™ƒџRK?• џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџS. {W=+ŽsMI‘Qa˜ŽYeЂŸcgЉЋgeЎЕkeВМlcАЛjc­ЗfaЊБb_ЁЃ\[—”VYŒ€MW‚pGUy`BUsTYџ';Uџ$7Pџ/Eџ&9џ,џ "џ!џ))+џGA@џh]Xџ{nhџˆ{tџ–‡€џЄ“Œџ­›“џАž•џАž•џАž•џАž•џФБЈџиХМџЊ™’џse`сD<9M џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџƒj6yŽ^џ‡qы)%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџTJBœˆ—скжїў§ќџщрмџОЋЄџІ…џ­Œџœtxџ–`[џЇm[џЭqџр›lџ№ЎwџпЎyџЋ˜xџЦПЅџupfџ;65џA==џ98:џ0/4џ(*/џ%'-џ%',џ'(,џ--1џ646џD@?џVNKџi_Zџzlfџƒuoџ‰{uџ“„}џ ˆџЊ˜џЏ”џАž•џАž•џАž•џА•џНЊЂџоЫТџЪЗЏџŒ|vљ]RNЃ*%#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ+% cTBЗ‹}rћME>aџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџPG@{mfCМАЊбѓюь§ћјїџтигџОЌЄџЈˆˆџ nsџЊzvџО„rџж—yџи™nџпЊ~џФšqџИЁ~џЦОџhaZџla\џrlџrlџrlџrkџrkџ~qkџrlџ‚tnџ…wrџŠ|vџ‘‚|џ™ŠƒџЃ’ŠџЊ˜џЎœ“џАž•џАž•џАž•џАž•џАž•џЛЉ џнЭФџрЮЦџІ•ާpd_еIA=O$ џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџH94)‡slчaQJƒџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџTIB Š{tiЪПКнѕёюџќћњџтздџЅ‚…џŽ[`џЈqnџО…tџЪ’xџМŒnџЁxYџšxZџгЙџœ{џqkџ›‹„џЅ”ŒџЂ‘ŠџЂ‘ŠџЂ‘ŠџЃ’‹џІ”ŒџЈ–џЊ™џЌ›’џЏœ”џАž•џАž•џАž•џАž•џАž•џА•џВŸ—џФГЋџтзЮџыодџЕЄџylgуRIEq)%#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ=*'?, џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџh[T’„}oХКЕзяъчћїѓѓџЈ†‹џ™fmџЁfcџР‡xџеšџнžuџпЂrџТŸzџФЊ†џvjcџ•†~џ­œ“џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џАž•џГ ˜џПЎІџиЫФџячсџшозџГЄ§zmhнVMIw=73џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџUIB …unUЕЈЁГвХСїФЉЌџ›mrџ­wtџЛoџ­w_џ‹iUџЌ^џеЊ€џЅ‘}џ—‡€џЋš‘џАž•џАž•џАž•џАž•џАž•џА•џА”џБž•џЖЃ›џПЎІџЯСКџржбџѓэъџђэъџЮЦСџ—‹†їj_ZЙOFC[;41џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџWKDqc[!}ukЉ‹УЎ†‰љЎ‰ˆџВtcџУ~aџН†cџР ‡џЩ­’џЕЋЅџШНЙџеЩФџеШТџеЧТџеЪФџкЮЩџтигџшснџёышџѕёюџ№эыџлжгџЛДА§‘‡‚щla\ЛRJFqD<8#B<6џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџNE>{``/…adБSPёЈeTџœhSћА{[џТ™|џЌœ‘џЦНКџсзгџрзгџмдаџзЮЫџаЩЦџУЙЕџАЇЃџœ’ї†|wлrgbЕ]SM}PFBKH?;G>9џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџrFMq‡PWѕŠSQѕƒH<чa<-Л–hJЛЁwXГF>:g`XR_dZUccXTc^TO_YPKSSIECQHB)MD?NF@џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŠaf3y~чioеAAљj;/л“]DхЅsW›џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџšzy ‚a`Н—qwч|IPћ\)+э]5.гwJ: џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŠldŸЖž§ˆ`fћ`%.џn77ёb;6Oџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџu[H#vjщˆccџc4;џ]*2Џ8() џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџjS>EcE;Ѓ`99“nDJџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџtkcvs-8.2.3.orig/tkcvs/bitmaps/locked.gif0000644000175000017500000000053211664612512016506 0ustar timtimGIF89aЅ? **- 7&%%%>-!999D'J1T, K:,m=o@uChH+nL2BBBgggzzzZ3”]+šb)›f3Јm(Ёm9Џr*Їr:Д{8ЋyCВ~EЙ=И…IКŠRОWЛœtУŽSТ”\֘dЭЃmЭЂrвЉy†††”””ЈЈЈЗЗЗгЎ‚зАƒмО›кР›уЩЌхЮАыаЏьдЕЩЩЩйййэпЭёфдшшшѕюфџџџ!љ?,wРŸpH,€dђxИt=eЩ*НЈNЇ 6=„в•ky‹N€аСЮD0…š† јМ^ >,‚ƒ„,}C<…ŒˆV‹Œ„‡~‘“”?Š˜ƒ•‰—†šœЂŽ–’ЄЁŸVВГДЋЄkЙКUМCA;tkcvs-8.2.3.orig/tkcvs/bitmaps/branchtag.gif0000644000175000017500000000024511664612512017177 0ustar timtimGIF89aТ‹Eџь‹џџџџџџџџџџџџ!ўMade with GIMP!љ ,XHКмЮ`…ч€d*ЛDХСe „xЯљ Nˆ.мјMR™ ƒ ŽЇ…<ХфŽb™І4№dфЄMjь„У†Ъ˜љ н5Ћ&Ж%АF\™Ќл ;tkcvs-8.2.3.orig/tkcvs/help.tcl0000644000175000017500000014636511664612512014572 0ustar timtim# # Tcl Library for TkCVS # # Help procedures and help data. # ######################################### # # Developers: Please don't majorly change the formatting of this # file unless you know what you're doing. # The script "mkmanpage.pl" builds a manpage out of it, and the # thing is the product of an unbelievable number of hours spent # tweaking this file and the script so that both the help and # the manpage look sort of OK. # # If you do add something to this, do "mkmanpage.pl > tkcvs.n" # to keep the manpage in sync, then look at it to make sure # it worked. # # - dorothy ######################################### proc aboutbox {} { global cvscfg global cvsglb toplevel .about wm title .about "About TkCVS!" frame .about.top message .about.top.msg1 -width 400 -justify c \ -text "\nTkCVS Version $cvscfg(version)\n" -font $cvscfg(guifont) pack .about.top -side top -expand 1 -fill both image create photo Tclfish -format gif -file \ [file join $cvscfg(bitmapdir) ticklefish_med.gif] label .about.top.gif1 -image Tclfish image create photo Anglerfish -format gif -file \ [file join $cvscfg(bitmapdir) anglerfish_med.gif] label .about.top.gif2 -image Anglerfish append string1 "A friendly interface to CVS\n" append string1 " * and Subversion *\n" message .about.top.msg2 -width 400 -justify c \ -text $string1 append string2 "\nConsult the Help menu to\n" append string2 "learn about its features.\n\n" append string2 "TkCVS was written by Del.\n" append string2 "The Subversion functionality\n" append string2 "was added by Dorothy.\n" message .about.top.msg3 -width 400 -justify c \ -text $string2 append about_string "Home page: http://www.twobarleycorns.net/tkcvs.html\n" append about_string "Source code: http://sourceforge.net/projects/tkcvs/\n" message .about.top.msg4 -width 365 -justify c \ -text $about_string -font $cvscfg(listboxfont) pack .about.top -side top -expand 1 -fill both pack .about.top.msg1 -expand 1 -fill x pack .about.top.gif1 pack .about.top.msg2 -expand 1 -fill x pack .about.top.gif2 pack .about.top.msg3 -expand 1 -fill x pack .about.top.msg4 -expand 1 -fill x frame .about.down button .about.down.ok -text "OK" -command {destroy .about} pack .about.down -side bottom -expand 1 -fill x -pady 2 pack .about.down.ok } proc help_cvs_version {} { # # This shows CVS banner. # global cvs global cvscfg gen_log:log T "ENTER" set v [viewer::new "Versions"] $v\::log "-----------------------------------------\n" set whichcvs [auto_execok $cvs] if {[llength $whichcvs]} { set whichcvs [join $whichcvs] set commandline "$cvs -v" catch {eval "exec $commandline"} output $v\::log "$whichcvs $output" } else { $v\::log "$cvs was not found in your path." } $v\::log "\n-----------------------------------------\n" set whichsvn [auto_execok svn] if {[llength $whichsvn]} { set whichsvn [join $whichsvn] set commandline "svn --version" set ret [catch {eval "exec $commandline"} output] $v\::log "$whichsvn\n$output" } else { $v\::log "svn was not found in your path." } $v\::log "\n-----------------------------------------\n" set whichrcs [auto_execok rcs] if {[llength $whichrcs]} { set whichrcs [join $whichrcs] set commandline "rcs -V" set ret [catch {eval "exec $commandline"} output] $v\::log "$whichrcs\n$output" if {$ret != 0} { $v\::log "\nIf you see a usage message here, you have" $v\::log " a very old version of RCS.\nSome things still work," $v\::log " but some won't.\n" } } else { $v\::log "rcs was not found in your path." } gen_log:log T "LEAVE" } proc wish_version {{parent {.}}} { global tk_patchLevel set version $tk_patchLevel set whichwish [info nameofexecutable] set about_string "$whichwish\n\n" append about_string "Tk version $version" tk_messageBox -title "About Wish" \ -message $about_string \ -parent $parent \ -type ok } ###################################################################### # # text formatting routines derived from Klondike # Reproduced here with permission from their author. # # Copyright (C) 1993,1994 by John Heidemann # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of John Heidemann may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY JOHN HEIDEMANN ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL JOHN HEIDEMANN BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # ###################################################################### proc put-text {tw txt} { gen_log:log T "ENTER ($tw ...)" $tw configure -font -*-Times-Medium-R-Normal-*-14-* $tw tag configure bld -font -*-Times-Bold-R-Normal-*-14-* $tw tag configure cmp -font -*-Courier-Medium-R-Normal-*-12-* $tw tag configure h1 -font -*-Helvetica-Bold-R-Normal-*-18-* -underline 1 $tw tag configure h2 -font -*-Helvetica-Bold-R-Normal-*-18-* $tw tag configure h3 -font -*-Helvetica-Bold-R-Normal-*-14-* $tw tag configure itl -font -*-Times-Medium-I-Normal-*-14-* $tw tag configure rev -foreground white -background black $tw tag configure btn \ -font -*-Courier-Medium-R-Normal-*-12-* \ -foreground black -background white \ -relief groove -borderwidth 2 $tw mark set insert 0.0 set t $txt while {[regexp -indices {<([^@>]*)>} $t match inds] == 1} { set start [lindex $inds 0] set end [lindex $inds 1] set keyword [string range $t $start $end] set oldend [$tw index end] $tw insert end [string range $t 0 [expr {$start - 2}]] purge-all-tags $tw $oldend insert if {[string range $keyword 0 0] == "/"} { set keyword [string trimleft $keyword "/"] if {[info exists tags($keyword)] == 0} { error "end tag $keyword without beginning" } #gen_log:log D "$tw tag add $keyword $tags($keyword) insert" $tw tag add $keyword $tags($keyword) insert unset tags($keyword) } else { if {[info exists tags($keyword)] == 1} { error "nesting of begin tag $keyword" } set tags($keyword) [$tw index insert] } set t [string range $t [expr {$end + 2}] end] } set oldend [$tw index end] $tw insert end $t purge-all-tags $tw $oldend insert gen_log:log T "LEAVE" } proc purge-all-tags {w start end} { foreach tag [$w tag names $start] { $w tag remove $tag $start $end } } ###################################################################### # # End of text formatting routines. # ###################################################################### proc do_help {title helptext} { global cvscfg global cvsglb global tcl_platform gen_log:log T "ENTER $title )" static {helpviewer 0} incr helpviewer set cvshelpview ".cvshelpview$helpviewer" toplevel $cvshelpview text $cvshelpview.text -setgrid yes -wrap word \ -exportselection 1 \ -width 55 -relief sunken -border 2 \ -yscroll "$cvshelpview.scroll set" scrollbar $cvshelpview.scroll -relief sunken \ -command "$cvshelpview.text yview" button $cvshelpview.close -text "Close" \ -command "destroy $cvshelpview; exit_cleanup 0" pack $cvshelpview.close -side bottom pack $cvshelpview.scroll -side right -fill y pack $cvshelpview.text -fill both -expand 1 wm title $cvshelpview "$title Help" if {$tcl_platform(platform) != "windows"} { wm iconbitmap $cvshelpview @$cvscfg(bitmapdir)/tkcvs-help.xbm } wm minsize $cvshelpview 1 1 put-text $cvshelpview.text $helptext gen_log:log T "LEAVE" } # This is here for the manpage. We don't call it from tkcvs. proc man_description {} { do_help "Description" {

DESCRIPTION

TkCVS is a Tcl/Tk-based graphical interface to the CVS and Subversion configuration management systems. It displays the status of the files in the current working directory, and provides buttons and menus to execute configuration-management commands on the selected files. Limited RCS functionality is also present. TkDiff is bundled in for browsing and merging your changes. TkCVS also aids in browsing the repository. For Subversion, the repository tree is browsed like an ordinary file tree. For CVS, the CVSROOT/modules file is read. TkCVS extends CVS with a method to produce a browsable, "user friendly" listing of modules. This requires special comments in the CVSROOT/modules file. See "CVS Modules File" for more guidance. } } # # Help procedures for the TkCVS users guide. # proc cli_options {} { do_help "Command Line Options" {

OPTIONS

TkCVS accepts the following options.

-dir

directory Start TkCVS in the specified directory.

-help

Print a usage message.

-log

file Invoke a log browser for the specified file. -log and -win are mutually exclusive.

-root

cvsroot Set $CVSROOT to the specified repository.

-win

workdir|module|merge Start by displaying the directory browser (the default), the module browser, or the directory-merge tool. -win and -log are mutually exclusive.

Examples

% tkcvs -win module -root /jaz/repository Browse the modules located in CVSROOT /jaz/repository % tkcvs -log tstheap.c View the log of the file tstheap.c } } proc current_directory {} { do_help "Working Directory" {

Working Directory Browser

The working directory browser shows the files in your local working copy, or "sandbox." It shows the status of the files at a glance and provides tools to help with most of the common CVS, SVN, and RCS operations you might do. At the top of the browser you will find: * The name of the current directory. You can change directories by typing in this field. Recently visited directories are saved in the picklist. * The relative path of the current directory in the repository. If it is not contained in the repository you may import it using the menu or toolbar button. * A Directory Tag name, if the directory is contained in the repository and it has been checked out against a particular branch or tag. In Subversion, the branch or tag is inferred from the URL based on the conventional trunk-branches-tags repository organization. * The CVSROOT of the current directory if it's under CVS control, or the URL of the Subversion repository if it's under Subversion control. If neither is true, it may default to the value of the $CVSROOT environment variable. The main part of the working directory browser is a list of the files in the current directory with an icon next to each showing its status. You select a file by clicking on its name or icon once with the left mouse button. Holding the Control key while clicking will add the file to the group of those already selected. You can select a contiguous group of files by holding the Shift key while clicking. You can also select a group of files by dragging the mouse with the middle or right button pressed to select an area. Selecting an item that's already selected de-selects that item. To unselect all files, click the left mouse button in an empty area of the file list. * The Date column (can be hidden) shows the modification time of the file is shown. The format of the date column may be specified with cvscfg(dateformat). The default format was chosen because it sorts the same way alphabetically as chronologically. If the directory belongs to a revision system, other columns are present. * The revision column shows which revision of the file is checked out, and whether it's on the trunk or on a branch. * The status column (can be hidden) shows the revision of the file spelled out in text. This information is mostly redundant to the icon in the file column. * The Editor/Author/Locker column (can be hidden) varies according to revision system. In Subversion, the author of the most recent checkin is shown. In CVS, it shows a list of people editing the files if your site uses "cvs watch" and/or "cvs edit". Otherwise, it will be empty. In RCS, it shows who, if anyone, has the file locked. The optional columns can be displayed or hidden using the Options menu. You can move into a directory by double-clicking on it. Double clicking on a file will load the file into a suitable editor so you can change it. A different editor can be used for different file types (see Configuration Files).

File Status

When you are in a directory that is under CVS or Subversion control, the file status will be shown by an icon next to each file. Checking the "Status Column" option causes the status to be displayed in text in its own column. Some possible statuses are:

Up-to-date

The file is up to date with respect to the repository.

Locally Modified

The file has been modified in the current directory since being checked out of the repository.

Locally Added

The file has been added to the repository. This file will become permanent in the repository once a commit is made.

Locally Removed

You have removed the file with remove, and not yet committed your changes.

Needs Checkout

Someone else has committed a newer revision to the repository. The name is slightly misleading; you will ordinarily use update rather than checkout to get that newer revision.

Needs Patch

Like Needs Checkout, but the CVS server will send a patch rather than the entire file. Sending a patch or sending an entire file accomplishes the same thing.

Needs Merge

Someone else has committed a newer revision to the repository, and you have also made modifications to the file.

Unresolved Conflict

This is like Locally Modified, except that a previous update command gave a conflict. You need to resolve the conflict before checking in.

?

The file is not contained in the repository. You may need to add the file to the repository by pressing the "Add" button.

[directory:CVS]

A directory which has been checked out from a CVS repository.

[directory:SVN]

The file is a directory which has been checked out from a Subversion repository. In Subversion, directories are themselves versioned objects.

[directory:RCS]

A directory which contains an RCS sub-directory or some files with the ,v suffix, presumably containing some files that are under RCS revision control.

[directory]

The file is a directory.

File Filters

You can specify file matching patterns to instruct TkCVS which files you wish to see. You can also specify patterns telling it which files to remove when you press the "Clean" button or select the File->Cleanup menu item. "Hide" works exactly the way a .cvsignore file works. That is, it causes non-CVS files with the pattern to be ignored. It's meant for hiding .o files and such. Any file under CVS control will be listed anyway. "Show" is the inverse. It hides non-CVS files except for those with the pattern.

Buttons

Module Browser: The big button at the upper right opens the module browser. Opens a module browser window which will enable you to explore items in the repository even if they're not checked out. In CVS, this requires that there be entries in the CVSROOT/modules file. Browsing can be improved by using TkCVS-specific comments in CVSROOT/modules. Go Up: The button to the left of the entry that shows the current directory. Press it and you go up one level. There are a number of buttons at the bottom of the window. Pressing on one of these causes the following actions: Delete: Press this button to delete the selected files. The files will not be removed from the repository. To remove the files from the repository as well as delete them, press the "Remove" button instead. Edit: Press this button to load the selected files in to an appropriate editor. View: Press this button to view the selected files in a Tk text window. This can be a lot faster then Edit, in case your preferred editor is xemacs or something of that magnitude. Refresh: Press this button to re-read the current directory, in case the status of some files may have changed. Status Check: Shows, in a searchable text window, the status of all the files. By default, it is recursive and lists unknown (?) files. These can be changed in the Options menu. Directory Branch Browse: For merging the entire directory. In Subversion, it opens the Branch Browser for "." In CVS, it chooses a "representative" file in the current directory and opens a graphical tool for directory merges. Log (Branch) Browse: This button will bring up the log browser window for each of the selected files in the window. See the Log Browser section. Annotate: This displays a window in which the selected file is shown with the lines highlighted according to when and by whom they were last revised. In Subversion, it's also called "blame." Diff: This compares the selected files with the equivalent files in the repository. A separate program called "TkDiff" (also supplied with TkCVS) is used to do this. For more information on TkDiff, see TkDiff's help menu. Merge Conflict: If a file's status says "Needs Merge", "Conflict", or is marked with a "C" in CVS Check, there was a difference which CVS needs help to reconcile. This button invokes TkDiff with the -conflict option, opening a merge window to help you merge the differences. Check In: This button commits your changes to the repository. This includes adding new files and removing deleted files. When you press this button, a dialog will appear asking you for the version number of the files you want to commit, and a comment. You need only enter a version number if you want to bring the files in the repository up to the next major version number. For example, if a file is version 1.10, and you do not enter a version number, it will be checked in as version 1.11. If you enter the version number 3, then it will be checked in as version 3.0 instead. It is usually better to use symbolic tags for that purpose. If you use rcsinfo to supply a template for the comment, you must use an external editor. Set cvscfg(use_cvseditor) to do this. For checking in to RCS, an externel editor is always used. Update: This updates your sandbox directory with any changes committed to the repository by other developers. Update with Options: Allows you to update from a different branch, with a tag, with empty directories, and so on. Add Files: Press this button when you want to add new files to the repository. You must create the file before adding it to the repository. To add some files, select them and press the Add Files button. The files that you have added to the repository will be committed next time you press the Check In button. It is not recursive. Use the menu CVS -> Add Recursively for that. Remove Files: This button will remove files. To remove files, select them and press the Remove button. The files will disappear from the directory, and will be removed from the repository next time you press the Check In button. It is not recursive. Use the menu CVS -> Remove Recursively for that. Tag: This button will tag the selected files. In CVS, the -F (force) option will move the tag if it already exists on the file. Branch Tag: This button will tag the selected files, creating a branch. In CVS, the -F (force) option will move the tag if it already exists on the file. Lock (CVS and RCS): Lock an RCS file for editing. If cvscfg(cvslock) is set, lock a CVS file. Use of locking is philosophically discouraged in CVS since it's against the "concurrent" part of Concurrent Versioning System, but locking policy is nevertheless used at some sites. One size doesn't fit all. Unlock (CVS and RCS): Unlock an RCS file. If cvscfg(cvslock) is set, unlock a CVS file. Set Edit Flag (CVS): This button sets the edit flag on the selected files, enabling other developers to see that you are currently editing those files (See "cvs edit" in the CVS documentation). Reset Edit Flag (CVS): This button resets the edit flag on the selected files, enabling other developers to see that you are no longer editing those files (See "cvs edit" in the CVS documentation). As the current version of cvs waits on a prompt for "cvs unedit" if changes have been made to the file in question (to ask if you want to revert the changes to the current revision), the current action of tkcvs is to abort the unedit (by piping in nothing to stdin). Therefore, to lose the changes and revert to the current revision, it is necessary to delete the file and do an update (this will also clear the edit flag). To keep the changes, make a copy of the file, delete the original, update, and then move the saved copy back to the original filename. Close: Press this button to close the Working Directory Browser. If no other windows are open, TkCVS exits. } } proc log_browser {} { do_help "Log (Branch) Browser" {

Log (Branch) Browser

The TkCVS Log Browser window enables you to view a graphical display of the revision log of a file, including all previous versions and any branched versions. You can get to the log browser window in three ways, either by invoking it directly with "tkcvs [-log] ", by selecting a file within the main window of TkCVS and pressing the Log Browse button, or by selecting a file in a list invoked from the module browser and pressing the Log Browse button. If the Log Browser is examining a checked-out file, the buttons for performing merge operations are enabled.

Log Browser Window

The log browser window has three components. These are the file name and version information section at the top, the log display in the middle, and a row of buttons along the bottom.

Log Display

The main log display is fairly self explanatory. It shows a group of boxes connected by lines indicating the main trunk of the file development (on the left hand side) and any branches that the file has (which spread out to the right of the main trunk). Each box contains the version number, author of the version, and other information determined by the menu View -> Revision Layout. Constructing the branch diagram from Subversion is inefficient, so the Log Browser counts the tags when doing a Subversion diagram and pops up a dialog giving you a chance to skip the tag step if there are too many tags (where "many" arbitrarily equals 10.)

Version Numbers

Once a file is loaded into the log browser, one or two version numbers may be selected. The primary version (Selection A) is selected by clicking the left mouse button on a version box in the main log display. The secondary version (Selection B) is selected by clicking the right mouse button on a version box in the main log display. Operations such as "View" and "Annotate" operate only on the primary version selected. Operations such as "Diff" and "Merge Changes to Current" require two versions to be selected.

Searching the Diagram

You can search the canvas for tags, revisions, authors, and dates. The following special characters are used in the search pattern: * Matches any sequence of characters in string, including a null string. ? Matches any single character in string. [chars] Matches any character in the set given by chars. If a sequence of the form x-y appears in chars, then any character between x and y, inclusive, will match. \x Matches the single character x. This provides a way of avoiding the special interpretation of the characters *?[]\ in pattern. If you only enter "FOO" (without the \") in the entry box, it searches the exact string "foo". If you want to search all strings starting with "foo", you have to put "foo*". For all strings containing "foo", you must put "*foo*".

Log Browser Buttons

The log browser contains the following buttons: Refresh: Re-reads the revision history of the file. View: Pressing this button displays a Tk text window containing the version of the file at Selection A. Annotate: This displays a window in which the file is shown with its lines highlighted according to when and by whom they were last revised. In Subversion, it's also called "blame." Diff: Pressing this button runs the "tkdiff" program to display the differences between version A and version B. Merge: To use this button, select a branch version of the file, other than the branch you are currently on, as the primary version (Selection A). The changes made along the branch up to that version will be merged into the current version, and stored in the current directory. Optionally, select another version (Selection B) and the changes will be from that point rather than from the base of the branch. The version of the file in the current directory will be merged, but no commit will occur. Then you inspect the merged files, correct any conflicts which may occur, and commit when you are satisfied. Optionally, TkCVS will tag the version that the merge is from. It suggests a tag of the form "mergefrom__date." If you use this auto-tagging function, another dialog containing a suggested tag for the merged-to version will appear. It's suggested to leave the dialog up until you are finished, then copy-and-paste the suggested tag into the "Tag" dialog. It is always a good practice to tag when doing merges, and if you use tags of the suggested form, the Branch Browser can diagram them. (Auto-tagging is not implemented in Subversion because, despite the fact that tags are "cheap," it's somewhat impractical to auto-tag single files. You can do the tagging manually, however.) View Tags: This button lists all the tags applied to the file in a searchable text window. Close: This button closes the Log Browser. If no other windows are open, TkCVS exits.

The View Options Menu

The View Menu allows you to control what you see in the branch diagram. You can choose how much information to show in the boxes, whether to show empty revisions, and whether to show tags. You can even control the size of the boxes. If you are using Subversion, you may wish to turn the display of tags off. If they aren't asked for they won't be read from the repository, which can save a lot of time. } } proc directory_branch_viewer {} { do_help "CVS Merge Tool" {

Merge Tool for CVS

The Merge Tool chooses a "representative" file in the current directory and diagrams the branch tags. It tries to pick the "bushiest" file, or failing that, the most-revised file. If you disagree with its choice, you can type the name of another file in the top entry and press Return to diagram that file instead. The main purpose of this tool is to do merges (cvs update -j rev [-j rev]) on the whole directory. For merging one file at a time, you should use the Log Browser. You can only merge to the line (trunk or branch) that you are currently on. Select a branch to merge from by clicking on it. Then press either the "Merge" or "Merge Changes" button. The version of the file in the current directory will be over-written, but it will not be committed to the repository. You do that after you've reconciled conflicts and decided if it's what you really want. Merge Branch to Current: The changes made on the branch since its beginning will be merged into the current version. Merge Changes to Current: Instead of merging from the base of the branch, this button merges the changes that were made since a particular version on the branch. It pops up a dialog in which you fill in the version. It should usually be the version that was last merged. } } proc module_browser {} { do_help "Repository Browser" {

Module Browser

Operations that are performed on the repository instead of in a checked-out working directory are done with the Module Browser. The most common of these operations is checking out or exporting from the repository. The Module Browser can be started from the command line (tkcvs -win module) or started from the main window by pressing the big button. Subversion repositories can be browsed like a file tree, and that is what you will see in the Module Browser. CVS repositories aren't directly browsable, but if the CVSROOT/modules file is maintained appropriately, TkCVS can display the modules and infer tree structures if they are present. See the "CVS Modules File" section. Using the module browser window, you can select a module to check out. When you check out a module, a new directory is created in the current working directory with the same name as the module.

Tagging and Branching (cvs rtag)

You can tag particular versions of a module or file in the repository, with plain or branch tags, without having the module checked out.

Exporting

Once a software release has been tagged, you can use a special type of checkout called an export. This allows you to cleanly check out files from the repository, without all of the administrivia that CVS needs to have while working on the files. It is useful for delivery of a software release to a customer.

Importing

TkCVS contains a special dialog to allow users to import new files into the repository. In CVS, new modules can be assigned places within the repository, as well as descriptive names (so that other people know what they are for). When the Module Browser displays a CVS repository, the first column is a tree showing the module codes and directory names of all of the items in the repository. The icon shows whether the item is a directory (which may contain other directories or modules), or whether it is a module (which may be checked out from TkCVS). It is possible for an item to be both a module and a directory. If it has a red ball on it, you can check it out. If it shows a plain folder icon, you have to open the folder to get to the items that you can check out. To select a module, click on it with the left mouse button. The right mouse button will perform a secondary selection, which is used only for Subversion diff and patch. To clear the selection, click on the item again or click in an empty area of the module column. There can only be one primary and one secondary selection.

Repository Browser Buttons

The module browser contains the following buttons: Who: Shows which modules are checked out by whom. Import: This item will import the contents of the current directory (the one shown in the Working Directory Display) into the repository as a module. See the section titled Importing for more information. File Browse: Displays a list of the selected module's files. From the file list, you can view the file, browse its revision history, or see a list of its tags. Check Out: Checks out the current version of a module. A dialog allows you to specify a tag, change the destination, and so on. Export: Exports the current version of a module. A dialog allows you to specify a tag, change the destination, and so on. Export is similar to check-out, except exported directories do not contain the CVS or administrative directories, and are therefore cleaner (but cannot be used for checking files back in to the repository). You must supply a tag name when you are exporting a module to make sure you can reproduce the exported files at a later date. Tag: This button tags an entire module. Branch Tag: This creates a branch of a module by giving it a branch tag. Patch Summary: This item displays a short summary of the differences between two versions of a module. Create Patch File: This item creates a Larry Wall format patch(1) file of the module selected. Close: This button closes the Repository Browser. If no other windows are open, TkCVS exits. } } proc importing_new_modules {} { do_help "Importing" {

Importing New Modules

Before importing a new module, first check to make sure that you have write permission to the repository. Also you'll have to make sure the module name is not already in use. To import a module you first need a directory where the module is located. Make sure that there is nothing in this directory except the files that you want to import. Press the big "Repository Browser" button in the top part of the tkcvs UI, or use CVS -> Import WD into Repository from the menu bar. In the module browser, press the Import button on the bottom, the one that shows a folder and an up arrow. In the dialog that pops up, fill in a descriptive title for the module. This will be what you see in the right side of the module browser. OK the dialog. Several things happen now. The directory is imported, the CVSROOT/module file is updated, your original directory is saved as directory.orig, and the newly created module is checked out. When it finishes, you should find the original Working Directory Browser showing the files in the newly created, checked out module. Here is a more detailed description of the fields in the Import Dialog. Module Name: A name for the module. This name must not already exist in the repository. Your organization could settle on a single unambiguous code for modules. One possibility is something like: [project code]-[subsystem code]-[module code] Module Path: The location in the repository tree where your new module will go. Descriptive Title: A one-line descriptive title for your module. This will be displayed in the right-hand column of the browser. Version Number: The current version number of the module. This should be a number of the form X.Y.Z where .Y and .Z are optional. You can leave this blank, in which case 1 will be used as the first version number. Importing a directory into Subversion is similar but not so complicated. You use the SVN -> Import CWD into Repository menu. You need supply only the path in the repository where you want the directory to go. The repository must be prepared and the path must exist, however. } } proc importing_to_existing_module {} { do_help "Importing To An Existing Module" {

Importing to an Existing Module (CVS)

Before importing to an existing module, first check to make sure that you have write permission to the repository. To import to an existing module you first need a directory where the code is located. Make sure that there is nothing in this directory (including no CVS directory) except the files that you want to import. Open up the Repository Browser by selecting File/Browse Modules from the menu bar. In the Repository Browser, select File/Import To An Existing Module from the menu bar. In the dialog that pops up, press the Browse button and select the name of an existing module. Press the OK to close this dialog box. Enter the version number of the code to be imported. OK the dialog. Several things happen now. The directory is imported, your original directory is saved as directory.orig, and the newly created module is checked out. When it finishes, you will find the original Working Directory Browser showing the original code. If you press the "Re-read the current directory" button you will see the results of the checked out code. Here is a more detailed description of the fields in the Import Dialog. Module Name: A name for the existing module. Filled in by the use of the the Browse button Module Path: The location in the repository tree where the existing module is. Filled in by the use of the Browse button. Version Number: The current version number of the module to be imported. This should be a number of the form X.Y.Z where .Y and .Z are optional. You can leave this blank, in which case 1 will be used as the first version number. } } proc vendor_merge {} { do_help "Vendor Merge" {

Vendor Merge (CVS)

Software development is sometimes based on source distribution from a vendor or third-party distributor. After building a local version of this distribution, merging or tracking the vendor's future release into the local version of the distribution can be done with the vendor merge command. The vendor merge command assumes that a separate module has already been defined for the vendor or third-party distribution with the use of the "Import To A New Module" and "Import To An Existing Module" commands. It also assumes that a separate module has already been defined for the local code for which the vendor merge operation is to be applied to. Start from an empty directory and invoke tkcvs. Open up the Repository Browser by selecting File/Browse Modules from the menu bar. Checkout the module of the local code to be merged with changes from the vendor module. (Use the red icon with the down arrow) In the Repository Browser, after verifying that the Module entry box still has the name the module of the local code to which the vendor code is to be merged into, select File/Vendor Merge from the menu bar. In the Module Level Merge With Vendor Code window, press the Browse button to select the module to be used as the vendor module. OK the dialog. All revisions from the vendor module will be shown in the two scroll lists. Fill in the From and To entry boxes by clicking in the appropriate scroll lists. Ok the dialog. Several things happens now. Several screens will appear showing the output from cvs commands for (1)checking out temp files, (2)cvs merge, and (3)cvs rdiff. Information in these screens will tell you what routines will have merge conflicts and what files are new or deleted. After perusing the files, close each screen. (In the preceeding dialog box, there was an option to save outputs from the merge and rdiff operations to files CVSmerge.out and CVSrdiff.out.) The checked out local code will now contain changes from a merge between two revisions of the vendor modules. This code will not be checked into the repository. You can do that after you've reconciled conflicts and decide if that is what you really want. A detailed example on how to use the vendor merge operation is provided in the PDF file vendor5readme.pdf. } } proc configuration_files {} { do_help "Configuration Files" {

Configuration Files

There are two configuration files for TkCVS. The first is stored in the directory in which the *.tcl files for TkCVS are installed. This is called tkcvs_def.tcl. You can put a file called site_def in that directory, too. That's a good place for site-specific things like tagcolours. Unlike tkcvs_def.tcl, it will not be overwritten when you install a newer version of TkCVS. Values in the site configuration files can be over-ridden at the user level by placing a .tkcvs file in your home directory. Commands in either of these files should use Tcl syntax. In other words, to set a variable name, you should have the following command in your .tkcvs file: set variablename value for example: set cvscfg(editor) "gvim" The following variables are supported by TkCVS:

Startup

cvscfg(startwindow)

Which window you want to see on startup. (workdir or module)

CVS

cvscfg(cvsroot)

If set, it overrides the CVSROOT environment variable.

Subversion

If your SVN repository has a structure similar to trunk, branches, and tags but with different names, you can tell TkCVS about it by setting variables in tkcvs_def.tcl: set cvscfg(svn_trunkdir) "elephants" set cvscfg(svn_branchdir) "dogs" set cvscfg(svn_tagdir) "ducklings" The branch browser depends on the convention of having a trunk, branches, and tags structure to draw the diagram. These variables may give you a little more flexibility.

GUI

Most colors and fonts can be customized by using the options database. For example, you can add lines like these to your .tkcvs file: option add *Canvas.background #c3c3c3 option add *Menu.background #c3c3c3 option add *selectColor #ffec8b option add *Text.background gray92 option add *Entry.background gray92 option add *Listbox.background gray92 option add *ToolTip.background LightGoldenrod1 option add *ToolTip.foreground black

cvscfg(picklist_items)

Maximum number of visited directories and repositories to save in the picklist history

Log browser

cvscfg(colourA) cvscfg(colourB)

Hilight colours for revision-log boxes

cvscfg(tagdepth)

Number of tags you want to see for each revision on the branching diagram before it says "more..." and offers a pop-up to show the rest

cvscfg(toomany_tags)

Number of tags in a Subversion repository that's "too many", ie. will take longer to proecess for the branch diagram than you're willing to wait. (Building a branch diagram for Subversion is very inefficient.) If there are more than that number and cvscfg(confirm_prompt) is true, a dialog will appear asking whether to process the tags or to draw the diagram without them.

cvscfg(tagcolour,tagstring)

Colors for marking tags. For example: set cvscfg(tagcolour,tkcvs_r6) Purple

Module browser

cvscfg(aliasfolder)

In the CVS module browser, if true this will cause the alias modules to be grouped in one folder. Cleans up clutter if there are a lot of aliases.

User preferences

cvscfg(allfiles)

Set this to false to see normal files only in the directory browser. Set it to true to see all files including hidden files.

cvscfg(auto_status)

Set the default for automatic status-refresh of a CVS controlled directory. Automatic updates are done when a directory is entered and after some operations.

cvscfg(auto_tag)

Whether to tag the merged-from revision when using TkCVS to merge different revisions of files by default. A dialog still lets you change your mind, regardless of the default.

cvscfg(confirm_prompt)

Ask for confirmation before performing an operation(true or false)

cvscfg(dateformat)

Format for the date string shown in the "Date" column, for example "%Y/%m/%d %H:%M"

cvscfg(cvslock)

Set to true to turn on the ability to use cvs-admin locking from the GUI.

cvscfg(econtrol)

Set this to true to turn on the ability to use CVS Edit and Unedit, if your site is configured to allow the feature.

cvscfg(editor)

Preferred default editor

cvscfg(editors)

String pairs giving the editor-command and string-match-pattern, for deciding which editor to use

cvscfg(editorargs)

Command-line arguments to send to the default editing program.

cvscfg(ldetail)

Detail level for status reports (latest, summary, verbose)

cvscfg(mergetoformat)

cvscfg(mergefromformat)

Format for mergeto- and mergefrom- tags. The _BRANCH_ part must be left as-is, but you can change the prefix and the date format, for example "mergeto_BRANCH_%d%b%y". The date format must be the same for both. CVS rule: a tag must not contain the characters `$,.:;@'

cvscfg(rdetail)

Detail for repository and workdir reports (terse, summary, verbose)

cvscfg(recurse)

Whether reports are recursive (true or false)

cvscfg(savelines)

How many lines to keep in the trace window

cvscfg(status_filter)

Filter out unknown files (status "?") from CVS Check and CVS Update reports.

cvscfg(use_cvseditor)

Let CVS invoke an editor for commit log messages rather than having tkcvs use its own input box. By doing this, your site's commit template (rcsinfo) can be used.

File filters

cvscfg(file_filter)

Pattern for which files to list. Empty string is equivalent to the entire directory (minus hidden files)

cvscfg(ignore_file_filter)

Pattern used in the workdir filter for files to be ignored

cvscfg(clean_these)

Pattern to be used for cleaning a directory (removing unwanted files)

System

cvscfg(print_cmd)

System command used for printing. lpr, enscript -Ghr, etc)

cvscfg(shell)

What you want to happen when you ask for a shell

cvscfg(terminal)

Command prefix to use to run something in a terminal window

Portability

cvscfg(aster)

File mask for all files (* for Unix, *.* for windows)

cvscfg(null)

The null device. /dev/null for Unix, nul for windows

cvscfg(tkdiff)

How to start tkdiff. Example sh /usr/local/bin/tkdiff

cvscfg(tmpdir)

Directory in which to do behind-the-scenes checkouts. Usually /tmp or /var/tmp)

Debugging

cvscfg(log_classes)

For debugging: C=CVS commands, E=CVS stderr output, F=File creation/deletion, T=Function entry/exit tracing, D=Debugging

cvscfg(logging)

Logging (debugging) on or off } } proc environment_variables {} { do_help "Environment Variables" {

Environment Variables

You should have the CVSROOT environment variable pointing to the location of your CVS repository before you run TkCVS. It will still allow you to work with different repositories within the same session. If you wish TkCVS to point to a Subversion repository by default, you can set the environment variable SVNROOT. This has no meaning to Subversion itself, but it will clue TkCVS if it's started in an un-versioned directory. } } proc user_defined_menu {} { do_help "User Defined Menu" {

User Configurable Menu Extensions

It is possible to extend the TkCVS menu by inserting additional commands into the .tkcvs or tkcvs_def.tcl files. These extensions appear on an extra menu to the right of the TkCVS Options menu. To create new menu entries on the user-defined menu, set the following variables:

cvsmenu(command)

Setting a variable with this name to a value like "commandname" causes the CVS command "cvs commandname" to be run when this menu option is selected. For example, the following line: set cvsmenu(update_A) "update -A" Causes a new menu option titled "update_A" to be added to the user defined menu that will run the command "cvs update -A" on the selected files when it is activated. (This example command, for versions of CVS later than 1.3, will force an update to the head version of a file, ignoring any sticky tags or versions attached to the file).

usermenu(command)

Setting a variable with this name to a value like "commandname" causes the command "commandname" to be run when this menu option is selected. For example, the following line: set usermenu(view) "cat" Causes a new menu option titled "view" to be added to the User defined menu that will run the command "cat" on the selected files when it is activated. Any user-defined commands will be passed a list of file names corresponding to the files selected on the directory listing on the main menu as arguments. The output of the user defined commands will be displayed in a window when the command is finished. } } proc cvs_modules_file {} { do_help "CVS Modules File" {

CVS Modules File

If you haven't put anything in your CVSROOT/modules file, please do so. See the "Administrative Files" section of the CVS manual. Then, you can add comments which TkCVS can use to title the modules and to display them in a tree structure. The simplest use of TkCVS's "#D" directive is to display a meaningful title for the module: #D softproj Software Development Projects softproj softproj A fancier use is to organize the modules into a tree which will mimic their directory nesting in the repository when they appear in the module browser. For example, suppose we have a directory called "chocolate" which is organized like this: chocolate/ truffle/ cocoa3/ biter/ sniffer/ snuffler/ To display its hierarchy, as well as make the deepest directories more accessible by giving them module names, we could put this in the modules file: #D chocolate Top Chocolate #D chocolate/truffle Cocoa Level 2 #D chocolate/truffle/cocoa3 Cocoa Level 3 #D sniffer Chocolate Sniffer sniffer chocolate/truffle/cocoa3/sniffer #D snuff Chocolate Snuffler snuff chocolate/truffle/cocoa3/snuffler #D biter Chocolate Biter biter chocolate/truffle/cocoa3/biter When you are installing TkCVS, you may like to add these additional lines to the modules file (remember to check out the modules module from the repository, and then commit it again when you have finished the edits). These extension lines commence with a "#" character, so CVS interprets them as comments. They can be safely left in the file whether you are using TkCVS or not. "#M" is equivalent to "#D". The two had different functions in previous versions of TkCVS, but now both are parsed the same way. } } # Populates the Help menu. Called from the browser windows. proc menu_std_help { w } { $w add cascade -label "Help" -menu $w.help -underline 0 menu $w.help $w.help add command -label "About TkCVS" -underline 0 \ -command aboutbox $w.help add command -label "About CVS SVN RCS" -underline 6 \ -command help_cvs_version $w.help add command -label "About Wish" -underline 6 \ -command "wish_version [winfo parent $w]" $w.help add separator $w.help add command -label "Current Directory Display" \ -command current_directory $w.help add command -label "Module Browser" \ -command module_browser $w.help add command -label "Log Browser" \ -command log_browser $w.help add command -label "Merge Tool" \ -command directory_branch_viewer $w.help add separator $w.help add command -label "Repository Browser" \ -command module_browser $w.help add command -label "Importing New Modules" \ -command importing_new_modules $w.help add command -label "Importing To An Existing Module" \ -command importing_to_existing_module $w.help add command -label "Vendor Merge" \ -command vendor_merge $w.help add separator $w.help add command -label "Configuration Files" \ -command configuration_files $w.help add command -label "Environment Variables" \ -command environment_variables $w.help add command -label "Command Line Options" \ -command cli_options $w.help add command -label "User Defined Menu" \ -command user_defined_menu $w.help add command -label "CVS modules File" \ -command cvs_modules_file } tkcvs-8.2.3.orig/tkcvs/exec.tcl0000644000175000017500000004410211664612512014550 0ustar timtimproc cvs_usercmd {args} { # # Run a cvs command from the user menu and view its output. # called for cvsmenu() entries. # global cvs gen_log:log T "ENTER ($args)" #gen_log:log C "$cvs $args" set my_viewer [viewer::new "CVS $args"] $my_viewer\::do "$cvs $args" gen_log:log T "LEAVE" } proc cvs_execcmd {args} { # # Run any command from the user menu without # a viewer to capture its output and without # the ability to abort it. # called for execmenu() entries. # gen_log:log T "ENTER ($args)" exec::new $args gen_log:log T "LEAVE" } proc cvs_catchcmd {args} { # # Run any command from the user menu and view its output. # You can abort it too. # called for usermenu() entries. # gen_log:log T "ENTER ($args)" #gen_log:log C "$args" set my_viewer [viewer::new "$args"] $my_viewer\::do "$args" gen_log:log T "LEAVE" } namespace eval ::exec { variable instance 0 proc new {command {viewer {}} {show_stderr {1}} {filter {}} {errok {0}} } { variable instance set my_idx $instance incr instance gen_log:log T "ENTER (\"$command\" \"$viewer\" \"$show_stderr\" \"$filter\" \"$errok\")" namespace eval $my_idx { set my_idx [uplevel {concat $my_idx}] variable command [uplevel {concat $command}] variable show_stderr [uplevel {concat $show_stderr}] variable viewer [uplevel {concat $viewer}] variable filter [uplevel {concat $filter}] variable errok [uplevel {concat $errok}] global cvscfg global errorCode variable data {} variable errmsg {} variable procout "" variable procerr "" variable errpos 0 variable ExecDone 0 variable v_w if {$viewer != ""} { set v_w [namespace inscope $viewer {set w}] } proc out_handler { {viewer {}} {filter {}} } { variable procout variable procerr variable ExecDone variable errmsg variable errok variable data variable v_w variable my_idx variable show_stderr global errorCode # Blocking read -- returns -1 on EOF. Then you get the process return # from errorCode if {[gets $procout line] < 0} { # [close] blocks until child process completes if {[catch {close $procout} res]} { gen_log:log E " Close Failed - errorCode $errorCode" set ExecDone [list 1 $res $errorCode] gen_log:log E " ExecDone $ExecDone" if {$errmsg == ""} { set errmsg $res } [namespace current]::err_handler if {! [info exists command]} {set command ""} if {! [info exists status]} {set status ""} if {$errmsg == "" && $status != ""} { set errmsg "$command exited status $status" } set errlen [string length $errmsg] if {0 < $errlen > 512 && ! $errok} { cvsfail $errmsg . } # If we don't pop up an error dialog, let's at least try to show # what happened in the viewer window, if there is one if {$viewer != {}} { $v_w.text insert end "\n$res" stderr if {[tell $procerr]} { seek $procerr 0 while {[gets $procerr erline] != -1} { $v_w.text insert end "$erline\n" stderr } } } ::exec::$my_idx\::abort } else { gen_log:log D " Close OK" # Many CVS commands write stderr without err exit if {[tell $procerr]} { seek $procerr 0 while {[gets $procerr erline] != -1} { gen_log:log E "$erline" if {$show_stderr && $viewer != {}} { $v_w.text insert end "$erline\n" stderr } } } set ExecDone [list 0] gen_log:log D " ExecDone $ExecDone" } catch {close $procerr} if {$viewer != {}} { pack forget $v_w.stop pack $v_w.close -in $v_w.bottom -side right -ipadx 15 $v_w.close configure -state normal } return } if {$filter != ""} { set filtered_line [$filter [namespace current] $line] set texttag [lindex $filtered_line 0] set line [lindex $filtered_line 1] } append data "$line\n" if {$viewer != ""} { if {$filter != ""} { if {$texttag != "noshow"} { $v_w.text insert end "$line\n" $texttag } } else { $v_w.text insert end "$line\n" } $v_w.text yview end } gen_log:log F "STDOUT: $line" } proc err_handler {} { variable errpos variable procerr variable errmsg variable viewer variable filter variable show_stderr variable v_w # When new stuff appears in the error output file, get it. There may # be more than one line. set errmsg "" if {[tell $procerr] != $errpos} { seek $procerr $errpos start while {[gets $procerr erline] != -1} { append errmsg "\n$erline" set errpos [tell $procerr] } gen_log:log E "$errmsg" if {$viewer != "" && $show_stderr == 1} { $v_w.text insert end "\n$errmsg" stderr } } } proc abort {} { variable procout variable procerr variable procid variable viewer variable v_w global tcl_platform #gen_log:log T "ENTER" # This does the trick but it wont work on windows if {![info exists procid]} { gen_log:log D "procid is not defined" return } catch "exec kill $procid" kres unset procid err_handler if {$viewer != {}} { pack forget $v_w.stop pack $v_w.close -in $v_w.bottom -side right -ipadx 15 $v_w.close configure -state normal } catch {close $procout} cres catch {close $procerr} cres gen_log:log D "$kres" #gen_log:log T "LEAVE" } proc destroy {} { if [catch {namespace delete [namespace current]} err] { puts "deleting [namespace current]" puts "$err" } } proc wait {} { variable ExecDone #gen_log:log T "ENTER" if {!$ExecDone} { vwait [namespace current]::ExecDone } #gen_log:log T "LEAVE" } proc output {} { variable data variable ExecDone #gen_log:log T "ENTER" if {!$ExecDone} { [namespace current]::wait } #gen_log:log T "LEAVE" return $data } proc run_exec {} { global cvscfg variable my_idx variable procout variable procerr variable procid variable errmsg variable command variable viewer variable filter variable v_w variable w fconfigure stderr -blocking false -buffering line fconfigure stdout -blocking false -buffering line # Set up the file we send the proc's stderr to set errordir [file join $cvscfg(tmpdir) "cvstmpdir.[pid]"] file mkdir $errordir set errorfile [file join $errordir "exec$my_idx"] set procerr [open $errorfile w+] # Here's where we do it gen_log:log C "$command" set procout [open "| $command 2>@$procerr" r] set procid [pid $procout] # Dont ever do this. The whole thing depends on procout blocking #fconfigure $procout -blocking false -buffering line fileevent $procout readable [list [namespace current]::out_handler $viewer $filter] flush $procerr fileevent $procerr readable [list [namespace current]::err_handler] # set buffering back to normal fconfigure stdout -blocking true -buffering line catch {fileevent $procerr readable {} } } after 0 [list [namespace current]::run_exec] return [namespace current] } } } namespace eval ::viewer { variable instance 0 # # Set up a dialog containing a text box to view # the report of the command during execution. # proc new {title} { variable instance set my_idx $instance incr instance namespace eval $my_idx { global cvscfg variable my_idx [uplevel {concat $my_idx}] variable title [uplevel {concat $title}] variable w ".view$my_idx" variable log {} variable searchstr {} variable searchidx 1.0 variable v_e viewer_window $w $title [namespace current] proc do { command {show_stderr {1}} {filter {}} } { global cvscfg variable w variable v_e gen_log:log T "ENTER (\"$command\" \"$show_stderr\" \"$filter\")" pack forget $w.close pack $w.stop -in $w.bottom -side right -ipadx 15 # Send the command to the execution module set v_e [::exec::new $command [namespace current] $show_stderr $filter] gen_log:log T "LEAVE" } proc abort {} { variable v_e namespace inscope $v_e abort } proc wait {} { variable v_e namespace inscope $v_e wait } proc clean_exec {} { variable v_e catch {namespace inscope $v_e destroy} } proc destroy {} { variable v_e catch {namespace inscope $v_e destroy} if [catch {namespace delete [namespace current]} err] { puts "deleting [namespace current]" puts $err } } # Call this proc to write arbitrary text to the viewer proc log { text {texttag {}} } { variable w $w.text insert end $text $texttag $w.text yview end } proc search {} { variable searchidx variable w set str [$w.bottom.entry get] set match [$w.text search -- $str $searchidx] if {[string length $match] > 0} { set length [string length $str] $w.text mark set insert $match $w.text tag add sel $match "$match + ${length}c" $w.text see $match set searchidx "$match + ${length}c" } } return [namespace current] } } } # Filters output lines from CVS # returns the name of the tag to use when printing # the line in the text widget # This filter doesn't need its exec argument, but filters # must have it because some do need it proc status_colortags {exec line} { global cvscfg #gen_log:log T "ENTER ($exec \"$line\")" set tag default # Return the type of the line being output # Neat trick I found on clt: -> is a valid variable name! if {[regexp {^([PUARMCD?!]) (.*)} $line -> mode file]} { gen_log:log D "$line" gen_log:log D "mode $mode file $file" switch -exact -- $mode { U { set tag updated } A { set tag added } R { set tag removed } D { set tag removed } M { set tag modified } C { set tag conflict } P { set tag patched } ! { set tag warning } ? { set tag [expr {$cvscfg(status_filter) ? {noshow} : {unknown}}] } default { set tag default } } } elseif {[regexp {^cvs server: warning: .*} $line]} { set tag warning } #gen_log:log T "LEAVE: $tag" return [list $tag $line] } proc patch_colortags {exec line} { global cvscfg #gen_log:log T "ENTER ($exec \"$line\")" set tag default # Return the type of the line being output switch -regexp -- $line { { is new;} { set tag added } { changed from } { set tag modified } { is removed;} { set tag removed } {^\+} { set tag added } {^\-} { set tag removed } {^Index} { set tag modified } default { set tag default } } #gen_log:log T "LEAVE: $tag" return [list $tag $line] } proc hilight_rcslog {exec line} { set tag default if {[string match "=============*" $line]} { set tag patched } elseif {[string match "RCS file:*" $line]} { set tag patched } elseif {[string match "Working file:*" $line]} { set tag patched } return [list $tag $line] } # This is a plain viewer that prints whatever text is sent to it namespace eval ::view_output { variable instance 0 proc new {title text_to_display} { variable instance set my_idx $instance incr instance gen_log:log T "ENTER ($title ...)" namespace eval $my_idx { global cvscfg variable my_idx [uplevel {concat $my_idx}] variable title [uplevel {concat $title}] variable text_to_display [uplevel {list $text_to_display}] variable w ".output$my_idx" variable searchstr {} variable searchidx 1.0 viewer_window $w $title [namespace current] foreach line $text_to_display { $w.text insert end "$line" } proc search {} { variable searchidx variable w set str [$w.bottom.entry get] set match [$w.text search -- $str $searchidx] if {[string length $match] > 0} { set length [string length $str] $w.text mark set insert $match $w.text tag add sel $match "$match + ${length}c" $w.text see $match set searchidx "$match + ${length}c" } } proc destroy {} { if [catch {namespace delete [namespace current]} err] { puts "deleting [namespace current]" puts "$err" } } } } } proc viewer_window {w title parent} { global cvscfg global tcl_platform toplevel $w if {$tcl_platform(platform) != "windows"} { wm iconbitmap $w @$cvscfg(bitmapdir)/cvs-says.xbm } wm protocol $w WM_DELETE_WINDOW "$w.close invoke" text $w.text -setgrid yes -relief sunken -border 2 \ -bg white -fg black \ -exportselection 1 -height 30 \ -yscroll "$w.scroll set" bind $w.text { switch -- %K { "Up" - "Left" - "Right" - "Down" - "Next" - "Prior" - "Home" - "End" {} "c" - "C" { if {(%s & 0x04) == 0} { break } } default { break } } } bind $w.text <> {break} bind $w.text <> {break} # Configure the various tags foreach outputcolor [array names cvscfg outputColor,*] { regsub {^.*,} $outputcolor {} mode $w.text tag configure "$mode" -foreground $cvscfg($outputcolor) } scrollbar $w.scroll -relief sunken -command "$w.text yview" frame $w.bottom button $w.bottom.srchbtn -text Search \ -command "$parent\::search" entry $w.bottom.entry -width 20 -textvariable searchstr bind $w.bottom.entry "$parent\::search" button $w.save -text "Save to File" \ -command "save_viewcontents $w" button $w.close -text "Close" \ -command "catch {$parent\::destroy}; destroy $w; exit_cleanup 0" button $w.stop -text "Stop" -bg red4 -fg white \ -activebackground red4 -activeforeground white \ -state [expr {$cvscfg(allow_abort) ? {normal} : {disabled}}] \ -command "$parent\::abort" pack $w.bottom -side bottom -fill x ;#-padx 25 pack $w.scroll -side right -fill y pack $w.text -fill both -expand 1 pack $w.bottom.srchbtn -side left pack $w.bottom.entry -side left pack $w.save -in $w.bottom -side left -padx 25 pack $w.close -in $w.bottom -side right -ipadx 15 # Focus to activate text bindings focus $w wm title $w "$title" } proc save_viewcontents {w} { set types { {{All Files} *} } set savfile [ \ tk_getSaveFile -title "Save Results Summary" \ -initialdir "." \ -filetypes $types \ -parent $w \ ] if {$savfile == ""} { return } if {[catch {set fo [open $savfile w]}]} { puts "Cannot open $savfile for writing" return } puts $fo [$w.text get 1.0 end] close $fo } # # Search functionality for text widgets # proc search_textwidget_init {} { # Initialize the globals for general text searches global cvsglb if {! [info exists cvsglb(searchstr)] } { set cvsglb(searchstr) "" set cvsglb(last_searchstr) "" } set cvsglb(searchidx) "1.0" } proc search_textwidget { wtx } { # Search the text widget global cvsglb global cvscfg #gen_log:log T "ENTER ($wtx)" if {$cvsglb(searchstr) != $cvsglb(last_searchstr)} { $wtx tag delete match set cvsglb(searchidx) "1.0" } $wtx tag configure sel -background gray -foreground black $wtx tag raise sel $wtx tag configure match -background gray -foreground black \ -relief groove -borderwidth 2 $wtx tag raise match set searchstr $cvsglb(searchstr) set match [$wtx search -- $searchstr $cvsglb(searchidx)] if {[string length $match] > 0} { set length [string length $searchstr] $wtx mark set insert $match $wtx tag add match $match "$match + ${length}c" $wtx see $match set cvsglb(searchidx) "$match + ${length}c" } set cvsglb(last_searchstr) $cvsglb(searchstr) } proc search_listbox_init {} { # Initialize the globals for searches global cvsglb if {! [info exists cvsglb(searchstr)] } { set cvsglb(searchstr) "" set cvsglb(last_searchstr) "" } set cvsglb(lsearchidx) 0 } proc search_listbox { lbx } { # Search a listbox global cvsglb gen_log:log T "ENTER ($lbx)" gen_log:log D "search string = \"$cvsglb(searchstr)\"" gen_log:log D "search index = \"$cvsglb(lsearchidx)\"" set ndx [$lbx index end] if {$cvsglb(searchstr) != $cvsglb(last_searchstr)} { set cvsglb(lsearchidx) 0 for {set i 0} {$i < $ndx} {incr i} { $lbx itemconfigure $i -background $cvsglb(bg) } } if {$cvsglb(lsearchidx) > $ndx} { gen_log:log D "No more matches" return } for {set i $cvsglb(lsearchidx)} {$i < $ndx} {incr i} { set str [$lbx get $i] if {[string match "*$cvsglb(searchstr)*" $str]} { gen_log:log D "MATCH $str $cvsglb(searchstr)" set cvsglb(lsearchidx) $i $lbx itemconfigure $i -background $cvsglb(hlbg) $lbx see $i break } else { $lbx itemconfigure $i -background $cvsglb(bg) } } set cvsglb(last_searchstr) $cvsglb(searchstr) incr cvsglb(lsearchidx) } tkcvs-8.2.3.orig/tkcvs/picklist.tcl0000644000175000017500000000775711664612512015465 0ustar timtimnamespace eval ::picklist { variable data } proc ::picklist::used { name args } { variable data if {[info exists data($name)]} { foreach item $args { if {[set i [lsearch -exact $data($name) $item]] >= 0} { set data($name) [lreplace $data($name) $i $i] } } set data($name) [lrange [concat $args $data($name)] 0 50] } else { set data($name) [lrange $args 0 50] } return } proc ::picklist::choose { w data } { global cvscfg global cvsglb set line_h [font metrics \ $cvscfg(listboxfont) -displayof $w -linespace] set x [winfo rootx $w] set y [expr {[winfo rooty $w] + [winfo height $w]}] set width [winfo width $w] toplevel .picklist listbox .picklist.list -relief raised -border 1 -font $cvscfg(listboxfont) pack .picklist.list -side left -fill both -expand 1 if {[llength $data] <= 8} { set height [expr {($line_h + [.picklist.list cget -borderwidth] + [.picklist.list cget -selectborderwidth]) * [llength $data] + 8}] } else { set height [expr {($line_h + [.picklist.list cget -borderwidth] + [.picklist.list cget -selectborderwidth]) * 8 + 4}] scrollbar .picklist.scroll -relief sunken \ -command ".picklist.list yview" pack .picklist.scroll -side right -fill y .picklist.list configure -yscroll ".picklist.scroll set" } foreach datum $data { .picklist.list insert end $datum } ::bind .picklist { grab release .picklist destroy .picklist } ::bind .picklist " if {\[.picklist.list curselection\] != {}} { $w.e delete 0 end $w.e insert 0 \[.picklist.list get \[.picklist.list curselection\]\] $w.e icursor end focus -force $w.e } grab release .picklist destroy .picklist event generate $w.e -when tail focus -force $w.e " ::bind .picklist " set eventw \[winfo containing -displayof .picklist.list %X %Y\] if {\$eventw == \".picklist.list\"} { $w.e delete 0 end $w.e insert 0 \[.picklist.list get @%x,%y\] } if {\$eventw != \".picklist.scroll\"} { grab release .picklist destroy .picklist event generate $w.e -when tail focus -force $w.e } " focus .picklist.list wm geometry .picklist "$width\x$height\+$x\+$y" wm overrideredirect .picklist 1 tkwait visibility .picklist grab set -global .picklist return } proc ::picklist::entry { w varName listName } { global cvsglb variable data if {! [info exists data($listName)]} { set data($listName) {} } frame $w -relief sunken -border 1 ::entry $w.e -relief flat -border 0 -textvariable $varName -bg $cvsglb(textbg) pack $w.e -side left -expand 1 -fill both button $w.b -image arr_dn -border 1 \ -padx 0 -pady 0 -takefocus 0 \ -command " ::picklist::choose $w \$::picklist::data($listName) " pack $w.b -side right ::bind $w.e "$w.b invoke" return } proc ::picklist::bind { w {sequence {}} {script {}} } { return [::bind $w.e $sequence $script] } proc ::picklist::clear {arr} { variable data set data($arr) {} } proc ::picklist::load { } { global cvscfg if {! [catch {set file [open [file join $cvscfg(home) {.tkcvs-picklists}] r]}]} { variable data while {[gets $file name] > 0} { while {[gets $file item] > 0} { lappend data($name) $item } } close $file } } proc ::picklist::save { } { global cvscfg if {! [catch {set file [open [file join $cvscfg(home) {.tkcvs-picklists}] w]}]} { variable data foreach name {cvsroot directory} { puts $file $name set c 0 if {! [info exists data($name)]} { continue } foreach item $data($name) { # number of items saved is a preference if {$c >= $cvscfg(picklist_items)} {break} puts $file $item incr c } puts $file "" } close $file } } tkcvs-8.2.3.orig/tkcvs/modtree.tcl0000644000175000017500000003552211664612512015271 0ustar timtim# Adapted from tree.tcl released under GPL by # # Copyright (C) 1997,1998 D. Richard Hipp # # # Create a new two-paned widget for the modules. # proc ModTree:create {w {open_func {}} } { global Tree global cvsglb gen_log:log T "ENTER ($w $open_func)" set Tree(open_function) $open_func if {[catch "image type ModTree:closedbm"]} { ModTree:loadimages } set winwid [winfo width $w] panedwindow $w.pw -relief sunk -bd 2 $w.pw configure -handlepad 35 -sashwidth 4 -sashpad 0 -handlesize 10 frame $w.tree frame $w.labl canvas $w.tree.list -highlightthickness 0 -width [expr {$winwid * 3/8}] canvas $w.labl.list -highlightthickness 0 $w.tree configure -bg $cvsglb(canvbg) $w.labl configure -bg $cvsglb(canvbg) $w.tree.list configure -bg $cvsglb(canvbg) $w.labl.list configure -bg $cvsglb(canvbg) scrollbar $w.yscroll -orient vertical -highlightthickness 0 \ -command "ModTree:scroll_windows $w" pack $w.yscroll -side right -fill y bind $w.tree.list <1> "ModTree:clearselection $w" foreach canv {tree labl} { $w.$canv.list configure -yscrollcommand "$w.yscroll set" bind $w.$canv.list "ModTree:scroll_windows $w scroll 1 pages" bind $w.$canv.list "ModTree:scroll_windows $w scroll -1 pages" bind $w.$canv.list "ModTree:scroll_windows $w scroll 1 units" bind $w.$canv.list "ModTree:scroll_windows $w scroll -1 units" bind $w.$canv.list \ "ModTree:scroll_windows $w scroll \[expr {-(%D/120)*4}\] units" bind $w.$canv.list \ "ModTree:scroll_windows $w scroll -1 units" bind $w.$canv.list \ "ModTree:scroll_windows $w scroll 1 units" label $w.$canv.lbl -relief raised -bd 2 pack $w.$canv -side left -fill both -expand yes pack $w.$canv.lbl -ipady 2 -fill x -expand no pack $w.$canv.list -side top -fill both -expand yes -padx 8 } $w.tree.lbl configure -text "Module" $w.labl.lbl configure -text "Information" $w.pw add $w.tree $w.pw add $w.labl ModTree:dfltconfig $w / set Tree(vsize) 16 ModTree:buildwhenidle $w set Tree($w:selection) {} set Tree($w:selB) {} set Tree($w:jtems) 0 focus $w.tree.list gen_log:log T "LEAVE" } # Initialize a element of the tree. # Internal use only # proc ModTree:dfltconfig {w v} { global Tree #gen_log:log T "ENTER ($w $v)" set Tree($w:$v:children) {} set Tree($w:$v:open) 0 set Tree($w:$v:icon) {} set Tree($w:$v:tags) {} #gen_log:log T "LEAVE" } # # Insert a new element $v into the tree $w. # proc ModTree:newitem {w v name title args} { global Tree #gen_log:log T "ENTER ($w $v $name \"$title\" $args)" set dir [file dirname $v] set n [file tail $v] #puts "MTNewitem: dir $dir (dirname $v) n $n (file tail $v)" # If a plain file starts with ~ file tail returns ./~ which is the # right thing for filesystem commands but not for this regsub {^\./} $n {} n if {![info exists Tree($w:$dir:open)]} { cvsfail "parent item \"$dir\" is missing" .modbrowse } set i [lsearch -exact $Tree($w:$dir:children) $n] if {$i>=0} { #cvsfail "item \"$v\" already exists" .modbrowse return } lappend Tree($w:$dir:children) $n set Tree($w:$dir:children) [lsort $Tree($w:$dir:children)] ModTree:dfltconfig $w $v set Tree($w:$v:name) $name set Tree($w:$v:title) $title foreach {op arg} $args { switch -exact -- $op { -image {set Tree($w:$v:icon) $arg} -tags {set Tree($w:$v:tags) $arg} } } ModTree:buildwhenidle $w #gen_log:log T "LEAVE" } # # Delete element $v from the tree $w. If $v is /, then the widget is # deleted. # proc ModTree:delitem {w v} { global Tree #gen_log:log T "ENTER ($w $v)" if {![info exists Tree($w:$v:open)]} return if {[string compare $v /]==0} { # delete the whole widget catch {destroy $w.tree} catch {destroy $w.labl} set parent [winfo parent $w] catch {destroy $w.yscroll} foreach t [array names Tree $w:*] { unset Tree($t) } return } if {[info exists Tree($w:$v:children)]} { foreach c $Tree($w:$v:children) { catch {ModTree:delitem $w $v/$c} } unset Tree($w:$v:open) unset Tree($w:$v:children) unset Tree($w:$v:icon) set dir [file dirname $v] set n [file tail $v] set i [lsearch -exact $Tree($w:$dir:children) $n] if {$i>=0} { set Tree($w:$dir:children) [lreplace $Tree($w:$dir:children) $i $i] } } ModTree:buildwhenidle $w #gen_log:log T "LEAVE" } proc ModTree:loadimages {} { # # Bitmaps used to show which parts of the tree can be opened. # global cvscfg set maskdata "#define solid_width 9\n#define solid_height 9" append maskdata { static unsigned char solid_bits[] = { 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, 0x01 }; } set data "#define open_width 9\n#define open_height 9" append data { static unsigned char open_bits[] = { 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01 }; } image create bitmap ModTree:openbm -data $data -maskdata $maskdata \ -foreground black -background white set data "#define closed_width 9\n#define closed_height 9" append data { static unsigned char closed_bits[] = { 0xff, 0x01, 0x01, 0x01, 0x11, 0x01, 0x11, 0x01, 0x7d, 0x01, 0x11, 0x01, 0x11, 0x01, 0x01, 0x01, 0xff, 0x01 }; } image create bitmap ModTree:closedbm -data $data -maskdata $maskdata \ -foreground black -background white image create photo dir \ -format gif -file [file join $cvscfg(bitmapdir) dir.gif] image create photo mdir \ -format gif -file [file join $cvscfg(bitmapdir) mdir.gif] image create photo mod \ -format gif -file [file join $cvscfg(bitmapdir) mod.gif] image create photo adir \ -format gif -file [file join $cvscfg(bitmapdir) adir.gif] image create photo amod \ -format gif -file [file join $cvscfg(bitmapdir) amod.gif] } # Internal use only. # Draw the tree on the canvas proc ModTree:build {w} { global Tree #gen_log:log T "ENTER ($w)" $w.tree.list delete all $w.labl.list delete all catch {unset Tree($w:buildpending)} set Tree($w:y) 30 ModTree:buildlayer $w / $Tree(vsize) set tbox [$w.tree.list bbox all] #if {$tbox == ""} {return} $w.tree.list config -scrollregion $tbox # Use tree's bbox for labl, because labl's is a little shorter # but we need to keep them in sync $w.labl.list config -scrollregion $tbox #gen_log:log T "LEAVE" } # Internal use only. # Build a single layer of the tree on the canvas. Indent by $in pixels proc ModTree:buildlayer {w v in} { global Tree global cvscfg global cvsglb #gen_log:log T "ENTER ($w $v $in)" if {$v=="/"} { set vx {} } else { set vx $v } set start [expr {$Tree($w:y)-10}] foreach c $Tree($w:$v:children) { set y $Tree($w:y) incr Tree($w:y) [expr {$Tree(vsize)+3}] $w.tree.list create line $in $y [expr {$in+$Tree(vsize)}] $y -fill gray50 if {! [string length $Tree($w:$vx/$c:children)]} { if {$Tree($w:$vx/$c:icon) == "mdir"} { set Tree($w:$vx/$c:icon) "mod" } } set icon $Tree($w:$vx/$c:icon) set x [expr {$in+12}] set j $Tree($w:jtems) incr Tree($w:jtems) # Draw the icon if {[string length $icon]>0} { set k [$w.tree.list create image $x $y -image $icon -anchor w ] $w.tree.list bind k <1> "ModTree:setselection $w \"$vx/$c\"" $w.tree.list bind k <2> "ModTree:setselB $w \"$vx/$c\"" $w.tree.list bind k <3> "ModTree:setselB $w \"$vx/$c\"" incr x 24 } # Draw the label set lbl $Tree($w:$vx/$c:name) $w.tree.list create text $x $y \ -text $lbl \ -fill $cvsglb(fg) \ -font $cvscfg(listboxfont) -anchor w \ -tag $w.tree.list.tx$j # In the bindings, filenames need any single percents replaced with # double to avoid interpretation as an event field set f "$vx/$c" regsub -all {\%} $f {%%} fn regsub -all {\$} $fn {\$} fn $w.tree.list bind $w.tree.list.tx$j <1> "ModTree:setselection $w \"$fn\"" $w.tree.list bind $w.tree.list.tx$j <2> "ModTree:setselB $w \"$fn\"" $w.tree.list bind $w.tree.list.tx$j <3> "ModTree:setselB $w \"$fn\"" $w.tree.list bind $w.tree.list.tx$j "ModTree:flash $w $j" $w.tree.list bind $w.tree.list.tx$j "ModTree:unflash $w $j" #gen_log:log D "$vx/$c $lbl j=$j" if {[info exists Tree($w:$vx/$c:title)]} { set k [$w.labl.list create text [expr {$x - $Tree(vsize) - 22}] $y \ -text $Tree($w:$vx/$c:title) \ -fill $cvsglb(fg) \ -font $cvscfg(listboxfont) -anchor w \ -tag $w.labl.list.tx$j] } $w.labl.list bind $w.labl.list.tx$j <1> "ModTree:setselection $w \"$fn\"" $w.labl.list bind $w.labl.list.tx$j <2> "ModTree:setselB $w \"$fn\"" $w.labl.list bind $w.labl.list.tx$j <3> "ModTree:setselB $w \"$fn\"" $w.labl.list bind $w.labl.list.tx$j "ModTree:flash $w $j" $w.labl.list bind $w.labl.list.tx$j "ModTree:unflash $w $j" set Tree($w:tag:$j) $vx/$c set Tree($w:$vx/$c:tag) $j # Put an open/close image on it if it has children if {[string length $Tree($w:$vx/$c:children)]} { if {$Tree($w:$vx/$c:open)} { # It's closed. Draw a plus sign and bind a function to close the sub-tree set k [$w.tree.list create image $in $y -image ModTree:openbm] $w.tree.list bind $k <1> "set \"Tree($w:$vx/$c:open)\" 0; \ ModTree:build $w" ModTree:buildlayer $w $vx/$c [expr {$in+$Tree(vsize)+8}] } else { # It's open. Draw a minus sign and bind a function to build the sub-tree set k [$w.tree.list create image $in $y -image ModTree:closedbm] if {$Tree(open_function) == {} } { $w.tree.list bind $k <1> "set \"Tree($w:$vx/$c:open)\" 1; \ ModTree:build $w" } else { $w.tree.list bind $k <1> "set \"Tree($w:$vx/$c:open)\" 1; \ $Tree(open_function) $w \"$vx/$c\"; \ ModTree:build $w" } } } } if {![info exists y]} {return} set j [$w.tree.list create line $in $start $in [expr {$y+1}] -fill gray50 ] $w.tree.list lower $j #gen_log:log T "LEAVE" } # Open a branch of a tree # proc ModTree:open {w v} { global Tree if {[info exists Tree($w:$v:open)] && $Tree($w:$v:open)==0 && [info exists Tree($w:$v:children)] && [string length $Tree($w:$v:children)]>0} { set Tree($w:$v:open) 1 ModTree:build $w } } proc ModTree:close {w v} { global Tree if {[info exists Tree($w:$v:open)] && $Tree($w:$v:open)==1} { set Tree($w:$v:open) 0 ModTree:build $w } } # Internal use only # Call ModTree:build then next time we're idle proc ModTree:buildwhenidle {w} { global Tree #gen_log:log T "ENTER ($w)" if {![info exists Tree($w:buildpending)]} { set Tree($w:buildpending) 1 after idle "ModTree:build $w" } #gen_log:log T "LEAVE" } # # Change the selection to the indicated item # proc ModTree:setselection {w v} { global Tree global modbrowse_module global modbrowse_path global modbrowse_title #gen_log:log T "ENTER ($w \"$v\")" # Clear old selection set oldv $Tree($w:selection) if {$oldv != ""} { set j $Tree($w:$oldv:tag) ModTree:clearTextHBox $w $j } #foreach a [array names Tree "$w:$v:*"] { puts "$a $Tree($a)" } # Hilight new selection if {$v != ""} { set Tree($w:selection) $v set j $Tree($w:$v:tag) #ModTree:setTextHBox $w $w.tree.list.tx$j ModTree:setTextHBox $w $j set modbrowse_module $Tree($w:$v:name) set modbrowse_title $Tree($w:$v:title) } set modbrowse_path [string trimleft $v /] } # # Change the secondary selection # proc ModTree:setselB {w v} { global Tree global selB_path # Clear old selection set oldv $Tree($w:selB) if {$oldv != ""} { set j $Tree($w:$oldv:tag) ModTree:clearTextHBox $w $j } # Hilight new selection if {$v != ""} { set Tree($w:selB) $v set j $Tree($w:$v:tag) #ModTree:setTextHBox $w $w.tree.list.tx$j ModTree:setTextHBox $w $j } set selB_path $v } # Clear selection, invoked when clicking over a blank proc ModTree:clearselection {w} { global Tree global modbrowse_module # Don't clear unless we aren't over anything if {[llength [$w.tree.list gettags current]] == 0 } { ModTree:setselection $w "" ModTree:setselB $w "" set Tree($w:selection) {} set Tree($w:selB) {} set modbrowse_module "" } } proc ModTree:flash {w y} { global cvscfg $w.tree.list itemconfigure $w.tree.list.tx$y -font $cvscfg(flashfont) $w.labl.list itemconfigure $w.labl.list.tx$y -font $cvscfg(flashfont) } proc ModTree:unflash {w y} { global cvscfg $w.tree.list itemconfigure $w.tree.list.tx$y -font $cvscfg(listboxfont) $w.labl.list itemconfigure $w.labl.list.tx$y -font $cvscfg(listboxfont) } proc ModTree:scroll_windows {w args} { #gen_log:log T "ENTER ($w $args)" set parent [winfo parent $w] set yget [$w.yscroll get] set way [lindex $args 2] set comd [lindex $args 1] set first [lindex $yget 0] set last [lindex $yget 1] #gen_log:log D "$comd $way: $first $last" # If you dont do this, the scrollbar fills the whole trough when # you page past the top or bottom case $comd { {units pages} { if {$way < 0} { if {$first == 0} { return } } else { if {$last == 1} { return } } } } eval $w.tree.list yview $args eval $w.labl.list yview $args } proc ModTree:destroy {w} { foreach u [winfo children $w] { catch {destroy $u} } } # clear any text highlight box (used by set/clearselection) proc ModTree:clearTextHBox {w id} { global cvsglb # clear the tag corresponding to the text label catch {$w.tree.list delete HBox$w.tree.list.tx$id} $w.tree.list itemconfigure $w.tree.list.tx$id -fill $cvsglb(fg) catch {$w.labl.list delete HBox$w.tree.list.tx$id} $w.labl.list itemconfigure $w.labl.list.tx$id -fill $cvsglb(fg) } # set a text highligh box (used by set/clearselection) proc ModTree:setTextHBox {w id} { global cvsglb # get the bounding box for the text id set bbox [$w.tree.list bbox $w.tree.list.tx$id] if {[llength $bbox] != 4} { return } set lx [lindex $bbox 0] #set uy [lindex $bbox 1] set ly [lindex $bbox 3] set ly [expr {$ly +1}] set uy [expr {$ly -16}] set i [eval $w.tree.list create rectangle \ $lx $ly [winfo width $w.tree] $uy \ -fill $cvsglb(hlbg) -tag HBox$w.tree.list.tx$id -outline \"\"] $w.tree.list itemconfigure $w.tree.list.tx$id -fill $cvsglb(hlfg) $w.tree.list lower $i set i [eval $w.labl.list create rectangle \ 0 $ly [winfo width $w.labl] $uy \ -fill $cvsglb(hlbg) -tag HBox$w.tree.list.tx$id -outline \"\"] $w.labl.list itemconfigure $w.labl.list.tx$id -fill $cvsglb(hlfg) $w.labl.list lower $i } tkcvs-8.2.3.orig/tkcvs/tclIndex0000644000175000017500000005362411664612512014626 0ustar timtim# Tcl autoload index file, version 2.0 # This file is generated by the "auto_mkindex" command # and sourced to set up indexing information for one or # more commands. Typically each line is a command that # sets an element in the auto_index array, where the # element name is the name of a command and the value is # a script that loads the command. set auto_index(::annotate::new) [list source [file join $dir annotate.tcl]] set auto_index(::logcanvas::new) [list source [file join $dir branch_diagram.tcl]] set auto_index(cde_open_resourcefile) [list source [file join $dir style_params.tcl]] set auto_index(get_cde_params) [list source [file join $dir style_params.tcl]] set auto_index(get_gtk_params) [list source [file join $dir style_params.tcl]] set auto_index(shades) [list source [file join $dir style_params.tcl]] set auto_index(cvs_notincvs) [list source [file join $dir cvs.tcl]] set auto_index(cvs_incvs) [list source [file join $dir cvs.tcl]] set auto_index(cvs_sandbox_runcmd) [list source [file join $dir cvs.tcl]] set auto_index(cvs_sandbox_filetags) [list source [file join $dir cvs.tcl]] set auto_index(cvs_workdir_status) [list source [file join $dir cvs.tcl]] set auto_index(cvs_remove) [list source [file join $dir cvs.tcl]] set auto_index(cvs_remove_dir) [list source [file join $dir cvs.tcl]] set auto_index(cvs_edit) [list source [file join $dir cvs.tcl]] set auto_index(cvs_unedit) [list source [file join $dir cvs.tcl]] set auto_index(cvs_history) [list source [file join $dir cvs.tcl]] set auto_index(cvs_add) [list source [file join $dir cvs.tcl]] set auto_index(cvs_add_dir) [list source [file join $dir cvs.tcl]] set auto_index(add_subdirs) [list source [file join $dir cvs.tcl]] set auto_index(rem_subdirs) [list source [file join $dir cvs.tcl]] set auto_index(cvs_fileview_update) [list source [file join $dir cvs.tcl]] set auto_index(cvs_fileview_checkout) [list source [file join $dir cvs.tcl]] set auto_index(cvs_log) [list source [file join $dir cvs.tcl]] set auto_index(cvs_log_rev) [list source [file join $dir cvs.tcl]] set auto_index(cvs_annotate) [list source [file join $dir cvs.tcl]] set auto_index(cvs_annotate_r) [list source [file join $dir cvs.tcl]] set auto_index(cvs_commit) [list source [file join $dir cvs.tcl]] set auto_index(cvs_tag) [list source [file join $dir cvs.tcl]] set auto_index(cvs_update) [list source [file join $dir cvs.tcl]] set auto_index(cvs_merge) [list source [file join $dir cvs.tcl]] set auto_index(cvs_merge_tag_seq) [list source [file join $dir cvs.tcl]] set auto_index(cvs_status) [list source [file join $dir cvs.tcl]] set auto_index(cvs_check) [list source [file join $dir cvs.tcl]] set auto_index(cvs_checkout) [list source [file join $dir cvs.tcl]] set auto_index(cvs_filelog) [list source [file join $dir cvs.tcl]] set auto_index(cvs_export) [list source [file join $dir cvs.tcl]] set auto_index(cvs_patch) [list source [file join $dir cvs.tcl]] set auto_index(cvs_version) [list source [file join $dir cvs.tcl]] set auto_index(cvs_merge_conflict) [list source [file join $dir cvs.tcl]] set auto_index(cvs_gettaglist) [list source [file join $dir cvs.tcl]] set auto_index(cvs_release) [list source [file join $dir cvs.tcl]] set auto_index(cvs_rtag) [list source [file join $dir cvs.tcl]] set auto_index(cvs_commit_dialog) [list source [file join $dir cvs.tcl]] set auto_index(cvs_ascii) [list source [file join $dir cvs.tcl]] set auto_index(cvs_binary) [list source [file join $dir cvs.tcl]] set auto_index(cvs_revert) [list source [file join $dir cvs.tcl]] set auto_index(read_cvs_dir) [list source [file join $dir cvs.tcl]] set auto_index(parse_cvsmodules) [list source [file join $dir cvs.tcl]] set auto_index(cvs_lock) [list source [file join $dir cvs.tcl]] set auto_index(cvs_directory_merge) [list source [file join $dir cvs.tcl]] set auto_index(cvs_branches) [list source [file join $dir cvs.tcl]] set auto_index(::cvs_branchlog::new) [list source [file join $dir cvs.tcl]] set auto_index(sortrevs) [list source [file join $dir cvs.tcl]] set auto_index(dialog_FormCreate) [list source [file join $dir dialog.tcl]] set auto_index(dialog_FormComplete) [list source [file join $dir dialog.tcl]] set auto_index(dialog_cvs_checkout) [list source [file join $dir dialog.tcl]] set auto_index(dialog_cvs_export) [list source [file join $dir dialog.tcl]] set auto_index(dialog_svn_checkout) [list source [file join $dir dialog.tcl]] set auto_index(dialog_svn_tag) [list source [file join $dir dialog.tcl]] set auto_index(dialog_cvs_patch) [list source [file join $dir dialog.tcl]] set auto_index(dialog_svn_patch) [list source [file join $dir dialog.tcl]] set auto_index(add_dialog) [list source [file join $dir dialog.tcl]] set auto_index(file_tag_dialog) [list source [file join $dir dialog.tcl]] set auto_index(rtag_dialog) [list source [file join $dir dialog.tcl]] set auto_index(subtract_dialog) [list source [file join $dir dialog.tcl]] set auto_index(edit_dialog) [list source [file join $dir dialog.tcl]] set auto_index(unedit_dialog) [list source [file join $dir dialog.tcl]] set auto_index(cvs_update_options) [list source [file join $dir dialog.tcl]] set auto_index(update_set_defaults) [list source [file join $dir dialog.tcl]] set auto_index(update_with_options) [list source [file join $dir dialog.tcl]] set auto_index(addir_dialog) [list source [file join $dir dialog.tcl]] set auto_index(subtractdir_dialog) [list source [file join $dir dialog.tcl]] set auto_index(file_input_and_do) [list source [file join $dir dialog.tcl]] set auto_index(release_dialog) [list source [file join $dir dialog.tcl]] set auto_index(svn_update_options) [list source [file join $dir dialog.tcl]] set auto_index(assemble_mergetags) [list source [file join $dir dialog.tcl]] set auto_index(dialog_merge_notice) [list source [file join $dir dialog.tcl]] set auto_index(commit_history) [list source [file join $dir dialog.tcl]] set auto_index(history_browser) [list source [file join $dir dialog.tcl]] set auto_index(comparediff) [list source [file join $dir diff.tcl]] set auto_index(comparediff_files) [list source [file join $dir diff.tcl]] set auto_index(comparediff_r) [list source [file join $dir diff.tcl]] set auto_index(comparediff_sandbox) [list source [file join $dir diff.tcl]] set auto_index(DirCanvas:create) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:headtext) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:column) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:map_column) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:unmap_column) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:newitem) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:loadimages) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:delitem) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:deltree) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:flash) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:unflash) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:setselection) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:addselection) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:clearTextHBox) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:setTextHBox) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:addrange) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:unselectall) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:build) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:buildwhenidle) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:yview_windows) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:scroll_windows) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:drag_windows) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:sort_by_col) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:toggle_col) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:makepopup) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:popup) [list source [file join $dir dircanvas.tcl]] set auto_index(DirCanvas:destroy) [list source [file join $dir dircanvas.tcl]] set auto_index(cvsok) [list source [file join $dir errors.tcl]] set auto_index(cvsconfirm) [list source [file join $dir errors.tcl]] set auto_index(cvsalwaysconfirm) [list source [file join $dir errors.tcl]] set auto_index(cvsfail) [list source [file join $dir errors.tcl]] set auto_index(cvserror) [list source [file join $dir errors.tcl]] set auto_index(cvs_usercmd) [list source [file join $dir exec.tcl]] set auto_index(cvs_execcmd) [list source [file join $dir exec.tcl]] set auto_index(cvs_catchcmd) [list source [file join $dir exec.tcl]] set auto_index(::exec::new) [list source [file join $dir exec.tcl]] set auto_index(::viewer::new) [list source [file join $dir exec.tcl]] set auto_index(status_colortags) [list source [file join $dir exec.tcl]] set auto_index(patch_colortags) [list source [file join $dir exec.tcl]] set auto_index(hilight_rcslog) [list source [file join $dir exec.tcl]] set auto_index(::view_output::new) [list source [file join $dir exec.tcl]] set auto_index(viewer_window) [list source [file join $dir exec.tcl]] set auto_index(save_viewcontents) [list source [file join $dir exec.tcl]] set auto_index(search_textwidget_init) [list source [file join $dir exec.tcl]] set auto_index(search_textwidget) [list source [file join $dir exec.tcl]] set auto_index(search_listbox_init) [list source [file join $dir exec.tcl]] set auto_index(search_listbox) [list source [file join $dir exec.tcl]] set auto_index(browse_files) [list source [file join $dir filebrowse.tcl]] set auto_index(filepath) [list source [file join $dir filebrowse.tcl]] set auto_index(module_filelog) [list source [file join $dir filebrowse.tcl]] set auto_index(module_fileview) [list source [file join $dir filebrowse.tcl]] set auto_index(module_tagview) [list source [file join $dir filebrowse.tcl]] set auto_index(gen_log:init) [list source [file join $dir gen_log.tcl]] set auto_index(gen_log:log) [list source [file join $dir gen_log.tcl]] set auto_index(gen_log:quit) [list source [file join $dir gen_log.tcl]] set auto_index(gen_log:clear) [list source [file join $dir gen_log.tcl]] set auto_index(gen_log:save) [list source [file join $dir gen_log.tcl]] set auto_index(gen_log:changeclass) [list source [file join $dir gen_log.tcl]] set auto_index(aboutbox) [list source [file join $dir help.tcl]] set auto_index(help_cvs_version) [list source [file join $dir help.tcl]] set auto_index(wish_version) [list source [file join $dir help.tcl]] set auto_index(put-text) [list source [file join $dir help.tcl]] set auto_index(purge-all-tags) [list source [file join $dir help.tcl]] set auto_index(do_help) [list source [file join $dir help.tcl]] set auto_index(man_description) [list source [file join $dir help.tcl]] set auto_index(cli_options) [list source [file join $dir help.tcl]] set auto_index(current_directory) [list source [file join $dir help.tcl]] set auto_index(log_browser) [list source [file join $dir help.tcl]] set auto_index(directory_branch_viewer) [list source [file join $dir help.tcl]] set auto_index(module_browser) [list source [file join $dir help.tcl]] set auto_index(importing_new_modules) [list source [file join $dir help.tcl]] set auto_index(importing_to_existing_module) [list source [file join $dir help.tcl]] set auto_index(vendor_merge) [list source [file join $dir help.tcl]] set auto_index(configuration_files) [list source [file join $dir help.tcl]] set auto_index(environment_variables) [list source [file join $dir help.tcl]] set auto_index(user_defined_menu) [list source [file join $dir help.tcl]] set auto_index(cvs_modules_file) [list source [file join $dir help.tcl]] set auto_index(menu_std_help) [list source [file join $dir help.tcl]] set auto_index(import_run) [list source [file join $dir import.tcl]] set auto_index(do_import) [list source [file join $dir import.tcl]] set auto_index(import2_run) [list source [file join $dir import2.tcl]] set auto_index(do_import2) [list source [file join $dir import2.tcl]] set auto_index(import_wait) [list source [file join $dir import2.tcl]] set auto_index(getExistModDialog) [list source [file join $dir import2.tcl]] set auto_index(moduleDialog) [list source [file join $dir import2.tcl]] set auto_index(::joincanvas::new) [list source [file join $dir joincanvas.tcl]] set auto_index(cvs_joincanvas) [list source [file join $dir joincanvas.tcl]] set auto_index(join_getlog) [list source [file join $dir joincanvas.tcl]] set auto_index(modbrowse_setup) [list source [file join $dir modbrowse.tcl]] set auto_index(modbrowse_images) [list source [file join $dir modbrowse.tcl]] set auto_index(modbrowse_menus) [list source [file join $dir modbrowse.tcl]] set auto_index(modbrowse_run) [list source [file join $dir modbrowse.tcl]] set auto_index(modbrowse_tree) [list source [file join $dir modbrowse.tcl]] set auto_index(module_exit) [list source [file join $dir modbrowse.tcl]] set auto_index(module_changedir) [list source [file join $dir modbrowse.tcl]] set auto_index(ModTree:create) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:dfltconfig) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:newitem) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:delitem) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:loadimages) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:build) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:buildlayer) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:open) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:close) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:buildwhenidle) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:setselection) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:setselB) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:clearselection) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:flash) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:unflash) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:scroll_windows) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:destroy) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:clearTextHBox) [list source [file join $dir modtree.tcl]] set auto_index(ModTree:setTextHBox) [list source [file join $dir modtree.tcl]] set auto_index(gather_mod_index) [list source [file join $dir modules.tcl]] set auto_index(find_filenames) [list source [file join $dir modules.tcl]] set auto_index(find_subdirs) [list source [file join $dir modules.tcl]] set auto_index(::picklist::used) [list source [file join $dir picklist.tcl]] set auto_index(::picklist::choose) [list source [file join $dir picklist.tcl]] set auto_index(::picklist::entry) [list source [file join $dir picklist.tcl]] set auto_index(::picklist::bind) [list source [file join $dir picklist.tcl]] set auto_index(::picklist::clear) [list source [file join $dir picklist.tcl]] set auto_index(::picklist::load) [list source [file join $dir picklist.tcl]] set auto_index(::picklist::save) [list source [file join $dir picklist.tcl]] set auto_index(rcs_branches) [list source [file join $dir rcs.tcl]] set auto_index(rcs_checkout) [list source [file join $dir rcs.tcl]] set auto_index(rcs_lock) [list source [file join $dir rcs.tcl]] set auto_index(rcs_checkin) [list source [file join $dir rcs.tcl]] set auto_index(rcs_commit_dialog) [list source [file join $dir rcs.tcl]] set auto_index(rcs_workdir_status) [list source [file join $dir rcs.tcl]] set auto_index(rcs_check) [list source [file join $dir rcs.tcl]] set auto_index(rcs_log) [list source [file join $dir rcs.tcl]] set auto_index(rcs_revert) [list source [file join $dir rcs.tcl]] set auto_index(scrollbindings) [list source [file join $dir ui_misc.tcl]] set auto_index(dragbind) [list source [file join $dir ui_misc.tcl]] set auto_index(wheelbind) [list source [file join $dir ui_misc.tcl]] set auto_index(bind_show) [list source [file join $dir ui_misc.tcl]] set auto_index(busy_start) [list source [file join $dir ui_misc.tcl]] set auto_index(busy_done) [list source [file join $dir ui_misc.tcl]] set auto_index(rgb_shadow) [list source [file join $dir ui_misc.tcl]] set auto_index(rgb_diff) [list source [file join $dir ui_misc.tcl]] set auto_index(is_gray) [list source [file join $dir ui_misc.tcl]] set auto_index(static) [list source [file join $dir ui_misc.tcl]] set auto_index(nop) [list source [file join $dir ui_misc.tcl]] set auto_index(svn_import_run) [list source [file join $dir svn-import.tcl]] set auto_index(svn_do_import) [list source [file join $dir svn-import.tcl]] set auto_index(svn_version) [list source [file join $dir svn.tcl]] set auto_index(read_svn_dir) [list source [file join $dir svn.tcl]] set auto_index(svn_lock) [list source [file join $dir svn.tcl]] set auto_index(svn_workdir_status) [list source [file join $dir svn.tcl]] set auto_index(svn_add) [list source [file join $dir svn.tcl]] set auto_index(svn_remove) [list source [file join $dir svn.tcl]] set auto_index(svn_check) [list source [file join $dir svn.tcl]] set auto_index(svn_update) [list source [file join $dir svn.tcl]] set auto_index(svn_opt_update) [list source [file join $dir svn.tcl]] set auto_index(svn_commit_dialog) [list source [file join $dir svn.tcl]] set auto_index(svn_commit) [list source [file join $dir svn.tcl]] set auto_index(svn_annotate) [list source [file join $dir svn.tcl]] set auto_index(svn_annotate_r) [list source [file join $dir svn.tcl]] set auto_index(svn_patch) [list source [file join $dir svn.tcl]] set auto_index(svn_list) [list source [file join $dir svn.tcl]] set auto_index(svn_delete) [list source [file join $dir svn.tcl]] set auto_index(svn_jit_listdir) [list source [file join $dir svn.tcl]] set auto_index(svn_jit_dircmd) [list source [file join $dir svn.tcl]] set auto_index(parse_svnmodules) [list source [file join $dir svn.tcl]] set auto_index(svn_log) [list source [file join $dir svn.tcl]] set auto_index(svn_log_rev) [list source [file join $dir svn.tcl]] set auto_index(svn_info) [list source [file join $dir svn.tcl]] set auto_index(svn_merge_conflict) [list source [file join $dir svn.tcl]] set auto_index(svn_resolve) [list source [file join $dir svn.tcl]] set auto_index(svn_revert) [list source [file join $dir svn.tcl]] set auto_index(svn_tag) [list source [file join $dir svn.tcl]] set auto_index(svn_rcopy) [list source [file join $dir svn.tcl]] set auto_index(svn_pathforcopy) [list source [file join $dir svn.tcl]] set auto_index(svn_merge) [list source [file join $dir svn.tcl]] set auto_index(svn_merge_tag_seq) [list source [file join $dir svn.tcl]] set auto_index(svn_checkout) [list source [file join $dir svn.tcl]] set auto_index(svn_filecat) [list source [file join $dir svn.tcl]] set auto_index(svn_filelog) [list source [file join $dir svn.tcl]] set auto_index(svn_fileview) [list source [file join $dir svn.tcl]] set auto_index(svn_directory_merge) [list source [file join $dir svn.tcl]] set auto_index(svn_branches) [list source [file join $dir svn.tcl]] set auto_index(safe_url) [list source [file join $dir svn.tcl]] set auto_index(::svn_branchlog::new) [list source [file join $dir svn.tcl]] set auto_index(set_tooltips) [list source [file join $dir tooltips.tcl]] set auto_index(internal_tooltips_PopUp) [list source [file join $dir tooltips.tcl]] set auto_index(internal_tooltips_PopDown) [list source [file join $dir tooltips.tcl]] set auto_index(merge_run) [list source [file join $dir vendor_merge.tcl]] set auto_index(get_j) [list source [file join $dir vendor_merge.tcl]] set auto_index(put_rev_tags) [list source [file join $dir vendor_merge.tcl]] set auto_index(do_merge) [list source [file join $dir vendor_merge.tcl]] set auto_index(unpack_tag_word) [list source [file join $dir vendor_merge.tcl]] set auto_index(get_rv_tags) [list source [file join $dir vendor_merge.tcl]] set auto_index(merge_taglist) [list source [file join $dir vendor_merge.tcl]] set auto_index(vendorDialog) [list source [file join $dir vendor_merge.tcl]] set auto_index(workdir_setup) [list source [file join $dir workdir.tcl]] set auto_index(workdir_images) [list source [file join $dir workdir.tcl]] set auto_index(workdir_menus) [list source [file join $dir workdir.tcl]] set auto_index(workdir_list_files) [list source [file join $dir workdir.tcl]] set auto_index(workdir_edit_command) [list source [file join $dir workdir.tcl]] set auto_index(workdir_newdir) [list source [file join $dir workdir.tcl]] set auto_index(workdir_edit_file) [list source [file join $dir workdir.tcl]] set auto_index(workdir_view_file) [list source [file join $dir workdir.tcl]] set auto_index(add_bookmark) [list source [file join $dir workdir.tcl]] set auto_index(delete_bookmark_dialog) [list source [file join $dir workdir.tcl]] set auto_index(delete_bookmark) [list source [file join $dir workdir.tcl]] set auto_index(change_dir) [list source [file join $dir workdir.tcl]] set auto_index(auto_setup_dir) [list source [file join $dir workdir.tcl]] set auto_index(setup_dir) [list source [file join $dir workdir.tcl]] set auto_index(directory_list) [list source [file join $dir workdir.tcl]] set auto_index(workdir_cleanup) [list source [file join $dir workdir.tcl]] set auto_index(workdir_delete_file) [list source [file join $dir workdir.tcl]] set auto_index(are_you_sure) [list source [file join $dir workdir.tcl]] set auto_index(workdir_print_file) [list source [file join $dir workdir.tcl]] set auto_index(cvsroot_check) [list source [file join $dir workdir.tcl]] set auto_index(isCmDirectory) [list source [file join $dir workdir.tcl]] set auto_index(getFiles) [list source [file join $dir workdir.tcl]] set auto_index(log_toggle) [list source [file join $dir workdir.tcl]] set auto_index(exit_cleanup) [list source [file join $dir workdir.tcl]] set auto_index(save_options) [list source [file join $dir workdir.tcl]] tkcvs-8.2.3.orig/tkcvs/errors.tcl0000644000175000017500000000322311664612512015137 0ustar timtim# # Tcl Library # # # Procedures for unimplemented procedures and error messages used by # TkCVS. # proc cvsok {mess {parent {.}} } { # Sometimes cancel is meaningless, we just want an acknowlegement if {! [winfo exists $parent]} {set parent .} set title {Acknowledge!} tk_messageBox \ -icon info \ -title $title \ -message $mess \ -parent $parent \ -type ok } proc cvsconfirm {mess {parent {.}} } { global cvscfg if {$cvscfg(confirm_prompt) != "true"} { return "ok" } if {! [winfo exists $parent]} {set parent .} set title {Confirm!} set answer [tk_messageBox \ -icon question \ -title $title \ -message $mess \ -parent $parent \ -type okcancel] gen_log:log D "$answer" return $answer } # This one doesn't check cvscfg(confirm_prompt) preference proc cvsalwaysconfirm {mess {parent {.}} } { if {! [winfo exists $parent]} {set parent .} set title {Confirm!} set answer [tk_messageBox \ -icon question \ -title $title \ -message $mess \ -parent $parent \ -type okcancel] gen_log:log D "$answer" return $answer } proc cvsfail {mess {parent {.}} } { if {! [winfo exists $parent]} {set parent .} set title {TkCVS Warning!} tk_messageBox \ -icon warning \ -title $title \ -message $mess \ -parent $parent \ -type ok } proc cvserror {mess {parent {.}} } { if {! [winfo exists $parent]} {set parent .} set title {TkCVS Error!} tk_messageBox \ -icon error \ -title $title \ -message $mess \ -parent $parent \ -type ok exit_cleanup 0 } tkcvs-8.2.3.orig/tkcvs/style_params.tcl0000644000175000017500000002215711664612512016335 0ustar timtimproc cde_open_resourcefile { file } { set ans "" set ret [catch {open $file r} ans] if {$ret == 0} { #puts "LEAVE cde_open_resourcefile ($ans)" return $ans } else { #puts "LEAVE cde_open_resourcefile ($ans)" #puts "Error: $ans" return "" } } proc get_cde_params { } { global cvsglb global cvscfg global tk_version # Set defaults for all the necessary things set bg [option get . background background] set fg [option get . foreground foreground] set guifont [option get . buttonFontList buttonFontList] set txtfont [option get . FontSet FontSet] set listfont [option get . textFontList textFontList] set textbg white set textfg black # If any of these aren't set, I don't think we're in CDE after all if {![string length $fg]} {return 0} if {![string length $bg]} {return 0} if {![string length $guifont]} { # For AIX set guifont [option get . FontList FontList] } if {![string length $guifont]} {return 0} if {![string length $txtfont]} {return 0} set guifont [string trimright $guifont ":"] set txtfont [string trimright $txtfont ":"] set listfont [string trimright $txtfont ":"] regsub {medium} $txtfont "bold" dlgfont # They don't tell us the slightly darker color they use for the # scrollbar backgrounds and graphics backgrounds, so we'll make # one up. shades $bg set cvscfg(guifont) $guifont set cvscfg(dialogfont) $dlgfont # If we can find the user's dt.resources file, we can find out the # palette and background/foreground colors set fh "" set palette "" set cur_rsrc ~/.dt/sessions/current/dt.resources set hom_rsrc ~/.dt/sessions/home/dt.resources if {[file readable $cur_rsrc] && [file readable $hom_rsrc]} { # Both exist. Use whichever is newer if {[file mtime $cur_rsrc] > [file mtime $hom_rsrc]} { #puts " $cur_rsrc is newer" set fh [cde_open_resourcefile $cur_rsrc] if {$fh == ""} { set fh [cde_open_resourcefile $hom_rsrc] } } else { #puts " $hom_rsrc is newer" set fh [cde_open_resourcefile $hom_rsrc] if {$fh == ""} { set fh [cde_open_resourcefile $cur_rsrc] } } } elseif {[file readable $cur_rsrc]} { # Otherwise try current first set fh [cde_open_resourcefile $cur_rsrc] if {$fh == ""} { set fh [cde_open_resourcefile $hom_rsrc] } } elseif {[file readable $hom_rsrc]} { set fh [cde_open_resourcefile $hom_rsrc] } if {[string length $fh]} { set palf "" while {[gets $fh ln] != -1} { regexp "^\\*background:\[ \t]*(.*)\$" $ln nil textbg regexp "^\\*foreground:\[ \t]*(.*)\$" $ln nil textfg regexp "^\\*0\\*ColorPalette:\[ \t]*(.*)\$" $ln nil palette regexp "^Window.Color.Background:\[ \t]*(.*)\$" $ln nil textbg regexp "^Window.Color.Foreground:\[ \t]*(.*)\$" $ln nil textfg } catch {close $fh} # # If the *0*ColorPalette setting was found above, try to find the # indicated file in ~/.dt, $DTHOME, or /usr/dt. # if {[string length $palette]} { foreach dtdir {/usr/dt /etc/dt ~/.dt} { # This uses the last palette that we find if {[file readable [file join $dtdir palettes $palette]]} { set palf [file join $dtdir palettes $palette] } } # puts "Using palette $palf" if {[string length $palf]} { if {![catch {open $palf r} fh]} { gets $fh activetitle gets $fh inactivetitle gets $fh wkspc1 gets $fh textbg gets $fh guibg ;#(*.background) - default for tk under cde gets $fh menubg gets $fh wkspc4 gets $fh iconbg ;#control panel bg too close $fh option add *Text.highlightColor $wkspc4 option add *Dialog.Background $menubg option add *Menu.Background $menubg option add *Menu.activeBackground $menubg option add *Menu.activeForeground $fg option add *Menubutton.Background $menubg option add *Menubutton.activeBackground $menubg option add *Menubutton.activeForeground $fg } } } } else { puts stderr "Neither ~/.dt/sessions/current/dt.resources nor" puts stderr " ~/.dt/sessions/home/dt.resources was readable" puts stderr " Falling back to plain X" return 0 } set hlfg $fg if {[info exists activetitle]} { set hlbg $activetitle } else { set hlbg "#b24d7a" } set cvsglb(bg) $bg set cvsglb(fg) $fg set cvsglb(textbg) $textbg set cvsglb(textfg) $textfg set cvsglb(hlbg) $hlbg set cvsglb(hlfg) $hlfg option add *selectColor $hlbg option add *Button.activeBackground $bg option add *Button.activeForeground $fg option add *Canvas.Background $cvsglb(shadow) option add *Canvas.Foreground black option add *Entry.Background $textbg option add *Entry.Foreground $textfg option add *Entry.readonlyBackground $bg option add *Entry.highlightBackground $bg option add *Entry.highlightColor $hlbg option add *Listbox.background $textbg option add *Listbox.selectBackground $hlbg option add *Listbox.selectForeground $hlfg option add *Menu.borderWidth 1 option add *Scrollbar.activeBackground $bg option add *Scrollbar.troughColor $cvsglb(shadow) option add *Text.Background $textbg option add *Text.Foreground $textfg option add *Text.highlightBackground $bg # checkbuttons and radiobuttons if {$tk_version >= 8.5} { # This makes it look like the native CDE checkbox option add *Checkbutton.offRelief sunken option add *Checkbutton.selectColor "" option add *Radiobutton.selectColor "" option add *Menu.selectColor $fg option add *Checkbutton.activeBackground $bg option add *Checkbutton.activeForeground $fg } else { option add *selectColor $hlbg } return 1 } proc get_gtk_params { } { global cvsglb global tk_version #puts " GTK: Getting X11 options" if {! [llength [auto_execok xrdb]]} { return 0 } set pipe [open "|xrdb -q" r] while {[gets $pipe ln] > -1} { switch -glob -- $ln { {\*Toplevel.background:*} { #puts $ln set bg [lindex $ln 1] } {\*Toplevel.foreground:*} { #puts $ln set fg [lindex $ln 1] } {\*Text.background:*} { #puts $ln set textbg [lindex $ln 1] } {\*Text.foreground:*} { #puts $ln set textfg [lindex $ln 1] } {\*Text.selectBackground:*} { #puts $ln set hlbg [lindex $ln 1] } {\*Text.selectForeground:*} { #puts $ln set hlfg [lindex $ln 1] } } } close $pipe if {! [info exists bg] || ! [info exists fg]} { return 0 } shades $bg set cvsglb(bg) $bg set cvsglb(fg) $fg set cvsglb(textbg) $textbg set cvsglb(textfg) $textfg set cvsglb(hlbg) $hlbg set cvsglb(hlfg) $hlfg # These are already set, but maybe I like mine better option add *Button.activeBackground $cvsglb(light) option add *Canvas.Background $cvsglb(shadow) option add *Canvas.Foreground black option add *Entry.Background $textbg option add *Entry.Foreground $textfg option add *Entry.selectBackground $hlbg option add *Entry.selectForeground $hlfg option add *Entry.readonlyBackground $bg option add *Listbox.background $textbg option add *Listbox.selectBackground $hlbg option add *Listbox.selectForeground $hlfg option add *Text.Background $textbg option add *Text.Foreground $textfg option add *Text.selectBackground $hlbg option add *Text.selectForeground $hlfg # checkbuttons and radiobuttons if {$tk_version >= 8.5} { option add *Menu.selectColor $fg option add *Checkbutton.selectColor "" option add *Radiobutton.selectColor "" } else { option add *selectColor $hlbg } return 1 } proc shades {bg} { global cvsglb set rgb_bg [winfo rgb . $bg] set bg0 [expr [lindex $rgb_bg 0] / 256 ] set bg1 [expr [lindex $rgb_bg 1] / 256 ] set bg2 [expr [lindex $rgb_bg 2] / 256 ] set factor .9 set shadow [format #%02x%02x%02x [expr int($factor * $bg0)] \ [expr int($factor * $bg1)] \ [expr int($factor * $bg2)]] set factor .3 set darkest [format #%02x%02x%02x [expr int($factor * $bg0)] \ [expr int($factor * $bg1)] \ [expr int($factor * $bg2)]] set inv0 [expr 255 - $bg0] set inv1 [expr 255 - $bg1] set inv2 [expr 255 - $bg2] set factor .2 set add0 [expr int($factor*$inv0)] set add1 [expr int($factor*$inv1)] set add2 [expr int($factor*$inv2)] set light [format #%02x%02x%02x [expr {$bg0 + $add0}] \ [expr {$bg1 + $add1}] \ [expr {$bg2 + $add2}]] set factor .5 set add0 [expr int($factor*$inv0)] set add1 [expr int($factor*$inv1)] set add2 [expr int($factor*$inv2)] set lighter [format #%02x%02x%02x [expr {$bg0 + $add0}] \ [expr {$bg1 + $add1}] \ [expr {$bg2 + $add2}]] set cvsglb(shadow) $shadow set cvsglb(canvbg) $shadow set cvsglb(darkest) $darkest set cvsglb(light) $light set cvsglb(lighter) $lighter } tkcvs-8.2.3.orig/tkcvs/tkcvs.tcl0000755000175000017500000003677711664612512015004 0ustar timtim#!/bin/sh #-*-tcl-*- # the next line restarts using wish \ exec wish "$0" -- ${1+"$@"} # # TkCVS Main program -- A Tk interface to CVS. # # Uses a structured modules file -- see the manpage for more details. # # Author: Del (del@babel.dialix.oz.au) # # If we can't get this far (maybe because X display connection refused) # quit now. If we get further, the error message is very misleading. if {[info exists starkit::topdir]} { package require Tk } if {! [info exists tk_version] } { puts "Initialization failed" exit 1 } if {$tk_version < 8.4} { puts "TkCVS requires Tcl/Tk 8.4 or better!" exit 1 } if {[info exists starkit::topdir]} { set TclRoot [file join $starkit::topdir lib] set ScriptBin $starkit::topdir } else { if {[info exists TclRoot]} { # Perhaps we are being sourced recursively. # That would be bad. return } set Script [info script] set ScriptTail [file tail $Script] #puts "Tail $ScriptTail" if {[file type $Script] == "link"} { #puts "$Script is a link" set ScriptBin [file join [file dirname $Script] [file readlink $Script]] } else { set ScriptBin $Script } #puts " ScriptBin $ScriptBin" set TclRoot [file join [file dirname $ScriptBin]] #puts "TclRoot $TclRoot" if {$TclRoot == "."} { set TclRoot [pwd] } #puts "TclRoot $TclRoot" set TclRoot [file join [file dirname $TclRoot] "lib"] #puts "TclRoot $TclRoot" # allow runtime replacement if {[info exists env(TCLROOT)]} { set TclRoot $env(TCLROOT) } #puts "TclRoot $TclRoot" } set TclExe [info nameofexecutable] if {$tcl_platform(platform) == "windows"} { set TclExe [file attributes $TclExe -shortname] } set TCDIR [file join $TclRoot tkcvs] set cvscfg(bitmapdir) [file join $TclRoot tkcvs bitmaps] #puts "TCDIR $TCDIR" #puts "BITMAPDIR $cvscfg(bitmapdir)" set cvscfg(version) "8.2.3" if {! [info exists cvscfg(editorargs)]} { set cvscfg(editorargs) {} } set auto_path [linsert $auto_path 0 $TCDIR] set cvscfg(allfiles) false if {! [info exists cvscfg(startwindow)]} { set cvscfg(startwindow) "workdir" } set cvscfg(auto_tag) false set cvscfg(econtrol) false set cvscfg(use_cvseditor) false set maxdirs 15 set dirlist {} set totaldirs 0 if { [info exists env(HOME)] } { set cvscfg(home) $env(HOME) } else { set cvscfg(home) "~" } # Read in defaults if {[file exists [file join $TCDIR tkcvs_def.tcl]]} { source [file join $TCDIR tkcvs_def.tcl] } # This helps us recover from a problem left behind by tkcvs 7.2 set cvscfg(checkrecursive) false set optfile [file join $cvscfg(home) .tkcvs] if {[file exists $optfile]} { catch {source $optfile} } ::picklist::load # Set some defaults set cvsglb(sort_pref) [list $cvscfg(sortcol) "-decreasing"] set cvsglb(commit_comment) "" set cvsglb(cvs_version) "" set cvsglb(svn_version) "" if {$cvscfg(use_cvseditor) && ![info exists cvscfg(terminal)]} { cvserror "cvscfg(terminal) is required if cvscfg(use_cvseditor) is set" } # Hilight colors. Get the colorful ones. entry .testent set cvsglb(textbg) white set cvsglb(textfg) black set cvsglb(hlbg) [lindex [.testent configure -selectbackground] 4] set cvsglb(hlfg) [lindex [.testent configure -selectforeground] 4] if {$cvsglb(hlfg) eq {} } { # This happens on the Mac set cvsglb(hlfg) [lindex [.testent configure -foreground] 4] } set cvscfg(listboxfont) [lindex [.testent configure -font] 4] destroy .testent set WSYS [tk windowingsystem] #puts "Windowing sytem is $WSYS" set theme_system "unknown" if {$WSYS eq "x11"} { # If X11, see if we can sense our environment somehow label .testlbl -text "LABEL" if [get_cde_params] { set theme_system "CDE" # Find out what the default gui font is if { ! [info exists cvscfg(guifont)] } { # Find out what the tk default is set cvscfg(guifont) [lindex [.testlbl configure -font] 4] } # Put the Help menu back on the right if {$tk_version >= 8.5} { tk::classic::restore menu } #set cvsglb(canvbg) [lindex [.testlbl configure -background] 4] set cvsglb(canvbg) $cvsglb(shadow) } elseif [get_gtk_params] { set theme_system "GTK" if { ! [info exists cvscfg(guifont)] } { set cvscfg(guifont) [lindex [.testlbl configure -font] 4] if {$tk_version >= 8.5} { font configure TkDefaultFont -size 9 set cvscfg(guifont) TkDefaultFont option add *Menu.font $cvscfg(guifont) option add *Label.font $cvscfg(guifont) option add *Button.font $cvscfg(guifont) } } # in KDE or Gnome or some such. It rather rudely sets all the Tk # backgrounds the same which I don't like, so I'm going to use the same # trick I use for CDE to give the canvases a little shading. set cvsglb(bg) [lindex [.testlbl cget -background] 0] set cvsglb(fg) [lindex [.testlbl cget -foreground] 0] set cvsglb(canvbg) [rgb_shadow $cvsglb(bg)] } else { set bg [lindex [.testlbl cget -background] 0] set fg [lindex [.testlbl cget -foreground] 0] set hlbg "#4a6984" set hlfg "#ffffff" set textbg "#ffffff" set textfg "#000000" shades $bg set cvsglb(bg) $bg set cvsglb(fg) $fg set cvsglb(textbg) $textbg set cvsglb(textfg) $textfg set cvsglb(hlbg) $hlbg set cvsglb(hlfg) $hlfg option add *Canvas.Background $cvsglb(shadow) option add *Canvas.Foreground black option add *Entry.Background $textbg option add *Entry.Foreground $textfg option add *Entry.selectBackground $hlbg option add *Entry.selectForeground $hlfg option add *Entry.readonlyBackground $bg option add *Listbox.background $textbg option add *Listbox.selectBackground $hlbg option add *Listbox.selectForeground $hlfg option add *Text.Background $textbg option add *Text.Foreground $textfg option add *Text.selectBackground $hlbg option add *Text.selectForeground $hlfg option add *Button.activeForeground $fg option add *Menu.activeForeground $fg # checkbuttons and radiobuttons if {$tk_version >= 8.5} { option add *Menu.selectColor $fg option add *Checkbutton.selectColor "#ffffff" option add *Radiobutton.selectColor "#ffffff" } else { option add *selectColor $hlbg } if { ! [info exists cvscfg(guifont)] } { set cvscfg(guifont) [lindex [.testlbl configure -font] 4] if {$tk_version >= 8.5} { # This makes it look like tk8.4. Want? dunno. font configure TkHeadingFont -size 9 set cvscfg(guifont) TkHeadingFont option add *Menu.font $cvscfg(guifont) option add *Label.font $cvscfg(guifont) option add *Button.font $cvscfg(guifont) } } } destroy .testlbl if {! [info exists cvscfg(dialogfont)]} { set cvscfg(dialogfont) $cvscfg(guifont) } if {$theme_system == "CDE"} { # This makes it consistent with the rest of the CDE interface option add *Menu.font $cvscfg(guifont) option add *Label.font $cvscfg(guifont) option add *Button.font $cvscfg(guifont) } #puts " Theme system: $theme_system" } else { # Find out what the default gui font is label .testlbl -text "LABEL" # Find out what the tk default is set cvscfg(guifont) [lindex [.testlbl configure -font] 4] set cvscfg(dialogfont) $cvscfg(guifont) set cvsglb(canvbg) [lindex [.testlbl configure -background] 4] set cvsglb(bg) [lindex [.testlbl cget -background] 0] set cvsglb(fg) [lindex [.testlbl cget -foreground] 0] set cvsglb(canvbg) [rgb_shadow $cvsglb(bg)] destroy .testlbl if {$WSYS eq "aqua"} { # keep evertyhing from being blinding white # button highlightbackground has to be the same as background # or else there are little white boxes around the button "pill" option add *background #ebebeb userDefault option add *Button.highlightBackground #ebebeb userDefault # That totally screws up the menus unless you fix it with this option add *Menu.Background white option add *Menu.Foreground black option add *Entry.HighlightThickness 2 userDefault option add *Entry.highlightBackground $cvsglb(hlbg) userDefault option add *Canvas.background #eeeeee userDefault option add *Entry.background #ffffff userDefault option add *Text.background white userDefault } } # Fonts for the canvas "listboxes" set cvscfg(flashfont) $cvscfg(listboxfont) set fsiz [lindex $cvscfg(listboxfont) 1] set lbf [font actual $cvscfg(listboxfont)] #puts "$tcl_platform(os) $tk_patchLevel [winfo server .]" #puts "listboxfont: $cvscfg(listboxfont)" #puts "actual listboxfont: $lbf" set ffam [lindex $lbf 1] set fsiz [lindex $lbf 3] regsub -- {-} $fsiz {} fsiz if {[tk windowingsystem] eq "x11"} { if {$tk_version >= 8.5} { # Put the help menu back on the right #tk::classic::restore menu if {$tcl_platform(os) eq "Linux" && [lindex $tk_patchLevel 2] < 8} { # FIXME: check for X.Org in server string? #puts [winfo server .] # Overstrike and underline fonts in Xorg were too big from 8.5.0 to 8.5.7 set fsiz -$fsiz } } } set cvscfg(flashfont) [list $ffam $fsiz underline] #puts "requested flashfont: $cvscfg(flashfont)" #puts "actual flashfont: [font actual $cvscfg(flashfont)]" #puts "try underline: [font actual $cvscfg(flashfont) -underline]" # Prefer underline, but it isn't working at all in tk8.5.0 on linux if {! [font actual $cvscfg(flashfont) -underline]} { puts "Underline font not working. Trying $ffam $fsiz bold" puts " (known problem in Tk 8.5.0 on Linux)" set cvscfg(flashfont) [list $ffam $fsiz bold] } #puts "final flashfont: $cvscfg(flashfont)" option add *ToolTip.background "LightGoldenrod1" userDefault option add *ToolTip.foreground "black" userDefault # This makes tk_messageBox use our font. The default tends to be terrible # no matter what platform option add *Dialog.msg.font $cvscfg(dialogfont) userDefault # Sometimes we'll want to override this but let's set a default option add *Message.font $cvscfg(dialogfont) userDefault # Initialize logging (classes are C,F,T,D) if { ! [info exists cvscfg(log_classes)] } { set cvscfg(log_classes) "C" } foreach class [split $cvscfg(log_classes) {}] { set logclass($class) $class } if { ! [info exists cvscfg(logging)] } { set cvscfg(logging) false } if {$cvscfg(logging)} { gen_log:init } # # Add directory where we last ran to the menu list if { ! [info exists cvscfg(lastdir)] } { set cvscfg(lastdir) [pwd] } # # Command line options # set usage "Usage:" append usage "\n tkcvs \[-dir \] \[-root \] \[-win workdir|module|merge\]" append usage "\n tkcvs \[-dir \] \[-root \] \[-log|blame \]" append usage "\n tkcvs - same as tkcvs -log " for {set i 0} {$i < [llength $argv]} {incr i} { set arg [lindex $argv $i] set val [lindex $argv [expr {$i+1}]] switch -regexp -- $arg { {^--*d.*} { # -ddir: Starting directory set dir $val; incr i cd $val } {^--*r.*} { # -root: CVS root set cvscfg(cvsroot) $val; incr i } {^--*w.*} { # workdir|module|merge: window to start with. workdir is default. set cvscfg(startwindow) $val; incr i } {^--*l.*} { # -log : Browse the log of specified file set cvscfg(startwindow) log set lcfile $val; incr i } {^--*[ab].*} { # annotate|blame: Browse colorcoded history of specified file set cvscfg(startwindow) blame set lcfile $val; incr i } {^-psn_.*} { # Ignore the Carbon Process Serial Number incr i } {^--*h.*} { puts $usage exit 0 } {\w*} { # If a filename is provided as an argument, assume -log set cvscfg(startwindow) log set lcfile $arg; incr i } default { puts $usage exit 1 } } } if {[info exists lcfile]} { set d [file dirname $lcfile] set f [file tail $lcfile] set lcfile $f cd $d } # If CVSROOT envvar is set, use it if { ! [info exists cvscfg(cvsroot)] } { if { ! [info exists env(CVSROOT)] } { #puts "warning: your \$CVSROOT environment variable is not set." set cvscfg(cvsroot) "" } else { set cvscfg(cvsroot) $env(CVSROOT) } } # This helps with Samba-mounted CVS repositories # And also completely messes up SVN repositories #set cvscfg(cvsroot) [file join $cvscfg(cvsroot)] # If SVNROOT is set, use that instead. SVNROOT isn't # known by Subversion itself, so if it's set we must have # done it for the present purpose if {! [info exists cvscfg(svnroot)] } { if { [info exists env(SVNROOT)] } { set cvscfg(svnroot) $env(SVNROOT) } else { set cvscfg(svnroot) "" } } set cvsglb(root) $cvscfg(cvsroot) if {$cvscfg(svnroot) != ""} { set cvsglb(root) $cvscfg(svnroot) } # Thought better of saving this catch unset cvscfg(svnconform_seen) if {![info exists cvscfg(ignore_file_filter)]} { set cvscfg(ignore_file_filter) "" } # Remember what the setting was. We'll have to restore it after # leaving a directory with a .cvsignore file. set cvsglb(default_ignore_filter) $cvscfg(ignore_file_filter) #foreach c [lsort [array names cvscfg]] { #gen_log:log D "cvscfg($c) $cvscfg($c)" #} # Load the images that are used in more than one module image create photo Log \ -format gif -file [file join $cvscfg(bitmapdir) log.gif] image create photo Checkout \ -format gif -file [file join $cvscfg(bitmapdir) checkout.gif] image create photo CheckoutOpts \ -format gif -file [file join $cvscfg(bitmapdir) checkout_opts.gif] image create photo Export \ -format gif -file [file join $cvscfg(bitmapdir) export.gif] image create photo Tag \ -format gif -file [file join $cvscfg(bitmapdir) tag.gif] image create photo Branchtag \ -format gif -file [file join $cvscfg(bitmapdir) branchtag.gif] image create photo Import \ -format gif -file [file join $cvscfg(bitmapdir) import.gif] image create photo Mergebranch \ -format gif -file [file join $cvscfg(bitmapdir) newmerge_simple.gif] image create photo Mergediff \ -format gif -file [file join $cvscfg(bitmapdir) newmerge.gif] image create photo Man \ -format gif -file [file join $cvscfg(bitmapdir) man.gif] set incvs 0 set insvn 0 set inrcs 0 # Create a window # Start with Module Browser if {[string match {mod*} $cvscfg(startwindow)]} { wm withdraw . foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } if {$insvn} { set cvsglb(root) $cvscfg(svnroot) modbrowse_run svn } else { # We still don't know if it's SVN or CVS. Let modbrowse_run figure out. modbrowse_run } # Start with Branch Browser } elseif {$cvscfg(startwindow) == "log"} { if {! [file exists $lcfile]} { puts "ERROR: $lcfile doesn't exist!" exit 1 } wm withdraw . foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } if {$incvs} { cvs_branches \"$lcfile"\ } elseif {$inrcs} { set cwd [pwd] set module_dir "" rcs_branches \"$lcfile\" } elseif {$insvn} { svn_branches \"$lcfile\" } else { puts "File doesn't seem to be in CVS, SVN, or RCS" } # Start with Annotation Browser } elseif {$cvscfg(startwindow) == "blame"} { if {! [file exists $lcfile]} { puts "ERROR: $lcfile doesn't exist!" exit 1 } wm withdraw . foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } if {$incvs} { cvs_annotate "" \"$lcfile"\ } elseif {$insvn} { svn_annotate "" \"$lcfile\" } else { puts "File doesn't seem to be in CVS or SVN" } # Start with Directory Merge } elseif {[string match {mer*} $cvscfg(startwindow)]} { wm withdraw . foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } if {$incvs} { cvs_joincanvas } elseif {$insvn} { svn_directory_merge } else { puts "Directory doesn't seem to be in CVS or SVN" } # The usual way, with the Workdir Browser } else { workdir_setup } tkcvs-8.2.3.orig/tkcvs/modbrowse.tcl0000644000175000017500000007027411664612512015636 0ustar timtim# # Set up a check out dialog. # proc modbrowse_setup {} { global cwd global repository_root global modbrowse_module global modbrowse_path global modbrowse_title global cvsglb global cvscfg global tcl_platform #gen_log:log T "ENTER" set cwd [pwd] # Window manager stuff. toplevel .modbrowse wm title .modbrowse "TkCVS $cvscfg(version) Repository Browser" wm iconname .modbrowse "TkCVS Repository Browser" if {$tcl_platform(platform) != "windows"} { wm iconbitmap .modbrowse @$cvscfg(bitmapdir)/tkcvs48.xbm } wm protocol .modbrowse WM_DELETE_WINDOW { .modbrowse.bottom.buttons.closefm.close invoke } if {[info exists cvscfg(modgeom)]} { wm geometry .modbrowse $cvscfg(modgeom) } else { wm geometry .modbrowse 510x470 } if {[catch "image type Who"]} { modbrowse_images } modbrowse_menus # # Top section - module, tags, root # frame .modbrowse.top -relief groove -border 2 pack .modbrowse.top -side top -fill x label .modbrowse.top.lmcode -text "Module" entry .modbrowse.top.tmcode -textvariable modbrowse_module \ -font $cvscfg(listboxfont) -border 2 label .modbrowse.top.lroot -text "CVSROOT" ::picklist::entry .modbrowse.top.troot cvsglb(root) cvsroot ::picklist::bind .modbrowse.top.troot \ {if {$repository_root != $cvsglb(root)} {modbrowse_run}} button .modbrowse.top.bworkdir -image Workdir \ -command {workdir_setup} label .modbrowse.top.lcwd -text "Current Directory" ::picklist::entry .modbrowse.top.tcwd cwd directory ::picklist::bind .modbrowse.top.tcwd \ {if {[pwd] != $cwd} {module_changedir "$cwd"}} grid columnconf .modbrowse.top 1 -weight 1 grid rowconf .modbrowse.top 3 -weight 1 grid .modbrowse.top.lroot -column 0 -row 0 -sticky w grid .modbrowse.top.troot -column 1 -row 0 -columnspan 2 -padx 4 -sticky ew grid .modbrowse.top.lmcode -column 0 -row 1 -sticky w grid .modbrowse.top.tmcode -column 1 -row 1 -padx 3 -sticky ew grid .modbrowse.top.lcwd -column 0 -row 2 -sticky w grid .modbrowse.top.tcwd -column 1 -row 2 -padx 4 -sticky ew grid .modbrowse.top.bworkdir -column 2 -row 1 -rowspan 2 -sticky w # Pack the bottom before the middle so it doesnt disappear if # the window is resized smaller frame .modbrowse.bottom -relief groove -border 2 -height 128 frame .modbrowse.bottom.buttons frame .modbrowse.bottom.buttons.cvsfuncs -relief groove -bd 2 frame .modbrowse.bottom.buttons.svnfuncs -relief groove -bd 2 frame .modbrowse.bottom.buttons.modfuncs -relief groove -bd 2 frame .modbrowse.bottom.buttons.closefm -relief groove -bd 2 pack .modbrowse.bottom -side bottom -fill x pack .modbrowse.bottom.buttons -side top -fill x -expand yes pack .modbrowse.bottom.buttons.closefm -side right -padx 10 pack .modbrowse.bottom.buttons.cvsfuncs -side left pack .modbrowse.bottom.buttons.svnfuncs -side left -expand yes pack .modbrowse.bottom.buttons.modfuncs -side left -expand yes # # Create buttons # button .modbrowse.bottom.buttons.modfuncs.filebrowse -image Files \ -command { browse_files $modbrowse_module } button .modbrowse.bottom.buttons.modfuncs.patchsummary -image Patches \ -command { dialog_cvs_patch $cvscfg(cvsroot) $modbrowse_module 1 } button .modbrowse.bottom.buttons.modfuncs.patchfile -image Patchfile \ -command { dialog_cvs_patch $cvscfg(cvsroot) $modbrowse_module 0 } button .modbrowse.bottom.buttons.modfuncs.checkout -image Checkout \ -command { dialog_cvs_checkout $cvscfg(cvsroot) $modbrowse_module } button .modbrowse.bottom.buttons.modfuncs.export -image Export \ -command { dialog_cvs_export $cvscfg(cvsroot) $modbrowse_module } button .modbrowse.bottom.buttons.modfuncs.tag -image Tag \ -command { rtag_dialog $cvscfg(cvsroot) $modbrowse_module "tag" } button .modbrowse.bottom.buttons.modfuncs.branchtag -image Branchtag \ -command { rtag_dialog $cvscfg(cvsroot) $modbrowse_module "branch" } button .modbrowse.bottom.buttons.svnfuncs.filecat -image Fileview \ -command { svn_filecat $cvscfg(svnroot) $modbrowse_path $modbrowse_title} button .modbrowse.bottom.buttons.svnfuncs.filelog -image Log \ -command { svn_filelog $cvscfg(svnroot) $modbrowse_path $modbrowse_title} button .modbrowse.bottom.buttons.svnfuncs.remove -image SvnRemove \ -command { svn_delete $cvscfg(svnroot) $modbrowse_path } button .modbrowse.bottom.buttons.cvsfuncs.import -image Import \ -command { import_run } button .modbrowse.bottom.buttons.cvsfuncs.who -image Who \ -command {cvs_history all $modbrowse_module} button .modbrowse.bottom.buttons.cvsfuncs.brefresh -image Refresh \ -command { modbrowse_run } button .modbrowse.bottom.buttons.closefm.close -text "Close" \ -command { module_exit; exit_cleanup 0 } grid .modbrowse.bottom.buttons.cvsfuncs.brefresh -column 0 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.cvsfuncs.who -column 1 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.cvsfuncs.import -column 2 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.modfuncs.filebrowse -column 0 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.modfuncs.checkout -column 1 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.modfuncs.export -column 2 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.modfuncs.tag -column 3 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.modfuncs.branchtag -column 4 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.modfuncs.patchsummary -column 5 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.modfuncs.patchfile -column 6 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.svnfuncs.filecat -column 0 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.svnfuncs.filelog -column 1 -row 0 \ -ipadx 4 -ipady 4 grid .modbrowse.bottom.buttons.svnfuncs.remove -column 2 -row 0 \ -ipadx 4 -ipady 4 pack .modbrowse.bottom.buttons.closefm.close \ -side right -fill both -expand yes set_tooltips .modbrowse.bottom.buttons.modfuncs.checkout \ {"Check out selection from the repository"} set_tooltips .modbrowse.bottom.buttons.modfuncs.export \ {"Export selection from the repository"} set_tooltips .modbrowse.bottom.buttons.modfuncs.tag \ {"Tag all files in a module"} set_tooltips .modbrowse.bottom.buttons.modfuncs.branchtag \ {"Branch all files in a module"} set_tooltips .modbrowse.bottom.buttons.modfuncs.filebrowse \ {"Browse the files in a CVS module"} set_tooltips .modbrowse.bottom.buttons.svnfuncs.filecat \ {"Show a file in the SVN repository"} set_tooltips .modbrowse.bottom.buttons.svnfuncs.filelog \ {"Show the history log of a file in the SVN repository"} set_tooltips .modbrowse.bottom.buttons.svnfuncs.remove \ {"Remove something from the SVN repository"} set_tooltips .modbrowse.bottom.buttons.modfuncs.patchsummary \ {"Show a summary of differences between versions"} set_tooltips .modbrowse.bottom.buttons.modfuncs.patchfile \ {"Create a patch file"} set_tooltips .modbrowse.bottom.buttons.cvsfuncs.import \ {"Import the current directory into the repository"} set_tooltips .modbrowse.bottom.buttons.cvsfuncs.who \ {"Show who has modules checked out"} set_tooltips .modbrowse.bottom.buttons.cvsfuncs.brefresh \ {"Re-read the modules"} set_tooltips .modbrowse.bottom.buttons.closefm.close \ {"Close the repository browser"} set_tooltips .modbrowse.top.bworkdir \ {"Open the Working Directory Browser"} frame .modbrowse.treeframe pack .modbrowse.treeframe -side bottom -fill both -expand yes -pady 0 set screenWidth [winfo vrootwidth .] set screenHeight [winfo vrootheight .] wm maxsize .modbrowse $screenWidth $screenHeight wm minsize .modbrowse 430 300 #gen_log:log T "LEAVE" } proc modbrowse_images {} { global cvscfg image create photo Workdir \ -format gif -file [file join $cvscfg(bitmapdir) folderopen.gif] image create photo Files \ -format gif -file [file join $cvscfg(bitmapdir) files.gif] image create photo Patches \ -format gif -file [file join $cvscfg(bitmapdir) rdiff.gif] image create photo Patchfile \ -format gif -file [file join $cvscfg(bitmapdir) patchfile.gif] image create photo Who \ -format gif -file [file join $cvscfg(bitmapdir) who.gif] image create photo SvnRemove \ -format gif -file [file join $cvscfg(bitmapdir) delete_red.gif] if {[catch "image type arr_dn"]} { workdir_images } } proc modbrowse_menus {} { global cvscfg global cvsglb global cvs global logclass #gen_log:log T "ENTER" menu .modbrowse.modmenu .modbrowse configure -menu .modbrowse.modmenu # # Create the Menu bar # .modbrowse.modmenu add cascade -menu .modbrowse.modmenu.file -label "File" -underline 0 menu .modbrowse.modmenu.file -tearoff 0 .modbrowse.modmenu add cascade -menu .modbrowse.modmenu.cvs -label "CVS" -underline 0 menu .modbrowse.modmenu.cvs -tearoff 0 .modbrowse.modmenu add cascade -menu .modbrowse.modmenu.svn -label "SVN" -underline 0 menu .modbrowse.modmenu.svn -tearoff 0 .modbrowse.modmenu add cascade -menu .modbrowse.modmenu.options -label "Options" -underline 0 menu .modbrowse.modmenu.options -tearoff 0 # # Create the menus # .modbrowse.modmenu.file add command -label "Browse Working Directory" -underline 0 \ -command workdir_setup .modbrowse.modmenu.file add separator .modbrowse.modmenu.file add command -label "Close" -underline 1 \ -command {.modbrowse.bottom.buttons.closefm.close invoke} .modbrowse.modmenu.file add command -label "Exit" -underline 1 \ -command { module_exit; exit_cleanup 1 } .modbrowse.modmenu.cvs add command -label "CVS Checkout" \ -command { dialog_cvs_checkout $cvscfg(cvsroot) $modbrowse_module} .modbrowse.modmenu.cvs add command -label "CVS Export" \ -command { dialog_cvs_export $cvscfg(cvsroot) $modbrowse_module} .modbrowse.modmenu.cvs add command -label "Tag Module" -underline 0 \ -command { rtag_dialog $cvscfg(cvsroot) $modbrowse_module "tag" } .modbrowse.modmenu.cvs add command -label "Branch Tag Module" -underline 0 \ -command { rtag_dialog $cvscfg(cvsroot) $modbrowse_module "branch" } .modbrowse.modmenu.cvs add command -label "Make Patch File" -underline 0 \ -command { dialog_cvs_patch $cvscfg(cvsroot) $modbrowse_module 0 } .modbrowse.modmenu.cvs add command -label "View Patch Summary" -underline 0 \ -command { dialog_cvs_patch $cvscfg(cvsroot) $modbrowse_module 1 } .modbrowse.modmenu.cvs add separator .modbrowse.modmenu.cvs add command -label "Import CWD to A New Module" -underline 0 \ -command { import_run } .modbrowse.modmenu.cvs add command -label "Import CWD to An Existing Module" -underline 0 \ -command { import2_run } .modbrowse.modmenu.cvs add command -label "Vendor Merge" -underline 0 \ -command {merge_run $modbrowse_module} .modbrowse.modmenu.cvs add separator .modbrowse.modmenu.cvs add command -label "Show My Checkouts" -underline 0 \ -command {cvs_history me ""} .modbrowse.modmenu.cvs add command -label "Show Checkouts of Selected Module" -underline 0 \ -command {cvs_history all $modbrowse_module} .modbrowse.modmenu.cvs add command -label "Show All Checkouts" -underline 0 \ -command {cvs_history all ""} .modbrowse.modmenu.svn add command -label "SVN Checkout" \ -command { dialog_svn_checkout $cvscfg(svnroot) $modbrowse_path checkout} .modbrowse.modmenu.svn add command -label "SVN Export" \ -command { dialog_svn_checkout $cvscfg(svnroot) $modbrowse_path export} .modbrowse.modmenu.svn add command -label "Tag Module" -underline 0 \ -command { dialog_svn_tag $cvscfg(svnroot) $modbrowse_path "tags" } .modbrowse.modmenu.svn add command -label "Branch Module" -underline 0 \ -command { dialog_svn_tag $cvscfg(svnroot) $modbrowse_path "branches" } .modbrowse.modmenu.svn add command -label "Make Patch File" -underline 0 \ -command { dialog_svn_patch $cvscfg(cvsroot) $modbrowse_path $selB_path 0 } .modbrowse.modmenu.svn add command -label "View Patch Summary" -underline 0 \ -command { dialog_svn_patch $cvscfg(cvsroot) $modbrowse_path $selB_path 1 } .modbrowse.modmenu.svn add separator .modbrowse.modmenu.svn add command -label "Import CWD into Repository" \ -command svn_import_run .modbrowse.modmenu.options add checkbutton -label "Group Aliases in a Folder (CVS)" \ -variable cvscfg(aliasfolder) -onvalue true -offvalue false \ -command { ModTree:delitem .modbrowse.treeframe / ModTree:destroy .modbrowse.treeframe busy_start .modbrowse ModTree:create .modbrowse.treeframe pack .modbrowse.treeframe.pw -side bottom -fill both -expand yes modbrowse_tree [lsort [array names modval]] "/" } .modbrowse.modmenu.options add separator .modbrowse.modmenu.options add checkbutton -label "Tracing On/Off" \ -variable cvscfg(logging) -onvalue true -offvalue false \ -command log_toggle .modbrowse.modmenu.options add cascade -label "Trace Level" \ -menu .modbrowse.modmenu.options.loglevel menu .modbrowse.modmenu.options.loglevel .modbrowse.modmenu.options.loglevel add checkbutton -label "CVS commands (C)" \ -variable logclass(C) -onvalue "C" -offvalue "" \ -command gen_log:changeclass .modbrowse.modmenu.options.loglevel add checkbutton -label "CVS stderr (E)" \ -variable logclass(E) -onvalue "E" -offvalue "" \ -command gen_log:changeclass .modbrowse.modmenu.options.loglevel add checkbutton -label "File creation/deletion (F)"\ -variable logclass(F) -onvalue "F" -offvalue "" \ -command gen_log:changeclass .modbrowse.modmenu.options.loglevel add checkbutton -label "Function entry/exit (T)" \ -variable logclass(T) -onvalue "T" -offvalue "" \ -command gen_log:changeclass .modbrowse.modmenu.options.loglevel add checkbutton -label "Debugging (D)" \ -variable logclass(D) -onvalue "D" -offvalue "" \ -command gen_log:changeclass menu_std_help .modbrowse.modmenu #gen_log:log T "LEAVE" } proc modbrowse_run { {CVSorSVN {}} } { global env global svnurl global insvn incvs inrcs global modval global cvscfg global cvsglb global cvs global cmd global repository_root gen_log:log T "ENTER ($CVSorSVN)" # If a checkout is already running, abort it if {[info exists cmd(cvs_co)]} { catch {$cmd(cvs_co)\::abort} catch {unset cmd(cvs_co)} } catch {unset modval} catch {unset modtitle} set modbrowse_module "" if {! [winfo exists .modbrowse]} { modbrowse_setup } wm deiconify .modbrowse raise .modbrowse ModTree:delitem .modbrowse.treeframe / ModTree:destroy .modbrowse.treeframe busy_start .modbrowse switch $CVSorSVN { svn { set svnurl 1 gen_log:log D "svn" set cvsglb(root) $cvscfg(svnroot) if {! [info exists cvscfg(svnroot)] } { read_svn_dir . } .modbrowse.top.lroot configure -text "SVN URL" .modbrowse.top.lmcode configure -text "Selection" # Call ModTree with the just-in-time level maker ModTree:create .modbrowse.treeframe svn_jit_listdir pack .modbrowse.treeframe.pw -side bottom -fill both -expand yes parse_svnmodules .modbrowse.treeframe $cvscfg(svnroot) } cvs { set svnurl 0 gen_log:log D "cvs" #gen_log:log D "cvsglb(root) $cvsglb(root)" #gen_log:log D "cvscfg(cvsroot) $cvscfg(cvsroot)" #gen_log:log D "cvscfg(svnroot) $cvscfg(svnroot)" set cvsglb(root) $cvscfg(cvsroot) set cmd(cvs_co) \ [exec::new "$cvs -d \"$cvscfg(cvsroot)\" checkout -p CVSROOT/modules"] .modbrowse.top.lroot configure -text "CVSROOT" .modbrowse.top.lmcode configure -text "Module" ModTree:create .modbrowse.treeframe pack .modbrowse.treeframe.pw -side bottom -fill both -expand yes if {[info exists cmd(cvs_co)]} { parse_cvsmodules [$cmd(cvs_co)\::output] } catch {unset cmd(cvs_co)} } default { set svnurl 0 # Detect a SVN URL if {[regexp {://} $cvsglb(root)]} { set svnurl 1 } if {$svnurl} { gen_log:log D "default,detected svn url" set cvscfg(svnroot) $cvsglb(root) .modbrowse.top.lroot configure -text "SVN URL" .modbrowse.top.lmcode configure -text "Selection" # Call ModTree with the just-in-time level maker ModTree:create .modbrowse.treeframe svn_jit_listdir pack .modbrowse.treeframe.pw -side bottom -fill both -expand yes parse_svnmodules .modbrowse.treeframe $cvscfg(svnroot) } else { gen_log:log D "default" set cvscfg(cvsroot) $cvsglb(root) if {$cvsglb(root) eq ""} { ModTree:create .modbrowse.treeframe pack .modbrowse.treeframe.pw -side bottom -fill both -expand yes busy_done .modbrowse return } #gen_log:log D "cvsglb(root) $cvsglb(root)" #gen_log:log D "cvscfg(cvsroot) $cvscfg(cvsroot)" #gen_log:log D "cvscfg(svnroot) $cvscfg(svnroot)" #set cvsglb(root) $cvscfg(cvsroot) set cmd(cvs_co) \ [exec::new "$cvs -d \"$cvscfg(cvsroot)\" checkout -p CVSROOT/modules"] .modbrowse.top.lroot configure -text "CVSROOT" .modbrowse.top.lmcode configure -text "Module" ModTree:create .modbrowse.treeframe pack .modbrowse.treeframe.pw -side bottom -fill both -expand yes if {[info exists cmd(cvs_co)]} { parse_cvsmodules [$cmd(cvs_co)\::output] } catch {unset cmd(cvs_co)} } } } set repository_root $cvsglb(root) ::picklist::used cvsroot $cvsglb(root) set bstate [expr {$svnurl ? {disabled} : {normal}}] .modbrowse.bottom.buttons.cvsfuncs.import configure -state $bstate .modbrowse.bottom.buttons.cvsfuncs.who configure -state $bstate .modbrowse.bottom.buttons.cvsfuncs.brefresh configure -state normal foreach widget [grid slaves .modbrowse.bottom.buttons.modfuncs ] { $widget configure -state $bstate } if {$svnurl} { .modbrowse.bottom.buttons.cvsfuncs.import configure -state normal \ -command { svn_import_run } .modbrowse.bottom.buttons.modfuncs.checkout configure -state normal \ -command { dialog_svn_checkout $cvscfg(svnroot) $modbrowse_path checkout} .modbrowse.bottom.buttons.modfuncs.export configure -state normal \ -command { dialog_svn_checkout $cvscfg(svnroot) $modbrowse_path export} .modbrowse.bottom.buttons.modfuncs.tag configure -state normal \ -command { dialog_svn_tag $cvscfg(svnroot) $modbrowse_path "tags" } .modbrowse.bottom.buttons.modfuncs.branchtag configure -state normal \ -command { dialog_svn_tag $cvscfg(svnroot) $modbrowse_path "branches" } .modbrowse.bottom.buttons.modfuncs.patchsummary configure -state normal \ -command { dialog_svn_patch $cvscfg(svnroot) $modbrowse_path $selB_path 1 } .modbrowse.bottom.buttons.modfuncs.patchfile configure -state normal \ -command { dialog_svn_patch $cvscfg(svnroot) $modbrowse_path $selB_path 0 } .modbrowse.bottom.buttons.svnfuncs.filecat configure -state normal .modbrowse.bottom.buttons.svnfuncs.filelog configure -state normal .modbrowse.bottom.buttons.svnfuncs.remove configure -state normal .modbrowse.modmenu entryconfigure "CVS" -state disabled .modbrowse.modmenu entryconfigure "SVN" -state normal } else { .modbrowse.bottom.buttons.modfuncs.filebrowse configure \ -command { browse_files $modbrowse_module } .modbrowse.bottom.buttons.modfuncs.checkout configure -state normal \ -command { cvs_checkout_dialog $cvscfg(cvsroot) $modbrowse_module } .modbrowse.bottom.buttons.cvsfuncs.import configure -state normal \ -command { import_run } .modbrowse.bottom.buttons.modfuncs.checkout configure -state normal \ -command { dialog_cvs_checkout $cvscfg(cvsroot) $modbrowse_module } .modbrowse.bottom.buttons.modfuncs.export configure -state normal \ -command { dialog_cvs_export $cvscfg(cvsroot) $modbrowse_module } .modbrowse.bottom.buttons.modfuncs.tag configure -state normal \ -command { rtag_dialog $cvscfg(cvsroot) $modbrowse_module "tag" } .modbrowse.bottom.buttons.modfuncs.branchtag configure -state normal \ -command { rtag_dialog $cvscfg(cvsroot) $modbrowse_module "branch" } .modbrowse.bottom.buttons.modfuncs.patchsummary configure -state normal \ -command { dialog_cvs_patch $cvscfg(cvsroot) $modbrowse_module 1 } .modbrowse.bottom.buttons.modfuncs.patchfile configure -state normal \ -command { dialog_cvs_patch $cvscfg(cvsroot) $modbrowse_module 0 } .modbrowse.bottom.buttons.svnfuncs.filecat configure -state disabled .modbrowse.bottom.buttons.svnfuncs.filelog configure -state disabled .modbrowse.bottom.buttons.svnfuncs.remove configure -state disabled .modbrowse.modmenu entryconfigure "CVS" -state normal .modbrowse.modmenu entryconfigure "SVN" -state disabled } if {$insvn || $incvs || $inrcs} { .modbrowse.bottom.buttons.cvsfuncs.import configure -state disabled } # Populate the tree if {$svnurl} { # Make sure branches and tags names come first, before any of their # contents, so we get the "# tags" and "# branches" labels set newlist "" foreach item [array names modval] { if {! ($item == $cvscfg(svn_branchdir) || $item == $cvscfg(svn_tagdir))} { lappend newlist $item } } set newlist [lsort $newlist] set newlist [concat {$cvscfg(svn_branchdir} {$cvscfg(svn_tagdir)} $newlist] } else { modbrowse_tree [lsort [array names modval]] "/" } busy_done .modbrowse gen_log:log T "LEAVE" } proc modbrowse_tree { mnames node } { # # Do this to update the display of the listbox (body proc). # global cvscfg global modval global modtitle global dcontents global Tree gen_log:log T "ENTER (... $node)" if {! [info exists cvscfg(aliasfolder)]} { set cvscfg(aliasfolder) false } set tf ".modbrowse.treeframe" foreach mname $mnames { #gen_log:log D "{$mname} {$modval($mname)}" set dimage "dir" # The descriptive title of the module. If not specified, modval is used. set title $modval($mname) if {[info exists modtitle($mname)]} { set title $modtitle($mname) #gen_log:log D "* modtitle($mname) {$title}" } if {[string match "-a *" $modval($mname)]} { # Its an alias module regsub {\-a } $modtitle($mname) "Alias for " title if {$cvscfg(aliasfolder)} { #gen_log:log D "path=Aliases/$mname pathtop=Aliases pathroot=/Aliases" if {! [info exists Tree($tf:/Aliases:children)]} { #gen_log:log D "Making Aliases" ModTree:newitem $tf /Aliases Aliases "Aliases" -image "adir" } ModTree:newitem $tf /Aliases/$mname $mname "$title" -image "amod" continue } set dimage amod } elseif {[string match "* *" $modval($mname)]} { # The value isn't a simple path #gen_log:log D "Found spaces in modval($mname) $modval($mname)" } elseif {[string match "*/*" $modval($mname)]} { #gen_log:log D "Set image to dir because $modval($mname) contains a slash" set dimage dir set path $modval($mname) if {[llength $modval($mname)] > 1} { regsub { &\S+} $path {} path } set pathitems [file split $path] set pathdepth [llength $pathitems] set pathtop [lindex [file split $path] 0] set pathroot [file join $node $pathtop] set pathroot "$pathroot" if {[info exists modtitle($pathtop)]} { set title $modtitle($pathtop) #gen_log:log D "* Using pathtop * modtitle($pathtop) {$title}" } elseif {[info exists modtitle($path)]} { set title $modtitle($path) #gen_log:log D "* Using path * modtitle($path) {$title}" } else { #gen_log:log D "* No modtitle($path)" } #gen_log:log D "path=$path pathtop=$pathtop pathroot=$pathroot" if {! [info exists Tree($tf:$pathroot:children)]} { #gen_log:log D "1 Making $pathtop for something with a \"/\" in its module name" if {[info exists modval($pathtop)]} { set dimage mdir } ModTree:newitem $tf $pathroot $pathtop "$title" -image $dimage } set pathroot "" for {set i 1} {$i < $pathdepth} {incr i} { set newnode [lindex $pathitems $i] set pathroot [file join $pathroot [lindex $pathitems [expr {$i -1} ]]] set newpath [file join "/" $pathroot $newnode] set namepath [string range $newpath 1 end] #gen_log:log D "* * mname=$mname namepath=$namepath pathroot=$pathroot newpath=$newpath newnode=$newnode" if {[info exists modtitle($namepath)]} { set title $modtitle($namepath) #gen_log:log D "* Using namepath * modtitle($namepath) {$title}" } elseif {[info exists modtitle($newnode)]} { set title $modtitle($newnode) #gen_log:log D "* Using newnode * modtitle($newnode) {$title}" } elseif {[info exists modtitle($mname)]} { set title $modtitle($mname) #gen_log:log D "* Using mname * modtitle($mname) {$title}" } else { #gen_log:log D "* * No modtitle($namepath)" } if {! [info exists Tree($tf:$newpath:children)]} { set modvalpath [file join "/" $modval($mname)] regsub { &\S+} $modvalpath {} modvalpath #gen_log:log D "* * mname=$mname modvalpath=$modvalpath newpath=$newpath newnode=$newnode" if {$modvalpath == $newpath} { set newnode $mname } set dimage dir #gen_log:log D "2 Making $newnode for an intermediate node" lappend dcontents($pathroot) $newnode if {[info exists modval($newnode)]} {set dimage mdir} ModTree:newitem $tf $newpath $newnode "$title" -image $dimage } } # If we got here we just did a leaf, so break out and dont put it # at the toplevel too. continue } set treepath [file join $node $mname] if {[info exists Tree($tf:$treepath:children)]} { #gen_log:log D " Already handled $treepath" continue } #gen_log:log D "3 Making $mname" if {[info exists modval($mname)] && ($dimage != "amod")} { set dimage mdir } ModTree:newitem $tf $treepath $mname $title -image $dimage } update idletasks gather_mod_index gen_log:log T "LEAVE" } proc module_exit { } { global cvscfg global cvs global cmd gen_log:log T "ENTER" if {[info exists cmd(cvs_co)]} { catch {$cmd(cvs_co)\::abort} catch {unset cmd(cvs_co)} } set pid [pid] set cwd [pwd] set sandbox [file join $cvscfg(tmpdir) cvstmpdir.$pid] if {[file isdirectory $sandbox]} { gen_log:log F "CD $sandbox" cd $sandbox set dirs {} foreach d [glob -nocomplain *] { lappend dirs $d } gen_log:log C "$cvs -Q release $dirs" catch {eval "exec $cvs -Q release $dirs"} # Doing it this way makes it pop up an error on windoze. # Very annoying. #set finish [exec::new "$cvs -Q release $dirs"] #$finish\::wait } cd $cwd gen_log:log F "CD [pwd]" ModTree:delitem .modbrowse.treeframe / set cvscfg(modgeom) [wm geometry .modbrowse] destroy .modbrowse catch {destroy .tooltips_wind} exit_cleanup 0 gen_log:log T "LEAVE" } proc module_changedir {new_dir} { # Make sure a directory exists before trying to cd to it global cwd global cvscfg global cvsglb gen_log:log T "ENTER ($new_dir)" #if {! [winfo exists .modbrowse]} { #modbrowse_setup #} if {[file exists $new_dir]} { cd $new_dir set cwd $new_dir gen_log:log F "CD [pwd]" foreach {incvs insvn inrcs} [cvsroot_check [pwd]] { break } # If this directory has a different cvsroot, redo the tree if {$incvs} { if {$cvscfg(cvsroot) != $cvsglb(root)} { set cvsglb(root) $cvscfg(cvsroot) modbrowse_run cvs } } elseif {$insvn} { if {$cvscfg(svnroot) != $cvsglb(root)} { set cvsglb(root) $cvscfg(svnroot) modbrowse_run svn } } #if {$insvn || $incvs || $inrcs} { #.modbrowse.bottom.buttons.cvsfuncs.import configure -state disabled #} else { #.modbrowse.bottom.buttons.cvsfuncs.import configure -state normal #} if {[winfo exists .workdir]} { ::picklist::used directory [pwd] setup_dir } } else { set cwd [pwd] cvsfail "Directory $new_dir doesn\'t exist!" .modbrowse } gen_log:log F "$cwd" gen_log:log T "LEAVE" } tkcvs-8.2.3.orig/INSTALL0000644000175000017500000000512111664612512013015 0ustar timtimTkCVS requires Tcl/Tk 8.4. Tkcvs and tkdiff expect to find a program called "wish" in the path. This can be a symbolic link to wish8.x or whatever is appropriate. Naturally, you also need CVS and diff. To install TkCVS, run "doinstall.tcl". The installer provides an entry containing the default path for installation, and it shows where it will put the various files, On Unix/Linux, the default location is /usr/local, and on Windows it's C:. If you want to put TkCVS somewhere else, type a new directory in the entry and hit return. doinstall.tcl can be run without the gui and with an argument specifying the destination. Usage: [sudo] doinstall.tcl [-nox] [destination] --- IMPORTANT: Configuration Files --- Look at /lib/tkcvs/tkcvs_def.tcl and see if there's anything you want to change. You may need to change the variables which specify how to invoke various programs and the location of the the temporary directory. You can also choose a default editor, specify colors for important tags, and set many other preferences. tkcvs_def.tcl will look for a file called site_def in the same directory it is in. That's a good place to put site-specific preferences such as tag colours, without having them overwritten the next time you update tkcvs. Also, you can put personal preferences in the .tkcvs file in your home directory. User interface preferences should usually go there. This is the order of precedence: ~/.tkcvs /lib/tkcvs/site_def /lib/tkcvs/tkcvs_def.tcl Some important preferences: cvscfg(editor) "xterm -e vi" cvscfg(shell) "xterm -name tkcvsxterm -n {TkCVS xterm}" -------------------------------------- On Windows: There's a Windows installer that includes diff.exe and tkdiff now. You still need to have Wish and CVS. Or, you can use the .gz source tarball and install as for unix/linux. In that case, you will need to find diff.exe somewhere. Make sure Wish, CVS, and diff are in your PATH. Also set CVSROOT. If your version of Windows doesn't set HOME, you should set that too. It isn't recommended to use Cygwin to set up TkCVS for Windows, unless you're using Cygwin versions of Wish and CVS also. The Windows version of Tcl won't understand Cygwin's paths. -------------------------------------- Aqua Tk on MacOSX: Just drop TkCVS.app and TkDiff.app into your Applications folder. No separate Wish shell is necessary. Pay attention to the .tkcvs file that's written in your home directory when TkCVS is run. You may want to specify a different editor or shell: set cvscfg(editor) /Applications/TextEdit.app/Contents/MacOS/TextEdit tkcvs-8.2.3.orig/FAQ.txt0000644000175000017500000002276011664612512013144 0ustar timtimQ1. I get an error that says "error in startup script-invalid command name "get_cde_params" while executing "get_cde_params" (file"usr/local/bin/tkcvs"line 1) Q2. The "Hide" and "Show" fields don't seem to do anything. Q3. How do I log in to a remote server? Q4. Can I use a diff tool other than tkdiff with tkcvs? Q5. How do I import a new module? Q6. I can't get the Module Browser to work with my CVSROOT/modules file! Q7. I haven't put the tkcvs extensions in my CVSROOT/modules file yet. Can I still use tkcvs to check out a project? Q8. What good are modules anyway? Q9. I'm seeing strange behavior - things just don't act right. Q10. How do you get the colors shown in your screenshots? Q11. Why is the Branch Browser so slow in Subversion? Q12. How can I set CVS_RSH before starting the application under Mac OSX? It does not seem like the application reads my .cshrc or my .tcshrc. Where do I set the environment I need? ----------------------------------------------------------------- Q1. I get an error that says "error in startup script-invalid command name "get_cde_params" while executing "get_cde_params" (file"usr/local/bin/tkcvs"line 1) A. Did you run doinstall.tcl? If yes, make sure you don't have a TCLROOT environment variable that's pointing to somewhere other than where it put tkcvs' .tcl files. If that's not the trouble, do "which tkcvs" or whatever tells you which tkcvs file you're executing. Look at that file and see what TclRoot is set to. Are the .tcl files in $TclRoot/tkcvs? Is there a tclIndex there? ----------------------------------------------------------------- Q2. The "Hide" and "Show" fields don't seem to do anything. A. "Hide" works exactly the way a .cvsignore file works. That is, it causes non-CVS files with the pattern to be ignored. It's meant for hiding .o files and such. Any file under CVS control will be listed anyway. "Show" is the inverse - it hides non-CVS files except for those with the pattern. ----------------------------------------------------------------- Q3. How do I log in to a remote server? A. There's no way to transmit a password through tkcvs, but luckily you don't have to. If it's a pserver, do "cvs login" once from the command line, then start tkcvs. CVS client saves your password (encrypted) in your .cvspass file. For remote access via ssh, you normally upload your public key to the remote machine so it won't ask for a password. (If you have a shell account, it goes in ~/.ssh/authorized_keys.) Then you set your environment like this: % setenv CVSROOT username@cvs.tkcvs.sourceforge.net:/cvsroot/project % setenv CVS_RSH ssh If you can't use authorized keys for some reason, you can use ssh-agent: % ssh-agent $SHELL % setenv CVSROOT username@cvs.tkcvs.sourceforge.net:/cvsroot/project % setenv CVS_RSH ssh % ssh-add (type passphrase) % tkcvs ----------------------------------------------------------------- Q4. Can I use a diff tool other than tkdiff with tkcvs? A. Yes, by changing cvscfg(tkdiff). You usually have to write a wrapper for your diff tool to get it to check out the versions, and and deal with its particular command-line options, which are probably different from tkdiff's. In the contrib directory, there is a gvim-wrapper called "cvsdiff" which can be used as-is or as a model for wrapping your favorite diff tool. ----------------------------------------------------------------- Q5. How do I import a new module? Get your new directory the way you want it. Cd into the directory. Press the big "Module Browser" button in the top part of the tkcvs UI. In the Module Browser, press the rightmost button on the bottom, the one that shows a folder and an up arrow. In the dialog that pops up, fill in a descriptive title for the module. This will be what you see in the right side of the Module Browser. OK the dialog. Several things happen now. The directory is imported, the CVSROOT/module file is updated, your original directory is saved as directory.orig, and the newly created module is checked out. When the dust clears, you should find the original Working Directory Browser showing the files in the newly created, checked out module. The most common cause of failure here is not having the proper permissions to write to the repository. ----------------------------------------------------------------- Q6. I can't get the Module Browser to work with my CVSROOT/modules file! A. Make sure the fields in your file are separated by TABS! Does your editor automatically de-tab when you save a file? (Hint: if your editor has syntax highlighting, use the Makefile mode.) Not quite every possible module arrangement that can be specified in the modules file can be reflected in the Module Browser. That's a little better in version 7.x than it was previously, but the tradeoff was that there's less freedom to create fictional directory trees. ----------------------------------------------------------------- Q7. I haven't put the tkcvs extensions in my CVSROOT/modules file yet. Can I still use tkcvs to check out a project? A. Yes. Start tkcvs. Open the Module Browser. It will be empty because there's no modules file, but type the name of the project in the "Module" entry. Press the Checkout button (the ball with the arrow). ----------------------------------------------------------------- Q8. What good are modules anyway? A. It's hard to see the utility of modules unless you have projects that have nested directories. Defining a directory as a module makes it behave as though it were at the top level of your repository. For example, the repository may contain documentation in a structure like this: manuals | | - programA - reference manual | | | |------appnotes | | - programB - reference manual | |------appnotes If I didn't use modules and I wanted to check out only the programA documents, I'd have to say "cvs co manuals/programA". But since I have a line in the CVSROOT/modules file that says programA manuals/programA I can say "cvs co programA". That may not help much in this simple example, but as the project tree gets deeper, it becomes handy. You can also make a module out of different directories that are not so obviously grouped, or make a module that includes some files in a directory and not others. ----------------------------------------------------------------- Q9. I'm seeing strange behavior - things just don't act right. A. Do you have a .cvsrc file or other .cvs* files lurking about? If you change cvs's behaviour in certain ways, it can trick tkcvs. Be especially careful of modifying "cvs log". ----------------------------------------------------------------- Q10. How do you get the colors shown in your screenshots? A. I put this in my ~/.tkcvs file: option add *Canvas.background #c3c3c3 option add *selectColor #ffec8b option add *Menu.background gray option add *Text.background gray92 option add *Entry.background gray92 ----------------------------------------------------------------- Q11. Why is the Branch Browser so slow in Subversion? A. It's because SVN doesn't have tags or branches, it only has copies. The brute-force method I came up with to reconstruct a diagram requires two repository accesses per tag. Open the trace window and take a look at what tkcvs is doing to build that diagram. It will give you something to watch while it's chugging, at least. If you convert a CVS repository to SVN, my recommendation would be to discard non-branch tags. If there are very many, most likely all but the most recent ones have value only as archaeological curiosities anyway. There's a cvscfg setting that determines how many tags are too many to process. I figured that would be different depending on how remote the repository is. There's also an option on the branch browser not to process non-branch tags at all. If you can't get rid of them, that's probably what you'll need to do. ----------------------------------------------------------------- Q12. How can I set CVS_RSH before starting the application under Mac OSX? It does not seem like the application reads my .cshrc or my .tcshrc. Where do I set the environment I need? A. It depends on how you start the Wish application. If you double-click on the Mac TkCVS.app that I distribute, your environment isn't preserved. For TkCVS, that can be helped by setting the environment variables you need in your ~/.tkcvs, using tcl syntax, like this: set env(CVS_RSH) ssh My favorite way of running tkcvs on the Mac is simply to install the platform-independent tkcvs in /sw/bin or someplace like that, and invoke it from the command line. With the full Mac version of Wish installed, there will be a /usr/bin/wish that invokes the Wish.app. (Install Wish and do "cat `which wish`" to see how that works.) Invoking a Wish app such as TkCVS in this way preserves your environment. Then too, you can just start X11 and use the X11 version of Tk. In fact, your .tcshrc might detect whether it's being sourced in an X11 environment ($TERM == xterm), and put /sw/bin/wish (the X11 version from Fink) in your PATH before /usr/bin/wish (the Aqua version) if it is. Then any Tk program that is invoked on the command line and starts by invoking wish will automatically start the correct version of wish.